From d8996b47b59bc8c55ed6da1b43060435b1459fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 5 Mar 2024 11:37:47 +0100 Subject: [PATCH 001/195] Implement DB proxy --- .env | 7 +- app/http.php | 1 + app/init.php | 14 ++- composer.json | 6 +- composer.lock | 243 +++++++++++++++++++++++++-------------------- docker-compose.yml | 59 ++++++++++- 6 files changed, 212 insertions(+), 118 deletions(-) diff --git a/.env b/.env index 09abb07be2..4e9f33ce4f 100644 --- a/.env +++ b/.env @@ -103,4 +103,9 @@ _APP_MESSAGE_SMS_TEST_DSN= _APP_MESSAGE_EMAIL_TEST_DSN= _APP_MESSAGE_PUSH_TEST_DSN= _APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10 -_APP_PROJECT_REGIONS=default \ No newline at end of file +_APP_PROJECT_REGIONS=default + +_APP_DATABASE_PROXY_SECRET=secret-key +_APP_DATABASE_PROXY_CONNECTION=mariadb://user:password@mariadb:3306/appwrite?pool_size=256 +_APP_CONNECTIONS_DB_PROJECT=managed=mariadb-proxy://secret-key@database-proxy/appwrite +_APP_CONNECTIONS_DB_CONSOLE=db_fra1_v14x_01=mariadb-proxy://secret-key@database-proxy/appwrite \ No newline at end of file diff --git a/app/http.php b/app/http.php index 086260cb62..bbb9834371 100644 --- a/app/http.php +++ b/app/http.php @@ -77,6 +77,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg try { $attempts++; $dbForConsole = $app->getResource('dbForConsole'); + $dbForConsole->ping(); /** @var Utopia\Database\Database $dbForConsole */ break; // leave the do-while if successful } catch (\Throwable $e) { diff --git a/app/init.php b/app/init.php index 0ce44fa181..2249d5d40a 100644 --- a/app/init.php +++ b/app/init.php @@ -83,6 +83,7 @@ use Utopia\Validator\IP; use Utopia\Validator\URL; use Utopia\Validator\WhiteList; use Utopia\CLI\Console; +use Utopia\Database\Adapter\MariaDBProxy; use Utopia\Domains\Validator\PublicDomain; const APP_NAME = 'Appwrite'; @@ -759,13 +760,13 @@ $register->set('pools', function () { 'type' => 'database', 'dsns' => App::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), 'multiple' => false, - 'schemes' => ['mariadb', 'mysql'], + 'schemes' => ['mariadb', 'mysql', 'mariadb-proxy'], ], 'database' => [ 'type' => 'database', 'dsns' => App::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), 'multiple' => true, - 'schemes' => ['mariadb', 'mysql'], + 'schemes' => ['mariadb', 'mysql', 'mariadb-proxy'], ], 'queue' => [ 'type' => 'queue', @@ -841,6 +842,14 @@ $register->set('pools', function () { * Resource assignment to an adapter will happen below. */ switch ($dsnScheme) { + case 'mariadb-proxy': + // Ignore port and password (user = password) + $resource = [ + 'endpoint' => 'http://' . $dsnHost . '/v1', + 'secret' => $dsnUser, + 'database' => $dsnDatabase + ]; + break; case 'mysql': case 'mariadb': $resource = function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { @@ -880,6 +889,7 @@ $register->set('pools', function () { $adapter = match ($dsn->getScheme()) { 'mariadb' => new MariaDB($resource()), 'mysql' => new MySQL($resource()), + 'mariadb-proxy' => new MariaDBProxy($resource['endpoint'], $resource['secret'], $resource['database']), default => null }; diff --git a/composer.json b/composer.json index 28dbde65be..6eb06efa50 100644 --- a/composer.json +++ b/composer.json @@ -44,13 +44,13 @@ "ext-sockets": "*", "appwrite/php-runtimes": "0.13.*", "appwrite/php-clamav": "2.0.*", - "utopia-php/abuse": "0.36.*", + "utopia-php/abuse": "dev-feat-database-proxy as 0.36.99", "utopia-php/analytics": "0.10.*", - "utopia-php/audit": "0.38.*", + "utopia-php/audit": "dev-feat-database-proxy as 0.38.99", "utopia-php/cache": "0.9.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.48.*", + "utopia-php/database": "0.49.*", "utopia-php/domains": "0.5.*", "utopia-php/dsn": "0.2.*", "utopia-php/framework": "0.33.*", diff --git a/composer.lock b/composer.lock index 79509e7d12..7b1d614660 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": "733cdc4128cc298318cf487442d3efed", + "content-hash": "76e3175bdffb16335168d35fc5322e8f", "packages": [ { "name": "adhocore/jwt", @@ -156,16 +156,16 @@ }, { "name": "appwrite/php-runtimes", - "version": "0.13.2", + "version": "0.13.3", "source": { "type": "git", "url": "https://github.com/appwrite/runtimes.git", - "reference": "214a37c2c66e0f2bc9c30fdfde66955d9fd084a1" + "reference": "5d93fc578a9a543bcdc9b2c0562d80a51d56c73d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/runtimes/zipball/214a37c2c66e0f2bc9c30fdfde66955d9fd084a1", - "reference": "214a37c2c66e0f2bc9c30fdfde66955d9fd084a1", + "url": "https://api.github.com/repos/appwrite/runtimes/zipball/5d93fc578a9a543bcdc9b2c0562d80a51d56c73d", + "reference": "5d93fc578a9a543bcdc9b2c0562d80a51d56c73d", "shasum": "" }, "require": { @@ -204,9 +204,9 @@ ], "support": { "issues": "https://github.com/appwrite/runtimes/issues", - "source": "https://github.com/appwrite/runtimes/tree/0.13.2" + "source": "https://github.com/appwrite/runtimes/tree/0.13.3" }, - "time": "2023-11-22T15:36:00+00:00" + "time": "2024-03-01T14:47:47+00:00" }, { "name": "beberlei/assert", @@ -1260,23 +1260,23 @@ }, { "name": "utopia-php/abuse", - "version": "0.36.0", + "version": "dev-feat-database-proxy", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "d3d09b4fa0db75935110714ad4b2a87f3ace31ed" + "reference": "5032d5220af527853e2d549e62be3f4ee2932db9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/d3d09b4fa0db75935110714ad4b2a87f3ace31ed", - "reference": "d3d09b4fa0db75935110714ad4b2a87f3ace31ed", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/5032d5220af527853e2d549e62be3f4ee2932db9", + "reference": "5032d5220af527853e2d549e62be3f4ee2932db9", "shasum": "" }, "require": { "ext-curl": "*", "ext-pdo": "*", "php": ">=8.0", - "utopia-php/database": "0.48.*" + "utopia-php/database": "0.49.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1303,9 +1303,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.36.0" + "source": "https://github.com/utopia-php/abuse/tree/feat-database-proxy" }, - "time": "2024-01-19T09:32:56+00:00" + "time": "2024-03-05T09:31:14+00:00" }, { "name": "utopia-php/analytics", @@ -1355,21 +1355,21 @@ }, { "name": "utopia-php/audit", - "version": "0.38.0", + "version": "dev-feat-database-proxy", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "a9067f4af76e8787f1d29850a8ec94fc32bb6539" + "reference": "973865837fd502f98e5762241ea0c0ebecf776f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/a9067f4af76e8787f1d29850a8ec94fc32bb6539", - "reference": "a9067f4af76e8787f1d29850a8ec94fc32bb6539", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/973865837fd502f98e5762241ea0c0ebecf776f2", + "reference": "973865837fd502f98e5762241ea0c0ebecf776f2", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.48.*" + "utopia-php/database": "0.49.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1396,9 +1396,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.38.0" + "source": "https://github.com/utopia-php/audit/tree/feat-database-proxy" }, - "time": "2024-01-19T09:33:05+00:00" + "time": "2024-03-05T09:31:19+00:00" }, { "name": "utopia-php/cache", @@ -1552,16 +1552,16 @@ }, { "name": "utopia-php/database", - "version": "0.48.4", + "version": "0.49.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "02f20bd901b8fab26d7dc2c58f7da1d6a08d21c0" + "reference": "02000f01e9329b92251825fdccde023feb88a915" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/02f20bd901b8fab26d7dc2c58f7da1d6a08d21c0", - "reference": "02f20bd901b8fab26d7dc2c58f7da1d6a08d21c0", + "url": "https://api.github.com/repos/utopia-php/database/zipball/02000f01e9329b92251825fdccde023feb88a915", + "reference": "02000f01e9329b92251825fdccde023feb88a915", "shasum": "" }, "require": { @@ -1569,6 +1569,7 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.9.*", + "utopia-php/fetch": "0.1.*", "utopia-php/framework": "0.33.*", "utopia-php/mongo": "0.3.*" }, @@ -1602,9 +1603,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.48.4" + "source": "https://github.com/utopia-php/database/tree/0.49.0" }, - "time": "2024-02-23T03:22:55+00:00" + "time": "2024-03-01T10:44:41+00:00" }, { "name": "utopia-php/domains", @@ -1713,6 +1714,45 @@ }, "time": "2023-11-02T12:01:43+00:00" }, + { + "name": "utopia-php/fetch", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/fetch.git", + "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/fetch/zipball/2fa214b9262acd1a3583515a364da4f35929d5c5", + "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.5.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Fetch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "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.1.0" + }, + "time": "2023-10-10T11:58:32+00:00" + }, { "name": "utopia-php/framework", "version": "0.33.2", @@ -3136,20 +3176,21 @@ }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", @@ -3190,9 +3231,15 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", @@ -3531,16 +3578,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.30", + "version": "9.2.31", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089" + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089", - "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", "shasum": "" }, "require": { @@ -3597,7 +3644,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" }, "funding": [ { @@ -3605,7 +3652,7 @@ "type": "github" } ], - "time": "2023-12-22T06:47:57+00:00" + "time": "2024-03-02T06:37:42+00:00" }, { "name": "phpunit/php-file-iterator", @@ -4003,16 +4050,16 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", "shasum": "" }, "require": { @@ -4047,7 +4094,7 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" }, "funding": [ { @@ -4055,7 +4102,7 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-03-02T06:27:43+00:00" }, { "name": "sebastian/code-unit", @@ -4301,16 +4348,16 @@ }, { "name": "sebastian/diff", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", "shasum": "" }, "require": { @@ -4355,7 +4402,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" }, "funding": [ { @@ -4363,7 +4410,7 @@ "type": "github" } ], - "time": "2023-05-07T05:35:17+00:00" + "time": "2024-03-02T06:30:58+00:00" }, { "name": "sebastian/environment", @@ -4430,16 +4477,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", "shasum": "" }, "require": { @@ -4495,7 +4542,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" }, "funding": [ { @@ -4503,20 +4550,20 @@ "type": "github" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2024-03-02T06:33:00+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.6", + "version": "5.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", "shasum": "" }, "require": { @@ -4559,7 +4606,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" }, "funding": [ { @@ -4567,7 +4614,7 @@ "type": "github" } ], - "time": "2023-08-02T09:26:13+00:00" + "time": "2024-03-02T06:35:11+00:00" }, { "name": "sebastian/lines-of-code", @@ -5287,16 +5334,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.2", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { @@ -5325,7 +5372,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.2" + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, "funding": [ { @@ -5333,7 +5380,7 @@ "type": "github" } ], - "time": "2023-11-20T00:12:19+00:00" + "time": "2024-03-03T12:36:25+00:00" }, { "name": "twig/twig", @@ -5406,50 +5453,26 @@ } ], "time": "2023-11-21T18:54:41+00:00" - }, - { - "name": "utopia-php/fetch", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/fetch.git", - "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/fetch/zipball/2fa214b9262acd1a3583515a364da4f35929d5c5", - "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "^1.5.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Fetch\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "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.1.0" - }, - "time": "2023-10-10T11:58:32+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/abuse", + "version": "dev-feat-database-proxy", + "alias": "0.36.99", + "alias_normalized": "0.36.99.0" + }, + { + "package": "utopia-php/audit", + "version": "dev-feat-database-proxy", + "alias": "0.38.99", + "alias_normalized": "0.38.99.0" + } + ], "minimum-stability": "stable", "stability-flags": { + "utopia-php/abuse": 20, + "utopia-php/audit": 20, "appwrite/sdk-generator": 5 }, "prefer-stable": false, @@ -5475,5 +5498,5 @@ "platform-overrides": { "php": "8.2" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/docker-compose.yml b/docker-compose.yml index eacee76fca..f82d920e93 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -117,6 +117,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -230,6 +232,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -260,6 +264,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -287,6 +293,8 @@ services: - _APP_OPENSSL_KEY_V1 - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -326,6 +334,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -378,6 +388,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -413,6 +425,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -480,6 +494,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -510,6 +526,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -582,6 +600,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -618,6 +638,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -651,6 +673,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -682,6 +706,8 @@ services: - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -713,6 +739,8 @@ services: - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -748,6 +776,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -775,6 +805,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -806,6 +838,8 @@ services: - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -838,6 +872,8 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_DB_HOST + - _APP_CONNECTIONS_DB_CONSOLE + - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -918,12 +954,29 @@ services: - OPR_PROXY_MAX_TIMEOUT=600 - OPR_PROXY_HEALTHCHECK=enabled + database-proxy: + container_name: database-proxy + image: appwrite/database-proxy:0.1.0 + build: + context: . + networks: + - appwrite + - database-proxy + ports: + - 9520:80 + environment: + - UTOPIA_DATA_API_ENV=$_APP_ENV + - UTOPIA_DATA_API_SECRET=$_APP_DATABASE_PROXY_SECRET + - UTOPIA_DATA_API_SECRET_CONNECTION=$_APP_DATABASE_PROXY_CONNECTION + - UTOPIA_DATA_API_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER + - UTOPIA_DATA_API_LOGGING_CONFIG=$_APP_LOGGING_CONFIG + mariadb: image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p container_name: appwrite-mariadb <<: *x-logging networks: - - appwrite + - database-proxy volumes: - appwrite-mariadb:/var/lib/mysql:rw ports: @@ -1010,7 +1063,7 @@ services: ports: - 9506:8080 networks: - - appwrite + - database-proxy redis-insight: image: redis/redisinsight:latest @@ -1040,6 +1093,8 @@ networks: name: gateway appwrite: name: appwrite + database-proxy: + name: database-proxy runtimes: name: runtimes From e807a5d20a419c78ec5104e30227e7057426d96a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 5 Mar 2024 13:41:26 +0000 Subject: [PATCH 002/195] Upgrade DB proxy --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index f82d920e93..459826bb7d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -956,7 +956,7 @@ services: database-proxy: container_name: database-proxy - image: appwrite/database-proxy:0.1.0 + image: appwrite/database-proxy:0.1.3 build: context: . networks: From a9731cd5bc2d5c117279582defb5c9fdaec53d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 6 Mar 2024 12:38:51 +0100 Subject: [PATCH 003/195] Fix account tests --- .env | 2 +- app/controllers/api/account.php | 14 +++++++------- app/controllers/shared/api.php | 3 +++ composer.lock | 24 ++++++++++++------------ docker-compose.yml | 4 +--- 5 files changed, 24 insertions(+), 23 deletions(-) diff --git a/.env b/.env index 4e9f33ce4f..ed0ea7f160 100644 --- a/.env +++ b/.env @@ -106,6 +106,6 @@ _APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10 _APP_PROJECT_REGIONS=default _APP_DATABASE_PROXY_SECRET=secret-key -_APP_DATABASE_PROXY_CONNECTION=mariadb://user:password@mariadb:3306/appwrite?pool_size=256 +_APP_DATABASE_PROXY_CONNECTION=mariadb://user:password@mariadb:3306/appwrite?pool_size=128 _APP_CONNECTIONS_DB_PROJECT=managed=mariadb-proxy://secret-key@database-proxy/appwrite _APP_CONNECTIONS_DB_CONSOLE=db_fra1_v14x_01=mariadb-proxy://secret-key@database-proxy/appwrite \ No newline at end of file diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 1f16a696c1..7ce05fa848 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -775,15 +775,15 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - $userDoc = Authorization::skip(fn() => $dbForProject->createDocument('users', $user)); + $user = Authorization::skip(fn() => $dbForProject->createDocument('users', $user)); $dbForProject->createDocument('targets', new Document([ '$permissions' => [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), Permission::delete(Role::user($user->getId())), ], - 'userId' => $userDoc->getId(), - 'userInternalId' => $userDoc->getInternalId(), + 'userId' => $user->getId(), + 'userInternalId' => $user->getInternalId(), 'providerType' => MESSAGE_TYPE_EMAIL, 'identifier' => $email, ])); @@ -1162,7 +1162,7 @@ App::post('/v1/account/tokens/magic-url') ]); $user->removeAttribute('$internalId'); - Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); + $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); } $tokenSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_MAGIC_URL); @@ -1401,7 +1401,7 @@ App::post('/v1/account/tokens/email') ]); $user->removeAttribute('$internalId'); - Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); + $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); } $tokenSecret = Auth::codeGenerator(6); @@ -1813,7 +1813,7 @@ App::post('/v1/account/tokens/phone') ]); $user->removeAttribute('$internalId'); - Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); + $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); try { $target = Authorization::skip(fn() => $dbForProject->createDocument('targets', new Document([ '$permissions' => [ @@ -1980,7 +1980,7 @@ App::post('/v1/account/sessions/anonymous') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - Authorization::skip(fn() => $dbForProject->createDocument('users', $user)); + $user = Authorization::skip(fn() => $dbForProject->createDocument('users', $user)); // Create session token $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index ce7041cac1..2fb8448edb 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -535,6 +535,9 @@ App::shutdown() ->inject('mode') ->inject('dbForConsole') ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole) use ($parseLabel) { + if (empty($user) || $user->isEmpty() || empty($user->getInternalId())) { + $user = Authorization::skip(fn () => $dbForProject->getDocument('users', $queueForEvents->getParam('userId'))); + } $responsePayload = $response->getPayload(); diff --git a/composer.lock b/composer.lock index 7b1d614660..05feae35ee 100644 --- a/composer.lock +++ b/composer.lock @@ -1552,16 +1552,16 @@ }, { "name": "utopia-php/database", - "version": "0.49.0", + "version": "0.49.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "02000f01e9329b92251825fdccde023feb88a915" + "reference": "4199fe8f00f4e181c7782c4a6862845d591c1f03" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/02000f01e9329b92251825fdccde023feb88a915", - "reference": "02000f01e9329b92251825fdccde023feb88a915", + "url": "https://api.github.com/repos/utopia-php/database/zipball/4199fe8f00f4e181c7782c4a6862845d591c1f03", + "reference": "4199fe8f00f4e181c7782c4a6862845d591c1f03", "shasum": "" }, "require": { @@ -1603,9 +1603,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.49.0" + "source": "https://github.com/utopia-php/database/tree/0.49.1" }, - "time": "2024-03-01T10:44:41+00:00" + "time": "2024-03-06T11:35:53+00:00" }, { "name": "utopia-php/domains", @@ -3118,16 +3118,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.0.1", + "version": "v5.0.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "2218c2252c874a4624ab2f613d86ac32d227bc69" + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/2218c2252c874a4624ab2f613d86ac32d227bc69", - "reference": "2218c2252c874a4624ab2f613d86ac32d227bc69", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", "shasum": "" }, "require": { @@ -3170,9 +3170,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" }, - "time": "2024-02-21T19:24:10+00:00" + "time": "2024-03-05T20:51:40+00:00" }, { "name": "phar-io/manifest", diff --git a/docker-compose.yml b/docker-compose.yml index 459826bb7d..439988fba2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -956,9 +956,7 @@ services: database-proxy: container_name: database-proxy - image: appwrite/database-proxy:0.1.3 - build: - context: . + image: appwrite/database-proxy:0.1.5 networks: - appwrite - database-proxy From 882d8e5a889f18c919401ab219ba4a95a372528b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 6 Mar 2024 16:19:23 +0100 Subject: [PATCH 004/195] Fix database tests --- .env | 4 ++-- app/controllers/shared/api.php | 4 ++-- tests/e2e/Services/Databases/DatabasesCustomClientTest.php | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.env b/.env index ed0ea7f160..fa8059a4a8 100644 --- a/.env +++ b/.env @@ -107,5 +107,5 @@ _APP_PROJECT_REGIONS=default _APP_DATABASE_PROXY_SECRET=secret-key _APP_DATABASE_PROXY_CONNECTION=mariadb://user:password@mariadb:3306/appwrite?pool_size=128 -_APP_CONNECTIONS_DB_PROJECT=managed=mariadb-proxy://secret-key@database-proxy/appwrite -_APP_CONNECTIONS_DB_CONSOLE=db_fra1_v14x_01=mariadb-proxy://secret-key@database-proxy/appwrite \ No newline at end of file +_APP_CONNECTIONS_DB_PROJECT=db_main=mariadb-proxy://secret-key@database-proxy/appwrite +_APP_CONNECTIONS_DB_CONSOLE=db_main=mariadb-proxy://secret-key@database-proxy/appwrite \ No newline at end of file diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 2fb8448edb..e25e47f617 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -535,8 +535,8 @@ App::shutdown() ->inject('mode') ->inject('dbForConsole') ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole) use ($parseLabel) { - if (empty($user) || $user->isEmpty() || empty($user->getInternalId())) { - $user = Authorization::skip(fn () => $dbForProject->getDocument('users', $queueForEvents->getParam('userId'))); + if (!empty($user) && !$user->isEmpty() && empty($user->getInternalId())) { + $user = Authorization::skip(fn () => $dbForProject->getDocument('users', $user->getId())); } $responsePayload = $response->getPayload(); diff --git a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php index f91fd4ff26..fb96e0b0a1 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php @@ -64,9 +64,10 @@ class DatabasesCustomClientTest extends Scope 'required' => true, ]); + $this->assertEquals(202, $response['headers']['status-code']); + sleep(1); - $this->assertEquals(202, $response['headers']['status-code']); // Document aliases write to update, delete $document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', array_merge([ @@ -82,6 +83,7 @@ class DatabasesCustomClientTest extends Scope ] ]); + $this->assertEquals(201, $document1['headers']['status-code']); $this->assertNotContains(Permission::create(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']); $this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']); $this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']); From f83d6e61aefa4b4f7fbbe4c58ac295e10d3aeb3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 6 Mar 2024 17:01:43 +0100 Subject: [PATCH 005/195] PR review changes --- .env | 12 +++++------- app/init.php | 11 ++++++++--- docker-compose.yml | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/.env b/.env index fa8059a4a8..0ff7c34a16 100644 --- a/.env +++ b/.env @@ -22,8 +22,9 @@ _APP_REDIS_HOST=redis _APP_REDIS_PORT=6379 _APP_REDIS_PASS= _APP_REDIS_USER= -_APP_DB_HOST=mariadb -_APP_DB_PORT=3306 +_APP_DB_ADAPTER=mariadb-proxy +_APP_DB_HOST=database-proxy +_APP_DB_PORT=80 _APP_DB_SCHEMA=appwrite _APP_DB_USER=user _APP_DB_PASS=password @@ -104,8 +105,5 @@ _APP_MESSAGE_EMAIL_TEST_DSN= _APP_MESSAGE_PUSH_TEST_DSN= _APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10 _APP_PROJECT_REGIONS=default - -_APP_DATABASE_PROXY_SECRET=secret-key -_APP_DATABASE_PROXY_CONNECTION=mariadb://user:password@mariadb:3306/appwrite?pool_size=128 -_APP_CONNECTIONS_DB_PROJECT=db_main=mariadb-proxy://secret-key@database-proxy/appwrite -_APP_CONNECTIONS_DB_CONSOLE=db_main=mariadb-proxy://secret-key@database-proxy/appwrite \ No newline at end of file +_APP_DATABASE_PROXY_SECRET=password +_APP_DATABASE_PROXY_CONNECTION=mariadb://user:password@mariadb:3306/appwrite?pool_size=128 \ No newline at end of file diff --git a/app/init.php b/app/init.php index 2249d5d40a..96fce26d41 100644 --- a/app/init.php +++ b/app/init.php @@ -740,7 +740,7 @@ $register->set('pools', function () { $group = new Group(); $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ - 'scheme' => 'mariadb', + 'scheme' => App::getEnv('_APP_DB_ADAPTER', 'mariadb'), 'host' => App::getEnv('_APP_DB_HOST', 'mariadb'), 'port' => App::getEnv('_APP_DB_PORT', '3306'), 'user' => App::getEnv('_APP_DB_USER', ''), @@ -843,10 +843,15 @@ $register->set('pools', function () { */ switch ($dsnScheme) { case 'mariadb-proxy': + $host = $dsnHost; + if($dsnPort) { + $host .= ':' . $dsnPort; + } + // Ignore port and password (user = password) $resource = [ - 'endpoint' => 'http://' . $dsnHost . '/v1', - 'secret' => $dsnUser, + 'endpoint' => 'http://' . $host . '/v1', + 'secret' => $dsnPass, 'database' => $dsnDatabase ]; break; diff --git a/docker-compose.yml b/docker-compose.yml index 439988fba2..bd6b1af583 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -116,6 +116,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -231,6 +232,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -263,6 +265,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -292,6 +295,7 @@ services: - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -333,6 +337,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -387,6 +392,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -424,6 +430,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -493,6 +500,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -525,6 +533,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -599,6 +608,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -637,6 +647,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -672,6 +683,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -705,6 +717,7 @@ services: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -738,6 +751,7 @@ services: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -775,6 +789,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -804,6 +819,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -837,6 +853,7 @@ services: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT @@ -871,6 +888,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT From 4fa9ccd9e42f4be4dae572c755a57eca0a08324e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 6 Mar 2024 22:25:43 +0100 Subject: [PATCH 006/195] upgrade libs --- composer.json | 4 ++-- composer.lock | 43 ++++++++++++++----------------------------- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/composer.json b/composer.json index 6eb06efa50..7137f8a434 100644 --- a/composer.json +++ b/composer.json @@ -44,9 +44,9 @@ "ext-sockets": "*", "appwrite/php-runtimes": "0.13.*", "appwrite/php-clamav": "2.0.*", - "utopia-php/abuse": "dev-feat-database-proxy as 0.36.99", + "utopia-php/abuse": "0.37.*", "utopia-php/analytics": "0.10.*", - "utopia-php/audit": "dev-feat-database-proxy as 0.38.99", + "utopia-php/audit": "0.39.*", "utopia-php/cache": "0.9.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", diff --git a/composer.lock b/composer.lock index 05feae35ee..fce79ef77e 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": "76e3175bdffb16335168d35fc5322e8f", + "content-hash": "e23977abd3961338f79e291bdf8124e1", "packages": [ { "name": "adhocore/jwt", @@ -1260,16 +1260,16 @@ }, { "name": "utopia-php/abuse", - "version": "dev-feat-database-proxy", + "version": "0.37.0", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "5032d5220af527853e2d549e62be3f4ee2932db9" + "reference": "2de5c12886cbd516e511e559afdd9e615d871062" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/5032d5220af527853e2d549e62be3f4ee2932db9", - "reference": "5032d5220af527853e2d549e62be3f4ee2932db9", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/2de5c12886cbd516e511e559afdd9e615d871062", + "reference": "2de5c12886cbd516e511e559afdd9e615d871062", "shasum": "" }, "require": { @@ -1303,9 +1303,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/feat-database-proxy" + "source": "https://github.com/utopia-php/abuse/tree/0.37.0" }, - "time": "2024-03-05T09:31:14+00:00" + "time": "2024-03-06T21:20:27+00:00" }, { "name": "utopia-php/analytics", @@ -1355,16 +1355,16 @@ }, { "name": "utopia-php/audit", - "version": "dev-feat-database-proxy", + "version": "0.39.0", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "973865837fd502f98e5762241ea0c0ebecf776f2" + "reference": "f0bc15012e05cc0b9dde012ab27d25f193768a2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/973865837fd502f98e5762241ea0c0ebecf776f2", - "reference": "973865837fd502f98e5762241ea0c0ebecf776f2", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/f0bc15012e05cc0b9dde012ab27d25f193768a2c", + "reference": "f0bc15012e05cc0b9dde012ab27d25f193768a2c", "shasum": "" }, "require": { @@ -1396,9 +1396,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/feat-database-proxy" + "source": "https://github.com/utopia-php/audit/tree/0.39.0" }, - "time": "2024-03-05T09:31:19+00:00" + "time": "2024-03-06T21:20:37+00:00" }, { "name": "utopia-php/cache", @@ -5455,24 +5455,9 @@ "time": "2023-11-21T18:54:41+00:00" } ], - "aliases": [ - { - "package": "utopia-php/abuse", - "version": "dev-feat-database-proxy", - "alias": "0.36.99", - "alias_normalized": "0.36.99.0" - }, - { - "package": "utopia-php/audit", - "version": "dev-feat-database-proxy", - "alias": "0.38.99", - "alias_normalized": "0.38.99.0" - } - ], + "aliases": [], "minimum-stability": "stable", "stability-flags": { - "utopia-php/abuse": 20, - "utopia-php/audit": 20, "appwrite/sdk-generator": 5 }, "prefer-stable": false, From 1b65c5fbc9585069fec28d374921e7de9fc2ffde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 6 Mar 2024 22:26:14 +0100 Subject: [PATCH 007/195] linter fix --- app/init.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/init.php b/app/init.php index 96fce26d41..04ef2c2d9f 100644 --- a/app/init.php +++ b/app/init.php @@ -844,7 +844,7 @@ $register->set('pools', function () { switch ($dsnScheme) { case 'mariadb-proxy': $host = $dsnHost; - if($dsnPort) { + if ($dsnPort) { $host .= ':' . $dsnPort; } From 27e995eb53be3a69938833cd4e76e99f36c8f27d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 6 Mar 2024 22:31:23 +0100 Subject: [PATCH 008/195] Reomve leftpover --- docker-compose.yml | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index bd6b1af583..3d9108cab3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -118,8 +118,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -234,8 +232,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -267,8 +263,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -297,8 +291,6 @@ services: - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -339,8 +331,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -394,8 +384,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -432,8 +420,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -502,8 +488,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -535,8 +519,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -610,8 +592,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -649,8 +629,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -685,8 +663,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -719,8 +695,6 @@ services: - _APP_OPENSSL_KEY_V1 - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -753,8 +727,6 @@ services: - _APP_OPENSSL_KEY_V1 - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -791,8 +763,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -821,8 +791,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -855,8 +823,6 @@ services: - _APP_OPENSSL_KEY_V1 - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER @@ -890,8 +856,6 @@ services: - _APP_REDIS_PASS - _APP_DB_ADAPTER - _APP_DB_HOST - - _APP_CONNECTIONS_DB_CONSOLE - - _APP_CONNECTIONS_DB_PROJECT - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER From 8a8638a81776d71da308fbe2ad7e462308560b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 7 Mar 2024 10:20:24 +0100 Subject: [PATCH 009/195] Formatting fix --- app/init.php | 6 +- composer.lock | 148 +++++++++++++++++++++++--------------------------- 2 files changed, 70 insertions(+), 84 deletions(-) diff --git a/app/init.php b/app/init.php index 34194244a4..8f8bdb9c66 100644 --- a/app/init.php +++ b/app/init.php @@ -48,8 +48,10 @@ use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; +use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Adapter\MariaDB; +use Utopia\Database\Adapter\MariaDBProxy; use Utopia\Database\Adapter\MySQL; use Utopia\Database\Adapter\SQL; use Utopia\Database\Database; @@ -60,6 +62,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Structure; use Utopia\Domains\Validator\PublicDomain; +use Utopia\Domains\Validator\PublicDomain; use Utopia\DSN\DSN; use Utopia\Locale\Locale; use Utopia\Logger\Log; @@ -82,9 +85,6 @@ use Utopia\Validator\IP; use Utopia\Validator\Range; use Utopia\Validator\URL; use Utopia\Validator\WhiteList; -use Utopia\CLI\Console; -use Utopia\Database\Adapter\MariaDBProxy; -use Utopia\Domains\Validator\PublicDomain; use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; const APP_NAME = 'Appwrite'; diff --git a/composer.lock b/composer.lock index fce79ef77e..0f02859947 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": "e23977abd3961338f79e291bdf8124e1", + "content-hash": "03de7dbb57fcecac386e5b710ec5fe49", "packages": [ { "name": "adhocore/jwt", @@ -2933,6 +2933,72 @@ ], "time": "2022-12-30T00:15:36+00:00" }, + { + "name": "laravel/pint", + "version": "v1.14.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "6b127276e3f263f7bb17d5077e9e0269e61b2a0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/6b127276e3f263f7bb17d5077e9e0269e61b2a0e", + "reference": "6b127276e3f263f7bb17d5077e9e0269e61b2a0e", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.49.0", + "illuminate/view": "^10.43.0", + "larastan/larastan": "^2.8.1", + "laravel-zero/framework": "^10.3.0", + "mockery/mockery": "^1.6.7", + "nunomaduro/termwind": "^1.15.1", + "pestphp/pest": "^2.33.6" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2024-02-20T17:38:05+00:00" + }, { "name": "matthiasmullie/minify", "version": "1.3.71", @@ -5012,86 +5078,6 @@ ], "time": "2020-09-28T06:39:44+00:00" }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.9.0", - "source": { - "type": "git", - "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/d63cee4890a8afaf86a22e51ad4d97c91dd4579b", - "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" - }, - "bin": [ - "bin/phpcbf", - "bin/phpcs" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "Former lead" - }, - { - "name": "Juliette Reinders Folmer", - "role": "Current lead" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", - "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", - "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", - "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" - }, - "funding": [ - { - "url": "https://github.com/PHPCSStandards", - "type": "github" - }, - { - "url": "https://github.com/jrfnl", - "type": "github" - }, - { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" - } - ], - "time": "2024-02-16T15:06:51+00:00" - }, { "name": "swoole/ide-helper", "version": "5.0.2", From 5a7c43ab32f352bb3424f39b7e67e93b0b4796ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 7 Mar 2024 10:59:08 +0100 Subject: [PATCH 010/195] Introduce code analysis --- .github/workflows/codeql-phpstan.yml | 16 +++++ app/cli.php | 2 + app/init.php | 2 - app/worker.php | 2 + composer.json | 6 +- composer.lock | 61 ++++++++++++++++++- phpstan.neon | 11 ++++ src/Appwrite/Auth/OAuth2/Autodesk.php | 2 +- src/Appwrite/Event/Func.php | 4 +- src/Appwrite/Platform/Tasks/SDKs.php | 4 +- src/Appwrite/Platform/Workers/Builds.php | 14 ++--- src/Appwrite/Platform/Workers/Messaging.php | 4 +- src/Appwrite/Promises/Promise.php | 2 +- src/Appwrite/Promises/Swoole.php | 5 -- .../Specification/Format/OpenAPI3.php | 7 ++- src/Appwrite/Utopia/Request/Filters/V14.php | 3 + src/Appwrite/Utopia/Response/Filters/V11.php | 1 + src/Appwrite/Utopia/Response/Filters/V12.php | 2 - .../Services/GraphQL/StorageClientTest.php | 1 - .../Services/GraphQL/StorageServerTest.php | 1 - 20 files changed, 119 insertions(+), 31 deletions(-) create mode 100644 .github/workflows/codeql-phpstan.yml create mode 100644 phpstan.neon diff --git a/.github/workflows/codeql-phpstan.yml b/.github/workflows/codeql-phpstan.yml new file mode 100644 index 0000000000..3253e2c38b --- /dev/null +++ b/.github/workflows/codeql-phpstan.yml @@ -0,0 +1,16 @@ +name: "CodeQL" + +on: [pull_request] +jobs: + lint: + name: CodeQL + runs-on: ubuntu-latest + + steps: + - name: Check out the repo + uses: actions/checkout@v2 + + - name: Run CodeQL + run: | + docker run --rm -v $PWD:/app composer sh -c \ + "composer install --profile --ignore-platform-reqs && composer check" \ No newline at end of file diff --git a/app/cli.php b/app/cli.php index 1a8c785a30..11b952dadc 100644 --- a/app/cli.php +++ b/app/cli.php @@ -23,6 +23,8 @@ use Utopia\Pools\Group; use Utopia\Queue\Connection; use Utopia\Registry\Registry; +global $register; + Authorization::disable(); CLI::setResource('register', fn () => $register); diff --git a/app/init.php b/app/init.php index 8f8bdb9c66..c068209613 100644 --- a/app/init.php +++ b/app/init.php @@ -48,7 +48,6 @@ use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; -use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Adapter\MariaDBProxy; @@ -62,7 +61,6 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Structure; use Utopia\Domains\Validator\PublicDomain; -use Utopia\Domains\Validator\PublicDomain; use Utopia\DSN\DSN; use Utopia\Locale\Locale; use Utopia\Logger\Log; diff --git a/app/worker.php b/app/worker.php index 98a1e40a88..8523b81cdb 100644 --- a/app/worker.php +++ b/app/worker.php @@ -36,6 +36,8 @@ use Utopia\Queue\Server; use Utopia\Registry\Registry; use Utopia\Storage\Device\Local; +global $register; + Authorization::disable(); Runtime::enableCoroutine(SWOOLE_HOOK_ALL); diff --git a/composer.json b/composer.json index ad17831252..96bf4dba43 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,8 @@ "scripts": { "test": "vendor/bin/phpunit", "lint": "vendor/bin/pint --test", - "format": "vendor/bin/pint" + "format": "vendor/bin/pint", + "check": "./vendor/bin/phpstan analyse -c phpstan.neon --memory-limit 1G app src tests" }, "autoload": { "psr-4": { @@ -84,7 +85,8 @@ "swoole/ide-helper": "5.0.2", "textalk/websocket": "1.5.7", "utopia-php/fetch": "0.1.*", - "laravel/pint": "^1.14" + "laravel/pint": "^1.14", + "phpstan/phpstan": "1.8.*" }, "provide": { "ext-phpiredis": "*" diff --git a/composer.lock b/composer.lock index 0f02859947..801b87c539 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": "03de7dbb57fcecac386e5b710ec5fe49", + "content-hash": "1c3c0b518e1486c5770b57519da2a797", "packages": [ { "name": "adhocore/jwt", @@ -3642,6 +3642,65 @@ }, "time": "2024-02-23T16:05:55+00:00" }, + { + "name": "phpstan/phpstan", + "version": "1.8.11", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "46e223dd68a620da18855c23046ddb00940b4014" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014", + "reference": "46e223dd68a620da18855c23046ddb00940b4014", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.8.11" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-10-24T15:45:13+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "9.2.31", diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000000..25771ef17c --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,11 @@ +parameters: + level: 0 + scanDirectories: + - vendor/swoole/ide-helper + excludePaths: + - tests/resources + ignoreErrors: + - '#Parameter \$geodb of anonymous function has invalid type MaxMind\\Db\\Reader\.#' + - '#Parameter \$geodb of function router\(\) has invalid type MaxMind\\Db\\Reader\.#' + - '#Instantiated class MaxMind\\Db\\Reader not found.#' + - '#Function scrypt not found\.#' diff --git a/src/Appwrite/Auth/OAuth2/Autodesk.php b/src/Appwrite/Auth/OAuth2/Autodesk.php index 0b268ead3b..9d99791962 100644 --- a/src/Appwrite/Auth/OAuth2/Autodesk.php +++ b/src/Appwrite/Auth/OAuth2/Autodesk.php @@ -88,7 +88,7 @@ class Autodesk extends OAuth2 'client_id' => $this->appID, 'client_secret' => $this->appSecret, 'grant_type' => 'refresh_token', - 'code' => $code, + 'code' => $refreshToken, 'redirect_uri' => $this->callback, ]) ); diff --git a/src/Appwrite/Event/Func.php b/src/Appwrite/Event/Func.php index 11c9e980ed..bdc5cd7cc9 100644 --- a/src/Appwrite/Event/Func.php +++ b/src/Appwrite/Event/Func.php @@ -152,9 +152,9 @@ class Func extends Event * * @return string */ - public function getData(): string + public function getBody(): string { - return $this->data; + return $this->body; } /** diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index 014e33109e..b3fb96808b 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -50,7 +50,7 @@ class SDKs extends Action $message = ($git) ? Console::confirm('Please enter your commit message:') : ''; if (!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', '0.14.x', '0.15.x', '1.0.x', '1.1.x', '1.2.x', '1.3.x', '1.4.x', '1.5.x', 'latest'])) { - throw new Exception('Unknown version given'); + throw new \Exception('Unknown version given'); } foreach ($platforms as $key => $platform) { @@ -196,7 +196,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND $config = new REST(); break; default: - throw new Exception('Language "' . $language['key'] . '" not supported'); + throw new \Exception('Language "' . $language['key'] . '" not supported'); } Console::info("Generating {$language['name']} SDK..."); diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index b9f02a8f67..31bf961c30 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -73,7 +73,7 @@ class Builds extends Action $payload = $message->getPayload() ?? []; if (empty($payload)) { - throw new Exception('Missing payload'); + throw new \Exception('Missing payload'); } $type = $payload['type'] ?? ''; @@ -124,7 +124,7 @@ class Builds extends Action $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404); + throw new \Exception('Function not found', 404); } $deploymentId = $deployment->getId(); @@ -132,11 +132,11 @@ class Builds extends Action $deployment = $dbForProject->getDocument('deployments', $deploymentId); if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404); + throw new \Exception('Deployment not found', 404); } if (empty($deployment->getAttribute('entrypoint', ''))) { - throw new Exception('Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 500); + throw new \Exception('Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 500); } $version = $function->getAttribute('version', 'v2'); @@ -144,7 +144,7 @@ class Builds extends Action $key = $function->getAttribute('runtime'); $runtime = $runtimes[$key] ?? null; if (\is_null($runtime)) { - throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); + throw new \Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } // Realtime preparation @@ -306,7 +306,7 @@ class Builds extends Action $directorySize = $localDevice->getDirectorySize($tmpDirectory); $functionsSizeLimit = (int) App::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000'); if ($directorySize > $functionsSizeLimit) { - throw new Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.'); + throw new \Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.'); } Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr); @@ -431,7 +431,7 @@ class Builds extends Action $build = $dbForProject->getDocument('builds', $build->getId()); if ($build->isEmpty()) { - throw new Exception('Build not found', 404); + throw new \Exception('Build not found', 404); } $build = $build->setAttribute('logs', $build->getAttribute('logs', '') . $logs); diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 6ebedd41a3..e94f9b495f 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -25,7 +25,7 @@ use Utopia\Messaging\Adapter\SMS as SMSAdapter; use Utopia\Messaging\Adapter\SMS\Mock; use Utopia\Messaging\Adapter\SMS\Msg91; use Utopia\Messaging\Adapter\SMS\Telesign; -use Utopia\Messaging\Adapter\SMS\Textmagic; +use Utopia\Messaging\Adapter\SMS\TextMagic; use Utopia\Messaging\Adapter\SMS\Twilio; use Utopia\Messaging\Adapter\SMS\Vonage; use Utopia\Messaging\Messages\Email; @@ -456,7 +456,7 @@ class Messaging extends Action return match ($provider->getAttribute('provider')) { 'mock' => new Mock('username', 'password'), 'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken']), - 'textmagic' => new Textmagic($credentials['username'], $credentials['apiKey']), + 'textmagic' => new TextMagic($credentials['username'], $credentials['apiKey']), 'telesign' => new Telesign($credentials['customerId'], $credentials['apiKey']), 'msg91' => new Msg91($credentials['senderId'], $credentials['authKey'], $credentials['templateId']), 'vonage' => new Vonage($credentials['apiKey'], $credentials['apiSecret']), diff --git a/src/Appwrite/Promises/Promise.php b/src/Appwrite/Promises/Promise.php index a6b1aa79d5..f12590dfed 100644 --- a/src/Appwrite/Promises/Promise.php +++ b/src/Appwrite/Promises/Promise.php @@ -12,7 +12,7 @@ abstract class Promise private mixed $result; - public function __construct(?callable $executor = null) + final public function __construct(?callable $executor = null) { if (\is_null($executor)) { return; diff --git a/src/Appwrite/Promises/Swoole.php b/src/Appwrite/Promises/Swoole.php index c258ef6a5e..d0b3eb8855 100644 --- a/src/Appwrite/Promises/Swoole.php +++ b/src/Appwrite/Promises/Swoole.php @@ -6,11 +6,6 @@ use Swoole\Coroutine\Channel; class Swoole extends Promise { - public function __construct(?callable $executor = null) - { - parent::__construct($executor); - } - protected function execute( callable $executor, callable $resolve, diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index bc85d8e507..4442dd9a0b 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -238,8 +238,11 @@ class OpenAPI3 extends Format } if ($route->getLabel('sdk.response.code', 500) === 204) { - $temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['description'] = 'No content'; - unset($temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['schema']); + $labelCode = (string)$route->getLabel('sdk.response.code', '500'); + $temp['responses'][$labelCode]['description'] = 'No content'; + if(isset($temp['responses'][$labelCode]['schema'])) { + unset($temp['responses'][$labelCode]['schema']); + } } if ((!empty($scope))) { // && 'public' != $scope diff --git a/src/Appwrite/Utopia/Request/Filters/V14.php b/src/Appwrite/Utopia/Request/Filters/V14.php index f42a351024..40fd3ebe86 100644 --- a/src/Appwrite/Utopia/Request/Filters/V14.php +++ b/src/Appwrite/Utopia/Request/Filters/V14.php @@ -24,11 +24,14 @@ class V14 extends Filter private function convertEvents($content) { + // TODO: If nessessary, implement V13 and use following code: + /* $migration = new MigrationV13(); $events = $content['events'] ?? []; $content['events'] = $migration->migrateEvents($events); return $content; + */ } } diff --git a/src/Appwrite/Utopia/Response/Filters/V11.php b/src/Appwrite/Utopia/Response/Filters/V11.php index 9d6459915d..941c1e1d2f 100644 --- a/src/Appwrite/Utopia/Response/Filters/V11.php +++ b/src/Appwrite/Utopia/Response/Filters/V11.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response\Filters; +use Appwrite\ID; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Filter; diff --git a/src/Appwrite/Utopia/Response/Filters/V12.php b/src/Appwrite/Utopia/Response/Filters/V12.php index 79d22ad044..c4ffcfef55 100644 --- a/src/Appwrite/Utopia/Response/Filters/V12.php +++ b/src/Appwrite/Utopia/Response/Filters/V12.php @@ -209,8 +209,6 @@ class V12 extends Filter unset($content['bucketsRead']); unset($content['bucketsUpdate']); unset($content['bucketsDelete']); - unset($content['filesCount']); - unset($content['bucketsDelete']); unset($content['filesCreate']); unset($content['filesRead']); unset($content['filesUpdate']); diff --git a/tests/e2e/Services/GraphQL/StorageClientTest.php b/tests/e2e/Services/GraphQL/StorageClientTest.php index 9896598c2d..7d6d617c87 100644 --- a/tests/e2e/Services/GraphQL/StorageClientTest.php +++ b/tests/e2e/Services/GraphQL/StorageClientTest.php @@ -183,7 +183,6 @@ class StorageClientTest extends Scope /** * @depends testCreateFile * @param $file - * @return array * @throws \Exception */ public function testGetFileDownload($file) diff --git a/tests/e2e/Services/GraphQL/StorageServerTest.php b/tests/e2e/Services/GraphQL/StorageServerTest.php index 7fea895b1c..6be103629e 100644 --- a/tests/e2e/Services/GraphQL/StorageServerTest.php +++ b/tests/e2e/Services/GraphQL/StorageServerTest.php @@ -232,7 +232,6 @@ class StorageServerTest extends Scope /** * @depends testCreateFile * @param $file - * @return array * @throws \Exception */ public function testGetFileDownload($file) From efeb898be1b18ba8da60266bc662c1d410074762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 7 Mar 2024 15:29:42 +0100 Subject: [PATCH 011/195] Import fixes --- app/cli.php | 6 +- app/config/runtimes-v2.php | 4 +- app/controllers/api/account.php | 162 ++++++------ app/controllers/api/avatars.php | 42 ++-- app/controllers/api/console.php | 38 +-- app/controllers/api/databases.php | 124 ++++----- app/controllers/api/functions.php | 89 +++---- app/controllers/api/graphql.php | 24 +- app/controllers/api/health.php | 64 ++--- app/controllers/api/locale.php | 18 +- app/controllers/api/messaging.php | 128 +++++----- app/controllers/api/migrations.php | 74 +++--- app/controllers/api/project.php | 18 +- app/controllers/api/projects.php | 102 ++++---- app/controllers/api/proxy.php | 26 +- app/controllers/api/storage.php | 68 ++--- app/controllers/api/teams.php | 46 ++-- app/controllers/api/users.php | 100 ++++---- app/controllers/api/vcs.php | 72 +++--- app/controllers/general.php | 52 ++-- app/controllers/mock.php | 32 +-- app/controllers/shared/api.php | 20 +- app/controllers/shared/api/auth.php | 8 +- app/controllers/web/console.php | 6 +- app/controllers/web/home.php | 4 +- app/http.php | 22 +- app/init.php | 236 +++++++++--------- app/realtime.php | 24 +- app/worker.php | 18 +- composer.json | 5 +- composer.lock | 222 ++++++---------- docs/tutorials/add-route.md | 30 +-- src/Appwrite/GraphQL/Resolvers.php | 6 +- src/Appwrite/GraphQL/Schema.php | 6 +- src/Appwrite/GraphQL/Types/Mapper.php | 38 +-- src/Appwrite/Messaging/Adapter/Realtime.php | 4 +- src/Appwrite/Migration/Migration.php | 4 +- src/Appwrite/Migration/Version/V15.php | 4 +- src/Appwrite/Migration/Version/V19.php | 4 +- src/Appwrite/Network/Validator/Origin.php | 2 +- src/Appwrite/Platform/Tasks/CalcTierStats.php | 8 +- .../Platform/Tasks/CreateInfMetric.php | 2 +- .../Platform/Tasks/DeleteOrphanedProjects.php | 4 +- .../Tasks/DevGenerateTranslations.php | 4 +- src/Appwrite/Platform/Tasks/Doctor.php | 40 +-- .../Platform/Tasks/GetMigrationStats.php | 6 +- src/Appwrite/Platform/Tasks/Hamster.php | 6 +- src/Appwrite/Platform/Tasks/Install.php | 4 +- src/Appwrite/Platform/Tasks/Maintenance.php | 12 +- src/Appwrite/Platform/Tasks/Migrate.php | 4 +- .../PatchRecreateRepositoriesDocuments.php | 2 +- src/Appwrite/Platform/Tasks/QueueCount.php | 2 +- src/Appwrite/Platform/Tasks/QueueRetry.php | 4 +- src/Appwrite/Platform/Tasks/SSL.php | 8 +- src/Appwrite/Platform/Tasks/ScheduleBase.php | 6 +- src/Appwrite/Platform/Tasks/Specs.php | 20 +- src/Appwrite/Platform/Tasks/Upgrade.php | 4 +- src/Appwrite/Platform/Tasks/Vars.php | 4 +- src/Appwrite/Platform/Tasks/Version.php | 4 +- src/Appwrite/Platform/Tasks/VolumeSync.php | 4 +- src/Appwrite/Platform/Workers/Builds.php | 14 +- .../Platform/Workers/Certificates.php | 14 +- src/Appwrite/Platform/Workers/Deletes.php | 6 +- src/Appwrite/Platform/Workers/Functions.php | 4 +- src/Appwrite/Platform/Workers/Hamster.php | 4 +- src/Appwrite/Platform/Workers/Mails.php | 12 +- src/Appwrite/Platform/Workers/Messaging.php | 10 +- src/Appwrite/Platform/Workers/Usage.php | 4 +- src/Appwrite/Platform/Workers/UsageDump.php | 4 +- src/Appwrite/Platform/Workers/Webhooks.php | 8 +- src/Appwrite/Specification/Format.php | 4 +- .../Specification/Format/OpenAPI3.php | 36 +-- .../Specification/Format/Swagger2.php | 36 +-- src/Appwrite/Utopia/Request.php | 8 +- src/Appwrite/Utopia/Response.php | 2 +- src/Appwrite/Vcs/Comment.php | 6 +- src/Executor/Executor.php | 14 +- tests/e2e/General/AbuseTest.php | 4 +- .../e2e/Services/Databases/DatabasesBase.php | 2 +- tests/e2e/Services/GraphQL/AbuseTest.php | 8 +- tests/e2e/Services/GraphQL/MessagingTest.php | 14 +- .../e2e/Services/Messaging/MessagingBase.php | 14 +- tests/e2e/Services/VCS/VCSBase.php | 4 +- .../e2e/Services/VCS/VCSConsoleClientTest.php | 6 +- tests/unit/Event/EventTest.php | 12 +- tests/unit/Usage/StatsTest.php | 12 +- 86 files changed, 1142 insertions(+), 1220 deletions(-) diff --git a/app/cli.php b/app/cli.php index 11b952dadc..95981033b3 100644 --- a/app/cli.php +++ b/app/cli.php @@ -8,7 +8,7 @@ use Appwrite\Event\Delete; use Appwrite\Event\Func; use Appwrite\Event\Hamster; use Appwrite\Platform\Appwrite; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\CLI; @@ -147,7 +147,7 @@ CLI::setResource('logError', function (Registry $register) { $logger = $register->get('logger'); if ($logger) { - $version = App::getEnv('_APP_VERSION', 'UNKNOWN'); + $version = Http::getEnv('_APP_VERSION', 'UNKNOWN'); $log = new Log(); $log->setNamespace($namespace); @@ -166,7 +166,7 @@ CLI::setResource('logError', function (Registry $register) { $log->setAction($action); - $isProduction = App::getEnv('_APP_ENV', 'development') === 'production'; + $isProduction = Http::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); $responseCode = $logger->addLog($log); diff --git a/app/config/runtimes-v2.php b/app/config/runtimes-v2.php index d249946d05..35ffb6109c 100644 --- a/app/config/runtimes-v2.php +++ b/app/config/runtimes-v2.php @@ -5,11 +5,11 @@ */ use Appwrite\Runtimes\Runtimes; -use Utopia\App; +use Utopia\Http\Http; $runtimes = new Runtimes('v2'); -$allowList = empty(App::getEnv('_APP_FUNCTIONS_RUNTIMES')) ? [] : \explode(',', App::getEnv('_APP_FUNCTIONS_RUNTIMES')); +$allowList = empty(Http::getEnv('_APP_FUNCTIONS_RUNTIMES')) ? [] : \explode(',', Http::getEnv('_APP_FUNCTIONS_RUNTIMES')); $runtimes = $runtimes->getAll(true, $allowList); diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 6ee46a8ebf..60b1e3d48d 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -28,7 +28,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Identities; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Audit\Audit as EventAudit; use Utopia\Config\Config; use Utopia\Database\Database; @@ -46,18 +46,18 @@ use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; -use Utopia\Validator\ArrayList; -use Utopia\Validator\Assoc; -use Utopia\Validator\Boolean; -use Utopia\Validator\Host; -use Utopia\Validator\Text; -use Utopia\Validator\URL; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\ArrayList; +use Utopia\Http\Validator\Assoc; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\Host; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\URL; +use Utopia\Http\Validator\WhiteList; $oauthDefaultSuccess = '/auth/oauth2/success'; $oauthDefaultFailure = '/auth/oauth2/failure'; -App::post('/v1/account') +Http::post('/v1/account') ->desc('Create account') ->groups(['api', 'account', 'auth']) ->label('event', 'users.[userId].create') @@ -197,7 +197,7 @@ App::post('/v1/account') ->dynamic($user, Response::MODEL_ACCOUNT); }); -App::post('/v1/account/sessions/email') +Http::post('/v1/account/sessions/email') ->alias('/v1/account/sessions') ->desc('Create email password session') ->groups(['api', 'account', 'auth', 'session']) @@ -323,7 +323,7 @@ App::post('/v1/account/sessions/email') $response->dynamic($session, Response::MODEL_SESSION); }); -App::get('/v1/account/sessions/oauth2/:provider') +Http::get('/v1/account/sessions/oauth2/:provider') ->desc('Create OAuth2 session') ->groups(['api', 'account']) ->label('error', __DIR__ . '/../../views/general/error.phtml') @@ -359,7 +359,7 @@ App::get('/v1/account/sessions/oauth2/:provider') $appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}'; if (!empty($appSecret) && isset($appSecret['version'])) { - $key = App::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']); + $key = Http::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']); $appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, \hex2bin($appSecret['iv']), \hex2bin($appSecret['tag'])); } @@ -393,7 +393,7 @@ App::get('/v1/account/sessions/oauth2/:provider') ->redirect($oauth2->getLoginURL()); }); -App::get('/v1/account/tokens/oauth2/:provider') +Http::get('/v1/account/tokens/oauth2/:provider') ->desc('Create OAuth2 token') ->groups(['api', 'account']) ->label('error', __DIR__ . '/../../views/general/error.phtml') @@ -428,7 +428,7 @@ App::get('/v1/account/tokens/oauth2/:provider') $appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}'; if (!empty($appSecret) && isset($appSecret['version'])) { - $key = App::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']); + $key = Http::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']); $appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, \hex2bin($appSecret['iv']), \hex2bin($appSecret['tag'])); } @@ -462,7 +462,7 @@ App::get('/v1/account/tokens/oauth2/:provider') ->redirect($oauth2->getLoginURL()); }); -App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId') +Http::get('/v1/account/sessions/oauth2/callback/:provider/:projectId') ->desc('OAuth2 callback') ->groups(['account']) ->label('error', __DIR__ . '/../../views/general/error.phtml') @@ -494,7 +494,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId') ])); }); -App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId') +Http::post('/v1/account/sessions/oauth2/callback/:provider/:projectId') ->desc('OAuth2 callback') ->groups(['account']) ->label('error', __DIR__ . '/../../views/general/error.phtml') @@ -527,7 +527,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId') ])); }); -App::get('/v1/account/sessions/oauth2/:provider/redirect') +Http::get('/v1/account/sessions/oauth2/:provider/redirect') ->desc('OAuth2 redirect') ->groups(['api', 'account', 'session']) ->label('error', __DIR__ . '/../../views/general/error.phtml') @@ -626,7 +626,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') } if (!empty($appSecret) && isset($appSecret['version'])) { - $key = App::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']); + $key = Http::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']); $appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, \hex2bin($appSecret['iv']), \hex2bin($appSecret['tag'])); } @@ -976,7 +976,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ; }); -App::get('/v1/account/identities') +Http::get('/v1/account/identities') ->desc('List Identities') ->groups(['api', 'account']) ->label('scope', 'account') @@ -1032,7 +1032,7 @@ App::get('/v1/account/identities') ]), Response::MODEL_IDENTITY_LIST); }); -App::delete('/v1/account/identities/:identityId') +Http::delete('/v1/account/identities/:identityId') ->desc('Delete identity') ->groups(['api', 'account']) ->label('scope', 'account') @@ -1068,7 +1068,7 @@ App::delete('/v1/account/identities/:identityId') return $response->noContent(); }); -App::post('/v1/account/tokens/magic-url') +Http::post('/v1/account/tokens/magic-url') ->alias('/v1/account/sessions/magic-url') ->desc('Create magic URL token') ->groups(['api', 'account', 'auth']) @@ -1100,7 +1100,7 @@ App::post('/v1/account/tokens/magic-url') ->inject('queueForMails') ->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { - if (empty(App::getEnv('_APP_SMTP_HOST'))) { + if (empty(Http::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); } @@ -1228,8 +1228,8 @@ App::post('/v1/account/tokens/magic-url') $smtp = $project->getAttribute('smtp', []); $smtpEnabled = $smtp['enabled'] ?? false; - $senderEmail = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - $senderName = App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); + $senderEmail = Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + $senderName = Http::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); $replyTo = ""; if ($smtpEnabled) { @@ -1312,7 +1312,7 @@ App::post('/v1/account/tokens/magic-url') ; }); -App::post('/v1/account/tokens/email') +Http::post('/v1/account/tokens/email') ->desc('Create email token (OTP)') ->groups(['api', 'account', 'auth']) ->label('scope', 'sessions.write') @@ -1341,7 +1341,7 @@ App::post('/v1/account/tokens/email') ->inject('queueForEvents') ->inject('queueForMails') ->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { - if (empty(App::getEnv('_APP_SMTP_HOST'))) { + if (empty(Http::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); } @@ -1457,8 +1457,8 @@ App::post('/v1/account/tokens/email') $smtp = $project->getAttribute('smtp', []); $smtpEnabled = $smtp['enabled'] ?? false; - $senderEmail = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - $senderName = App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); + $senderEmail = Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + $senderName = Http::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); $replyTo = ""; if ($smtpEnabled) { @@ -1648,7 +1648,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res $response->dynamic($session, Response::MODEL_SESSION); }; -App::put('/v1/account/sessions/magic-url') +Http::put('/v1/account/sessions/magic-url') ->desc('Update magic URL session') ->label('event', 'users.[userId].sessions.[sessionId].create') ->groups(['api', 'account', 'session']) @@ -1678,7 +1678,7 @@ App::put('/v1/account/sessions/magic-url') ->inject('queueForEvents') ->action($createSession); -App::put('/v1/account/sessions/phone') +Http::put('/v1/account/sessions/phone') ->desc('Update phone session') ->label('event', 'users.[userId].sessions.[sessionId].create') ->groups(['api', 'account', 'session']) @@ -1708,7 +1708,7 @@ App::put('/v1/account/sessions/phone') ->inject('queueForEvents') ->action($createSession); -App::post('/v1/account/sessions/token') +Http::post('/v1/account/sessions/token') ->desc('Create session') ->label('event', 'users.[userId].sessions.[sessionId].create') ->groups(['api', 'account', 'session']) @@ -1737,7 +1737,7 @@ App::post('/v1/account/sessions/token') ->inject('queueForEvents') ->action($createSession); -App::post('/v1/account/tokens/phone') +Http::post('/v1/account/tokens/phone') ->alias('/v1/account/sessions/phone') ->desc('Create phone token') ->groups(['api', 'account']) @@ -1766,7 +1766,7 @@ App::post('/v1/account/tokens/phone') ->inject('queueForMessaging') ->inject('locale') ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale) { - if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { + if (empty(Http::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -1907,7 +1907,7 @@ App::post('/v1/account/tokens/phone') ; }); -App::post('/v1/account/sessions/anonymous') +Http::post('/v1/account/sessions/anonymous') ->desc('Create anonymous session') ->groups(['api', 'account', 'auth', 'session']) ->label('event', 'users.[userId].sessions.[sessionId].create') @@ -2045,7 +2045,7 @@ App::post('/v1/account/sessions/anonymous') $response->dynamic($session, Response::MODEL_SESSION); }); -App::post('/v1/account/jwt') +Http::post('/v1/account/jwt') ->desc('Create JWT') ->groups(['api', 'account', 'auth']) ->label('scope', 'account') @@ -2078,7 +2078,7 @@ App::post('/v1/account/jwt') throw new Exception(Exception::USER_SESSION_NOT_FOUND); } - $jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. + $jwt = new JWT(Http::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -2092,7 +2092,7 @@ App::post('/v1/account/jwt') ])]), Response::MODEL_JWT); }); -App::get('/v1/account') +Http::get('/v1/account') ->desc('Get account') ->groups(['api', 'account']) ->label('scope', 'account') @@ -2115,7 +2115,7 @@ App::get('/v1/account') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -App::get('/v1/account/prefs') +Http::get('/v1/account/prefs') ->desc('Get account preferences') ->groups(['api', 'account']) ->label('scope', 'account') @@ -2137,7 +2137,7 @@ App::get('/v1/account/prefs') $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); -App::get('/v1/account/sessions') +Http::get('/v1/account/sessions') ->desc('List sessions') ->groups(['api', 'account']) ->label('scope', 'account') @@ -2178,7 +2178,7 @@ App::get('/v1/account/sessions') ]), Response::MODEL_SESSION_LIST); }); -App::get('/v1/account/logs') +Http::get('/v1/account/logs') ->desc('List logs') ->groups(['api', 'account']) ->label('scope', 'account') @@ -2243,7 +2243,7 @@ App::get('/v1/account/logs') ]), Response::MODEL_LOG_LIST); }); -App::get('/v1/account/sessions/:sessionId') +Http::get('/v1/account/sessions/:sessionId') ->desc('Get session') ->groups(['api', 'account']) ->label('scope', 'account') @@ -2289,7 +2289,7 @@ App::get('/v1/account/sessions/:sessionId') throw new Exception(Exception::USER_SESSION_NOT_FOUND); }); -App::patch('/v1/account/name') +Http::patch('/v1/account/name') ->desc('Update name') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.name') @@ -2322,7 +2322,7 @@ App::patch('/v1/account/name') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -App::patch('/v1/account/password') +Http::patch('/v1/account/password') ->desc('Update password') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.password') @@ -2391,7 +2391,7 @@ App::patch('/v1/account/password') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -App::patch('/v1/account/email') +Http::patch('/v1/account/email') ->desc('Update email') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.email') @@ -2483,7 +2483,7 @@ App::patch('/v1/account/email') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -App::patch('/v1/account/phone') +Http::patch('/v1/account/phone') ->desc('Update phone') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.phone') @@ -2564,7 +2564,7 @@ App::patch('/v1/account/phone') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -App::patch('/v1/account/prefs') +Http::patch('/v1/account/prefs') ->desc('Update preferences') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.prefs') @@ -2597,7 +2597,7 @@ App::patch('/v1/account/prefs') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -App::patch('/v1/account/status') +Http::patch('/v1/account/status') ->desc('Update status') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.status') @@ -2640,7 +2640,7 @@ App::patch('/v1/account/status') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -App::delete('/v1/account/sessions/:sessionId') +Http::delete('/v1/account/sessions/:sessionId') ->desc('Delete session') ->groups(['api', 'account', 'mfa']) ->label('scope', 'account') @@ -2720,7 +2720,7 @@ App::delete('/v1/account/sessions/:sessionId') throw new Exception(Exception::USER_SESSION_NOT_FOUND); }); -App::patch('/v1/account/sessions/:sessionId') +Http::patch('/v1/account/sessions/:sessionId') ->desc('Update session') ->groups(['api', 'account']) ->label('scope', 'account') @@ -2796,7 +2796,7 @@ App::patch('/v1/account/sessions/:sessionId') return $response->dynamic($session, Response::MODEL_SESSION); }); -App::delete('/v1/account/sessions') +Http::delete('/v1/account/sessions') ->desc('Delete sessions') ->groups(['api', 'account']) ->label('scope', 'account') @@ -2861,7 +2861,7 @@ App::delete('/v1/account/sessions') $response->noContent(); }); -App::post('/v1/account/recovery') +Http::post('/v1/account/recovery') ->desc('Create password recovery') ->groups(['api', 'account']) ->label('scope', 'sessions.write') @@ -2890,7 +2890,7 @@ App::post('/v1/account/recovery') ->inject('queueForEvents') ->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents) { - if (empty(App::getEnv('_APP_SMTP_HOST'))) { + if (empty(Http::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); } @@ -2960,8 +2960,8 @@ App::post('/v1/account/recovery') $smtp = $project->getAttribute('smtp', []); $smtpEnabled = $smtp['enabled'] ?? false; - $senderEmail = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - $senderName = App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); + $senderEmail = Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + $senderName = Http::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); $replyTo = ""; if ($smtpEnabled) { @@ -3039,7 +3039,7 @@ App::post('/v1/account/recovery') ->dynamic($recovery, Response::MODEL_TOKEN); }); -App::put('/v1/account/recovery') +Http::put('/v1/account/recovery') ->desc('Create password recovery (confirmation)') ->groups(['api', 'account']) ->label('scope', 'sessions.write') @@ -3124,7 +3124,7 @@ App::put('/v1/account/recovery') $response->dynamic($recoveryDocument, Response::MODEL_TOKEN); }); -App::post('/v1/account/verification') +Http::post('/v1/account/verification') ->desc('Create email verification') ->groups(['api', 'account']) ->label('scope', 'account') @@ -3151,7 +3151,7 @@ App::post('/v1/account/verification') ->inject('queueForMails') ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { - if (empty(App::getEnv('_APP_SMTP_HOST'))) { + if (empty(Http::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); } @@ -3208,8 +3208,8 @@ App::post('/v1/account/verification') $smtp = $project->getAttribute('smtp', []); $smtpEnabled = $smtp['enabled'] ?? false; - $senderEmail = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - $senderName = App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); + $senderEmail = Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + $senderName = Http::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); $replyTo = ""; if ($smtpEnabled) { @@ -3285,7 +3285,7 @@ App::post('/v1/account/verification') ->dynamic($verification, Response::MODEL_TOKEN); }); -App::put('/v1/account/verification') +Http::put('/v1/account/verification') ->desc('Create email verification (confirmation)') ->groups(['api', 'account']) ->label('scope', 'public') @@ -3345,7 +3345,7 @@ App::put('/v1/account/verification') $response->dynamic($verificationDocument, Response::MODEL_TOKEN); }); -App::post('/v1/account/verification/phone') +Http::post('/v1/account/verification/phone') ->desc('Create phone verification') ->groups(['api', 'account', 'auth']) ->label('scope', 'account') @@ -3371,7 +3371,7 @@ App::post('/v1/account/verification/phone') ->inject('project') ->inject('locale') ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale) { - if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { + if (empty(Http::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -3457,7 +3457,7 @@ App::post('/v1/account/verification/phone') ->dynamic($verification, Response::MODEL_TOKEN); }); -App::put('/v1/account/verification/phone') +Http::put('/v1/account/verification/phone') ->desc('Create phone verification (confirmation)') ->groups(['api', 'account']) ->label('scope', 'public') @@ -3515,7 +3515,7 @@ App::put('/v1/account/verification/phone') $response->dynamic($verificationDocument, Response::MODEL_TOKEN); }); -App::patch('/v1/account/mfa') +Http::patch('/v1/account/mfa') ->desc('Update MFA') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.mfa') @@ -3549,7 +3549,7 @@ App::patch('/v1/account/mfa') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -App::get('/v1/account/mfa/factors') +Http::get('/v1/account/mfa/factors') ->desc('List Factors') ->groups(['api', 'account', 'mfa']) ->label('scope', 'account') @@ -3577,7 +3577,7 @@ App::get('/v1/account/mfa/factors') $response->dynamic($factors, Response::MODEL_MFA_FACTORS); }); -App::post('/v1/account/mfa/authenticators/:type') +Http::post('/v1/account/mfa/authenticators/:type') ->desc('Add Authenticator') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.mfa') @@ -3649,7 +3649,7 @@ App::post('/v1/account/mfa/authenticators/:type') $response->dynamic($model, Response::MODEL_MFA_TYPE); }); -App::put('/v1/account/mfa/authenticators/:type') +Http::put('/v1/account/mfa/authenticators/:type') ->desc('Verify Authenticator') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.mfa') @@ -3712,7 +3712,7 @@ App::put('/v1/account/mfa/authenticators/:type') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -App::post('/v1/account/mfa/recovery-codes') +Http::post('/v1/account/mfa/recovery-codes') ->desc('Create MFA Recovery Codes') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.mfa') @@ -3754,7 +3754,7 @@ App::post('/v1/account/mfa/recovery-codes') $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); }); -App::patch('/v1/account/mfa/recovery-codes') +Http::patch('/v1/account/mfa/recovery-codes') ->desc('Regenerate MFA Recovery Codes') ->groups(['api', 'account', 'mfaProtected']) ->label('event', 'users.[userId].update.mfa') @@ -3795,7 +3795,7 @@ App::patch('/v1/account/mfa/recovery-codes') $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); }); -App::get('/v1/account/mfa/recovery-codes') +Http::get('/v1/account/mfa/recovery-codes') ->desc('Get MFA Recovery Codes') ->groups(['api', 'account', 'mfaProtected']) ->label('scope', 'account') @@ -3825,7 +3825,7 @@ App::get('/v1/account/mfa/recovery-codes') $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); }); -App::delete('/v1/account/mfa/authenticators/:type') +Http::delete('/v1/account/mfa/authenticators/:type') ->desc('Delete Authenticator') ->groups(['api', 'account']) ->label('event', 'users.[userId].delete.mfa') @@ -3885,7 +3885,7 @@ App::delete('/v1/account/mfa/authenticators/:type') $response->noContent(); }); -App::post('/v1/account/mfa/challenge') +Http::post('/v1/account/mfa/challenge') ->desc('Create 2FA Challenge') ->groups(['api', 'account', 'mfa']) ->label('scope', 'account') @@ -3934,7 +3934,7 @@ App::post('/v1/account/mfa/challenge') switch ($factor) { case Type::PHONE: - if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { + if (empty(Http::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } if (empty($user->getAttribute('phone'))) { @@ -3972,7 +3972,7 @@ App::post('/v1/account/mfa/challenge') ->setProviderType(MESSAGE_TYPE_SMS); break; case Type::EMAIL: - if (empty(App::getEnv('_APP_SMTP_HOST'))) { + if (empty(Http::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); } if (empty($user->getAttribute('email'))) { @@ -4003,8 +4003,8 @@ App::post('/v1/account/mfa/challenge') $smtp = $project->getAttribute('smtp', []); $smtpEnabled = $smtp['enabled'] ?? false; - $senderEmail = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - $senderName = App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); + $senderEmail = Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + $senderName = Http::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); $replyTo = ""; if ($smtpEnabled) { @@ -4073,7 +4073,7 @@ App::post('/v1/account/mfa/challenge') $response->dynamic($challenge, Response::MODEL_MFA_CHALLENGE); }); -App::put('/v1/account/mfa/challenge') +Http::put('/v1/account/mfa/challenge') ->desc('Create MFA Challenge (confirmation)') ->groups(['api', 'account', 'mfa']) ->label('scope', 'account') @@ -4158,7 +4158,7 @@ App::put('/v1/account/mfa/challenge') $response->dynamic($session, Response::MODEL_SESSION); }); -App::delete('/v1/account') +Http::delete('/v1/account') ->desc('Delete account') ->groups(['api', 'account']) ->label('event', 'users.[userId].delete') @@ -4206,7 +4206,7 @@ App::delete('/v1/account') $response->noContent(); }); -App::post('/v1/account/targets/push') +Http::post('/v1/account/targets/push') ->desc('Create push target') ->groups(['api', 'account']) ->label('scope', 'targets.write') @@ -4279,7 +4279,7 @@ App::post('/v1/account/targets/push') ->dynamic($target, Response::MODEL_TARGET); }); -App::put('/v1/account/targets/:targetId/push') +Http::put('/v1/account/targets/:targetId/push') ->desc('Update push target') ->groups(['api', 'account']) ->label('scope', 'targets.write') @@ -4334,7 +4334,7 @@ App::put('/v1/account/targets/:targetId/push') ->dynamic($target, Response::MODEL_TARGET); }); -App::delete('/v1/account/targets/:targetId/push') +Http::delete('/v1/account/targets/:targetId/push') ->desc('Delete push target') ->groups(['api', 'account']) ->label('scope', 'targets.write') diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index b393355085..cd63da8cfe 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -5,7 +5,7 @@ use Appwrite\URL\URL as URLParse; use Appwrite\Utopia\Response; use chillerlan\QRCode\QRCode; use chillerlan\QRCode\QROptions; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -17,12 +17,12 @@ use Utopia\Domains\Domain; use Utopia\Image\Image; use Utopia\Logger\Log; use Utopia\Logger\Logger; -use Utopia\Validator\Boolean; -use Utopia\Validator\HexColor; -use Utopia\Validator\Range; -use Utopia\Validator\Text; -use Utopia\Validator\URL; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\HexColor; +use Utopia\Http\Validator\Range; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\URL; +use Utopia\Http\Validator\WhiteList; $avatarCallback = function (string $type, string $code, int $width, int $height, int $quality, Response $response) { @@ -155,7 +155,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro ]; } catch (Exception $error) { if ($logger) { - $version = App::getEnv('_APP_VERSION', 'UNKNOWN'); + $version = Http::getEnv('_APP_VERSION', 'UNKNOWN'); $log = new Log(); $log->setNamespace('console'); @@ -174,7 +174,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro $log->setAction('avatarsGetGitHub'); - $isProduction = App::getEnv('_APP_ENV', 'development') === 'production'; + $isProduction = Http::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); $responseCode = $logger->addLog($log); @@ -190,7 +190,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro return []; }; -App::get('/v1/avatars/credit-cards/:code') +Http::get('/v1/avatars/credit-cards/:code') ->desc('Get credit card icon') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -210,7 +210,7 @@ App::get('/v1/avatars/credit-cards/:code') ->inject('response') ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response)); -App::get('/v1/avatars/browsers/:code') +Http::get('/v1/avatars/browsers/:code') ->desc('Get browser icon') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -230,7 +230,7 @@ App::get('/v1/avatars/browsers/:code') ->inject('response') ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response)); -App::get('/v1/avatars/flags/:code') +Http::get('/v1/avatars/flags/:code') ->desc('Get country flag') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -250,7 +250,7 @@ App::get('/v1/avatars/flags/:code') ->inject('response') ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response)); -App::get('/v1/avatars/image') +Http::get('/v1/avatars/image') ->desc('Get image from URL') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -306,7 +306,7 @@ App::get('/v1/avatars/image') unset($image); }); -App::get('/v1/avatars/favicon') +Http::get('/v1/avatars/favicon') ->desc('Get favicon') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -348,8 +348,8 @@ App::get('/v1/avatars/favicon') CURLOPT_URL => $url, CURLOPT_USERAGENT => \sprintf( APP_USERAGENT, - App::getEnv('_APP_VERSION', 'UNKNOWN'), - App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY) + Http::getEnv('_APP_VERSION', 'UNKNOWN'), + Http::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY) ), ]); @@ -448,7 +448,7 @@ App::get('/v1/avatars/favicon') unset($image); }); -App::get('/v1/avatars/qr') +Http::get('/v1/avatars/qr') ->desc('Get QR code') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -488,7 +488,7 @@ App::get('/v1/avatars/qr') ->send($image->output('png', 9)); }); -App::get('/v1/avatars/initials') +Http::get('/v1/avatars/initials') ->desc('Get user initials') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -571,7 +571,7 @@ App::get('/v1/avatars/initials') ->file($image->getImageBlob()); }); -App::get('/v1/cards/cloud') +Http::get('/v1/cards/cloud') ->desc('Get Front Of Cloud Card') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -778,7 +778,7 @@ App::get('/v1/cards/cloud') ->file($baseImage->getImageBlob()); }); -App::get('/v1/cards/cloud-back') +Http::get('/v1/cards/cloud-back') ->desc('Get Back Of Cloud Card') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -856,7 +856,7 @@ App::get('/v1/cards/cloud-back') ->file($baseImage->getImageBlob()); }); -App::get('/v1/cards/cloud-og') +Http::get('/v1/cards/cloud-og') ->desc('Get OG Image From Cloud Card') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') diff --git a/app/controllers/api/console.php b/app/controllers/api/console.php index 5abcd0fa23..4edec4394a 100644 --- a/app/controllers/api/console.php +++ b/app/controllers/api/console.php @@ -2,11 +2,11 @@ use Appwrite\Extend\Exception; use Appwrite\Utopia\Response; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\Document; -use Utopia\Validator\Text; +use Utopia\Http\Validator\Text; -App::init() +Http::init() ->groups(['console']) ->inject('project') ->action(function (Document $project) { @@ -16,7 +16,7 @@ App::init() }); -App::get('/v1/console/variables') +Http::get('/v1/console/variables') ->desc('Get variables') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -29,24 +29,24 @@ App::get('/v1/console/variables') ->label('sdk.response.model', Response::MODEL_CONSOLE_VARIABLES) ->inject('response') ->action(function (Response $response) { - $isDomainEnabled = !empty(App::getEnv('_APP_DOMAIN', '')) - && !empty(App::getEnv('_APP_DOMAIN_TARGET', '')) - && App::getEnv('_APP_DOMAIN', '') !== 'localhost' - && App::getEnv('_APP_DOMAIN_TARGET', '') !== 'localhost'; + $isDomainEnabled = !empty(Http::getEnv('_APP_DOMAIN', '')) + && !empty(Http::getEnv('_APP_DOMAIN_TARGET', '')) + && Http::getEnv('_APP_DOMAIN', '') !== 'localhost' + && Http::getEnv('_APP_DOMAIN_TARGET', '') !== 'localhost'; - $isVcsEnabled = !empty(App::getEnv('_APP_VCS_GITHUB_APP_NAME', '')) - && !empty(App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY', '')) - && !empty(App::getEnv('_APP_VCS_GITHUB_APP_ID', '')) - && !empty(App::getEnv('_APP_VCS_GITHUB_CLIENT_ID', '')) - && !empty(App::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', '')); + $isVcsEnabled = !empty(Http::getEnv('_APP_VCS_GITHUB_APP_NAME', '')) + && !empty(Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY', '')) + && !empty(Http::getEnv('_APP_VCS_GITHUB_APP_ID', '')) + && !empty(Http::getEnv('_APP_VCS_GITHUB_CLIENT_ID', '')) + && !empty(Http::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', '')); - $isAssistantEnabled = !empty(App::getEnv('_APP_ASSISTANT_OPENAI_API_KEY', '')); + $isAssistantEnabled = !empty(Http::getEnv('_APP_ASSISTANT_OPENAI_API_KEY', '')); $variables = new Document([ - '_APP_DOMAIN_TARGET' => App::getEnv('_APP_DOMAIN_TARGET'), - '_APP_STORAGE_LIMIT' => +App::getEnv('_APP_STORAGE_LIMIT'), - '_APP_FUNCTIONS_SIZE_LIMIT' => +App::getEnv('_APP_FUNCTIONS_SIZE_LIMIT'), - '_APP_USAGE_STATS' => App::getEnv('_APP_USAGE_STATS'), + '_APP_DOMAIN_TARGET' => Http::getEnv('_APP_DOMAIN_TARGET'), + '_APP_STORAGE_LIMIT' => +Http::getEnv('_APP_STORAGE_LIMIT'), + '_APP_FUNCTIONS_SIZE_LIMIT' => +Http::getEnv('_APP_FUNCTIONS_SIZE_LIMIT'), + '_APP_USAGE_STATS' => Http::getEnv('_APP_USAGE_STATS'), '_APP_VCS_ENABLED' => $isVcsEnabled, '_APP_DOMAIN_ENABLED' => $isDomainEnabled, '_APP_ASSISTANT_ENABLED' => $isAssistantEnabled @@ -55,7 +55,7 @@ App::get('/v1/console/variables') $response->dynamic($variables, Response::MODEL_CONSOLE_VARIABLES); }); -App::post('/v1/console/assistant') +Http::post('/v1/console/assistant') ->desc('Ask Query') ->groups(['api', 'assistant']) ->label('scope', 'assistant.read') diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 2b655ab5d8..4668adc63c 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -15,7 +15,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Indexes; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\Config\Config; use Utopia\Database\Database; @@ -42,17 +42,17 @@ use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\Structure; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; -use Utopia\Validator\ArrayList; -use Utopia\Validator\Boolean; -use Utopia\Validator\FloatValidator; -use Utopia\Validator\Integer; -use Utopia\Validator\IP; -use Utopia\Validator\JSON; -use Utopia\Validator\Nullable; -use Utopia\Validator\Range; -use Utopia\Validator\Text; -use Utopia\Validator\URL; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\ArrayList; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\FloatValidator; +use Utopia\Http\Validator\Integer; +use Utopia\Http\Validator\IP; +use Utopia\Http\Validator\JSON; +use Utopia\Http\Validator\Nullable; +use Utopia\Http\Validator\Range; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\URL; +use Utopia\Http\Validator\WhiteList; /** * * Create attribute of varying type @@ -381,19 +381,19 @@ function updateAttribute( return $attribute; } -App::init() +Http::init() ->groups(['api', 'database']) ->inject('request') ->inject('dbForProject') ->action(function (Request $request, Database $dbForProject) { $timeout = \intval($request->getHeader('x-appwrite-timeout')); - if (!empty($timeout) && App::isDevelopment()) { + if (!empty($timeout) && Http::isDevelopment()) { $dbForProject->setTimeout($timeout); } }); -App::post('/v1/databases') +Http::post('/v1/databases') ->desc('Create database') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].create') @@ -469,7 +469,7 @@ App::post('/v1/databases') ->dynamic($database, Response::MODEL_DATABASE); }); -App::get('/v1/databases') +Http::get('/v1/databases') ->desc('List databases') ->groups(['api', 'database']) ->label('scope', 'databases.read') @@ -522,7 +522,7 @@ App::get('/v1/databases') ]), Response::MODEL_DATABASE_LIST); }); -App::get('/v1/databases/:databaseId') +Http::get('/v1/databases/:databaseId') ->desc('Get database') ->groups(['api', 'database']) ->label('scope', 'databases.read') @@ -547,7 +547,7 @@ App::get('/v1/databases/:databaseId') $response->dynamic($database, Response::MODEL_DATABASE); }); -App::get('/v1/databases/:databaseId/logs') +Http::get('/v1/databases/:databaseId/logs') ->desc('List database logs') ->groups(['api', 'database']) ->label('scope', 'databases.read') @@ -638,7 +638,7 @@ App::get('/v1/databases/:databaseId/logs') }); -App::put('/v1/databases/:databaseId') +Http::put('/v1/databases/:databaseId') ->desc('Update database') ->groups(['api', 'database', 'schema']) ->label('scope', 'databases.write') @@ -682,7 +682,7 @@ App::put('/v1/databases/:databaseId') $response->dynamic($database, Response::MODEL_DATABASE); }); -App::delete('/v1/databases/:databaseId') +Http::delete('/v1/databases/:databaseId') ->desc('Delete database') ->groups(['api', 'database', 'schema']) ->label('scope', 'databases.write') @@ -726,7 +726,7 @@ App::delete('/v1/databases/:databaseId') $response->noContent(); }); -App::post('/v1/databases/:databaseId/collections') +Http::post('/v1/databases/:databaseId/collections') ->desc('Create collection') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].create') @@ -793,7 +793,7 @@ App::post('/v1/databases/:databaseId/collections') ->dynamic($collection, Response::MODEL_COLLECTION); }); -App::get('/v1/databases/:databaseId/collections') +Http::get('/v1/databases/:databaseId/collections') ->alias('/v1/database/collections', ['databaseId' => 'default']) ->desc('List collections') ->groups(['api', 'database']) @@ -856,7 +856,7 @@ App::get('/v1/databases/:databaseId/collections') ]), Response::MODEL_COLLECTION_LIST); }); -App::get('/v1/databases/:databaseId/collections/:collectionId') +Http::get('/v1/databases/:databaseId/collections/:collectionId') ->alias('/v1/database/collections/:collectionId', ['databaseId' => 'default']) ->desc('Get collection') ->groups(['api', 'database']) @@ -890,7 +890,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId') $response->dynamic($collection, Response::MODEL_COLLECTION); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/logs') +Http::get('/v1/databases/:databaseId/collections/:collectionId/logs') ->alias('/v1/database/collections/:collectionId/logs', ['databaseId' => 'default']) ->desc('List collection logs') ->groups(['api', 'database']) @@ -990,7 +990,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs') }); -App::put('/v1/databases/:databaseId/collections/:collectionId') +Http::put('/v1/databases/:databaseId/collections/:collectionId') ->alias('/v1/database/collections/:collectionId', ['databaseId' => 'default']) ->desc('Update collection') ->groups(['api', 'database', 'schema']) @@ -1058,7 +1058,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') $response->dynamic($collection, Response::MODEL_COLLECTION); }); -App::delete('/v1/databases/:databaseId/collections/:collectionId') +Http::delete('/v1/databases/:databaseId/collections/:collectionId') ->alias('/v1/database/collections/:collectionId', ['databaseId' => 'default']) ->desc('Delete collection') ->groups(['api', 'database', 'schema']) @@ -1113,7 +1113,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') $response->noContent(); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string') +Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string') ->alias('/v1/database/collections/:collectionId/attributes/string', ['databaseId' => 'default']) ->desc('Create string attribute') ->groups(['api', 'database', 'schema']) @@ -1169,7 +1169,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string ->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email') +Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email') ->alias('/v1/database/collections/:collectionId/attributes/email', ['databaseId' => 'default']) ->desc('Create email attribute') ->groups(['api', 'database', 'schema']) @@ -1211,7 +1211,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email' ->dynamic($attribute, Response::MODEL_ATTRIBUTE_EMAIL); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') +Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') ->alias('/v1/database/collections/:collectionId/attributes/enum', ['databaseId' => 'default']) ->desc('Create enum attribute') ->groups(['api', 'database', 'schema']) @@ -1258,7 +1258,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') ->dynamic($attribute, Response::MODEL_ATTRIBUTE_ENUM); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') +Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->alias('/v1/database/collections/:collectionId/attributes/ip', ['databaseId' => 'default']) ->desc('Create IP address attribute') ->groups(['api', 'database', 'schema']) @@ -1300,7 +1300,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->dynamic($attribute, Response::MODEL_ATTRIBUTE_IP); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') +Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->alias('/v1/database/collections/:collectionId/attributes/url', ['databaseId' => 'default']) ->desc('Create URL attribute') ->groups(['api', 'database', 'schema']) @@ -1342,7 +1342,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->dynamic($attribute, Response::MODEL_ATTRIBUTE_URL); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integer') +Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integer') ->alias('/v1/database/collections/:collectionId/attributes/integer', ['databaseId' => 'default']) ->desc('Create integer attribute') ->groups(['api', 'database', 'schema']) @@ -1413,7 +1413,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege ->dynamic($attribute, Response::MODEL_ATTRIBUTE_INTEGER); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float') +Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float') ->alias('/v1/database/collections/:collectionId/attributes/float', ['databaseId' => 'default']) ->desc('Create float attribute') ->groups(['api', 'database', 'schema']) @@ -1487,7 +1487,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' ->dynamic($attribute, Response::MODEL_ATTRIBUTE_FLOAT); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean') +Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean') ->alias('/v1/database/collections/:collectionId/attributes/boolean', ['databaseId' => 'default']) ->desc('Create boolean attribute') ->groups(['api', 'database', 'schema']) @@ -1528,7 +1528,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea ->dynamic($attribute, Response::MODEL_ATTRIBUTE_BOOLEAN); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime') +Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime') ->alias('/v1/database/collections/:collectionId/attributes/datetime', ['databaseId' => 'default']) ->desc('Create datetime attribute') ->groups(['api', 'database']) @@ -1572,7 +1572,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti ->dynamic($attribute, Response::MODEL_ATTRIBUTE_DATETIME); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relationship') +Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relationship') ->alias('/v1/database/collections/:collectionId/attributes/relationship', ['databaseId' => 'default']) ->desc('Create relationship attribute') ->groups(['api', 'database']) @@ -1700,7 +1700,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati ->dynamic($attribute, Response::MODEL_ATTRIBUTE_RELATIONSHIP); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') +Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes') ->alias('/v1/database/collections/:collectionId/attributes', ['databaseId' => 'default']) ->desc('List attributes') ->groups(['api', 'database']) @@ -1778,7 +1778,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') ]), Response::MODEL_ATTRIBUTE_LIST); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') +Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') ->alias('/v1/database/collections/:collectionId/attributes/:key', ['databaseId' => 'default']) ->desc('Get attribute') ->groups(['api', 'database']) @@ -1853,7 +1853,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') $response->dynamic($attribute, $model); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/string/:key') +Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/string/:key') ->desc('Update string attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -1892,7 +1892,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin ->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email/:key') +Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email/:key') ->desc('Update email attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -1931,7 +1931,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email ->dynamic($attribute, Response::MODEL_ATTRIBUTE_EMAIL); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/:key') +Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/:key') ->desc('Update enum attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -1972,7 +1972,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/ ->dynamic($attribute, Response::MODEL_ATTRIBUTE_ENUM); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:key') +Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:key') ->desc('Update IP address attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2011,7 +2011,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k ->dynamic($attribute, Response::MODEL_ATTRIBUTE_IP); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:key') +Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:key') ->desc('Update URL attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2050,7 +2050,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/: ->dynamic($attribute, Response::MODEL_ATTRIBUTE_URL); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integer/:key') +Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integer/:key') ->desc('Update integer attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2099,7 +2099,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ ->dynamic($attribute, Response::MODEL_ATTRIBUTE_INTEGER); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float/:key') +Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float/:key') ->desc('Update float attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2148,7 +2148,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float ->dynamic($attribute, Response::MODEL_ATTRIBUTE_FLOAT); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean/:key') +Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean/:key') ->desc('Update boolean attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2186,7 +2186,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole ->dynamic($attribute, Response::MODEL_ATTRIBUTE_BOOLEAN); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime/:key') +Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime/:key') ->desc('Update dateTime attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2224,7 +2224,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet ->dynamic($attribute, Response::MODEL_ATTRIBUTE_DATETIME); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/relationship') +Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/relationship') ->desc('Update relationship attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2277,7 +2277,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/ ->dynamic($attribute, Response::MODEL_ATTRIBUTE_RELATIONSHIP); }); -App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') +Http::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') ->alias('/v1/database/collections/:collectionId/attributes/:key', ['databaseId' => 'default']) ->desc('Delete attribute') ->groups(['api', 'database', 'schema']) @@ -2386,7 +2386,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key $response->noContent(); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') +Http::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->alias('/v1/database/collections/:collectionId/indexes', ['databaseId' => 'default']) ->desc('Create index') ->groups(['api', 'database']) @@ -2556,7 +2556,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->dynamic($index, Response::MODEL_INDEX); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') +Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes') ->alias('/v1/database/collections/:collectionId/indexes', ['databaseId' => 'default']) ->desc('List indexes') ->groups(['api', 'database']) @@ -2626,7 +2626,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') ]), Response::MODEL_INDEX_LIST); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') +Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->alias('/v1/database/collections/:collectionId/indexes/:key', ['databaseId' => 'default']) ->desc('Get index') ->groups(['api', 'database']) @@ -2665,7 +2665,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') }); -App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') +Http::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->alias('/v1/database/collections/:collectionId/indexes/:key', ['databaseId' => 'default']) ->desc('Delete index') ->groups(['api', 'database']) @@ -2729,7 +2729,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') $response->noContent(); }); -App::post('/v1/databases/:databaseId/collections/:collectionId/documents') +Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->alias('/v1/database/collections/:collectionId/documents', ['databaseId' => 'default']) ->desc('Create document') ->groups(['api', 'database']) @@ -2968,7 +2968,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->dynamic($document, Response::MODEL_DOCUMENT); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/documents') +Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') ->alias('/v1/database/collections/:collectionId/documents', ['databaseId' => 'default']) ->desc('List documents') ->groups(['api', 'database']) @@ -3123,7 +3123,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') ]), Response::MODEL_DOCUMENT_LIST); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') +Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') ->alias('/v1/database/collections/:collectionId/documents/:documentId', ['databaseId' => 'default']) ->desc('Get document') ->groups(['api', 'database']) @@ -3215,7 +3215,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $response->dynamic($document, Response::MODEL_DOCUMENT); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/logs') +Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/logs') ->alias('/v1/database/collections/:collectionId/documents/:documentId/logs', ['databaseId' => 'default']) ->desc('List document logs') ->groups(['api', 'database']) @@ -3319,7 +3319,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ]), Response::MODEL_LOG_LIST); }); -App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') +Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') ->alias('/v1/database/collections/:collectionId/documents/:documentId', ['databaseId' => 'default']) ->desc('Update document') ->groups(['api', 'database']) @@ -3547,7 +3547,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $response->dynamic($document, Response::MODEL_DOCUMENT); }); -App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') +Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') ->alias('/v1/database/collections/:collectionId/documents/:documentId', ['databaseId' => 'default']) ->desc('Delete document') ->groups(['api', 'database']) @@ -3661,7 +3661,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $response->noContent(); }); -App::get('/v1/databases/usage') +Http::get('/v1/databases/usage') ->desc('Get databases usage stats') ->groups(['api', 'database', 'usage']) ->label('scope', 'collections.read') @@ -3739,7 +3739,7 @@ App::get('/v1/databases/usage') ]), Response::MODEL_USAGE_DATABASES); }); -App::get('/v1/databases/:databaseId/usage') +Http::get('/v1/databases/:databaseId/usage') ->desc('Get database usage stats') ->groups(['api', 'database', 'usage']) ->label('scope', 'collections.read') @@ -3822,7 +3822,7 @@ App::get('/v1/databases/:databaseId/usage') ]), Response::MODEL_USAGE_DATABASE); }); -App::get('/v1/databases/:databaseId/collections/:collectionId/usage') +Http::get('/v1/databases/:databaseId/collections/:collectionId/usage') ->alias('/v1/database/:collectionId/usage', ['databaseId' => 'default']) ->desc('Get collection usage stats') ->groups(['api', 'database', 'usage']) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index e4b353dd0d..29d8ddb9da 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -19,7 +19,7 @@ use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model\Rule; use Executor\Executor; use MaxMind\Db\Reader; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -40,12 +40,12 @@ use Utopia\Storage\Validator\FileExt; use Utopia\Storage\Validator\FileSize; use Utopia\Storage\Validator\Upload; use Utopia\Swoole\Request; -use Utopia\Validator\ArrayList; -use Utopia\Validator\Assoc; -use Utopia\Validator\Boolean; -use Utopia\Validator\Range; -use Utopia\Validator\Text; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\ArrayList; +use Utopia\Http\Validator\Assoc; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\Range; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub; use Utopia\VCS\Exception\RepositoryNotFound; @@ -55,8 +55,8 @@ $redeployVcs = function (Request $request, Document $function, Document $project $deploymentId = ID::unique(); $entrypoint = $function->getAttribute('entrypoint', ''); $providerInstallationId = $installation->getAttribute('providerInstallationId', ''); - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId); $providerRepositoryId = $function->getAttribute('providerRepositoryId', ''); @@ -124,7 +124,7 @@ $redeployVcs = function (Request $request, Document $function, Document $project ->setTemplate($template); }; -App::post('/v1/functions') +Http::post('/v1/functions') ->groups(['api', 'functions']) ->desc('Create function') ->label('scope', 'functions.write') @@ -144,7 +144,7 @@ App::post('/v1/functions') ->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with execution permissions. By default no user is granted with any execute permissions. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true) ->param('events', [], new ArrayList(new FunctionEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true) ->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true) - ->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.', true) + ->param('timeout', 15, new Range(1, (int) Http::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.', true) ->param('enabled', true, new Boolean(), 'Is function enabled? When set to \'disabled\', users cannot access the function but Server SDKs with and API key can still access the function. No data is lost when this is toggled.', true) ->param('logging', true, new Boolean(), 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', true) ->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true) @@ -167,10 +167,11 @@ App::post('/v1/functions') ->inject('queueForBuilds') ->inject('dbForConsole') ->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, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateBranch, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) { + ->inject('auth') + ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateBranch, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $auth) use ($redeployVcs) { $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId; - $allowList = \array_filter(\explode(',', App::getEnv('_APP_FUNCTIONS_RUNTIMES', ''))); + $allowList = \array_filter(\explode(',', Http::getEnv('_APP_FUNCTIONS_RUNTIMES', ''))); if (!empty($allowList) && !\in_array($runtime, $allowList)) { throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $runtime . '" is not supported'); @@ -229,9 +230,9 @@ App::post('/v1/functions') 'providerSilentMode' => $providerSilentMode, ])); - $schedule = Authorization::skip( + $schedule = $auth->skip( fn () => $dbForConsole->createDocument('schedules', new Document([ - 'region' => App::getEnv('_APP_REGION', 'default'), // Todo replace with projects region + 'region' => Http::getEnv('_APP_REGION', 'default'), // Todo replace with projects region 'resourceType' => 'function', 'resourceId' => $function->getId(), 'resourceInternalId' => $function->getInternalId(), @@ -280,13 +281,13 @@ App::post('/v1/functions') $redeployVcs($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github); } - $functionsDomain = App::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + $functionsDomain = Http::getEnv('_APP_DOMAIN_FUNCTIONS', ''); if (!empty($functionsDomain)) { $ruleId = ID::unique(); $routeSubdomain = ID::unique(); $domain = "{$routeSubdomain}.{$functionsDomain}"; - $rule = Authorization::skip( + $rule = $auth->skip( fn () => $dbForConsole->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), @@ -353,7 +354,7 @@ App::post('/v1/functions') ->dynamic($function, Response::MODEL_FUNCTION); }); -App::get('/v1/functions') +Http::get('/v1/functions') ->groups(['api', 'functions']) ->desc('List functions') ->label('scope', 'functions.read') @@ -407,7 +408,7 @@ App::get('/v1/functions') ]), Response::MODEL_FUNCTION_LIST); }); -App::get('/v1/functions/runtimes') +Http::get('/v1/functions/runtimes') ->groups(['api', 'functions']) ->desc('List runtimes') ->label('scope', 'functions.read') @@ -422,7 +423,7 @@ App::get('/v1/functions/runtimes') ->action(function (Response $response) { $runtimes = Config::getParam('runtimes'); - $allowList = \array_filter(\explode(',', App::getEnv('_APP_FUNCTIONS_RUNTIMES', ''))); + $allowList = \array_filter(\explode(',', Http::getEnv('_APP_FUNCTIONS_RUNTIMES', ''))); $allowed = []; foreach ($runtimes as $key => $runtime) { @@ -440,7 +441,7 @@ App::get('/v1/functions/runtimes') ]), Response::MODEL_RUNTIME_LIST); }); -App::get('/v1/functions/:functionId') +Http::get('/v1/functions/:functionId') ->groups(['api', 'functions']) ->desc('Get function') ->label('scope', 'functions.read') @@ -464,7 +465,7 @@ App::get('/v1/functions/:functionId') $response->dynamic($function, Response::MODEL_FUNCTION); }); -App::get('/v1/functions/:functionId/usage') +Http::get('/v1/functions/:functionId/usage') ->desc('Get function usage') ->groups(['api', 'functions', 'usage']) ->label('scope', 'functions.read') @@ -562,7 +563,7 @@ App::get('/v1/functions/:functionId/usage') ]), Response::MODEL_USAGE_FUNCTION); }); -App::get('/v1/functions/usage') +Http::get('/v1/functions/usage') ->desc('Get functions usage') ->groups(['api', 'functions']) ->label('scope', 'functions.read') @@ -655,7 +656,7 @@ App::get('/v1/functions/usage') ]), Response::MODEL_USAGE_FUNCTIONS); }); -App::put('/v1/functions/:functionId') +Http::put('/v1/functions/:functionId') ->groups(['api', 'functions']) ->desc('Update function') ->label('scope', 'functions.write') @@ -675,7 +676,7 @@ App::put('/v1/functions/:functionId') ->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with execution permissions. By default no user is granted with any execute permissions. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true) ->param('events', [], new ArrayList(new FunctionEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true) ->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true) - ->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true) + ->param('timeout', 15, new Range(1, (int) Http::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true) ->param('enabled', true, new Boolean(), 'Is function enabled? When set to \'disabled\', users cannot access the function but Server SDKs with and API key can still access the function. No data is lost when this is toggled.', true) ->param('logging', true, new Boolean(), 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', true) ->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true) @@ -834,7 +835,7 @@ App::put('/v1/functions/:functionId') $response->dynamic($function, Response::MODEL_FUNCTION); }); -App::get('/v1/functions/:functionId/deployments/:deploymentId/download') +Http::get('/v1/functions/:functionId/deployments/:deploymentId/download') ->groups(['api', 'functions']) ->desc('Download Deployment') ->label('scope', 'functions.read') @@ -919,7 +920,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download') } }); -App::patch('/v1/functions/:functionId/deployments/:deploymentId') +Http::patch('/v1/functions/:functionId/deployments/:deploymentId') ->groups(['api', 'functions']) ->desc('Update function deployment') ->label('scope', 'functions.write') @@ -981,7 +982,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId') $response->dynamic($function, Response::MODEL_FUNCTION); }); -App::delete('/v1/functions/:functionId') +Http::delete('/v1/functions/:functionId') ->groups(['api', 'functions']) ->desc('Delete function') ->label('scope', 'functions.write') @@ -1028,7 +1029,7 @@ App::delete('/v1/functions/:functionId') $response->noContent(); }); -App::post('/v1/functions/:functionId/deployments') +Http::post('/v1/functions/:functionId/deployments') ->groups(['api', 'functions']) ->desc('Create deployment') ->label('scope', 'functions.write') @@ -1091,7 +1092,7 @@ App::post('/v1/functions/:functionId/deployments') } $fileExt = new FileExt([FileExt::TYPE_GZIP]); - $fileSizeValidator = new FileSize(App::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000')); + $fileSizeValidator = new FileSize(Http::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000')); $upload = new Upload(); // Make sure we handle a single file and multiple files the same way @@ -1246,7 +1247,7 @@ App::post('/v1/functions/:functionId/deployments') ->dynamic($deployment, Response::MODEL_DEPLOYMENT); }); -App::get('/v1/functions/:functionId/deployments') +Http::get('/v1/functions/:functionId/deployments') ->groups(['api', 'functions']) ->desc('List deployments') ->label('scope', 'functions.read') @@ -1322,7 +1323,7 @@ App::get('/v1/functions/:functionId/deployments') ]), Response::MODEL_DEPLOYMENT_LIST); }); -App::get('/v1/functions/:functionId/deployments/:deploymentId') +Http::get('/v1/functions/:functionId/deployments/:deploymentId') ->groups(['api', 'functions']) ->desc('Get deployment') ->label('scope', 'functions.read') @@ -1364,7 +1365,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId') $response->dynamic($deployment, Response::MODEL_DEPLOYMENT); }); -App::delete('/v1/functions/:functionId/deployments/:deploymentId') +Http::delete('/v1/functions/:functionId/deployments/:deploymentId') ->groups(['api', 'functions']) ->desc('Delete deployment') ->label('scope', 'functions.write') @@ -1428,7 +1429,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId') $response->noContent(); }); -App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') +Http::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') ->groups(['api', 'functions']) ->desc('Create build') ->label('scope', 'functions.write') @@ -1494,7 +1495,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') $response->noContent(); }); -App::post('/v1/functions/:functionId/executions') +Http::post('/v1/functions/:functionId/executions') ->groups(['api', 'functions']) ->desc('Create execution') ->label('scope', 'execution.write') @@ -1580,7 +1581,7 @@ App::post('/v1/functions/:functionId/executions') } if (!$current->isEmpty()) { - $jwtObj = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. + $jwtObj = new JWT(Http::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. $jwt = $jwtObj->encode([ 'userId' => $user->getId(), 'sessionId' => $current->getId(), @@ -1703,7 +1704,7 @@ App::post('/v1/functions/:functionId/executions') ]); /** Execute function */ - $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $executor = new Executor(Http::getEnv('_APP_EXECUTOR_HOST')); try { $version = $function->getAttribute('version', 'v2'); $command = $runtime['startCommand']; @@ -1785,7 +1786,7 @@ App::post('/v1/functions/:functionId/executions') ->dynamic($execution, Response::MODEL_EXECUTION); }); -App::get('/v1/functions/:functionId/executions') +Http::get('/v1/functions/:functionId/executions') ->groups(['api', 'functions']) ->desc('List executions') ->label('scope', 'execution.read') @@ -1866,7 +1867,7 @@ App::get('/v1/functions/:functionId/executions') ]), Response::MODEL_EXECUTION_LIST); }); -App::get('/v1/functions/:functionId/executions/:executionId') +Http::get('/v1/functions/:functionId/executions/:executionId') ->groups(['api', 'functions']) ->desc('Get execution') ->label('scope', 'execution.read') @@ -1915,7 +1916,7 @@ App::get('/v1/functions/:functionId/executions/:executionId') // Variables -App::post('/v1/functions/:functionId/variables') +Http::post('/v1/functions/:functionId/variables') ->desc('Create variable') ->groups(['api', 'functions']) ->label('scope', 'functions.write') @@ -1979,7 +1980,7 @@ App::post('/v1/functions/:functionId/variables') ->dynamic($variable, Response::MODEL_VARIABLE); }); -App::get('/v1/functions/:functionId/variables') +Http::get('/v1/functions/:functionId/variables') ->desc('List variables') ->groups(['api', 'functions']) ->label('scope', 'functions.read') @@ -2006,7 +2007,7 @@ App::get('/v1/functions/:functionId/variables') ]), Response::MODEL_VARIABLE_LIST); }); -App::get('/v1/functions/:functionId/variables/:variableId') +Http::get('/v1/functions/:functionId/variables/:variableId') ->desc('Get variable') ->groups(['api', 'functions']) ->label('scope', 'functions.read') @@ -2045,7 +2046,7 @@ App::get('/v1/functions/:functionId/variables/:variableId') $response->dynamic($variable, Response::MODEL_VARIABLE); }); -App::put('/v1/functions/:functionId/variables/:variableId') +Http::put('/v1/functions/:functionId/variables/:variableId') ->desc('Update variable') ->groups(['api', 'functions']) ->label('scope', 'functions.write') @@ -2106,7 +2107,7 @@ App::put('/v1/functions/:functionId/variables/:variableId') $response->dynamic($variable, Response::MODEL_VARIABLE); }); -App::delete('/v1/functions/:functionId/variables/:variableId') +Http::delete('/v1/functions/:functionId/variables/:variableId') ->desc('Delete variable') ->groups(['api', 'functions']) ->label('scope', 'functions.write') diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index 830aecbe0c..c50811e0a3 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -12,12 +12,12 @@ use GraphQL\Validator\Rules\DisableIntrospection; use GraphQL\Validator\Rules\QueryComplexity; use GraphQL\Validator\Rules\QueryDepth; use Swoole\Coroutine\WaitGroup; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\Document; -use Utopia\Validator\JSON; -use Utopia\Validator\Text; +use Utopia\Http\Validator\JSON; +use Utopia\Http\Validator\Text; -App::get('/v1/graphql') +Http::get('/v1/graphql') ->desc('GraphQL endpoint') ->groups(['graphql']) ->label('scope', 'graphql') @@ -57,7 +57,7 @@ App::get('/v1/graphql') ->json($output); }); -App::post('/v1/graphql/mutation') +Http::post('/v1/graphql/mutation') ->desc('GraphQL endpoint') ->groups(['graphql']) ->label('scope', 'graphql') @@ -102,7 +102,7 @@ App::post('/v1/graphql/mutation') ->json($output); }); -App::post('/v1/graphql') +Http::post('/v1/graphql') ->desc('GraphQL endpoint') ->groups(['graphql']) ->label('scope', 'graphql') @@ -161,9 +161,9 @@ function execute( Adapter $promiseAdapter, array $query ): array { - $maxBatchSize = App::getEnv('_APP_GRAPHQL_MAX_BATCH_SIZE', 10); - $maxComplexity = App::getEnv('_APP_GRAPHQL_MAX_COMPLEXITY', 250); - $maxDepth = App::getEnv('_APP_GRAPHQL_MAX_DEPTH', 3); + $maxBatchSize = Http::getEnv('_APP_GRAPHQL_MAX_BATCH_SIZE', 10); + $maxComplexity = Http::getEnv('_APP_GRAPHQL_MAX_COMPLEXITY', 250); + $maxDepth = Http::getEnv('_APP_GRAPHQL_MAX_DEPTH', 3); if (!empty($query) && !isset($query[0])) { $query = [$query]; @@ -183,12 +183,12 @@ function execute( $flags = DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::INCLUDE_TRACE; $validations = GraphQL::getStandardValidationRules(); - if (App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') { + if (Http::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') { $validations[] = new DisableIntrospection(); $validations[] = new QueryComplexity($maxComplexity); $validations[] = new QueryDepth($maxDepth); } - if (App::getMode() === App::MODE_TYPE_PRODUCTION) { + if (Http::getMode() === Http::MODE_TYPE_PRODUCTION) { $flags = DebugFlag::NONE; } @@ -289,7 +289,7 @@ function processResult($result, $debugFlags): array ); } -App::shutdown() +Http::shutdown() ->groups(['schema']) ->inject('project') ->action(function (Document $project) { diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 2749013411..1fc0541d2e 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -4,7 +4,7 @@ use Appwrite\ClamAV\Network; use Appwrite\Event\Event; use Appwrite\Extend\Exception; use Appwrite\Utopia\Response; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Config\Config; use Utopia\Database\Document; use Utopia\Domains\Validator\PublicDomain; @@ -15,13 +15,13 @@ use Utopia\Registry\Registry; use Utopia\Storage\Device; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; -use Utopia\Validator\Domain; -use Utopia\Validator\Integer; -use Utopia\Validator\Multiple; -use Utopia\Validator\Text; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\Domain; +use Utopia\Http\Validator\Integer; +use Utopia\Http\Validator\Multiple; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; -App::get('/v1/health') +Http::get('/v1/health') ->desc('Get HTTP') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -44,7 +44,7 @@ App::get('/v1/health') $response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS); }); -App::get('/v1/health/version') +Http::get('/v1/health/version') ->desc('Get version') ->groups(['api', 'health']) ->label('scope', 'public') @@ -56,7 +56,7 @@ App::get('/v1/health/version') $response->dynamic(new Document([ 'version' => APP_VERSION_STABLE ]), Response::MODEL_HEALTH_VERSION); }); -App::get('/v1/health/db') +Http::get('/v1/health/db') ->desc('Get DB') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -110,7 +110,7 @@ App::get('/v1/health/db') ]), Response::MODEL_HEALTH_STATUS_LIST); }); -App::get('/v1/health/cache') +Http::get('/v1/health/cache') ->desc('Get cache') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -167,7 +167,7 @@ App::get('/v1/health/cache') ]), Response::MODEL_HEALTH_STATUS_LIST); }); -App::get('/v1/health/queue') +Http::get('/v1/health/queue') ->desc('Get queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -224,7 +224,7 @@ App::get('/v1/health/queue') ]), Response::MODEL_HEALTH_STATUS_LIST); }); -App::get('/v1/health/pubsub') +Http::get('/v1/health/pubsub') ->desc('Get pubsub') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -281,7 +281,7 @@ App::get('/v1/health/pubsub') ]), Response::MODEL_HEALTH_STATUS_LIST); }); -App::get('/v1/health/time') +Http::get('/v1/health/time') ->desc('Get time') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -338,7 +338,7 @@ App::get('/v1/health/time') $response->dynamic(new Document($output), Response::MODEL_HEALTH_TIME); }); -App::get('/v1/health/queue/webhooks') +Http::get('/v1/health/queue/webhooks') ->desc('Get webhooks queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -365,7 +365,7 @@ App::get('/v1/health/queue/webhooks') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/queue/logs') +Http::get('/v1/health/queue/logs') ->desc('Get logs queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -392,7 +392,7 @@ App::get('/v1/health/queue/logs') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/certificate') +Http::get('/v1/health/certificate') ->desc('Get the SSL certificate for a domain') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -442,7 +442,7 @@ App::get('/v1/health/certificate') ]), Response::MODEL_HEALTH_CERTIFICATE); }, ['response']); -App::get('/v1/health/queue/certificates') +Http::get('/v1/health/queue/certificates') ->desc('Get certificates queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -469,7 +469,7 @@ App::get('/v1/health/queue/certificates') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/queue/builds') +Http::get('/v1/health/queue/builds') ->desc('Get builds queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -496,7 +496,7 @@ App::get('/v1/health/queue/builds') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/queue/databases') +Http::get('/v1/health/queue/databases') ->desc('Get databases queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -524,7 +524,7 @@ App::get('/v1/health/queue/databases') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/queue/deletes') +Http::get('/v1/health/queue/deletes') ->desc('Get deletes queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -551,7 +551,7 @@ App::get('/v1/health/queue/deletes') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/queue/mails') +Http::get('/v1/health/queue/mails') ->desc('Get mails queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -578,7 +578,7 @@ App::get('/v1/health/queue/mails') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/queue/messaging') +Http::get('/v1/health/queue/messaging') ->desc('Get messaging queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -605,7 +605,7 @@ App::get('/v1/health/queue/messaging') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/queue/migrations') +Http::get('/v1/health/queue/migrations') ->desc('Get migrations queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -632,7 +632,7 @@ App::get('/v1/health/queue/migrations') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/queue/functions') +Http::get('/v1/health/queue/functions') ->desc('Get functions queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -659,7 +659,7 @@ App::get('/v1/health/queue/functions') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/storage/local') +Http::get('/v1/health/storage/local') ->desc('Get local storage') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -702,7 +702,7 @@ App::get('/v1/health/storage/local') $response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS); }); -App::get('/v1/health/storage') +Http::get('/v1/health/storage') ->desc('Get storage') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -743,7 +743,7 @@ App::get('/v1/health/storage') $response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS); }); -App::get('/v1/health/anti-virus') +Http::get('/v1/health/anti-virus') ->desc('Get antivirus') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -762,13 +762,13 @@ App::get('/v1/health/anti-virus') 'version' => '' ]; - if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'disabled') { // Check if scans are enabled + if (Http::getEnv('_APP_STORAGE_ANTIVIRUS') === 'disabled') { // Check if scans are enabled $output['status'] = 'disabled'; $output['version'] = ''; } else { $antivirus = new Network( - App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'), - (int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310) + Http::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'), + (int) Http::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310) ); try { @@ -782,7 +782,7 @@ App::get('/v1/health/anti-virus') $response->dynamic(new Document($output), Response::MODEL_HEALTH_ANTIVIRUS); }); -App::get('/v1/health/queue/failed/:name') +Http::get('/v1/health/queue/failed/:name') ->desc('Get number of failed queue jobs') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -823,7 +823,7 @@ App::get('/v1/health/queue/failed/:name') $response->dynamic(new Document([ 'size' => $failed ]), Response::MODEL_HEALTH_QUEUE); }); -App::get('/v1/health/stats') // Currently only used internally +Http::get('/v1/health/stats') // Currently only used internally ->desc('Get system stats') ->groups(['api', 'health']) ->label('scope', 'root') diff --git a/app/controllers/api/locale.php b/app/controllers/api/locale.php index abb47ab3c4..e4bf57d5ab 100644 --- a/app/controllers/api/locale.php +++ b/app/controllers/api/locale.php @@ -3,12 +3,12 @@ use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Config\Config; use Utopia\Database\Document; use Utopia\Locale\Locale; -App::get('/v1/locale') +Http::get('/v1/locale') ->desc('Get user locale') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -68,7 +68,7 @@ App::get('/v1/locale') $response->dynamic(new Document($output), Response::MODEL_LOCALE); }); -App::get('/v1/locale/codes') +Http::get('/v1/locale/codes') ->desc('List Locale Codes') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -90,7 +90,7 @@ App::get('/v1/locale/codes') ]), Response::MODEL_LOCALE_CODE_LIST); }); -App::get('/v1/locale/countries') +Http::get('/v1/locale/countries') ->desc('List countries') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -123,7 +123,7 @@ App::get('/v1/locale/countries') $response->dynamic(new Document(['countries' => $output, 'total' => \count($output)]), Response::MODEL_COUNTRY_LIST); }); -App::get('/v1/locale/countries/eu') +Http::get('/v1/locale/countries/eu') ->desc('List EU countries') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -158,7 +158,7 @@ App::get('/v1/locale/countries/eu') $response->dynamic(new Document(['countries' => $output, 'total' => \count($output)]), Response::MODEL_COUNTRY_LIST); }); -App::get('/v1/locale/countries/phones') +Http::get('/v1/locale/countries/phones') ->desc('List countries phone codes') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -192,7 +192,7 @@ App::get('/v1/locale/countries/phones') $response->dynamic(new Document(['phones' => $output, 'total' => \count($output)]), Response::MODEL_PHONE_LIST); }); -App::get('/v1/locale/continents') +Http::get('/v1/locale/continents') ->desc('List continents') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -224,7 +224,7 @@ App::get('/v1/locale/continents') $response->dynamic(new Document(['continents' => $output, 'total' => \count($output)]), Response::MODEL_CONTINENT_LIST); }); -App::get('/v1/locale/currencies') +Http::get('/v1/locale/currencies') ->desc('List currencies') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -247,7 +247,7 @@ App::get('/v1/locale/currencies') }); -App::get('/v1/locale/languages') +Http::get('/v1/locale/languages') ->desc('List languages') ->groups(['api', 'locale']) ->label('scope', 'locale.read') diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 8a15de5173..1bb08c74a0 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -19,7 +19,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Targets; use Appwrite\Utopia\Database\Validator\Queries\Topics; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\Database\Database; use Utopia\Database\DateTime; @@ -37,17 +37,17 @@ use Utopia\Database\Validator\Roles; use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; use Utopia\Locale\Locale; -use Utopia\Validator\ArrayList; -use Utopia\Validator\Boolean; -use Utopia\Validator\Integer; -use Utopia\Validator\JSON; -use Utopia\Validator\Range; -use Utopia\Validator\Text; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\ArrayList; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\Integer; +use Utopia\Http\Validator\JSON; +use Utopia\Http\Validator\Range; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; use function Swoole\Coroutine\batch; -App::post('/v1/messaging/providers/mailgun') +Http::post('/v1/messaging/providers/mailgun') ->desc('Create Mailgun provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -134,7 +134,7 @@ App::post('/v1/messaging/providers/mailgun') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::post('/v1/messaging/providers/sendgrid') +Http::post('/v1/messaging/providers/sendgrid') ->desc('Create Sendgrid provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -209,7 +209,7 @@ App::post('/v1/messaging/providers/sendgrid') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::post('/v1/messaging/providers/smtp') +Http::post('/v1/messaging/providers/smtp') ->desc('Create SMTP provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -297,7 +297,7 @@ App::post('/v1/messaging/providers/smtp') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::post('/v1/messaging/providers/msg91') +Http::post('/v1/messaging/providers/msg91') ->desc('Create Msg91 provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -374,7 +374,7 @@ App::post('/v1/messaging/providers/msg91') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::post('/v1/messaging/providers/telesign') +Http::post('/v1/messaging/providers/telesign') ->desc('Create Telesign provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -451,7 +451,7 @@ App::post('/v1/messaging/providers/telesign') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::post('/v1/messaging/providers/textmagic') +Http::post('/v1/messaging/providers/textmagic') ->desc('Create Textmagic provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -528,7 +528,7 @@ App::post('/v1/messaging/providers/textmagic') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::post('/v1/messaging/providers/twilio') +Http::post('/v1/messaging/providers/twilio') ->desc('Create Twilio provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -605,7 +605,7 @@ App::post('/v1/messaging/providers/twilio') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::post('/v1/messaging/providers/vonage') +Http::post('/v1/messaging/providers/vonage') ->desc('Create Vonage provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -682,7 +682,7 @@ App::post('/v1/messaging/providers/vonage') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::post('/v1/messaging/providers/fcm') +Http::post('/v1/messaging/providers/fcm') ->desc('Create FCM provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -745,7 +745,7 @@ App::post('/v1/messaging/providers/fcm') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::post('/v1/messaging/providers/apns') +Http::post('/v1/messaging/providers/apns') ->desc('Create APNS provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -831,7 +831,7 @@ App::post('/v1/messaging/providers/apns') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::get('/v1/messaging/providers') +Http::get('/v1/messaging/providers') ->desc('List providers') ->groups(['api', 'messaging']) ->label('scope', 'providers.read') @@ -882,7 +882,7 @@ App::get('/v1/messaging/providers') ]), Response::MODEL_PROVIDER_LIST); }); -App::get('/v1/messaging/providers/:providerId/logs') +Http::get('/v1/messaging/providers/:providerId/logs') ->desc('List provider logs') ->groups(['api', 'messaging']) ->label('scope', 'providers.read') @@ -970,7 +970,7 @@ App::get('/v1/messaging/providers/:providerId/logs') ]), Response::MODEL_LOG_LIST); }); -App::get('/v1/messaging/providers/:providerId') +Http::get('/v1/messaging/providers/:providerId') ->desc('Get provider') ->groups(['api', 'messaging']) ->label('scope', 'providers.read') @@ -994,7 +994,7 @@ App::get('/v1/messaging/providers/:providerId') $response->dynamic($provider, Response::MODEL_PROVIDER); }); -App::patch('/v1/messaging/providers/mailgun/:providerId') +Http::patch('/v1/messaging/providers/mailgun/:providerId') ->desc('Update Mailgun provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1100,7 +1100,7 @@ App::patch('/v1/messaging/providers/mailgun/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::patch('/v1/messaging/providers/sendgrid/:providerId') +Http::patch('/v1/messaging/providers/sendgrid/:providerId') ->desc('Update Sendgrid provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1191,7 +1191,7 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::patch('/v1/messaging/providers/smtp/:providerId') +Http::patch('/v1/messaging/providers/smtp/:providerId') ->desc('Update SMTP provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1313,7 +1313,7 @@ App::patch('/v1/messaging/providers/smtp/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::patch('/v1/messaging/providers/msg91/:providerId') +Http::patch('/v1/messaging/providers/msg91/:providerId') ->desc('Update Msg91 provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1395,7 +1395,7 @@ App::patch('/v1/messaging/providers/msg91/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::patch('/v1/messaging/providers/telesign/:providerId') +Http::patch('/v1/messaging/providers/telesign/:providerId') ->desc('Update Telesign provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1477,7 +1477,7 @@ App::patch('/v1/messaging/providers/telesign/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::patch('/v1/messaging/providers/textmagic/:providerId') +Http::patch('/v1/messaging/providers/textmagic/:providerId') ->desc('Update Textmagic provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1559,7 +1559,7 @@ App::patch('/v1/messaging/providers/textmagic/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::patch('/v1/messaging/providers/twilio/:providerId') +Http::patch('/v1/messaging/providers/twilio/:providerId') ->desc('Update Twilio provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1641,7 +1641,7 @@ App::patch('/v1/messaging/providers/twilio/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::patch('/v1/messaging/providers/vonage/:providerId') +Http::patch('/v1/messaging/providers/vonage/:providerId') ->desc('Update Vonage provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1723,7 +1723,7 @@ App::patch('/v1/messaging/providers/vonage/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::patch('/v1/messaging/providers/fcm/:providerId') +Http::patch('/v1/messaging/providers/fcm/:providerId') ->desc('Update FCM provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1792,7 +1792,7 @@ App::patch('/v1/messaging/providers/fcm/:providerId') }); -App::patch('/v1/messaging/providers/apns/:providerId') +Http::patch('/v1/messaging/providers/apns/:providerId') ->desc('Update APNS provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1887,7 +1887,7 @@ App::patch('/v1/messaging/providers/apns/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -App::delete('/v1/messaging/providers/:providerId') +Http::delete('/v1/messaging/providers/:providerId') ->desc('Delete provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.delete') @@ -1922,7 +1922,7 @@ App::delete('/v1/messaging/providers/:providerId') ->noContent(); }); -App::post('/v1/messaging/topics') +Http::post('/v1/messaging/topics') ->desc('Create topic') ->groups(['api', 'messaging']) ->label('audits.event', 'topic.create') @@ -1965,7 +1965,7 @@ App::post('/v1/messaging/topics') ->dynamic($topic, Response::MODEL_TOPIC); }); -App::get('/v1/messaging/topics') +Http::get('/v1/messaging/topics') ->desc('List topics') ->groups(['api', 'messaging']) ->label('scope', 'topics.read') @@ -2016,7 +2016,7 @@ App::get('/v1/messaging/topics') ]), Response::MODEL_TOPIC_LIST); }); -App::get('/v1/messaging/topics/:topicId/logs') +Http::get('/v1/messaging/topics/:topicId/logs') ->desc('List topic logs') ->groups(['api', 'messaging']) ->label('scope', 'topics.read') @@ -2105,7 +2105,7 @@ App::get('/v1/messaging/topics/:topicId/logs') ]), Response::MODEL_LOG_LIST); }); -App::get('/v1/messaging/topics/:topicId') +Http::get('/v1/messaging/topics/:topicId') ->desc('Get topic') ->groups(['api', 'messaging']) ->label('scope', 'topics.read') @@ -2132,7 +2132,7 @@ App::get('/v1/messaging/topics/:topicId') ->dynamic($topic, Response::MODEL_TOPIC); }); -App::patch('/v1/messaging/topics/:topicId') +Http::patch('/v1/messaging/topics/:topicId') ->desc('Update topic') ->groups(['api', 'messaging']) ->label('audits.event', 'topic.update') @@ -2176,7 +2176,7 @@ App::patch('/v1/messaging/topics/:topicId') ->dynamic($topic, Response::MODEL_TOPIC); }); -App::delete('/v1/messaging/topics/:topicId') +Http::delete('/v1/messaging/topics/:topicId') ->desc('Delete topic') ->groups(['api', 'messaging']) ->label('audits.event', 'topic.delete') @@ -2216,7 +2216,7 @@ App::delete('/v1/messaging/topics/:topicId') ->noContent(); }); -App::post('/v1/messaging/topics/:topicId/subscribers') +Http::post('/v1/messaging/topics/:topicId/subscribers') ->desc('Create subscriber') ->groups(['api', 'messaging']) ->label('audits.event', 'subscriber.create') @@ -2312,7 +2312,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers') ->dynamic($subscriber, Response::MODEL_SUBSCRIBER); }); -App::get('/v1/messaging/topics/:topicId/subscribers') +Http::get('/v1/messaging/topics/:topicId/subscribers') ->desc('List subscribers') ->groups(['api', 'messaging']) ->label('scope', 'subscribers.read') @@ -2386,7 +2386,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers') ]), Response::MODEL_SUBSCRIBER_LIST); }); -App::get('/v1/messaging/subscribers/:subscriberId/logs') +Http::get('/v1/messaging/subscribers/:subscriberId/logs') ->desc('List subscriber logs') ->groups(['api', 'messaging']) ->label('scope', 'subscribers.read') @@ -2475,7 +2475,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs') ]), Response::MODEL_LOG_LIST); }); -App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') +Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->desc('Get subscriber') ->groups(['api', 'messaging']) ->label('scope', 'subscribers.read') @@ -2514,7 +2514,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->dynamic($subscriber, Response::MODEL_SUBSCRIBER); }); -App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') +Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->desc('Delete subscriber') ->groups(['api', 'messaging']) ->label('audits.event', 'subscriber.delete') @@ -2573,7 +2573,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->noContent(); }); -App::post('/v1/messaging/messages/email') +Http::post('/v1/messaging/messages/email') ->desc('Create email') ->groups(['api', 'messaging']) ->label('audits.event', 'message.create') @@ -2695,7 +2695,7 @@ App::post('/v1/messaging/messages/email') break; case MessageStatus::SCHEDULED: $schedule = $dbForConsole->createDocument('schedules', new Document([ - 'region' => App::getEnv('_APP_REGION', 'default'), + 'region' => Http::getEnv('_APP_REGION', 'default'), 'resourceType' => 'message', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), @@ -2725,7 +2725,7 @@ App::post('/v1/messaging/messages/email') ->dynamic($message, Response::MODEL_MESSAGE); }); -App::post('/v1/messaging/messages/sms') +Http::post('/v1/messaging/messages/sms') ->desc('Create SMS') ->groups(['api', 'messaging']) ->label('audits.event', 'message.create') @@ -2811,7 +2811,7 @@ App::post('/v1/messaging/messages/sms') break; case MessageStatus::SCHEDULED: $schedule = $dbForConsole->createDocument('schedules', new Document([ - 'region' => App::getEnv('_APP_REGION', 'default'), + 'region' => Http::getEnv('_APP_REGION', 'default'), 'resourceType' => 'message', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), @@ -2841,7 +2841,7 @@ App::post('/v1/messaging/messages/sms') ->dynamic($message, Response::MODEL_MESSAGE); }); -App::post('/v1/messaging/messages/push') +Http::post('/v1/messaging/messages/push') ->desc('Create push notification') ->groups(['api', 'messaging']) ->label('audits.event', 'message.create') @@ -2937,9 +2937,9 @@ App::post('/v1/messaging/messages/push') throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED); } - $host = App::getEnv('_APP_DOMAIN', 'localhost'); + $host = Http::getEnv('_APP_DOMAIN', 'localhost'); $domain = new Domain(\parse_url($host, PHP_URL_HOST)); - $protocol = App::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https'; + $protocol = Http::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https'; if (!$domain->isKnown()) { throw new Exception(Exception::STORAGE_FILE_NOT_PUBLIC); @@ -2977,7 +2977,7 @@ App::post('/v1/messaging/messages/push') break; case MessageStatus::SCHEDULED: $schedule = $dbForConsole->createDocument('schedules', new Document([ - 'region' => App::getEnv('_APP_REGION', 'default'), + 'region' => Http::getEnv('_APP_REGION', 'default'), 'resourceType' => 'message', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), @@ -3007,7 +3007,7 @@ App::post('/v1/messaging/messages/push') ->dynamic($message, Response::MODEL_MESSAGE); }); -App::get('/v1/messaging/messages') +Http::get('/v1/messaging/messages') ->desc('List messages') ->groups(['api', 'messaging']) ->label('scope', 'messages.read') @@ -3058,7 +3058,7 @@ App::get('/v1/messaging/messages') ]), Response::MODEL_MESSAGE_LIST); }); -App::get('/v1/messaging/messages/:messageId/logs') +Http::get('/v1/messaging/messages/:messageId/logs') ->desc('List message logs') ->groups(['api', 'messaging']) ->label('scope', 'messages.read') @@ -3147,7 +3147,7 @@ App::get('/v1/messaging/messages/:messageId/logs') ]), Response::MODEL_LOG_LIST); }); -App::get('/v1/messaging/messages/:messageId/targets') +Http::get('/v1/messaging/messages/:messageId/targets') ->desc('List message targets') ->groups(['api', 'messaging']) ->label('scope', 'messages.read') @@ -3212,7 +3212,7 @@ App::get('/v1/messaging/messages/:messageId/targets') ]), Response::MODEL_TARGET_LIST); }); -App::get('/v1/messaging/messages/:messageId') +Http::get('/v1/messaging/messages/:messageId') ->desc('Get message') ->groups(['api', 'messaging']) ->label('scope', 'messages.read') @@ -3236,7 +3236,7 @@ App::get('/v1/messaging/messages/:messageId') $response->dynamic($message, Response::MODEL_MESSAGE); }); -App::patch('/v1/messaging/messages/email/:messageId') +Http::patch('/v1/messaging/messages/email/:messageId') ->desc('Update email') ->groups(['api', 'messaging']) ->label('audits.event', 'message.update') @@ -3320,7 +3320,7 @@ App::patch('/v1/messaging/messages/email/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForConsole->createDocument('schedules', new Document([ - 'region' => App::getEnv('_APP_REGION', 'default'), + 'region' => Http::getEnv('_APP_REGION', 'default'), 'resourceType' => 'message', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), @@ -3411,7 +3411,7 @@ App::patch('/v1/messaging/messages/email/:messageId') ->dynamic($message, Response::MODEL_MESSAGE); }); -App::patch('/v1/messaging/messages/sms/:messageId') +Http::patch('/v1/messaging/messages/sms/:messageId') ->desc('Update SMS') ->groups(['api', 'messaging']) ->label('audits.event', 'message.update') @@ -3491,7 +3491,7 @@ App::patch('/v1/messaging/messages/sms/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForConsole->createDocument('schedules', new Document([ - 'region' => App::getEnv('_APP_REGION', 'default'), + 'region' => Http::getEnv('_APP_REGION', 'default'), 'resourceType' => 'message', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), @@ -3566,7 +3566,7 @@ App::patch('/v1/messaging/messages/sms/:messageId') ->dynamic($message, Response::MODEL_MESSAGE); }); -App::patch('/v1/messaging/messages/push/:messageId') +Http::patch('/v1/messaging/messages/push/:messageId') ->desc('Update push notification') ->groups(['api', 'messaging']) ->label('audits.event', 'message.update') @@ -3655,7 +3655,7 @@ App::patch('/v1/messaging/messages/push/:messageId') if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) { $schedule = $dbForConsole->createDocument('schedules', new Document([ - 'region' => App::getEnv('_APP_REGION', 'default'), + 'region' => Http::getEnv('_APP_REGION', 'default'), 'resourceType' => 'message', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), @@ -3762,9 +3762,9 @@ App::patch('/v1/messaging/messages/push/:messageId') throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED); } - $host = App::getEnv('_APP_DOMAIN', 'localhost'); + $host = Http::getEnv('_APP_DOMAIN', 'localhost'); $domain = new Domain(\parse_url($host, PHP_URL_HOST)); - $protocol = App::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https'; + $protocol = Http::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https'; if (!$domain->isKnown()) { throw new Exception(Exception::STORAGE_FILE_NOT_PUBLIC); @@ -3794,7 +3794,7 @@ App::patch('/v1/messaging/messages/push/:messageId') ->dynamic($message, Response::MODEL_MESSAGE); }); -App::delete('/v1/messaging/messages/:messageId') +Http::delete('/v1/messaging/messages/:messageId') ->desc('Delete message') ->groups(['api', 'messaging']) ->label('audits.event', 'message.delete') diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index 0831a473cb..badb482c34 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -9,7 +9,7 @@ use Appwrite\Role; use Appwrite\Utopia\Database\Validator\Queries\Migrations; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -21,16 +21,16 @@ use Utopia\Migration\Sources\Appwrite; use Utopia\Migration\Sources\Firebase; use Utopia\Migration\Sources\NHost; use Utopia\Migration\Sources\Supabase; -use Utopia\Validator\ArrayList; -use Utopia\Validator\Host; -use Utopia\Validator\Integer; -use Utopia\Validator\Text; -use Utopia\Validator\URL; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\ArrayList; +use Utopia\Http\Validator\Host; +use Utopia\Http\Validator\Integer; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\URL; +use Utopia\Http\Validator\WhiteList; include_once __DIR__ . '/../shared/api.php'; -App::post('/v1/migrations/appwrite') +Http::post('/v1/migrations/appwrite') ->groups(['api', 'migrations']) ->desc('Migrate Appwrite Data') ->label('scope', 'migrations.write') @@ -84,7 +84,7 @@ App::post('/v1/migrations/appwrite') ->dynamic($migration, Response::MODEL_MIGRATION); }); -App::post('/v1/migrations/firebase/oauth') +Http::post('/v1/migrations/firebase/oauth') ->groups(['api', 'migrations']) ->desc('Migrate Firebase Data (OAuth)') ->label('scope', 'migrations.write') @@ -109,8 +109,8 @@ App::post('/v1/migrations/firebase/oauth') ->inject('request') ->action(function (array $resources, string $projectId, Response $response, Database $dbForProject, Database $dbForConsole, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations, Request $request) { $firebase = new OAuth2Firebase( - App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), - App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), + Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), + Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), $request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect' ); @@ -186,7 +186,7 @@ App::post('/v1/migrations/firebase/oauth') ->dynamic($migration, Response::MODEL_MIGRATION); }); -App::post('/v1/migrations/firebase') +Http::post('/v1/migrations/firebase') ->groups(['api', 'migrations']) ->desc('Migrate Firebase Data (Service Account)') ->label('scope', 'migrations.write') @@ -246,7 +246,7 @@ App::post('/v1/migrations/firebase') ->dynamic($migration, Response::MODEL_MIGRATION); }); -App::post('/v1/migrations/supabase') +Http::post('/v1/migrations/supabase') ->groups(['api', 'migrations']) ->desc('Migrate Supabase Data') ->label('scope', 'migrations.write') @@ -306,7 +306,7 @@ App::post('/v1/migrations/supabase') ->dynamic($migration, Response::MODEL_MIGRATION); }); -App::post('/v1/migrations/nhost') +Http::post('/v1/migrations/nhost') ->groups(['api', 'migrations']) ->desc('Migrate NHost Data') ->label('scope', 'migrations.write') @@ -368,7 +368,7 @@ App::post('/v1/migrations/nhost') ->dynamic($migration, Response::MODEL_MIGRATION); }); -App::get('/v1/migrations') +Http::get('/v1/migrations') ->groups(['api', 'migrations']) ->desc('List Migrations') ->label('scope', 'migrations.read') @@ -421,7 +421,7 @@ App::get('/v1/migrations') ]), Response::MODEL_MIGRATION_LIST); }); -App::get('/v1/migrations/:migrationId') +Http::get('/v1/migrations/:migrationId') ->groups(['api', 'migrations']) ->desc('Get Migration') ->label('scope', 'migrations.read') @@ -445,7 +445,7 @@ App::get('/v1/migrations/:migrationId') $response->dynamic($migration, Response::MODEL_MIGRATION); }); -App::get('/v1/migrations/appwrite/report') +Http::get('/v1/migrations/appwrite/report') ->groups(['api', 'migrations']) ->desc('Generate a report on Appwrite Data') ->label('scope', 'migrations.write') @@ -487,7 +487,7 @@ App::get('/v1/migrations/appwrite/report') ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); -App::get('/v1/migrations/firebase/report') +Http::get('/v1/migrations/firebase/report') ->groups(['api', 'migrations']) ->desc('Generate a report on Firebase Data') ->label('scope', 'migrations.write') @@ -534,7 +534,7 @@ App::get('/v1/migrations/firebase/report') ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); -App::get('/v1/migrations/firebase/report/oauth') +Http::get('/v1/migrations/firebase/report/oauth') ->groups(['api', 'migrations']) ->desc('Generate a report on Firebase Data using OAuth') ->label('scope', 'migrations.write') @@ -553,8 +553,8 @@ App::get('/v1/migrations/firebase/report/oauth') ->inject('dbForConsole') ->action(function (array $resources, string $projectId, Response $response, Request $request, Document $user, Database $dbForConsole) { $firebase = new OAuth2Firebase( - App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), - App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), + Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), + Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), $request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect' ); @@ -575,7 +575,7 @@ App::get('/v1/migrations/firebase/report/oauth') throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); } - if (App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', '') === '' || App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', '') === '') { + if (Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', '') === '' || Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', '') === '') { throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); } @@ -625,7 +625,7 @@ App::get('/v1/migrations/firebase/report/oauth') ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); -App::get('/v1/migrations/firebase/connect') +Http::get('/v1/migrations/firebase/connect') ->desc('Authorize with firebase') ->groups(['api', 'migrations']) ->label('scope', 'migrations.write') @@ -655,8 +655,8 @@ App::get('/v1/migrations/firebase/connect') $dbForConsole->updateDocument('users', $user->getId(), $user); $oauth2 = new OAuth2Firebase( - App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), - App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), + Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), + Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), $request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect' ); $url = $oauth2->getLoginURL(); @@ -667,7 +667,7 @@ App::get('/v1/migrations/firebase/connect') ->redirect($url); }); -App::get('/v1/migrations/firebase/redirect') +Http::get('/v1/migrations/firebase/redirect') ->desc('Capture and receive data on Firebase authorization') ->groups(['api', 'migrations']) ->label('scope', 'public') @@ -710,8 +710,8 @@ App::get('/v1/migrations/firebase/redirect') // OAuth Authroization if (!empty($code)) { $oauth2 = new OAuth2Firebase( - App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), - App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), + Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), + Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), $request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect' ); @@ -779,7 +779,7 @@ App::get('/v1/migrations/firebase/redirect') ->redirect($redirect); }); -App::get('/v1/migrations/firebase/projects') +Http::get('/v1/migrations/firebase/projects') ->desc('List Firebase Projects') ->groups(['api', 'migrations']) ->label('scope', 'migrations.read') @@ -797,8 +797,8 @@ App::get('/v1/migrations/firebase/projects') ->inject('request') ->action(function (Document $user, Response $response, Document $project, Database $dbForConsole, Request $request) { $firebase = new OAuth2Firebase( - App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), - App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), + Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), + Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), $request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect' ); @@ -819,7 +819,7 @@ App::get('/v1/migrations/firebase/projects') throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); } - if (App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', '') === '' || App::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', '') === '') { + if (Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', '') === '' || Http::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', '') === '') { throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); } @@ -868,7 +868,7 @@ App::get('/v1/migrations/firebase/projects') ]), Response::MODEL_MIGRATION_FIREBASE_PROJECT_LIST); }); -App::get('/v1/migrations/firebase/deauthorize') +Http::get('/v1/migrations/firebase/deauthorize') ->desc('Revoke Appwrite\'s authorization to access Firebase Projects') ->groups(['api', 'migrations']) ->label('scope', 'migrations.write') @@ -896,7 +896,7 @@ App::get('/v1/migrations/firebase/deauthorize') $response->noContent(); }); -App::get('/v1/migrations/supabase/report') +Http::get('/v1/migrations/supabase/report') ->groups(['api', 'migrations']) ->desc('Generate a report on Supabase Data') ->label('scope', 'migrations.write') @@ -939,7 +939,7 @@ App::get('/v1/migrations/supabase/report') ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); -App::get('/v1/migrations/nhost/report') +Http::get('/v1/migrations/nhost/report') ->groups(['api', 'migrations']) ->desc('Generate a report on NHost Data') ->label('scope', 'migrations.write') @@ -982,7 +982,7 @@ App::get('/v1/migrations/nhost/report') ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); -App::patch('/v1/migrations/:migrationId') +Http::patch('/v1/migrations/:migrationId') ->groups(['api', 'migrations']) ->desc('Retry Migration') ->label('scope', 'migrations.write') @@ -1027,7 +1027,7 @@ App::patch('/v1/migrations/:migrationId') $response->noContent(); }); -App::delete('/v1/migrations/:migrationId') +Http::delete('/v1/migrations/:migrationId') ->groups(['api', 'migrations']) ->desc('Delete Migration') ->label('scope', 'migrations.write') diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 208e288614..dc84dc060a 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -2,7 +2,7 @@ use Appwrite\Extend\Exception; use Appwrite\Utopia\Response; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; @@ -13,10 +13,10 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Datetime as DateTimeValidator; use Utopia\Database\Validator\UID; -use Utopia\Validator\Text; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; -App::get('/v1/project/usage') +Http::get('/v1/project/usage') ->desc('Get project usage stats') ->groups(['api', 'usage']) ->label('scope', 'projects.read') @@ -182,7 +182,7 @@ App::get('/v1/project/usage') // Variables -App::post('/v1/project/variables') +Http::post('/v1/project/variables') ->desc('Create Variable') ->groups(['api']) ->label('scope', 'projects.write') @@ -237,7 +237,7 @@ App::post('/v1/project/variables') ->dynamic($variable, Response::MODEL_VARIABLE); }); -App::get('/v1/project/variables') +Http::get('/v1/project/variables') ->desc('List Variables') ->groups(['api']) ->label('scope', 'projects.read') @@ -262,7 +262,7 @@ App::get('/v1/project/variables') ]), Response::MODEL_VARIABLE_LIST); }); -App::get('/v1/project/variables/:variableId') +Http::get('/v1/project/variables/:variableId') ->desc('Get Variable') ->groups(['api']) ->label('scope', 'projects.read') @@ -286,7 +286,7 @@ App::get('/v1/project/variables/:variableId') $response->dynamic($variable, Response::MODEL_VARIABLE); }); -App::put('/v1/project/variables/:variableId') +Http::put('/v1/project/variables/:variableId') ->desc('Update Variable') ->groups(['api']) ->label('scope', 'projects.write') @@ -332,7 +332,7 @@ App::put('/v1/project/variables/:variableId') $response->dynamic($variable, Response::MODEL_VARIABLE); }); -App::delete('/v1/project/variables/:variableId') +Http::delete('/v1/project/variables/:variableId') ->desc('Delete Variable') ->groups(['api']) ->label('scope', 'projects.write') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index cb7b4f61cc..17f19a926f 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -13,7 +13,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Projects; use Appwrite\Utopia\Response; use PHPMailer\PHPMailer\PHPMailer; use Utopia\Abuse\Adapters\TimeLimit; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\Config\Config; @@ -40,7 +40,7 @@ use Utopia\Validator\Text; use Utopia\Validator\URL; use Utopia\Validator\WhiteList; -App::init() +Http::init() ->groups(['projects']) ->inject('project') ->action(function (Document $project) { @@ -49,7 +49,7 @@ App::init() } }); -App::post('/v1/projects') +Http::post('/v1/projects') ->desc('Create project') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -62,7 +62,7 @@ App::post('/v1/projects') ->param('projectId', '', new ProjectId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, and hyphen. Can\'t start with a special char. Max length is 36 chars.') ->param('name', null, new Text(128), 'Project name. Max length: 128 chars.') ->param('teamId', '', new UID(), 'Team unique ID.') - ->param('region', App::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn ($config) => !$config['disabled']))), 'Project Region.', true) + ->param('region', Http::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn ($config) => !$config['disabled']))), 'Project Region.', true) ->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true) ->param('logo', '', new Text(1024), 'Project logo.', true) ->param('url', '', new URL(), 'Project URL.', true) @@ -84,15 +84,15 @@ App::post('/v1/projects') throw new Exception(Exception::TEAM_NOT_FOUND); } - $allowList = \array_filter(\explode(',', App::getEnv('_APP_PROJECT_REGIONS', ''))); + $allowList = \array_filter(\explode(',', Http::getEnv('_APP_PROJECT_REGIONS', ''))); if (!empty($allowList) && !\in_array($region, $allowList)) { throw new Exception(Exception::PROJECT_REGION_UNSUPPORTED, 'Region "' . $region . '" is not supported'); } - $auth = Config::getParam('auth', []); + $authConfig = Config::getParam('auth', []); $auths = ['limit' => 0, 'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT, 'passwordHistory' => 0, 'passwordDictionary' => false, 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, 'personalDataCheck' => false]; - foreach ($auth as $index => $method) { + foreach ($authConfig as $index => $method) { $auths[$method['key'] ?? ''] = true; } @@ -127,7 +127,7 @@ App::post('/v1/projects') } } - $databaseOverride = App::getEnv('_APP_DATABASE_OVERRIDE', null); + $databaseOverride = Http::getEnv('_APP_DATABASE_OVERRIDE', null); $index = array_search($databaseOverride, $databases); if ($index !== false) { $database = $databases[$index]; @@ -228,7 +228,7 @@ App::post('/v1/projects') ->dynamic($project, Response::MODEL_PROJECT); }); -App::get('/v1/projects') +Http::get('/v1/projects') ->desc('List projects') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -281,7 +281,7 @@ App::get('/v1/projects') ]), Response::MODEL_PROJECT_LIST); }); -App::get('/v1/projects/:projectId') +Http::get('/v1/projects/:projectId') ->desc('Get project') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -305,7 +305,7 @@ App::get('/v1/projects/:projectId') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::patch('/v1/projects/:projectId') +Http::patch('/v1/projects/:projectId') ->desc('Update project') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -352,7 +352,7 @@ App::patch('/v1/projects/:projectId') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::patch('/v1/projects/:projectId/team') +Http::patch('/v1/projects/:projectId/team') ->desc('Update Project Team') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -420,7 +420,7 @@ App::patch('/v1/projects/:projectId/team') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::patch('/v1/projects/:projectId/service') +Http::patch('/v1/projects/:projectId/service') ->desc('Update service status') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -451,7 +451,7 @@ App::patch('/v1/projects/:projectId/service') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::patch('/v1/projects/:projectId/service/all') +Http::patch('/v1/projects/:projectId/service/all') ->desc('Update all service status') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -485,7 +485,7 @@ App::patch('/v1/projects/:projectId/service/all') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::patch('/v1/projects/:projectId/oauth2') +Http::patch('/v1/projects/:projectId/oauth2') ->desc('Update project OAuth2') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -529,7 +529,7 @@ App::patch('/v1/projects/:projectId/oauth2') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::patch('/v1/projects/:projectId/auth/limit') +Http::patch('/v1/projects/:projectId/auth/limit') ->desc('Update project users limit') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -560,7 +560,7 @@ App::patch('/v1/projects/:projectId/auth/limit') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::patch('/v1/projects/:projectId/auth/duration') +Http::patch('/v1/projects/:projectId/auth/duration') ->desc('Update project authentication duration') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -591,7 +591,7 @@ App::patch('/v1/projects/:projectId/auth/duration') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::patch('/v1/projects/:projectId/auth/:method') +Http::patch('/v1/projects/:projectId/auth/:method') ->desc('Update project auth method status. Use this endpoint to enable or disable a given auth method for this project.') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -609,8 +609,8 @@ App::patch('/v1/projects/:projectId/auth/:method') ->action(function (string $projectId, string $method, bool $status, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); - $auth = Config::getParam('auth')[$method] ?? []; - $authKey = $auth['key'] ?? ''; + $authConfig = Config::getParam('auth')[$method] ?? []; + $authKey = $authConfig['key'] ?? ''; $status = ($status === '1' || $status === 'true' || $status === 1 || $status === true); if ($project->isEmpty()) { @@ -625,7 +625,7 @@ App::patch('/v1/projects/:projectId/auth/:method') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::patch('/v1/projects/:projectId/auth/password-history') +Http::patch('/v1/projects/:projectId/auth/password-history') ->desc('Update authentication password history. Use this endpoint to set the number of password history to save and 0 to disable password history.') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -656,7 +656,7 @@ App::patch('/v1/projects/:projectId/auth/password-history') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::patch('/v1/projects/:projectId/auth/password-dictionary') +Http::patch('/v1/projects/:projectId/auth/password-dictionary') ->desc('Update authentication password dictionary status. Use this endpoint to enable or disable the dicitonary check for user password') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -687,7 +687,7 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::patch('/v1/projects/:projectId/auth/personal-data') +Http::patch('/v1/projects/:projectId/auth/personal-data') ->desc('Enable or disable checking user passwords for similarity with their personal data.') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -718,7 +718,7 @@ App::patch('/v1/projects/:projectId/auth/personal-data') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::patch('/v1/projects/:projectId/auth/max-sessions') +Http::patch('/v1/projects/:projectId/auth/max-sessions') ->desc('Update project user sessions limit') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -749,7 +749,7 @@ App::patch('/v1/projects/:projectId/auth/max-sessions') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::delete('/v1/projects/:projectId') +Http::delete('/v1/projects/:projectId') ->desc('Delete project') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -783,7 +783,7 @@ App::delete('/v1/projects/:projectId') // Webhooks -App::post('/v1/projects/:projectId/webhooks') +Http::post('/v1/projects/:projectId/webhooks') ->desc('Create webhook') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -841,7 +841,7 @@ App::post('/v1/projects/:projectId/webhooks') ->dynamic($webhook, Response::MODEL_WEBHOOK); }); -App::get('/v1/projects/:projectId/webhooks') +Http::get('/v1/projects/:projectId/webhooks') ->desc('List webhooks') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -873,7 +873,7 @@ App::get('/v1/projects/:projectId/webhooks') ]), Response::MODEL_WEBHOOK_LIST); }); -App::get('/v1/projects/:projectId/webhooks/:webhookId') +Http::get('/v1/projects/:projectId/webhooks/:webhookId') ->desc('Get webhook') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -907,7 +907,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') $response->dynamic($webhook, Response::MODEL_WEBHOOK); }); -App::put('/v1/projects/:projectId/webhooks/:webhookId') +Http::put('/v1/projects/:projectId/webhooks/:webhookId') ->desc('Update webhook') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -966,7 +966,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') $response->dynamic($webhook, Response::MODEL_WEBHOOK); }); -App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') +Http::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') ->desc('Update webhook signature key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1005,7 +1005,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') $response->dynamic($webhook, Response::MODEL_WEBHOOK); }); -App::delete('/v1/projects/:projectId/webhooks/:webhookId') +Http::delete('/v1/projects/:projectId/webhooks/:webhookId') ->desc('Delete webhook') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1044,7 +1044,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') // Keys -App::post('/v1/projects/:projectId/keys') +Http::post('/v1/projects/:projectId/keys') ->desc('Create key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1094,7 +1094,7 @@ App::post('/v1/projects/:projectId/keys') ->dynamic($key, Response::MODEL_KEY); }); -App::get('/v1/projects/:projectId/keys') +Http::get('/v1/projects/:projectId/keys') ->desc('List keys') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -1126,7 +1126,7 @@ App::get('/v1/projects/:projectId/keys') ]), Response::MODEL_KEY_LIST); }); -App::get('/v1/projects/:projectId/keys/:keyId') +Http::get('/v1/projects/:projectId/keys/:keyId') ->desc('Get key') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -1160,7 +1160,7 @@ App::get('/v1/projects/:projectId/keys/:keyId') $response->dynamic($key, Response::MODEL_KEY); }); -App::put('/v1/projects/:projectId/keys/:keyId') +Http::put('/v1/projects/:projectId/keys/:keyId') ->desc('Update key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1206,7 +1206,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') $response->dynamic($key, Response::MODEL_KEY); }); -App::delete('/v1/projects/:projectId/keys/:keyId') +Http::delete('/v1/projects/:projectId/keys/:keyId') ->desc('Delete key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1245,7 +1245,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId') // Platforms -App::post('/v1/projects/:projectId/platforms') +Http::post('/v1/projects/:projectId/platforms') ->desc('Create platform') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1295,7 +1295,7 @@ App::post('/v1/projects/:projectId/platforms') ->dynamic($platform, Response::MODEL_PLATFORM); }); -App::get('/v1/projects/:projectId/platforms') +Http::get('/v1/projects/:projectId/platforms') ->desc('List platforms') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -1327,7 +1327,7 @@ App::get('/v1/projects/:projectId/platforms') ]), Response::MODEL_PLATFORM_LIST); }); -App::get('/v1/projects/:projectId/platforms/:platformId') +Http::get('/v1/projects/:projectId/platforms/:platformId') ->desc('Get platform') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -1361,7 +1361,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') $response->dynamic($platform, Response::MODEL_PLATFORM); }); -App::put('/v1/projects/:projectId/platforms/:platformId') +Http::put('/v1/projects/:projectId/platforms/:platformId') ->desc('Update platform') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1408,7 +1408,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') $response->dynamic($platform, Response::MODEL_PLATFORM); }); -App::delete('/v1/projects/:projectId/platforms/:platformId') +Http::delete('/v1/projects/:projectId/platforms/:platformId') ->desc('Delete platform') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1447,7 +1447,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') // CUSTOM SMTP and Templates -App::patch('/v1/projects/:projectId/smtp') +Http::patch('/v1/projects/:projectId/smtp') ->desc('Update SMTP') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1537,7 +1537,7 @@ App::patch('/v1/projects/:projectId/smtp') $response->dynamic($project, Response::MODEL_PROJECT); }); -App::post('/v1/projects/:projectId/smtp/tests') +Http::post('/v1/projects/:projectId/smtp/tests') ->desc('Create SMTP test') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1548,8 +1548,8 @@ App::post('/v1/projects/:projectId/smtp/tests') ->label('sdk.response.model', Response::MODEL_NONE) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('emails', [], new ArrayList(new Email(), 10), 'Array of emails to send test email to. Maximum of 10 emails are allowed.') - ->param('senderName', App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'), new Text(255, 0), 'Name of the email sender') - ->param('senderEmail', App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), new Email(), 'Email of the sender') + ->param('senderName', Http::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'), new Text(255, 0), 'Name of the email sender') + ->param('senderEmail', Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), new Email(), 'Email of the sender') ->param('replyTo', '', new Email(), 'Reply to email', true) ->param('host', '', new HostName(), 'SMTP server host name') ->param('port', 587, new Integer(), 'SMTP server port', true) @@ -1596,7 +1596,7 @@ App::post('/v1/projects/:projectId/smtp/tests') return $response->noContent(); }); -App::get('/v1/projects/:projectId/templates/sms/:type/:locale') +Http::get('/v1/projects/:projectId/templates/sms/:type/:locale') ->desc('Get custom SMS template') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1637,7 +1637,7 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale') }); -App::get('/v1/projects/:projectId/templates/email/:type/:locale') +Http::get('/v1/projects/:projectId/templates/email/:type/:locale') ->desc('Get custom email template') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1689,7 +1689,7 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale') $response->dynamic(new Document($template), Response::MODEL_EMAIL_TEMPLATE); }); -App::patch('/v1/projects/:projectId/templates/sms/:type/:locale') +Http::patch('/v1/projects/:projectId/templates/sms/:type/:locale') ->desc('Update custom SMS template') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1729,7 +1729,7 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale') ]), Response::MODEL_SMS_TEMPLATE); }); -App::patch('/v1/projects/:projectId/templates/email/:type/:locale') +Http::patch('/v1/projects/:projectId/templates/email/:type/:locale') ->desc('Update custom email templates') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1779,7 +1779,7 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale') ]), Response::MODEL_EMAIL_TEMPLATE); }); -App::delete('/v1/projects/:projectId/templates/sms/:type/:locale') +Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale') ->desc('Reset custom SMS template') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1822,7 +1822,7 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale') ]), Response::MODEL_SMS_TEMPLATE); }); -App::delete('/v1/projects/:projectId/templates/email/:type/:locale') +Http::delete('/v1/projects/:projectId/templates/email/:type/:locale') ->desc('Reset custom email template') ->groups(['api', 'projects']) ->label('scope', 'projects.write') diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php index b8f889c958..7f5564050f 100644 --- a/app/controllers/api/proxy.php +++ b/app/controllers/api/proxy.php @@ -7,7 +7,7 @@ use Appwrite\Extend\Exception; use Appwrite\Network\Validator\CNAME; use Appwrite\Utopia\Database\Validator\Queries\Rules; use Appwrite\Utopia\Response; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Query as QueryException; @@ -16,11 +16,11 @@ use Utopia\Database\Query; use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; use Utopia\Logger\Log; -use Utopia\Validator\Domain as ValidatorDomain; -use Utopia\Validator\Text; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\Domain as ValidatorDomain; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; -App::post('/v1/proxy/rules') +Http::post('/v1/proxy/rules') ->groups(['api', 'proxy']) ->desc('Create Rule') ->label('scope', 'rules.write') @@ -44,7 +44,7 @@ App::post('/v1/proxy/rules') ->inject('dbForConsole') ->inject('dbForProject') ->action(function (string $domain, string $resourceType, string $resourceId, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForConsole, Database $dbForProject) { - $mainDomain = App::getEnv('_APP_DOMAIN', ''); + $mainDomain = Http::getEnv('_APP_DOMAIN', ''); if ($domain === $mainDomain) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.'); } @@ -108,13 +108,13 @@ App::post('/v1/proxy/rules') ]); $status = 'created'; - $functionsDomain = App::getEnv('_APP_DOMAIN_FUNCTIONS'); + $functionsDomain = Http::getEnv('_APP_DOMAIN_FUNCTIONS'); if (!empty($functionsDomain) && \str_ends_with($domain->get(), $functionsDomain)) { $status = 'verified'; } if ($status === 'created') { - $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); + $target = new Domain(Http::getEnv('_APP_DOMAIN_TARGET', '')); $validator = new CNAME($target->get()); // Verify Domain with DNS records if ($validator->isValid($domain->get())) { @@ -140,7 +140,7 @@ App::post('/v1/proxy/rules') ->dynamic($rule, Response::MODEL_PROXY_RULE); }); -App::get('/v1/proxy/rules') +Http::get('/v1/proxy/rules') ->groups(['api', 'proxy']) ->desc('List Rules') ->label('scope', 'rules.read') @@ -203,7 +203,7 @@ App::get('/v1/proxy/rules') ]), Response::MODEL_PROXY_RULE_LIST); }); -App::get('/v1/proxy/rules/:ruleId') +Http::get('/v1/proxy/rules/:ruleId') ->groups(['api', 'proxy']) ->desc('Get Rule') ->label('scope', 'rules.read') @@ -232,7 +232,7 @@ App::get('/v1/proxy/rules/:ruleId') $response->dynamic($rule, Response::MODEL_PROXY_RULE); }); -App::delete('/v1/proxy/rules/:ruleId') +Http::delete('/v1/proxy/rules/:ruleId') ->groups(['api', 'proxy']) ->desc('Delete Rule') ->label('scope', 'rules.write') @@ -269,7 +269,7 @@ App::delete('/v1/proxy/rules/:ruleId') $response->noContent(); }); -App::patch('/v1/proxy/rules/:ruleId/verification') +Http::patch('/v1/proxy/rules/:ruleId/verification') ->desc('Update Rule Verification Status') ->groups(['api', 'proxy']) ->label('scope', 'rules.write') @@ -296,7 +296,7 @@ App::patch('/v1/proxy/rules/:ruleId/verification') throw new Exception(Exception::RULE_NOT_FOUND); } - $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); + $target = new Domain(Http::getEnv('_APP_DOMAIN_TARGET', '')); if (!$target->isKnown() || $target->isTest()) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Domain target must be configured as environment variable.'); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 62b0ef64c8..591ac5f58d 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -10,7 +10,7 @@ use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\Queries\Buckets; use Appwrite\Utopia\Database\Validator\Queries\Files; use Appwrite\Utopia\Response; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -37,14 +37,14 @@ use Utopia\Storage\Validator\FileExt; use Utopia\Storage\Validator\FileSize; use Utopia\Storage\Validator\Upload; use Utopia\Swoole\Request; -use Utopia\Validator\ArrayList; -use Utopia\Validator\Boolean; -use Utopia\Validator\HexColor; -use Utopia\Validator\Range; -use Utopia\Validator\Text; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\ArrayList; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\HexColor; +use Utopia\Http\Validator\Range; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; -App::post('/v1/storage/buckets') +Http::post('/v1/storage/buckets') ->desc('Create bucket') ->groups(['api', 'storage']) ->label('scope', 'buckets.write') @@ -63,7 +63,7 @@ App::post('/v1/storage/buckets') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true) - ->param('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true) + ->param('maximumFileSize', (int) Http::getEnv('_APP_STORAGE_LIMIT', 0), new Range(1, (int) Http::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(Http::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true) ->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true) ->param('compression', Compression::NONE, new WhiteList([Compression::NONE, Compression::GZIP, Compression::ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . Compression::NONE . ', [' . Compression::GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . Compression::ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true) ->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true) @@ -142,7 +142,7 @@ App::post('/v1/storage/buckets') ->dynamic($bucket, Response::MODEL_BUCKET); }); -App::get('/v1/storage/buckets') +Http::get('/v1/storage/buckets') ->desc('List buckets') ->groups(['api', 'storage']) ->label('scope', 'buckets.read') @@ -196,7 +196,7 @@ App::get('/v1/storage/buckets') ]), Response::MODEL_BUCKET_LIST); }); -App::get('/v1/storage/buckets/:bucketId') +Http::get('/v1/storage/buckets/:bucketId') ->desc('Get bucket') ->groups(['api', 'storage']) ->label('scope', 'buckets.read') @@ -221,7 +221,7 @@ App::get('/v1/storage/buckets/:bucketId') $response->dynamic($bucket, Response::MODEL_BUCKET); }); -App::put('/v1/storage/buckets/:bucketId') +Http::put('/v1/storage/buckets/:bucketId') ->desc('Update bucket') ->groups(['api', 'storage']) ->label('scope', 'buckets.write') @@ -240,7 +240,7 @@ App::put('/v1/storage/buckets/:bucketId') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true) - ->param('maximumFileSize', null, new Range(1, (int) App::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)App::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true) + ->param('maximumFileSize', null, new Range(1, (int) Http::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)Http::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true) ->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true) ->param('compression', Compression::NONE, new WhiteList([Compression::NONE, Compression::GZIP, Compression::ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . Compression::NONE . ', [' . Compression::GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . Compression::ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true) ->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true) @@ -256,7 +256,7 @@ App::put('/v1/storage/buckets/:bucketId') } $permissions ??= $bucket->getPermissions(); - $maximumFileSize ??= $bucket->getAttribute('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0)); + $maximumFileSize ??= $bucket->getAttribute('maximumFileSize', (int) Http::getEnv('_APP_STORAGE_LIMIT', 0)); $allowedFileExtensions ??= $bucket->getAttribute('allowedFileExtensions', []); $enabled ??= $bucket->getAttribute('enabled', true); $encryption ??= $bucket->getAttribute('encryption', true); @@ -288,7 +288,7 @@ App::put('/v1/storage/buckets/:bucketId') $response->dynamic($bucket, Response::MODEL_BUCKET); }); -App::delete('/v1/storage/buckets/:bucketId') +Http::delete('/v1/storage/buckets/:bucketId') ->desc('Delete bucket') ->groups(['api', 'storage']) ->label('scope', 'buckets.write') @@ -329,7 +329,7 @@ App::delete('/v1/storage/buckets/:bucketId') $response->noContent(); }); -App::post('/v1/storage/buckets/:bucketId/files') +Http::post('/v1/storage/buckets/:bucketId/files') ->alias('/v1/storage/files', ['bucketId' => 'default']) ->desc('Create file') ->groups(['api', 'storage']) @@ -418,7 +418,7 @@ App::post('/v1/storage/buckets/:bucketId/files') } $maximumFileSize = $bucket->getAttribute('maximumFileSize', 0); - if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) { + if ($maximumFileSize > (int) Http::getEnv('_APP_STORAGE_LIMIT', 0)) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Maximum bucket file size is larger than _APP_STORAGE_LIMIT'); } @@ -520,10 +520,10 @@ App::post('/v1/storage/buckets/:bucketId/files') } if ($chunksUploaded === $chunks) { - if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antivirus', true) && $fileSize <= APP_LIMIT_ANTIVIRUS && $deviceForFiles->getType() === Storage::DEVICE_LOCAL) { + if (Http::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antivirus', true) && $fileSize <= APP_LIMIT_ANTIVIRUS && $deviceForFiles->getType() === Storage::DEVICE_LOCAL) { $antivirus = new Network( - App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'), - (int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310) + Http::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'), + (int) Http::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310) ); if (!$antivirus->fileScan($path)) { @@ -560,7 +560,7 @@ App::post('/v1/storage/buckets/:bucketId/files') if (empty($data)) { $data = $deviceForFiles->read($path); } - $key = App::getEnv('_APP_OPENSSL_KEY_V1'); + $key = Http::getEnv('_APP_OPENSSL_KEY_V1'); $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); $data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag); } @@ -706,7 +706,7 @@ App::post('/v1/storage/buckets/:bucketId/files') ->dynamic($file, Response::MODEL_FILE); }); -App::get('/v1/storage/buckets/:bucketId/files') +Http::get('/v1/storage/buckets/:bucketId/files') ->alias('/v1/storage/files', ['bucketId' => 'default']) ->desc('List files') ->groups(['api', 'storage']) @@ -791,7 +791,7 @@ App::get('/v1/storage/buckets/:bucketId/files') ]), Response::MODEL_FILE_LIST); }); -App::get('/v1/storage/buckets/:bucketId/files/:fileId') +Http::get('/v1/storage/buckets/:bucketId/files/:fileId') ->alias('/v1/storage/files/:fileId', ['bucketId' => 'default']) ->desc('Get file') ->groups(['api', 'storage']) @@ -838,7 +838,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') $response->dynamic($file, Response::MODEL_FILE); }); -App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') +Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->alias('/v1/storage/files/:fileId/preview', ['bucketId' => 'default']) ->desc('Get file preview') ->groups(['api', 'storage']) @@ -918,7 +918,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $algorithm = $file->getAttribute('algorithm', Compression::NONE); $cipher = $file->getAttribute('openSSLCipher'); $mime = $file->getAttribute('mimeType'); - if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) App::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) { + if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) Http::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) { if (!\in_array($mime, $inputs)) { $path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default']; } else { @@ -955,7 +955,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $source = OpenSSL::decrypt( $source, $file->getAttribute('openSSLCipher'), - App::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')), + Http::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')), 0, \hex2bin($file->getAttribute('openSSLIV')), \hex2bin($file->getAttribute('openSSLTag')) @@ -1014,7 +1014,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') unset($image); }); -App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') +Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->alias('/v1/storage/files/:fileId/download', ['bucketId' => 'default']) ->desc('Get file for download') ->groups(['api', 'storage']) @@ -1103,7 +1103,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') $source = OpenSSL::decrypt( $source, $file->getAttribute('openSSLCipher'), - App::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')), + Http::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')), 0, \hex2bin($file->getAttribute('openSSLIV')), \hex2bin($file->getAttribute('openSSLTag')) @@ -1154,7 +1154,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') } }); -App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') +Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->alias('/v1/storage/files/:fileId/view', ['bucketId' => 'default']) ->desc('Get file for view') ->groups(['api', 'storage']) @@ -1252,7 +1252,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') $source = OpenSSL::decrypt( $source, $file->getAttribute('openSSLCipher'), - App::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')), + Http::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')), 0, \hex2bin($file->getAttribute('openSSLIV')), \hex2bin($file->getAttribute('openSSLTag')) @@ -1306,7 +1306,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') } }); -App::put('/v1/storage/buckets/:bucketId/files/:fileId') +Http::put('/v1/storage/buckets/:bucketId/files/:fileId') ->alias('/v1/storage/files/:fileId', ['bucketId' => 'default']) ->desc('Update file') ->groups(['api', 'storage']) @@ -1415,7 +1415,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') $response->dynamic($file, Response::MODEL_FILE); }); -App::delete('/v1/storage/buckets/:bucketId/files/:fileId') +Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->desc('Delete File') ->groups(['api', 'storage']) ->label('scope', 'files.write') @@ -1511,7 +1511,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') $response->noContent(); }); -App::get('/v1/storage/usage') +Http::get('/v1/storage/usage') ->desc('Get storage usage stats') ->groups(['api', 'storage']) ->label('scope', 'files.read') @@ -1590,7 +1590,7 @@ App::get('/v1/storage/usage') ]), Response::MODEL_USAGE_STORAGE); }); -App::get('/v1/storage/:bucketId/usage') +Http::get('/v1/storage/:bucketId/usage') ->desc('Get bucket usage stats') ->groups(['api', 'storage']) ->label('scope', 'files.read') diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 5d13fcfa6e..8f1e6018e0 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -17,7 +17,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Teams; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\Config\Config; use Utopia\Database\Database; @@ -37,12 +37,12 @@ use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; -use Utopia\Validator\ArrayList; -use Utopia\Validator\Assoc; -use Utopia\Validator\Host; -use Utopia\Validator\Text; +use Utopia\Http\Validator\ArrayList; +use Utopia\Http\Validator\Assoc; +use Utopia\Http\Validator\Host; +use Utopia\Http\Validator\Text; -App::post('/v1/teams') +Http::post('/v1/teams') ->desc('Create team') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].create') @@ -130,7 +130,7 @@ App::post('/v1/teams') ->dynamic($team, Response::MODEL_TEAM); }); -App::get('/v1/teams') +Http::get('/v1/teams') ->desc('List teams') ->groups(['api', 'teams']) ->label('scope', 'teams.read') @@ -188,7 +188,7 @@ App::get('/v1/teams') ]), Response::MODEL_TEAM_LIST); }); -App::get('/v1/teams/:teamId') +Http::get('/v1/teams/:teamId') ->desc('Get team') ->groups(['api', 'teams']) ->label('scope', 'teams.read') @@ -215,7 +215,7 @@ App::get('/v1/teams/:teamId') $response->dynamic($team, Response::MODEL_TEAM); }); -App::get('/v1/teams/:teamId/prefs') +Http::get('/v1/teams/:teamId/prefs') ->desc('Get team preferences') ->groups(['api', 'teams']) ->label('scope', 'teams.read') @@ -243,7 +243,7 @@ App::get('/v1/teams/:teamId/prefs') $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); -App::put('/v1/teams/:teamId') +Http::put('/v1/teams/:teamId') ->desc('Update name') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].update') @@ -286,7 +286,7 @@ App::put('/v1/teams/:teamId') $response->dynamic($team, Response::MODEL_TEAM); }); -App::put('/v1/teams/:teamId/prefs') +Http::put('/v1/teams/:teamId/prefs') ->desc('Update preferences') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].update.prefs') @@ -322,7 +322,7 @@ App::put('/v1/teams/:teamId/prefs') $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); -App::delete('/v1/teams/:teamId') +Http::delete('/v1/teams/:teamId') ->desc('Delete team') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].delete') @@ -364,7 +364,7 @@ App::delete('/v1/teams/:teamId') $response->noContent(); }); -App::post('/v1/teams/:teamId/memberships') +Http::post('/v1/teams/:teamId/memberships') ->desc('Create team membership') ->groups(['api', 'teams', 'auth']) ->label('event', 'teams.[teamId].memberships.[membershipId].create') @@ -412,7 +412,7 @@ App::post('/v1/teams/:teamId/memberships') $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); $isAppUser = Auth::isAppUser(Authorization::getRoles()); - if (!$isPrivilegedUser && !$isAppUser && empty(App::getEnv('_APP_SMTP_HOST'))) { + if (!$isPrivilegedUser && !$isAppUser && empty(Http::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED); } @@ -575,8 +575,8 @@ App::post('/v1/teams/:teamId/memberships') $smtp = $project->getAttribute('smtp', []); $smtpEnabled = $smtp['enabled'] ?? false; - $senderEmail = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - $senderName = App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); + $senderEmail = Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + $senderName = Http::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); $replyTo = ""; if ($smtpEnabled) { @@ -637,7 +637,7 @@ App::post('/v1/teams/:teamId/memberships') ->trigger() ; } elseif (!empty($phone)) { - if (empty(App::getEnv('_APP_SMS_PROVIDER'))) { + if (empty(Http::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -682,7 +682,7 @@ App::post('/v1/teams/:teamId/memberships') ); }); -App::get('/v1/teams/:teamId/memberships') +Http::get('/v1/teams/:teamId/memberships') ->desc('List team memberships') ->groups(['api', 'teams']) ->label('scope', 'teams.read') @@ -785,7 +785,7 @@ App::get('/v1/teams/:teamId/memberships') ]), Response::MODEL_MEMBERSHIP_LIST); }); -App::get('/v1/teams/:teamId/memberships/:membershipId') +Http::get('/v1/teams/:teamId/memberships/:membershipId') ->desc('Get team membership') ->groups(['api', 'teams']) ->label('scope', 'teams.read') @@ -841,7 +841,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId') $response->dynamic($membership, Response::MODEL_MEMBERSHIP); }); -App::patch('/v1/teams/:teamId/memberships/:membershipId') +Http::patch('/v1/teams/:teamId/memberships/:membershipId') ->desc('Update membership') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].memberships.[membershipId].update') @@ -912,7 +912,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') ); }); -App::patch('/v1/teams/:teamId/memberships/:membershipId/status') +Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->desc('Update team membership status') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].memberships.[membershipId].update.status') @@ -1049,7 +1049,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ); }); -App::delete('/v1/teams/:teamId/memberships/:membershipId') +Http::delete('/v1/teams/:teamId/memberships/:membershipId') ->desc('Delete team membership') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].memberships.[membershipId].delete') @@ -1114,7 +1114,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') $response->noContent(); }); -App::get('/v1/teams/:teamId/logs') +Http::get('/v1/teams/:teamId/logs') ->desc('List team logs') ->groups(['api', 'teams']) ->label('scope', 'teams.read') diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index d29712e000..74b3138313 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -21,7 +21,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Users; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\Config\Config; use Utopia\Database\Database; @@ -39,13 +39,13 @@ use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Locale\Locale; -use Utopia\Validator\ArrayList; -use Utopia\Validator\Assoc; -use Utopia\Validator\Boolean; -use Utopia\Validator\Integer; -use Utopia\Validator\Range; -use Utopia\Validator\Text; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\ArrayList; +use Utopia\Http\Validator\Assoc; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\Integer; +use Utopia\Http\Validator\Range; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; /** TODO: Remove function when we move to using utopia/platform */ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks): Document @@ -174,7 +174,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e return $user; } -App::post('/v1/users') +Http::post('/v1/users') ->desc('Create user') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -205,7 +205,7 @@ App::post('/v1/users') ->dynamic($user, Response::MODEL_USER); }); -App::post('/v1/users/bcrypt') +Http::post('/v1/users/bcrypt') ->desc('Create user with bcrypt password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -236,7 +236,7 @@ App::post('/v1/users/bcrypt') ->dynamic($user, Response::MODEL_USER); }); -App::post('/v1/users/md5') +Http::post('/v1/users/md5') ->desc('Create user with MD5 password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -267,7 +267,7 @@ App::post('/v1/users/md5') ->dynamic($user, Response::MODEL_USER); }); -App::post('/v1/users/argon2') +Http::post('/v1/users/argon2') ->desc('Create user with Argon2 password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -298,7 +298,7 @@ App::post('/v1/users/argon2') ->dynamic($user, Response::MODEL_USER); }); -App::post('/v1/users/sha') +Http::post('/v1/users/sha') ->desc('Create user with SHA password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -336,7 +336,7 @@ App::post('/v1/users/sha') ->dynamic($user, Response::MODEL_USER); }); -App::post('/v1/users/phpass') +Http::post('/v1/users/phpass') ->desc('Create user with PHPass password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -367,7 +367,7 @@ App::post('/v1/users/phpass') ->dynamic($user, Response::MODEL_USER); }); -App::post('/v1/users/scrypt') +Http::post('/v1/users/scrypt') ->desc('Create user with Scrypt password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -411,7 +411,7 @@ App::post('/v1/users/scrypt') ->dynamic($user, Response::MODEL_USER); }); -App::post('/v1/users/scrypt-modified') +Http::post('/v1/users/scrypt-modified') ->desc('Create user with Scrypt modified password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -445,7 +445,7 @@ App::post('/v1/users/scrypt-modified') ->dynamic($user, Response::MODEL_USER); }); -App::post('/v1/users/:userId/targets') +Http::post('/v1/users/:userId/targets') ->desc('Create User Target') ->groups(['api', 'users']) ->label('audits.event', 'target.create') @@ -542,7 +542,7 @@ App::post('/v1/users/:userId/targets') ->dynamic($target, Response::MODEL_TARGET); }); -App::get('/v1/users') +Http::get('/v1/users') ->desc('List users') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -596,7 +596,7 @@ App::get('/v1/users') ]), Response::MODEL_USER_LIST); }); -App::get('/v1/users/:userId') +Http::get('/v1/users/:userId') ->desc('Get user') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -621,7 +621,7 @@ App::get('/v1/users/:userId') $response->dynamic($user, Response::MODEL_USER); }); -App::get('/v1/users/:userId/prefs') +Http::get('/v1/users/:userId/prefs') ->desc('Get user preferences') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -648,7 +648,7 @@ App::get('/v1/users/:userId/prefs') $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); -App::get('/v1/users/:userId/targets/:targetId') +Http::get('/v1/users/:userId/targets/:targetId') ->desc('Get User Target') ->groups(['api', 'users']) ->label('scope', 'targets.read') @@ -680,7 +680,7 @@ App::get('/v1/users/:userId/targets/:targetId') $response->dynamic($target, Response::MODEL_TARGET); }); -App::get('/v1/users/:userId/sessions') +Http::get('/v1/users/:userId/sessions') ->desc('List user sessions') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -721,7 +721,7 @@ App::get('/v1/users/:userId/sessions') ]), Response::MODEL_SESSION_LIST); }); -App::get('/v1/users/:userId/memberships') +Http::get('/v1/users/:userId/memberships') ->desc('List user memberships') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -760,7 +760,7 @@ App::get('/v1/users/:userId/memberships') ]), Response::MODEL_MEMBERSHIP_LIST); }); -App::get('/v1/users/:userId/logs') +Http::get('/v1/users/:userId/logs') ->desc('List user logs') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -846,7 +846,7 @@ App::get('/v1/users/:userId/logs') ]), Response::MODEL_LOG_LIST); }); -App::get('/v1/users/:userId/targets') +Http::get('/v1/users/:userId/targets') ->desc('List User Targets') ->groups(['api', 'users']) ->label('scope', 'targets.read') @@ -901,7 +901,7 @@ App::get('/v1/users/:userId/targets') ]), Response::MODEL_TARGET_LIST); }); -App::get('/v1/users/identities') +Http::get('/v1/users/identities') ->desc('List Identities') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -955,7 +955,7 @@ App::get('/v1/users/identities') ]), Response::MODEL_IDENTITY_LIST); }); -App::patch('/v1/users/:userId/status') +Http::patch('/v1/users/:userId/status') ->desc('Update user status') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.status') @@ -991,7 +991,7 @@ App::patch('/v1/users/:userId/status') $response->dynamic($user, Response::MODEL_USER); }); -App::put('/v1/users/:userId/labels') +Http::put('/v1/users/:userId/labels') ->desc('Update user labels') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.labels') @@ -1028,7 +1028,7 @@ App::put('/v1/users/:userId/labels') $response->dynamic($user, Response::MODEL_USER); }); -App::patch('/v1/users/:userId/verification/phone') +Http::patch('/v1/users/:userId/verification/phone') ->desc('Update phone verification') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.verification') @@ -1063,7 +1063,7 @@ App::patch('/v1/users/:userId/verification/phone') $response->dynamic($user, Response::MODEL_USER); }); -App::patch('/v1/users/:userId/name') +Http::patch('/v1/users/:userId/name') ->desc('Update name') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.name') @@ -1100,7 +1100,7 @@ App::patch('/v1/users/:userId/name') $response->dynamic($user, Response::MODEL_USER); }); -App::patch('/v1/users/:userId/password') +Http::patch('/v1/users/:userId/password') ->desc('Update password') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.password') @@ -1177,7 +1177,7 @@ App::patch('/v1/users/:userId/password') $response->dynamic($user, Response::MODEL_USER); }); -App::patch('/v1/users/:userId/email') +Http::patch('/v1/users/:userId/email') ->desc('Update email') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.email') @@ -1272,7 +1272,7 @@ App::patch('/v1/users/:userId/email') $response->dynamic($user, Response::MODEL_USER); }); -App::patch('/v1/users/:userId/phone') +Http::patch('/v1/users/:userId/phone') ->desc('Update phone') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.phone') @@ -1355,7 +1355,7 @@ App::patch('/v1/users/:userId/phone') $response->dynamic($user, Response::MODEL_USER); }); -App::patch('/v1/users/:userId/verification') +Http::patch('/v1/users/:userId/verification') ->desc('Update email verification') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.verification') @@ -1390,7 +1390,7 @@ App::patch('/v1/users/:userId/verification') $response->dynamic($user, Response::MODEL_USER); }); -App::patch('/v1/users/:userId/prefs') +Http::patch('/v1/users/:userId/prefs') ->desc('Update user preferences') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.prefs') @@ -1423,7 +1423,7 @@ App::patch('/v1/users/:userId/prefs') $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); -App::patch('/v1/users/:userId/targets/:targetId') +Http::patch('/v1/users/:userId/targets/:targetId') ->desc('Update User target') ->groups(['api', 'users']) ->label('audits.event', 'target.update') @@ -1517,7 +1517,7 @@ App::patch('/v1/users/:userId/targets/:targetId') ->dynamic($target, Response::MODEL_TARGET); }); -App::patch('/v1/users/:userId/mfa') +Http::patch('/v1/users/:userId/mfa') ->desc('Update MFA') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.mfa') @@ -1555,7 +1555,7 @@ App::patch('/v1/users/:userId/mfa') $response->dynamic($user, Response::MODEL_USER); }); -App::get('/v1/users/:userId/mfa/factors') +Http::get('/v1/users/:userId/mfa/factors') ->desc('List Factors') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -1588,7 +1588,7 @@ App::get('/v1/users/:userId/mfa/factors') $response->dynamic($factors, Response::MODEL_MFA_FACTORS); }); -App::get('/v1/users/:userId/mfa/recovery-codes') +Http::get('/v1/users/:userId/mfa/recovery-codes') ->desc('Get MFA Recovery Codes') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -1623,7 +1623,7 @@ App::get('/v1/users/:userId/mfa/recovery-codes') $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); }); -App::patch('/v1/users/:userId/mfa/recovery-codes') +Http::patch('/v1/users/:userId/mfa/recovery-codes') ->desc('Create MFA Recovery Codes') ->groups(['api', 'users']) ->label('event', 'users.[userId].create.mfa.recovery-codes') @@ -1669,7 +1669,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes') $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); }); -App::put('/v1/users/:userId/mfa/recovery-codes') +Http::put('/v1/users/:userId/mfa/recovery-codes') ->desc('Regenerate MFA Recovery Codes') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.mfa.recovery-codes') @@ -1714,7 +1714,7 @@ App::put('/v1/users/:userId/mfa/recovery-codes') $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); }); -App::delete('/v1/users/:userId/mfa/authenticators/:type') +Http::delete('/v1/users/:userId/mfa/authenticators/:type') ->desc('Delete Authenticator') ->groups(['api', 'users']) ->label('event', 'users.[userId].delete.mfa') @@ -1756,7 +1756,7 @@ App::delete('/v1/users/:userId/mfa/authenticators/:type') $response->noContent(); }); -App::post('/v1/users/:userId/sessions') +Http::post('/v1/users/:userId/sessions') ->desc('Create session') ->groups(['api', 'users']) ->label('event', 'users.[userId].sessions.[sessionId].create') @@ -1826,7 +1826,7 @@ App::post('/v1/users/:userId/sessions') ->dynamic($session, Response::MODEL_SESSION); }); -App::post('/v1/users/:userId/tokens') +Http::post('/v1/users/:userId/tokens') ->desc('Create token') ->groups(['api', 'users']) ->label('event', 'users.[userId].tokens.[tokenId].create') @@ -1883,7 +1883,7 @@ App::post('/v1/users/:userId/tokens') ->dynamic($token, Response::MODEL_TOKEN); }); -App::delete('/v1/users/:userId/sessions/:sessionId') +Http::delete('/v1/users/:userId/sessions/:sessionId') ->desc('Delete user session') ->groups(['api', 'users']) ->label('event', 'users.[userId].sessions.[sessionId].delete') @@ -1926,7 +1926,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId') $response->noContent(); }); -App::delete('/v1/users/:userId/sessions') +Http::delete('/v1/users/:userId/sessions') ->desc('Delete user sessions') ->groups(['api', 'users']) ->label('event', 'users.[userId].sessions.delete') @@ -1968,7 +1968,7 @@ App::delete('/v1/users/:userId/sessions') $response->noContent(); }); -App::delete('/v1/users/:userId') +Http::delete('/v1/users/:userId') ->desc('Delete user') ->groups(['api', 'users']) ->label('event', 'users.[userId].delete') @@ -2010,7 +2010,7 @@ App::delete('/v1/users/:userId') $response->noContent(); }); -App::delete('/v1/users/:userId/targets/:targetId') +Http::delete('/v1/users/:userId/targets/:targetId') ->desc('Delete user target') ->groups(['api', 'users']) ->label('audits.event', 'target.delete') @@ -2061,7 +2061,7 @@ App::delete('/v1/users/:userId/targets/:targetId') $response->noContent(); }); -App::delete('/v1/users/identities/:identityId') +Http::delete('/v1/users/identities/:identityId') ->desc('Delete identity') ->groups(['api', 'users']) ->label('event', 'users.[userId].identities.[identityId].delete') @@ -2096,7 +2096,7 @@ App::delete('/v1/users/identities/:identityId') return $response->noContent(); }); -App::get('/v1/users/usage') +Http::get('/v1/users/usage') ->desc('Get users usage stats') ->groups(['api', 'users']) ->label('scope', 'users.read') diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index ea30d6b11d..7be9c2e1ad 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -8,7 +8,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Installations; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Vcs\Comment; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -32,9 +32,9 @@ use Utopia\Detector\Adapter\Python; use Utopia\Detector\Adapter\Ruby; use Utopia\Detector\Adapter\Swift; use Utopia\Detector\Detector; -use Utopia\Validator\Boolean; -use Utopia\Validator\Host; -use Utopia\Validator\Text; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\Host; +use Utopia\Http\Validator\Text; use Utopia\VCS\Adapter\Git\GitHub; use Utopia\VCS\Exception\RepositoryNotFound; @@ -245,7 +245,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId } }; -App::get('/v1/vcs/github/authorize') +Http::get('/v1/vcs/github/authorize') ->desc('Install GitHub App') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -270,7 +270,7 @@ App::get('/v1/vcs/github/authorize') 'failure' => $failure, ]); - $appName = App::getEnv('_APP_VCS_GITHUB_APP_NAME'); + $appName = Http::getEnv('_APP_VCS_GITHUB_APP_NAME'); if (empty($appName)) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'GitHub App name is not configured. Please configure VCS (Version Control System) variables in .env file.'); @@ -287,7 +287,7 @@ App::get('/v1/vcs/github/authorize') ->redirect($url); }); -App::get('/v1/vcs/github/callback') +Http::get('/v1/vcs/github/callback') ->desc('Capture installation and authorization from GitHub App') ->groups(['api', 'vcs']) ->label('scope', 'public') @@ -341,7 +341,7 @@ App::get('/v1/vcs/github/callback') // OAuth Authroization if (!empty($code)) { - $oauth2 = new OAuth2Github(App::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''), App::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''), ""); + $oauth2 = new OAuth2Github(Http::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''), Http::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''), ""); $accessToken = $oauth2->getAccessToken($code) ?? ''; $refreshToken = $oauth2->getRefreshToken($code) ?? ''; $accessTokenExpiry = $oauth2->getAccessTokenExpiry($code) ?? ''; @@ -388,8 +388,8 @@ App::get('/v1/vcs/github/callback') // Create / Update installation if (!empty($providerInstallationId)) { - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId) ?? ''; @@ -447,7 +447,7 @@ App::get('/v1/vcs/github/callback') ->redirect($redirectSuccess); }); -App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection') +Http::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection') ->desc('Detect runtime settings from source code') ->groups(['api', 'vcs']) ->label('scope', 'vcs.write') @@ -473,8 +473,8 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:pr } $providerInstallationId = $installation->getAttribute('providerInstallationId'); - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId); @@ -518,7 +518,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:pr $response->dynamic(new Document($detection), Response::MODEL_DETECTION); }); -App::get('/v1/vcs/github/installations/:installationId/providerRepositories') +Http::get('/v1/vcs/github/installations/:installationId/providerRepositories') ->desc('List Repositories') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -547,8 +547,8 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories') } $providerInstallationId = $installation->getAttribute('providerInstallationId'); - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $page = 1; @@ -612,7 +612,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories') ]), Response::MODEL_PROVIDER_REPOSITORY_LIST); }); -App::post('/v1/vcs/github/installations/:installationId/providerRepositories') +Http::post('/v1/vcs/github/installations/:installationId/providerRepositories') ->desc('Create repository') ->groups(['api', 'vcs']) ->label('scope', 'vcs.write') @@ -639,7 +639,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories') } if ($installation->getAttribute('personal', false) === true) { - $oauth2 = new OAuth2Github(App::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''), App::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''), ""); + $oauth2 = new OAuth2Github(Http::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''), Http::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''), ""); $identity = $dbForConsole->findOne('identities', [ Query::equal('provider', ['github']), @@ -681,8 +681,8 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories') } } else { $providerInstallationId = $installation->getAttribute('providerInstallationId'); - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId); @@ -713,7 +713,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories') $response->dynamic(new Document($repository), Response::MODEL_PROVIDER_REPOSITORY); }); -App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId') +Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId') ->desc('Get repository') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -738,8 +738,8 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro } $providerInstallationId = $installation->getAttribute('providerInstallationId'); - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId) ?? ''; @@ -762,7 +762,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro $response->dynamic(new Document($repository), Response::MODEL_PROVIDER_REPOSITORY); }); -App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/branches') +Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/branches') ->desc('List Repository Branches') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -787,8 +787,8 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro } $providerInstallationId = $installation->getAttribute('providerInstallationId'); - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId) ?? ''; @@ -811,7 +811,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro ]), Response::MODEL_BRANCH_LIST); }); -App::post('/v1/vcs/github/events') +Http::post('/v1/vcs/github/events') ->desc('Create Event') ->groups(['api', 'vcs']) ->label('scope', 'public') @@ -825,7 +825,7 @@ App::post('/v1/vcs/github/events') function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) { $payload = $request->getRawPayload(); $signatureRemote = $request->getHeader('x-hub-signature-256', ''); - $signatureLocal = App::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', ''); + $signatureLocal = Http::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', ''); $valid = empty($signatureRemote) ? true : $github->validateWebhookEvent($payload, $signatureRemote, $signatureLocal); @@ -834,8 +834,8 @@ App::post('/v1/vcs/github/events') } $event = $request->getHeader('x-github-event', ''); - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); $parsedPayload = $github->getEvent($event, $payload); if ($event == $github::EVENT_PUSH) { @@ -950,7 +950,7 @@ App::post('/v1/vcs/github/events') } ); -App::get('/v1/vcs/installations') +Http::get('/v1/vcs/installations') ->desc('List installations') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -1010,7 +1010,7 @@ App::get('/v1/vcs/installations') ]), Response::MODEL_INSTALLATION_LIST); }); -App::get('/v1/vcs/installations/:installationId') +Http::get('/v1/vcs/installations/:installationId') ->desc('Get installation') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -1039,7 +1039,7 @@ App::get('/v1/vcs/installations/:installationId') $response->dynamic($installation, Response::MODEL_INSTALLATION); }); -App::delete('/v1/vcs/installations/:installationId') +Http::delete('/v1/vcs/installations/:installationId') ->desc('Delete Installation') ->groups(['api', 'vcs']) ->label('scope', 'vcs.write') @@ -1072,7 +1072,7 @@ App::delete('/v1/vcs/installations/:installationId') $response->noContent(); }); -App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositoryId') +Http::patch('/v1/vcs/github/installations/:installationId/repositories/:repositoryId') ->desc('Authorize external deployment') ->groups(['api', 'vcs']) ->label('scope', 'vcs.write') @@ -1118,8 +1118,8 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor $repository = Authorization::skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); $providerInstallationId = $installation->getAttribute('providerInstallationId'); $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); diff --git a/app/controllers/general.php b/app/controllers/general.php index fab7100239..d14da35f7c 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -26,7 +26,7 @@ use Appwrite\Utopia\View; use Executor\Executor; use MaxMind\Db\Reader; use Swoole\Http\Request as SwooleRequest; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -39,8 +39,8 @@ use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Logger\Log\User; use Utopia\Logger\Logger; -use Utopia\Validator\Hostname; -use Utopia\Validator\Text; +use Utopia\Http\Validator\Hostname; +use Utopia\Http\Validator\Text; Config::setParam('domainVerification', false); Config::setParam('cookieDomain', 'localhost'); @@ -60,15 +60,15 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo )[0] ?? null; if ($route === null) { - if ($host === App::getEnv('_APP_DOMAIN_FUNCTIONS', '')) { + if ($host === Http::getEnv('_APP_DOMAIN_FUNCTIONS', '')) { throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.'); } - if (\str_ends_with($host, App::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) { + if (\str_ends_with($host, Http::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) { throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.'); } - if (App::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') { + if (Http::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') { if ($host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.'); } @@ -99,7 +99,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo $type = $route->getAttribute('resourceType'); if ($type === 'function') { - if (App::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS + if (Http::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS if ($request->getProtocol() !== 'https') { if ($request->getMethod() !== Request::METHOD_GET) { throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.'); @@ -259,7 +259,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo ]); /** Execute function */ - $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $executor = new Executor(Http::getEnv('_APP_EXECUTOR_HOST')); try { $version = $function->getAttribute('version', 'v2'); $command = $runtime['startCommand']; @@ -365,7 +365,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo return false; } -App::init() +Http::init() ->groups(['api', 'web']) ->inject('utopia') ->inject('swooleRequest') @@ -387,7 +387,7 @@ App::init() * Appwrite Router */ $host = $request->getHostname() ?? ''; - $mainDomain = App::getEnv('_APP_DOMAIN', ''); + $mainDomain = Http::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain if ($host !== $mainDomain) { if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb)) { @@ -405,7 +405,7 @@ App::init() return $response->setStatusCode(404)->send('Not Found'); } - $requestFormat = $request->getHeader('x-appwrite-response-format', App::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', '')); + $requestFormat = $request->getHeader('x-appwrite-response-format', Http::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', '')); if ($requestFormat) { switch ($requestFormat) { case version_compare($requestFormat, '0.12.0', '<'): @@ -446,7 +446,7 @@ App::init() } else { Authorization::disable(); - $envDomain = App::getEnv('_APP_DOMAIN', ''); + $envDomain = Http::getEnv('_APP_DOMAIN', ''); $mainDomain = null; if (!empty($envDomain) && $envDomain !== 'localhost') { $mainDomain = $envDomain; @@ -523,7 +523,7 @@ App::init() $isIpAddress = filter_var($request->getHostname(), FILTER_VALIDATE_IP) !== false; $isConsoleProject = $project->getAttribute('$id', '') === 'console'; - $isConsoleRootSession = App::getEnv('_APP_CONSOLE_ROOT_SESSION', 'disabled') === 'enabled'; + $isConsoleRootSession = Http::getEnv('_APP_CONSOLE_ROOT_SESSION', 'disabled') === 'enabled'; Config::setParam( 'cookieDomain', @@ -539,7 +539,7 @@ App::init() /* * Response format */ - $responseFormat = $request->getHeader('x-appwrite-response-format', App::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', '')); + $responseFormat = $request->getHeader('x-appwrite-response-format', Http::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', '')); if ($responseFormat) { switch ($responseFormat) { case version_compare($responseFormat, '0.11.2', '<='): @@ -576,7 +576,7 @@ App::init() * As recommended at: * @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers */ - if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS + if (Http::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS if ($request->getProtocol() !== 'https' && ($swooleRequest->header['host'] ?? '') !== 'localhost' && ($swooleRequest->header['host'] ?? '') !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations if ($request->getMethod() !== Request::METHOD_GET) { throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.'); @@ -617,7 +617,7 @@ App::init() } }); -App::options() +Http::options() ->inject('utopia') ->inject('swooleRequest') ->inject('request') @@ -632,7 +632,7 @@ App::options() * Appwrite Router */ $host = $request->getHostname() ?? ''; - $mainDomain = App::getEnv('_APP_DOMAIN', ''); + $mainDomain = Http::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain if ($host !== $mainDomain) { if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb)) { @@ -652,7 +652,7 @@ App::options() ->noContent(); }); -App::error() +Http::error() ->inject('error') ->inject('utopia') ->inject('request') @@ -661,7 +661,7 @@ App::error() ->inject('logger') ->inject('log') ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log) { - $version = App::getEnv('_APP_VERSION', 'UNKNOWN'); + $version = Http::getEnv('_APP_VERSION', 'UNKNOWN'); $route = $utopia->getRoute(); if ($error instanceof AppwriteException) { @@ -706,7 +706,7 @@ App::error() $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); $log->setAction($action); - $isProduction = App::getEnv('_APP_ENV', 'development') === 'production'; + $isProduction = Http::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); $responseCode = $logger->addLog($log); @@ -783,7 +783,7 @@ App::error() $type = $error->getType(); - $output = ((App::isDevelopment())) ? [ + $output = ((Http::isDevelopment())) ? [ 'message' => $message, 'code' => $code, 'file' => $file, @@ -811,7 +811,7 @@ App::error() $layout ->setParam('title', $project->getAttribute('name') . ' - Error') - ->setParam('development', App::isDevelopment()) + ->setParam('development', Http::isDevelopment()) ->setParam('projectName', $project->getAttribute('name')) ->setParam('projectURL', $project->getAttribute('url')) ->setParam('message', $output['message'] ?? '') @@ -828,7 +828,7 @@ App::error() ); }); -App::get('/robots.txt') +Http::get('/robots.txt') ->desc('Robots.txt File') ->label('scope', 'public') ->label('docs', false) @@ -838,7 +838,7 @@ App::get('/robots.txt') $response->text($template->render(false)); }); -App::get('/humans.txt') +Http::get('/humans.txt') ->desc('Humans.txt File') ->label('scope', 'public') ->label('docs', false) @@ -848,7 +848,7 @@ App::get('/humans.txt') $response->text($template->render(false)); }); -App::get('/.well-known/acme-challenge/*') +Http::get('/.well-known/acme-challenge/*') ->desc('SSL Verification') ->label('scope', 'public') ->label('docs', false) @@ -901,7 +901,7 @@ App::get('/.well-known/acme-challenge/*') include_once __DIR__ . '/shared/api.php'; include_once __DIR__ . '/shared/api/auth.php'; -App::wildcard() +Http::wildcard() ->groups(['api']) ->label('scope', 'global') ->action(function () { diff --git a/app/controllers/mock.php b/app/controllers/mock.php index f933c62e6e..544c2a7dc8 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -5,19 +5,19 @@ global $utopia, $request, $response; use Appwrite\Extend\Exception; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\UID; -use Utopia\Validator\Host; -use Utopia\Validator\Text; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\WhiteList; +use Utopia\Http\Validator\Host; +use Utopia\Http\Validator\Text; use Utopia\VCS\Adapter\Git\GitHub; -App::get('/v1/mock/tests/general/oauth2') +Http::get('/v1/mock/tests/general/oauth2') ->desc('OAuth Login') ->groups(['mock']) ->label('scope', 'public') @@ -33,7 +33,7 @@ App::get('/v1/mock/tests/general/oauth2') $response->redirect($redirectURI . '?' . \http_build_query(['code' => 'abcdef', 'state' => $state])); }); -App::get('/v1/mock/tests/general/oauth2/token') +Http::get('/v1/mock/tests/general/oauth2/token') ->desc('OAuth2 Token') ->groups(['mock']) ->label('scope', 'public') @@ -79,7 +79,7 @@ App::get('/v1/mock/tests/general/oauth2/token') } }); -App::get('/v1/mock/tests/general/oauth2/user') +Http::get('/v1/mock/tests/general/oauth2/user') ->desc('OAuth2 User') ->groups(['mock']) ->label('scope', 'public') @@ -99,7 +99,7 @@ App::get('/v1/mock/tests/general/oauth2/user') ]); }); -App::get('/v1/mock/tests/general/oauth2/success') +Http::get('/v1/mock/tests/general/oauth2/success') ->desc('OAuth2 Success') ->groups(['mock']) ->label('scope', 'public') @@ -112,7 +112,7 @@ App::get('/v1/mock/tests/general/oauth2/success') ]); }); -App::get('/v1/mock/tests/general/oauth2/failure') +Http::get('/v1/mock/tests/general/oauth2/failure') ->desc('OAuth2 Failure') ->groups(['mock']) ->label('scope', 'public') @@ -127,7 +127,7 @@ App::get('/v1/mock/tests/general/oauth2/failure') ]); }); -App::patch('/v1/mock/functions-v2') +Http::patch('/v1/mock/functions-v2') ->desc('Update Function Version to V2 (outdated code syntax)') ->groups(['mock', 'api', 'functions']) ->label('scope', 'functions.write') @@ -136,7 +136,7 @@ App::patch('/v1/mock/functions-v2') ->inject('response') ->inject('dbForProject') ->action(function (string $functionId, Response $response, Database $dbForProject) { - $isDevelopment = App::getEnv('_APP_ENV', 'development') === 'development'; + $isDevelopment = Http::getEnv('_APP_ENV', 'development') === 'development'; if (!$isDevelopment) { throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED); @@ -153,7 +153,7 @@ App::patch('/v1/mock/functions-v2') $response->noContent(); }); -App::get('/v1/mock/github/callback') +Http::get('/v1/mock/github/callback') ->desc('Create installation document using GitHub installation id') ->groups(['mock', 'api', 'vcs']) ->label('scope', 'public') @@ -165,7 +165,7 @@ App::get('/v1/mock/github/callback') ->inject('response') ->inject('dbForConsole') ->action(function (string $providerInstallationId, string $projectId, GitHub $github, Document $project, Response $response, Database $dbForConsole) { - $isDevelopment = App::getEnv('_APP_ENV', 'development') === 'development'; + $isDevelopment = Http::getEnv('_APP_ENV', 'development') === 'development'; if (!$isDevelopment) { throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED); @@ -179,8 +179,8 @@ App::get('/v1/mock/github/callback') } if (!empty($providerInstallationId)) { - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $owner = $github->getOwnerName($providerInstallationId) ?? ''; @@ -213,7 +213,7 @@ App::get('/v1/mock/github/callback') ]); }); -App::shutdown() +Http::shutdown() ->groups(['mock']) ->inject('utopia') ->inject('response') diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 605b6cf237..3c17f3ae8c 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -16,7 +16,7 @@ use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Adapter\Filesystem; use Utopia\Cache\Cache; use Utopia\Config\Config; @@ -25,7 +25,7 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\WhiteList; $parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) { preg_match_all('/{(.*?)}/', $label, $matches); @@ -146,7 +146,7 @@ $databaseListener = function (string $event, Document $document, Document $proje } }; -App::init() +Http::init() ->groups(['api']) ->inject('utopia') ->inject('request') @@ -295,7 +295,7 @@ App::init() } }); -App::init() +Http::init() ->groups(['api']) ->inject('utopia') ->inject('request') @@ -365,7 +365,7 @@ App::init() ; } - $enabled = App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled'; + $enabled = Http::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled'; if ( $enabled // Abuse is enabled @@ -463,7 +463,7 @@ App::init() } }); -App::init() +Http::init() ->groups(['session']) ->inject('user') ->inject('request') @@ -483,7 +483,7 @@ App::init() * Delete older sessions if the number of sessions have crossed * the session limit set for the project */ -App::shutdown() +Http::shutdown() ->groups(['session']) ->inject('utopia') ->inject('request') @@ -517,7 +517,7 @@ App::shutdown() $dbForProject->purgeCachedDocument('users', $userId); }); -App::shutdown() +Http::shutdown() ->groups(['api']) ->inject('utopia') ->inject('request') @@ -737,10 +737,10 @@ App::shutdown() } }); -App::init() +Http::init() ->groups(['usage']) ->action(function () { - if (App::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') { + if (Http::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') { throw new Exception(Exception::GENERAL_USAGE_DISABLED); } }); diff --git a/app/controllers/shared/api/auth.php b/app/controllers/shared/api/auth.php index 7f304454be..6880f73e81 100644 --- a/app/controllers/shared/api/auth.php +++ b/app/controllers/shared/api/auth.php @@ -4,12 +4,12 @@ use Appwrite\Auth\Auth; use Appwrite\Extend\Exception; use Appwrite\Utopia\Request; use MaxMind\Db\Reader; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -App::init() +Http::init() ->groups(['mfaProtected']) ->inject('session') ->action(function (Document $session) { @@ -28,14 +28,14 @@ App::init() } }); -App::init() +Http::init() ->groups(['auth']) ->inject('utopia') ->inject('request') ->inject('project') ->inject('geodb') ->action(function (App $utopia, Request $request, Document $project, Reader $geodb) { - $denylist = App::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', ''); + $denylist = Http::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', ''); if (!empty($denylist && $project->getId() === 'console')) { $countries = explode(',', $denylist); $record = $geodb->get($request->getIP()) ?? []; diff --git a/app/controllers/web/console.php b/app/controllers/web/console.php index fac8d21261..dac929d876 100644 --- a/app/controllers/web/console.php +++ b/app/controllers/web/console.php @@ -2,9 +2,9 @@ use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Utopia\App; +use Utopia\Http\Http; -App::init() +Http::init() ->groups(['web']) ->inject('request') ->inject('response') @@ -16,7 +16,7 @@ App::init() ; }); -App::get('/console/*') +Http::get('/console/*') ->alias('/') ->alias('auth/*') ->alias('/invite') diff --git a/app/controllers/web/home.php b/app/controllers/web/home.php index 27b2614c37..1d894b95bb 100644 --- a/app/controllers/web/home.php +++ b/app/controllers/web/home.php @@ -1,10 +1,10 @@ desc('Get Version') ->groups(['home', 'web']) ->label('scope', 'public') diff --git a/app/http.php b/app/http.php index e8a3cc5124..34e6b12c1a 100644 --- a/app/http.php +++ b/app/http.php @@ -10,7 +10,7 @@ use Swoole\Http\Response as SwooleResponse; use Swoole\Http\Server; use Swoole\Process; use Utopia\Abuse\Adapters\TimeLimit; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -27,12 +27,12 @@ use Utopia\Swoole\Files; $http = new Server( host: "0.0.0.0", - port: App::getEnv('PORT', 80), + port: Http::getEnv('PORT', 80), mode: SWOOLE_PROCESS, ); $payloadSize = 6 * (1024 * 1024); // 6MB -$workerNumber = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)); +$workerNumber = swoole_cpu_num() * intval(Http::getEnv('_APP_WORKER_PER_CORE', 6)); $http ->set([ @@ -66,7 +66,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg go(function () use ($register, $app) { $pools = $register->get('pools'); /** @var Group $pools */ - App::setResource('pools', fn () => $pools); + Http::setResource('pools', fn () => $pools); // wait for database to be ready $attempts = 0; @@ -157,7 +157,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg '$id' => ID::custom('default'), '$collection' => ID::custom('buckets'), 'name' => 'Default', - 'maximumFileSize' => (int) App::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB + 'maximumFileSize' => (int) Http::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB 'allowedFileExtensions' => [], 'enabled' => true, 'compression' => 'gzip', @@ -227,8 +227,8 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg }); $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register) { - App::setResource('swooleRequest', fn () => $swooleRequest); - App::setResource('swooleResponse', fn () => $swooleResponse); + Http::setResource('swooleRequest', fn () => $swooleRequest); + Http::setResource('swooleResponse', fn () => $swooleResponse); $request = new Request($swooleRequest); $response = new Response($swooleResponse); @@ -248,7 +248,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo $app = new App('UTC'); $pools = $register->get('pools'); - App::setResource('pools', fn () => $pools); + Http::setResource('pools', fn () => $pools); try { Authorization::cleanRoles(); @@ -256,7 +256,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo $app->run($request, $response); } catch (\Throwable $th) { - $version = App::getEnv('_APP_VERSION', 'UNKNOWN'); + $version = Http::getEnv('_APP_VERSION', 'UNKNOWN'); $logger = $app->getResource("logger"); if ($logger) { @@ -298,7 +298,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); $log->setAction($action); - $isProduction = App::getEnv('_APP_ENV', 'development') === 'production'; + $isProduction = Http::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); $responseCode = $logger->addLog($log); @@ -312,7 +312,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo $swooleResponse->setStatusCode(500); - $output = ((App::isDevelopment())) ? [ + $output = ((Http::isDevelopment())) ? [ 'message' => 'Error: ' . $th->getMessage(), 'code' => 500, 'file' => $th->getFile(), diff --git a/app/init.php b/app/init.php index c068209613..ed21d599b9 100644 --- a/app/init.php +++ b/app/init.php @@ -43,7 +43,7 @@ use Appwrite\URL\URL as AppwriteURL; use MaxMind\Db\Reader; use PHPMailer\PHPMailer\PHPMailer; use Swoole\Database\PDOProxy; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; @@ -78,11 +78,11 @@ use Utopia\Storage\Device\Local; use Utopia\Storage\Device\S3; use Utopia\Storage\Device\Wasabi; use Utopia\Storage\Storage; -use Utopia\Validator\Hostname; -use Utopia\Validator\IP; -use Utopia\Validator\Range; -use Utopia\Validator\URL; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\Hostname; +use Utopia\Http\Validator\IP; +use Utopia\Http\Validator\Range; +use Utopia\Http\Validator\URL; +use Utopia\Http\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; const APP_NAME = 'Appwrite'; @@ -245,9 +245,9 @@ const METRIC_NETWORK_OUTBOUND = 'network.outbound'; $register = new Registry(); -App::setMode(App::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION)); +Http::setMode(Http::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION)); -if (!App::isProduction()) { +if (!Http::isProduction()) { // Allow specific domains to skip public domain validation in dev environment // Useful for existing tests involving webhooks PublicDomain::allow(['request-catcher']); @@ -517,7 +517,7 @@ Database::addFilter( Database::addFilter( 'encrypt', function (mixed $value) { - $key = App::getEnv('_APP_OPENSSL_KEY_V1'); + $key = Http::getEnv('_APP_OPENSSL_KEY_V1'); $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); $tag = null; @@ -534,7 +534,7 @@ Database::addFilter( return; } $value = json_decode($value, true); - $key = App::getEnv('_APP_OPENSSL_KEY_V' . $value['version']); + $key = Http::getEnv('_APP_OPENSSL_KEY_V' . $value['version']); return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag'])); } @@ -720,8 +720,8 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) { */ $register->set('logger', function () { // Register error logger - $providerName = App::getEnv('_APP_LOGGING_PROVIDER', ''); - $providerConfig = App::getEnv('_APP_LOGGING_CONFIG', ''); + $providerName = Http::getEnv('_APP_LOGGING_PROVIDER', ''); + $providerConfig = Http::getEnv('_APP_LOGGING_CONFIG', ''); if (empty($providerName) || empty($providerConfig)) { return; @@ -739,61 +739,61 @@ $register->set('pools', function () { $group = new Group(); $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ - 'scheme' => App::getEnv('_APP_DB_ADAPTER', 'mariadb'), - 'host' => App::getEnv('_APP_DB_HOST', 'mariadb'), - 'port' => App::getEnv('_APP_DB_PORT', '3306'), - 'user' => App::getEnv('_APP_DB_USER', ''), - 'pass' => App::getEnv('_APP_DB_PASS', ''), - 'path' => App::getEnv('_APP_DB_SCHEMA', ''), + 'scheme' => Http::getEnv('_APP_DB_ADAPTER', 'mariadb'), + 'host' => Http::getEnv('_APP_DB_HOST', 'mariadb'), + 'port' => Http::getEnv('_APP_DB_PORT', '3306'), + 'user' => Http::getEnv('_APP_DB_USER', ''), + 'pass' => Http::getEnv('_APP_DB_PASS', ''), + 'path' => Http::getEnv('_APP_DB_SCHEMA', ''), ]); $fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([ 'scheme' => 'redis', - 'host' => App::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => App::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => App::getEnv('_APP_REDIS_USER', ''), - 'pass' => App::getEnv('_APP_REDIS_PASS', ''), + 'host' => Http::getEnv('_APP_REDIS_HOST', 'redis'), + 'port' => Http::getEnv('_APP_REDIS_PORT', '6379'), + 'user' => Http::getEnv('_APP_REDIS_USER', ''), + 'pass' => Http::getEnv('_APP_REDIS_PASS', ''), ]); $connections = [ 'console' => [ 'type' => 'database', - 'dsns' => App::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), + 'dsns' => Http::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), 'multiple' => false, 'schemes' => ['mariadb', 'mysql', 'mariadb-proxy'], ], 'database' => [ 'type' => 'database', - 'dsns' => App::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), + 'dsns' => Http::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), 'multiple' => true, 'schemes' => ['mariadb', 'mysql', 'mariadb-proxy'], ], 'queue' => [ 'type' => 'queue', - 'dsns' => App::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), + 'dsns' => Http::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), 'multiple' => false, 'schemes' => ['redis'], ], 'pubsub' => [ 'type' => 'pubsub', - 'dsns' => App::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), + 'dsns' => Http::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), 'multiple' => false, 'schemes' => ['redis'], ], 'cache' => [ 'type' => 'cache', - 'dsns' => App::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), + 'dsns' => Http::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), 'multiple' => true, 'schemes' => ['redis'], ], ]; - $maxConnections = App::getEnv('_APP_CONNECTIONS_MAX', 151); - $instanceConnections = $maxConnections / App::getEnv('_APP_POOL_CLIENTS', 14); + $maxConnections = Http::getEnv('_APP_CONNECTIONS_MAX', 151); + $instanceConnections = $maxConnections / Http::getEnv('_APP_POOL_CLIENTS', 14); - $multiprocessing = App::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; + $multiprocessing = Http::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; if ($multiprocessing) { - $workerCount = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)); + $workerCount = swoole_cpu_num() * intval(Http::getEnv('_APP_WORKER_PER_CORE', 6)); } else { $workerCount = 1; } @@ -933,11 +933,11 @@ $register->set('pools', function () { $register->set('db', function () { // This is usually for our workers or CLI commands scope - $dbHost = App::getEnv('_APP_DB_HOST', ''); - $dbPort = App::getEnv('_APP_DB_PORT', ''); - $dbUser = App::getEnv('_APP_DB_USER', ''); - $dbPass = App::getEnv('_APP_DB_PASS', ''); - $dbScheme = App::getEnv('_APP_DB_SCHEMA', ''); + $dbHost = Http::getEnv('_APP_DB_HOST', ''); + $dbPort = Http::getEnv('_APP_DB_PORT', ''); + $dbUser = Http::getEnv('_APP_DB_USER', ''); + $dbPass = Http::getEnv('_APP_DB_PASS', ''); + $dbScheme = Http::getEnv('_APP_DB_SCHEMA', ''); return new PDO( "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", @@ -952,21 +952,21 @@ $register->set('smtp', function () { $mail->isSMTP(); - $username = App::getEnv('_APP_SMTP_USERNAME'); - $password = App::getEnv('_APP_SMTP_PASSWORD'); + $username = Http::getEnv('_APP_SMTP_USERNAME'); + $password = Http::getEnv('_APP_SMTP_PASSWORD'); $mail->XMailer = 'Appwrite Mailer'; - $mail->Host = App::getEnv('_APP_SMTP_HOST', 'smtp'); - $mail->Port = App::getEnv('_APP_SMTP_PORT', 25); + $mail->Host = Http::getEnv('_APP_SMTP_HOST', 'smtp'); + $mail->Port = Http::getEnv('_APP_SMTP_PORT', 25); $mail->SMTPAuth = !empty($username) && !empty($password); $mail->Username = $username; $mail->Password = $password; - $mail->SMTPSecure = App::getEnv('_APP_SMTP_SECURE', ''); + $mail->SMTPSecure = Http::getEnv('_APP_SMTP_SECURE', ''); $mail->SMTPAutoTLS = false; $mail->CharSet = 'UTF-8'; - $from = \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); - $email = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + $from = \urldecode(Http::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); + $email = Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); $mail->setFrom($email, $from); $mail->addReplyTo($email, $from); @@ -1017,68 +1017,68 @@ foreach ($locales as $locale) { 'method' => 'GET', 'user_agent' => \sprintf( APP_USERAGENT, - App::getEnv('_APP_VERSION', 'UNKNOWN'), - App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY) + Http::getEnv('_APP_VERSION', 'UNKNOWN'), + Http::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY) ), 'timeout' => 2, ], ]); // Runtime Execution -App::setResource('log', fn () => new Log()); -App::setResource('logger', function ($register) { +Http::setResource('log', fn () => new Log()); +Http::setResource('logger', function ($register) { return $register->get('logger'); }, ['register']); -App::setResource('hooks', function ($register) { +Http::setResource('hooks', function ($register) { return $register->get('hooks'); }, ['register']); -App::setResource('register', fn () => $register); -App::setResource('locale', fn () => new Locale(App::getEnv('_APP_LOCALE', 'en'))); +Http::setResource('register', fn () => $register); +Http::setResource('locale', fn () => new Locale(Http::getEnv('_APP_LOCALE', 'en'))); -App::setResource('localeCodes', function () { +Http::setResource('localeCodes', function () { return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); }); // Queues -App::setResource('queue', function (Group $pools) { +Http::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); -App::setResource('queueForMessaging', function (Connection $queue) { +Http::setResource('queueForMessaging', function (Connection $queue) { return new Messaging($queue); }, ['queue']); -App::setResource('queueForMails', function (Connection $queue) { +Http::setResource('queueForMails', function (Connection $queue) { return new Mail($queue); }, ['queue']); -App::setResource('queueForBuilds', function (Connection $queue) { +Http::setResource('queueForBuilds', function (Connection $queue) { return new Build($queue); }, ['queue']); -App::setResource('queueForDatabase', function (Connection $queue) { +Http::setResource('queueForDatabase', function (Connection $queue) { return new EventDatabase($queue); }, ['queue']); -App::setResource('queueForDeletes', function (Connection $queue) { +Http::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); -App::setResource('queueForEvents', function (Connection $queue) { +Http::setResource('queueForEvents', function (Connection $queue) { return new Event($queue); }, ['queue']); -App::setResource('queueForAudits', function (Connection $queue) { +Http::setResource('queueForAudits', function (Connection $queue) { return new Audit($queue); }, ['queue']); -App::setResource('queueForFunctions', function (Connection $queue) { +Http::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); -App::setResource('queueForUsage', function (Connection $queue) { +Http::setResource('queueForUsage', function (Connection $queue) { return new Usage($queue); }, ['queue']); -App::setResource('queueForCertificates', function (Connection $queue) { +Http::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); -App::setResource('queueForMigrations', function (Connection $queue) { +Http::setResource('queueForMigrations', function (Connection $queue) { return new Migration($queue); }, ['queue']); -App::setResource('clients', function ($request, $console, $project) { +Http::setResource('clients', function ($request, $console, $project) { $console->setAttribute('platforms', [ // Always allow current host '$collection' => ID::custom('platforms'), 'name' => 'Current Host', @@ -1086,7 +1086,7 @@ App::setResource('clients', function ($request, $console, $project) { 'hostname' => $request->getHostname(), ], Document::SET_TYPE_APPEND); - $hostnames = explode(',', App::getEnv('_APP_CONSOLE_HOSTNAMES', '')); + $hostnames = explode(',', Http::getEnv('_APP_CONSOLE_HOSTNAMES', '')); $validator = new Hostname(); foreach ($hostnames as $hostname) { $hostname = trim($hostname); @@ -1129,7 +1129,7 @@ App::setResource('clients', function ($request, $console, $project) { return $clients; }, ['request', 'console', 'project']); -App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { +Http::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { /** @var Appwrite\Utopia\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Document $project */ @@ -1210,7 +1210,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons $authJWT = $request->getHeader('x-appwrite-jwt', ''); if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication - $jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. + $jwt = new JWT(Http::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. try { $payload = $jwt->decode($authJWT); @@ -1236,7 +1236,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons return $user; }, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); -App::setResource('project', function ($dbForConsole, $request, $console) { +Http::setResource('project', function ($dbForConsole, $request, $console) { /** @var Appwrite\Utopia\Request $request */ /** @var Utopia\Database\Database $dbForConsole */ /** @var Utopia\Database\Document $console */ @@ -1252,7 +1252,7 @@ App::setResource('project', function ($dbForConsole, $request, $console) { return $project; }, ['dbForConsole', 'request', 'console']); -App::setResource('session', function (Document $user, Document $project) { +Http::setResource('session', function (Document $user, Document $project) { if ($user->isEmpty()) { return; } @@ -1274,7 +1274,7 @@ App::setResource('session', function (Document $user, Document $project) { return; }, ['user', 'project']); -App::setResource('console', function () { +Http::setResource('console', function () { return new Document([ '$id' => ID::custom('console'), '$internalId' => ID::custom('console'), @@ -1300,21 +1300,21 @@ App::setResource('console', function () { 'legalAddress' => '', 'legalTaxId' => '', 'auths' => [ - 'invites' => App::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', - 'limit' => (App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user + 'invites' => Http::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', + 'limit' => (Http::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds ], - 'authWhitelistEmails' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], - 'authWhitelistIPs' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], + 'authWhitelistEmails' => (!empty(Http::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', Http::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], + 'authWhitelistIPs' => (!empty(Http::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', Http::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], 'oAuthProviders' => [ 'githubEnabled' => true, - 'githubSecret' => App::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), - 'githubAppid' => App::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') + 'githubSecret' => Http::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), + 'githubAppid' => Http::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') ], ]); }, []); -App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { +Http::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } @@ -1335,7 +1335,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, return $database; }, ['pools', 'dbForConsole', 'cache', 'project']); -App::setResource('dbForConsole', function (Group $pools, Cache $cache) { +Http::setResource('dbForConsole', function (Group $pools, Cache $cache) { $dbAdapter = $pools ->get('console') ->pop() @@ -1353,7 +1353,7 @@ App::setResource('dbForConsole', function (Group $pools, Cache $cache) { return $database; }, ['pools', 'cache']); -App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { +Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools $getProjectDB = function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { @@ -1396,7 +1396,7 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, return $getProjectDB; }, ['pools', 'dbForConsole', 'cache']); -App::setResource('cache', function (Group $pools) { +Http::setResource('cache', function (Group $pools) { $list = Config::getParam('pools-cache', []); $adapters = []; @@ -1411,25 +1411,25 @@ App::setResource('cache', function (Group $pools) { return new Cache(new Sharding($adapters)); }, ['pools']); -App::setResource('deviceForLocal', function () { +Http::setResource('deviceForLocal', function () { return new Local(); }); -App::setResource('deviceForFiles', function ($project) { +Http::setResource('deviceForFiles', function ($project) { return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); }, ['project']); -App::setResource('deviceForFunctions', function ($project) { +Http::setResource('deviceForFunctions', function ($project) { return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); }, ['project']); -App::setResource('deviceForBuilds', function ($project) { +Http::setResource('deviceForBuilds', function ($project) { return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); }, ['project']); function getDevice($root): Device { - $connection = App::getEnv('_APP_CONNECTIONS_STORAGE', ''); + $connection = Http::getEnv('_APP_CONNECTIONS_STORAGE', ''); if (!empty($connection)) { $acl = 'private'; @@ -1466,50 +1466,50 @@ function getDevice($root): Device return new Local($root); } } else { - switch (strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { + switch (strtolower(Http::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { case Storage::DEVICE_LOCAL: default: return new Local($root); case Storage::DEVICE_S3: - $s3AccessKey = App::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); - $s3SecretKey = App::getEnv('_APP_STORAGE_S3_SECRET', ''); - $s3Region = App::getEnv('_APP_STORAGE_S3_REGION', ''); - $s3Bucket = App::getEnv('_APP_STORAGE_S3_BUCKET', ''); + $s3AccessKey = Http::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); + $s3SecretKey = Http::getEnv('_APP_STORAGE_S3_SECRET', ''); + $s3Region = Http::getEnv('_APP_STORAGE_S3_REGION', ''); + $s3Bucket = Http::getEnv('_APP_STORAGE_S3_BUCKET', ''); $s3Acl = 'private'; return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); case Storage::DEVICE_DO_SPACES: - $doSpacesAccessKey = App::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); - $doSpacesSecretKey = App::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); - $doSpacesRegion = App::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); - $doSpacesBucket = App::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); + $doSpacesAccessKey = Http::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); + $doSpacesSecretKey = Http::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); + $doSpacesRegion = Http::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); + $doSpacesBucket = Http::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); $doSpacesAcl = 'private'; return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); case Storage::DEVICE_BACKBLAZE: - $backblazeAccessKey = App::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); - $backblazeSecretKey = App::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); - $backblazeRegion = App::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); - $backblazeBucket = App::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); + $backblazeAccessKey = Http::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); + $backblazeSecretKey = Http::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); + $backblazeRegion = Http::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); + $backblazeBucket = Http::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); $backblazeAcl = 'private'; return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); case Storage::DEVICE_LINODE: - $linodeAccessKey = App::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); - $linodeSecretKey = App::getEnv('_APP_STORAGE_LINODE_SECRET', ''); - $linodeRegion = App::getEnv('_APP_STORAGE_LINODE_REGION', ''); - $linodeBucket = App::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); + $linodeAccessKey = Http::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); + $linodeSecretKey = Http::getEnv('_APP_STORAGE_LINODE_SECRET', ''); + $linodeRegion = Http::getEnv('_APP_STORAGE_LINODE_REGION', ''); + $linodeBucket = Http::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); $linodeAcl = 'private'; return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); case Storage::DEVICE_WASABI: - $wasabiAccessKey = App::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); - $wasabiSecretKey = App::getEnv('_APP_STORAGE_WASABI_SECRET', ''); - $wasabiRegion = App::getEnv('_APP_STORAGE_WASABI_REGION', ''); - $wasabiBucket = App::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); + $wasabiAccessKey = Http::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); + $wasabiSecretKey = Http::getEnv('_APP_STORAGE_WASABI_SECRET', ''); + $wasabiRegion = Http::getEnv('_APP_STORAGE_WASABI_REGION', ''); + $wasabiBucket = Http::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); $wasabiAcl = 'private'; return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); } } } -App::setResource('mode', function ($request) { +Http::setResource('mode', function ($request) { /** @var Appwrite\Utopia\Request $request */ /** @@ -1520,18 +1520,18 @@ App::setResource('mode', function ($request) { return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); }, ['request']); -App::setResource('geodb', function ($register) { +Http::setResource('geodb', function ($register) { /** @var Utopia\Registry\Registry $register */ return $register->get('geodb'); }, ['register']); -App::setResource('passwordsDictionary', function ($register) { +Http::setResource('passwordsDictionary', function ($register) { /** @var Utopia\Registry\Registry $register */ return $register->get('passwordsDictionary'); }, ['register']); -App::setResource('servers', function () { +Http::setResource('servers', function () { $platforms = Config::getParam('platforms'); $server = $platforms[APP_PLATFORM_SERVER]; @@ -1542,11 +1542,11 @@ App::setResource('servers', function () { return $languages; }); -App::setResource('promiseAdapter', function ($register) { +Http::setResource('promiseAdapter', function ($register) { return $register->get('promiseAdapter'); }, ['register']); -App::setResource('schema', function ($utopia, $dbForProject) { +Http::setResource('schema', function ($utopia, $dbForProject) { $complexity = function (int $complexity, array $args) { $queries = Query::parseQueries($args['queries'] ?? []); @@ -1632,29 +1632,29 @@ App::setResource('schema', function ($utopia, $dbForProject) { ); }, ['utopia', 'dbForProject']); -App::setResource('contributors', function () { +Http::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 () { +Http::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 () { +Http::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) { +Http::setResource('gitHub', function (Cache $cache) { return new VcsGitHub($cache); }, ['cache']); -App::setResource('requestTimestamp', function ($request) { +Http::setResource('requestTimestamp', function ($request) { //TODO: Move this to the Request class itself $timestampHeader = $request->getHeader('x-appwrite-timestamp'); $requestTimestamp = null; @@ -1667,3 +1667,5 @@ App::setResource('requestTimestamp', function ($request) { } return $requestTimestamp; }, ['request']); + +Http::setResource('auth', fn () => new Authorization()); \ No newline at end of file diff --git a/app/realtime.php b/app/realtime.php index fd7d69bbf5..1fa6733575 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -13,7 +13,7 @@ use Swoole\Table; use Swoole\Timer; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; @@ -121,9 +121,9 @@ $stats->create(); $containerId = uniqid(); $statsDocument = null; -$workerNumber = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6)); +$workerNumber = swoole_cpu_num() * intval(Http::getEnv('_APP_WORKER_PER_CORE', 6)); -$adapter = new Adapter\Swoole(port: App::getEnv('PORT', 80)); +$adapter = new Adapter\Swoole(port: Http::getEnv('PORT', 80)); $adapter ->setPackageMaxLength(64000) // Default maximum Package Size (64kb) ->setWorkerNumber($workerNumber); @@ -134,7 +134,7 @@ $logError = function (Throwable $error, string $action) use ($register) { $logger = $register->get('logger'); if ($logger && !$error instanceof Exception) { - $version = App::getEnv('_APP_VERSION', 'UNKNOWN'); + $version = Http::getEnv('_APP_VERSION', 'UNKNOWN'); $log = new Log(); $log->setNamespace("realtime"); @@ -153,7 +153,7 @@ $logError = function (Throwable $error, string $action) use ($register) { $log->setAction($action); - $isProduction = App::getEnv('_APP_ENV', 'development') === 'production'; + $isProduction = Http::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); $responseCode = $logger->addLog($log); @@ -355,7 +355,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $receivers = $realtime->getSubscribers($event); - if (App::isDevelopment() && !empty($receivers)) { + if (Http::isDevelopment() && !empty($receivers)) { Console::log("[Debug][Worker {$workerId}] Receivers: " . count($receivers)); Console::log("[Debug][Worker {$workerId}] Receivers Connection IDs: " . json_encode($receivers)); Console::log("[Debug][Worker {$workerId}] Event: " . $payload); @@ -395,9 +395,9 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::info("Connection open (user: {$connection})"); - App::setResource('pools', fn () => $register->get('pools')); - App::setResource('request', fn () => $request); - App::setResource('response', fn () => $response); + Http::setResource('pools', fn () => $register->get('pools')); + Http::setResource('request', fn () => $request); + Http::setResource('response', fn () => $response); try { /** @var Document $project */ @@ -426,7 +426,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $abuse = new Abuse($timeLimit); - if (App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled' && $abuse->check()) { + if (Http::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled' && $abuse->check()) { throw new Exception(Exception::REALTIME_TOO_MANY_MESSAGES, 'Too many requests'); } @@ -485,7 +485,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $server->send([$connection], json_encode($response)); $server->close($connection, $th->getCode()); - if (App::isDevelopment()) { + if (Http::isDevelopment()) { Console::error('[Error] Connection Error'); Console::error('[Error] Code: ' . $response['data']['code']); Console::error('[Error] Message: ' . $response['data']['message']); @@ -521,7 +521,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re $abuse = new Abuse($timeLimit); - if ($abuse->check() && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') { + if ($abuse->check() && Http::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') { throw new Exception(Exception::REALTIME_TOO_MANY_MESSAGES, 'Too many messages.'); } diff --git a/app/worker.php b/app/worker.php index 8523b81cdb..ea1c67734c 100644 --- a/app/worker.php +++ b/app/worker.php @@ -17,7 +17,7 @@ use Appwrite\Event\Usage; use Appwrite\Event\UsageDump; use Appwrite\Platform\Appwrite; use Swoole\Runtime; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; @@ -115,15 +115,15 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForConso }, ['pools', 'dbForConsole', 'cache']); Server::setResource('abuseRetention', function () { - return DateTime::addSeconds(new \DateTime(), -1 * App::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400)); + return DateTime::addSeconds(new \DateTime(), -1 * Http::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400)); }); Server::setResource('auditRetention', function () { - return DateTime::addSeconds(new \DateTime(), -1 * App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600)); + return DateTime::addSeconds(new \DateTime(), -1 * Http::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600)); }); Server::setResource('executionRetention', function () { - return DateTime::addSeconds(new \DateTime(), -1 * App::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); + return DateTime::addSeconds(new \DateTime(), -1 * Http::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); }); Server::setResource('cache', function (Registry $register) { @@ -246,9 +246,9 @@ if (!empty($workerIndex)) { } if (\str_starts_with($workerName, 'databases')) { - $queueName = App::getEnv('_APP_QUEUE_NAME', 'database_db_main'); + $queueName = Http::getEnv('_APP_QUEUE_NAME', 'database_db_main'); } else { - $queueName = App::getEnv('_APP_QUEUE_NAME', 'v1-' . strtolower($workerName)); + $queueName = Http::getEnv('_APP_QUEUE_NAME', 'v1-' . strtolower($workerName)); } try { @@ -259,7 +259,7 @@ try { * - _APP_QUEUE_NAME The name of the queue to read for database events */ $platform->init(Service::TYPE_WORKER, [ - 'workersNum' => App::getEnv('_APP_WORKERS_NUM', 1), + 'workersNum' => Http::getEnv('_APP_WORKERS_NUM', 1), 'connection' => $pools->get('queue')->pop()->getResource(), 'workerName' => strtolower($workerName) ?? null, 'queueName' => $queueName @@ -286,7 +286,7 @@ $worker ->inject('project') ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($queueName) { $pools->reclaim(); - $version = App::getEnv('_APP_VERSION', 'UNKNOWN'); + $version = Http::getEnv('_APP_VERSION', 'UNKNOWN'); if ($error instanceof PDOException) { throw $error; @@ -308,7 +308,7 @@ $worker $log->addExtra('detailedTrace', $error->getTrace()); $log->addExtra('roles', Authorization::getRoles()); - $isProduction = App::getEnv('_APP_ENV', 'development') === 'production'; + $isProduction = Http::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); $responseCode = $logger->addLog($log); diff --git a/composer.json b/composer.json index 96bf4dba43..c95f4012bc 100644 --- a/composer.json +++ b/composer.json @@ -51,10 +51,10 @@ "utopia-php/cache": "0.9.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.49.*", + "utopia-php/database": "dev-feat-framework-v2 as 0.49.99", "utopia-php/domains": "0.5.*", "utopia-php/dsn": "0.2.*", - "utopia-php/framework": "0.33.*", + "utopia-php/framework": "0.34.*", "utopia-php/image": "0.6.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.3.*", @@ -66,7 +66,6 @@ "utopia-php/queue": "0.7.*", "utopia-php/registry": "0.5.*", "utopia-php/storage": "0.18.*", - "utopia-php/swoole": "0.8.*", "utopia-php/vcs": "0.6.*", "utopia-php/websocket": "0.1.*", "matomo/device-detector": "6.1.*", diff --git a/composer.lock b/composer.lock index 801b87c539..3052c56b4a 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": "1c3c0b518e1486c5770b57519da2a797", + "content-hash": "f51e2c69cdc67f3a07c4cf5d91bf5de2", "packages": [ { "name": "adhocore/jwt", @@ -771,50 +771,11 @@ "version": "0.6.3", "source": { "type": "git", - "url": "git@github.com:mustangostang/spyc.git", + "url": "https://github.com/mustangostang/spyc", "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", - "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", - "shasum": "" - }, - "require": { - "php": ">=5.3.1" - }, - "require-dev": { - "phpunit/phpunit": "4.3.*@dev" - }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.5.x-dev" - } - }, - "autoload": { - "files": [ - "Spyc.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "mustangostang", - "email": "vlad.andersen@gmail.com" - } - ], - "description": "A simple YAML loader/dumper class for PHP", - "homepage": "https://github.com/mustangostang/spyc/", - "keywords": [ - "spyc", - "yaml", - "yml" - ], - "time": "2019-09-10T13:16:29+00:00" + "notification-url": "https://packagist.org/downloads/" }, { "name": "paragonie/constant_time_encoding", @@ -1552,16 +1513,16 @@ }, { "name": "utopia-php/database", - "version": "0.49.1", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "4199fe8f00f4e181c7782c4a6862845d591c1f03" + "reference": "79fd5790227e039832372f2c6c9c284f6f1284bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/4199fe8f00f4e181c7782c4a6862845d591c1f03", - "reference": "4199fe8f00f4e181c7782c4a6862845d591c1f03", + "url": "https://api.github.com/repos/utopia-php/database/zipball/79fd5790227e039832372f2c6c9c284f6f1284bb", + "reference": "79fd5790227e039832372f2c6c9c284f6f1284bb", "shasum": "" }, "require": { @@ -1569,8 +1530,7 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.9.*", - "utopia-php/fetch": "0.1.*", - "utopia-php/framework": "0.33.*", + "utopia-php/framework": "0.34.*", "utopia-php/mongo": "0.3.*" }, "require-dev": { @@ -1581,7 +1541,7 @@ "phpunit/phpunit": "^9.4", "rregeer/phpunit-coverage-check": "^0.3.1", "swoole/ide-helper": "4.8.0", - "utopia-php/cli": "^0.14.0" + "utopia-php/cli": "0.17.*" }, "type": "library", "autoload": { @@ -1603,9 +1563,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.49.1" + "source": "https://github.com/utopia-php/database/tree/feat-framework-v2" }, - "time": "2024-03-06T11:35:53+00:00" + "time": "2024-02-27T10:05:08+00:00" }, { "name": "utopia-php/domains", @@ -1714,89 +1674,53 @@ }, "time": "2023-11-02T12:01:43+00:00" }, - { - "name": "utopia-php/fetch", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/fetch.git", - "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/fetch/zipball/2fa214b9262acd1a3583515a364da4f35929d5c5", - "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "^1.5.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Fetch\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "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.1.0" - }, - "time": "2023-10-10T11:58:32+00:00" - }, { "name": "utopia-php/framework", - "version": "0.33.2", + "version": "0.34.2", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "b1423ca3e3b61c6c4c2e619d2cb80672809a19f3" + "reference": "fd126c02b78cc80678c9638f7b335dfb4a841b78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/b1423ca3e3b61c6c4c2e619d2cb80672809a19f3", - "reference": "b1423ca3e3b61c6c4c2e619d2cb80672809a19f3", + "url": "https://api.github.com/repos/utopia-php/http/zipball/fd126c02b78cc80678c9638f7b335dfb4a841b78", + "reference": "fd126c02b78cc80678c9638f7b335dfb4a841b78", "shasum": "" }, "require": { + "ext-swoole": "*", "php": ">=8.0" }, "require-dev": { "laravel/pint": "^1.2", "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" }, "type": "library", "autoload": { "psr-4": { - "Utopia\\": "src/" + "Utopia\\Http\\": "src/Http" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "A simple, light and advanced PHP framework", + "description": "A simple, light and advanced PHP HTTP framework", "keywords": [ "framework", + "http", "php", "upf" ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.2" + "source": "https://github.com/utopia-php/http/tree/0.34.2" }, - "time": "2024-01-31T10:35:59+00:00" + "time": "2024-02-20T11:36:56+00:00" }, { "name": "utopia-php/image", @@ -2426,57 +2350,6 @@ }, "time": "2023-12-31T11:45:12+00:00" }, - { - "name": "utopia-php/swoole", - "version": "0.8.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.33.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "5.0.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.8.2" - }, - "time": "2024-02-01T14:54:12+00:00" - }, { "name": "utopia-php/system", "version": "0.7.2", @@ -5498,11 +5371,58 @@ } ], "time": "2023-11-21T18:54:41+00:00" + }, + { + "name": "utopia-php/fetch", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/fetch.git", + "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/fetch/zipball/2fa214b9262acd1a3583515a364da4f35929d5c5", + "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.5.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Fetch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "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.1.0" + }, + "time": "2023-10-10T11:58:32+00:00" + } + ], + "aliases": [ + { + "package": "utopia-php/database", + "version": "dev-feat-framework-v2", + "alias": "0.49.99", + "alias_normalized": "0.49.99.0" } ], - "aliases": [], "minimum-stability": "stable", "stability-flags": { + "utopia-php/database": 20, "appwrite/sdk-generator": 5 }, "prefer-stable": false, diff --git a/docs/tutorials/add-route.md b/docs/tutorials/add-route.md index ac6fd40bdb..0baa51b5c0 100644 --- a/docs/tutorials/add-route.md +++ b/docs/tutorials/add-route.md @@ -8,7 +8,7 @@ Setting an alias allows the route to be also accessible from the alias URL. The first parameter specifies the alias URL, the second parameter specifies default values for route parameters. ```php -App::post('/v1/storage/buckets/:bucketId/files') +Http::post('/v1/storage/buckets/:bucketId/files') ->alias('/v1/storage/files', ['bucketId' => 'default']) ``` @@ -17,7 +17,7 @@ App::post('/v1/storage/buckets/:bucketId/files') Used as an abstract description of the route. ```php -App::post('/v1/storage/buckets/:bucketId/files') +Http::post('/v1/storage/buckets/:bucketId/files') ->desc('Create File') ``` @@ -26,14 +26,14 @@ App::post('/v1/storage/buckets/:bucketId/files') Groups array is used to group one or more routes with one or more hooks functionality. ```php -App::post('/v1/storage/buckets/:bucketId/files') +Http::post('/v1/storage/buckets/:bucketId/files') ->groups(['api']) ``` In the above example groups() is used to define the current route as part of the routes that shares a common init middleware hook. ```php -App::init() +Http::init() ->groups(['api']) ->action( some code..... @@ -52,7 +52,7 @@ Appwrite uses different labels to achieve different things, for example: - scope - Defines the route permissions scope. ```php -App::post('/v1/storage/buckets/:bucketId/files') +Http::post('/v1/storage/buckets/:bucketId/files') ->label('scope', 'files.write') ``` @@ -66,7 +66,7 @@ App::post('/v1/storage/buckets/:bucketId/files') - audits.resource - Signals the extraction part of the resource. ```php -App::post('/v1/account/create') +Http::post('/v1/account/create') ->label('audits.event', 'account.create') ->label('audits.resource', 'user/{response.$id}') ->label('audits.userId', '{response.$id}') @@ -84,7 +84,7 @@ App::post('/v1/account/create') * sdk.offline.response.key - JSON property name that has the ID. Defaults to $id ```php -App::post('/v1/account/jwt') +Http::post('/v1/account/jwt') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createJWT') @@ -100,7 +100,7 @@ App::post('/v1/account/jwt') - cache.resource - Identifies the cached resource. ```php -App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') +Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->label('cache', true) ->label('cache.resource', 'file/{request.fileId}') ``` @@ -115,7 +115,7 @@ When using the example below, we configure the abuse mechanism to allow this key constructed from the combination of the ip, http method, url, userId to hit the route maximum 60 times in 1 hour (60 seconds \* 60 minutes). ```php -App::post('/v1/storage/buckets/:bucketId/files') +Http::post('/v1/storage/buckets/:bucketId/files') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', 60) ->label('abuse-time', 3600) @@ -127,7 +127,7 @@ App::post('/v1/storage/buckets/:bucketId/files') Placeholders marked as `[]` are parsed and replaced with their real values. ```php -App::post('/v1/storage/buckets/:bucketId/files') +Http::post('/v1/storage/buckets/:bucketId/files') ->label('event', 'buckets.[bucketId].files.[fileId].create') ``` @@ -145,7 +145,7 @@ As the name implies, `param()` is used to define a request parameter. - An array of injections ```php -App::get('/v1/account/logs') +Http::get('/v1/account/logs') ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) ``` @@ -154,14 +154,14 @@ App::get('/v1/account/logs') inject is used to inject dependencies pre-bounded to the app. ```php -App::post('/v1/storage/buckets/:bucketId/files') +Http::post('/v1/storage/buckets/:bucketId/files') ->inject('user') ``` -In the example above, the user object is injected into the route pre-bounded using `App::setResource()`. +In the example above, the user object is injected into the route pre-bounded using `Http::setResource()`. ```php -App::setResource('user', function() { +Http::setResource('user', function() { some code... }); ``` @@ -170,7 +170,7 @@ some code... Action populates the actual route code and has to be very clear and understandable. A good route stays simple and doesn't contain complex logic. An action is where we describe our business needs in code, and combine different libraries to work together and tell our story. ```php -App::post('/v1/account/sessions/anonymous') +Http::post('/v1/account/sessions/anonymous') ->action(function (Request $request) { some code... }); diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index a1f6af0f3f..69b153ef72 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -6,9 +6,9 @@ use Appwrite\GraphQL\Exception as GQLException; use Appwrite\Promises\Swoole; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Exception; -use Utopia\Route; +use Utopia\Http\Route; class Resolvers { @@ -302,7 +302,7 @@ class Resolvers private static function escapePayload(array $payload, int $depth) { - if ($depth > App::getEnv('_APP_GRAPHQL_MAX_DEPTH', 3)) { + if ($depth > Http::getEnv('_APP_GRAPHQL_MAX_DEPTH', 3)) { return; } diff --git a/src/Appwrite/GraphQL/Schema.php b/src/Appwrite/GraphQL/Schema.php index 833ea9d032..2399452797 100644 --- a/src/Appwrite/GraphQL/Schema.php +++ b/src/Appwrite/GraphQL/Schema.php @@ -6,9 +6,9 @@ use Appwrite\GraphQL\Types\Mapper; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; use GraphQL\Type\Schema as GQLSchema; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Exception; -use Utopia\Route; +use Utopia\Http\Route; class Schema { @@ -32,7 +32,7 @@ class Schema array $urls, array $params, ): GQLSchema { - App::setResource('utopia:graphql', static function () use ($utopia) { + Http::setResource('utopia:graphql', static function () use ($utopia) { return $utopia; }); diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index 36b246b28b..3f50bd4967 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -8,10 +8,10 @@ use Exception; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\UnionType; -use Utopia\App; -use Utopia\Route; +use Utopia\Http\Http; +use Utopia\Http\Route; use Utopia\Validator; -use Utopia\Validator\Nullable; +use Utopia\Http\Validator\Nullable; class Mapper { @@ -232,20 +232,20 @@ class Mapper case 'Appwrite\Network\Validator\CNAME': case 'Appwrite\Task\Validator\Cron': case 'Appwrite\Utopia\Database\Validator\CustomId': - case 'Utopia\Validator\Domain': + case 'Utopia\Http\Validator\Domain': case 'Appwrite\Network\Validator\Email': case 'Appwrite\Event\Validator\Event': case 'Appwrite\Event\Validator\FunctionEvent': - case 'Utopia\Validator\HexColor': - case 'Utopia\Validator\Host': - case 'Utopia\Validator\IP': + case 'Utopia\Http\Validator\HexColor': + case 'Utopia\Http\Validator\Host': + case 'Utopia\Http\Validator\IP': case 'Utopia\Database\Validator\Key': - case 'Utopia\Validator\Origin': + case 'Utopia\Http\Validator\Origin': case 'Appwrite\Auth\Validator\Password': - case 'Utopia\Validator\Text': + case 'Utopia\Http\Validator\Text': case 'Utopia\Database\Validator\UID': - case 'Utopia\Validator\URL': - case 'Utopia\Validator\WhiteList': + case 'Utopia\Http\Validator\URL': + case 'Utopia\Http\Validator\WhiteList': default: $type = Type::string(); break; @@ -273,10 +273,10 @@ class Mapper case 'Appwrite\Utopia\Database\Validator\Queries\Variables': $type = Type::listOf(Type::string()); break; - case 'Utopia\Validator\Boolean': + case 'Utopia\Http\Validator\Boolean': $type = Type::boolean(); break; - case 'Utopia\Validator\ArrayList': + case 'Utopia\Http\Validator\ArrayList': $type = Type::listOf(self::param( $utopia, $validator->getValidator(), @@ -284,18 +284,18 @@ class Mapper $injections )); break; - case 'Utopia\Validator\Integer': - case 'Utopia\Validator\Numeric': - case 'Utopia\Validator\Range': + case 'Utopia\Http\Validator\Integer': + case 'Utopia\Http\Validator\Numeric': + case 'Utopia\Http\Validator\Range': $type = Type::int(); break; - case 'Utopia\Validator\FloatValidator': + case 'Utopia\Http\Validator\FloatValidator': $type = Type::float(); break; - case 'Utopia\Validator\Assoc': + case 'Utopia\Http\Validator\Assoc': $type = Types::assoc(); break; - case 'Utopia\Validator\JSON': + case 'Utopia\Http\Validator\JSON': $type = Types::json(); break; case 'Utopia\Storage\Validator\File': diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 792adfa5a4..475b92f2f7 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -3,7 +3,7 @@ namespace Appwrite\Messaging\Adapter; use Appwrite\Messaging\Adapter; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; @@ -140,7 +140,7 @@ class Realtime extends Adapter $userId = array_key_exists('userId', $options) ? $options['userId'] : null; $redis = new \Redis(); //TODO: make this part of the constructor - $redis->connect(App::getEnv('_APP_REDIS_HOST', ''), App::getEnv('_APP_REDIS_PORT', '')); + $redis->connect(Http::getEnv('_APP_REDIS_HOST', ''), Http::getEnv('_APP_REDIS_PORT', '')); $redis->publish('realtime', json_encode([ 'project' => $projectId, 'roles' => $roles, diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index cbcc7a9322..646fa8b8fe 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -4,7 +4,7 @@ namespace Appwrite\Migration; use Exception; use Swoole\Runtime; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -248,7 +248,7 @@ abstract class Migration default => 'projects', }; - if (!$this->projectDB->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'), $name)) { + if (!$this->projectDB->exists(Http::getEnv('_APP_DB_SCHEMA', 'appwrite'), $name)) { $attributes = []; $indexes = []; $collection = $this->collections[$collectionType][$id]; diff --git a/src/Appwrite/Migration/Version/V15.php b/src/Appwrite/Migration/Version/V15.php index e2de0422de..b724dc33e3 100644 --- a/src/Appwrite/Migration/Version/V15.php +++ b/src/Appwrite/Migration/Version/V15.php @@ -4,7 +4,7 @@ namespace Appwrite\Migration\Version; use Appwrite\Migration\Migration; use Appwrite\OpenSSL\OpenSSL; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -1484,7 +1484,7 @@ class V15 extends Migration */ protected function encryptFilter(string $value): string { - $key = App::getEnv('_APP_OPENSSL_KEY_V1'); + $key = Http::getEnv('_APP_OPENSSL_KEY_V1'); $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); $tag = null; diff --git a/src/Appwrite/Migration/Version/V19.php b/src/Appwrite/Migration/Version/V19.php index 7934c1aaa6..75e5b31d17 100644 --- a/src/Appwrite/Migration/Version/V19.php +++ b/src/Appwrite/Migration/Version/V19.php @@ -3,7 +3,7 @@ namespace Appwrite\Migration\Version; use Appwrite\Migration\Migration; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -731,7 +731,7 @@ class V19 extends Migration if (empty($document->getAttribute('scheduleId', null))) { $schedule = $this->consoleDB->createDocument('schedules', new Document([ - 'region' => App::getEnv('_APP_REGION', 'default'), // Todo replace with projects region + 'region' => Http::getEnv('_APP_REGION', 'default'), // Todo replace with projects region 'resourceType' => 'function', 'resourceId' => $document->getId(), 'resourceInternalId' => $document->getInternalId(), diff --git a/src/Appwrite/Network/Validator/Origin.php b/src/Appwrite/Network/Validator/Origin.php index fe10d87e59..e64f42ce6d 100644 --- a/src/Appwrite/Network/Validator/Origin.php +++ b/src/Appwrite/Network/Validator/Origin.php @@ -3,7 +3,7 @@ namespace Appwrite\Network\Validator; use Utopia\Validator; -use Utopia\Validator\Hostname; +use Utopia\Http\Validator\Hostname; class Origin extends Validator { diff --git a/src/Appwrite/Platform/Tasks/CalcTierStats.php b/src/Appwrite/Platform/Tasks/CalcTierStats.php index fc01881523..7256047c35 100644 --- a/src/Appwrite/Platform/Tasks/CalcTierStats.php +++ b/src/Appwrite/Platform/Tasks/CalcTierStats.php @@ -4,7 +4,7 @@ namespace Appwrite\Platform\Tasks; use League\Csv\Writer; use PHPMailer\PHPMailer\PHPMailer; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Database\Database; @@ -14,7 +14,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; use Utopia\Pools\Group; use Utopia\Registry\Registry; -use Utopia\Validator\Text; +use Utopia\Http\Validator\Text; class CalcTierStats extends Action { @@ -184,8 +184,8 @@ class CalcTierStats extends Action try { /** Addresses */ - $mail->setFrom(App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), 'Appwrite Cloud Hamster'); - $recipients = explode(',', App::getEnv('_APP_USERS_STATS_RECIPIENTS', '')); + $mail->setFrom(Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), 'Appwrite Cloud Hamster'); + $recipients = explode(',', Http::getEnv('_APP_USERS_STATS_RECIPIENTS', '')); foreach ($recipients as $recipient) { $mail->addAddress($recipient); } diff --git a/src/Appwrite/Platform/Tasks/CreateInfMetric.php b/src/Appwrite/Platform/Tasks/CreateInfMetric.php index c50b6e09f9..099076fdd5 100644 --- a/src/Appwrite/Platform/Tasks/CreateInfMetric.php +++ b/src/Appwrite/Platform/Tasks/CreateInfMetric.php @@ -9,7 +9,7 @@ use Utopia\Database\Exception; use Utopia\Database\Exception\Duplicate; use Utopia\Database\Query; use Utopia\Platform\Action; -use Utopia\Validator\Text; +use Utopia\Http\Validator\Text; class CreateInfMetric extends Action { diff --git a/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php b/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php index 94879a4386..c2d4128b72 100644 --- a/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php +++ b/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php @@ -2,7 +2,7 @@ namespace Appwrite\Platform\Tasks; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -12,7 +12,7 @@ use Utopia\Database\Query; use Utopia\Platform\Action; use Utopia\Pools\Group; use Utopia\Registry\Registry; -use Utopia\Validator\Boolean; +use Utopia\Http\Validator\Boolean; class DeleteOrphanedProjects extends Action { diff --git a/src/Appwrite/Platform/Tasks/DevGenerateTranslations.php b/src/Appwrite/Platform/Tasks/DevGenerateTranslations.php index 66eee00d0d..d47d3e990b 100644 --- a/src/Appwrite/Platform/Tasks/DevGenerateTranslations.php +++ b/src/Appwrite/Platform/Tasks/DevGenerateTranslations.php @@ -7,8 +7,8 @@ use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Fetch\Client; use Utopia\Platform\Action; -use Utopia\Validator\Boolean; -use Utopia\Validator\Text; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\Text; class DevGenerateTranslations extends Action { diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index 2886dcd4dd..e53f2ba8d7 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Tasks; use Appwrite\ClamAV\Network; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Domains\Domain; @@ -35,11 +35,11 @@ class Doctor extends Action / \ ) __/ ) __/\ /\ / ) / )( )( ) _) _ )(( O ) \_/\_/(__) (__) (_/\_)(__\_)(__) (__) (____)(_)(__)\__/ "); - Console::log("\n" . '👩‍⚕️ Running ' . APP_NAME . ' Doctor for version ' . App::getEnv('_APP_VERSION', 'UNKNOWN') . ' ...' . "\n"); + Console::log("\n" . '👩‍⚕️ Running ' . APP_NAME . ' Doctor for version ' . Http::getEnv('_APP_VERSION', 'UNKNOWN') . ' ...' . "\n"); Console::log('[Settings]'); - $domain = new Domain(App::getEnv('_APP_DOMAIN')); + $domain = new Domain(Http::getEnv('_APP_DOMAIN')); if (!$domain->isKnown() || $domain->isTest()) { Console::log('🔴 Hostname has no public suffix (' . $domain->get() . ')'); @@ -47,7 +47,7 @@ class Doctor extends Action Console::log('🟢 Hostname has a public suffix (' . $domain->get() . ')'); } - $domain = new Domain(App::getEnv('_APP_DOMAIN_TARGET')); + $domain = new Domain(Http::getEnv('_APP_DOMAIN_TARGET')); if (!$domain->isKnown() || $domain->isTest()) { Console::log('🔴 CNAME target has no public suffix (' . $domain->get() . ')'); @@ -55,27 +55,27 @@ class Doctor extends Action Console::log('🟢 CNAME target has a public suffix (' . $domain->get() . ')'); } - if (App::getEnv('_APP_OPENSSL_KEY_V1') === 'your-secret-key' || empty(App::getEnv('_APP_OPENSSL_KEY_V1'))) { + if (Http::getEnv('_APP_OPENSSL_KEY_V1') === 'your-secret-key' || empty(Http::getEnv('_APP_OPENSSL_KEY_V1'))) { Console::log('🔴 Not using a unique secret key for encryption'); } else { Console::log('🟢 Using a unique secret key for encryption'); } - if (App::getEnv('_APP_ENV', 'development') !== 'production') { + if (Http::getEnv('_APP_ENV', 'development') !== 'production') { Console::log('🔴 App environment is set for development'); } else { Console::log('🟢 App environment is set for production'); } - if ('enabled' !== App::getEnv('_APP_OPTIONS_ABUSE', 'disabled')) { + if ('enabled' !== Http::getEnv('_APP_OPTIONS_ABUSE', 'disabled')) { Console::log('🔴 Abuse protection is disabled'); } else { Console::log('🟢 Abuse protection is enabled'); } - $authWhitelistRoot = App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', null); - $authWhitelistEmails = App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null); - $authWhitelistIPs = App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null); + $authWhitelistRoot = Http::getEnv('_APP_CONSOLE_WHITELIST_ROOT', null); + $authWhitelistEmails = Http::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null); + $authWhitelistIPs = Http::getEnv('_APP_CONSOLE_WHITELIST_IPS', null); if ( empty($authWhitelistRoot) @@ -87,20 +87,20 @@ class Doctor extends Action Console::log('🟢 Console access limits are enabled'); } - if ('enabled' !== App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled')) { + if ('enabled' !== Http::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled')) { Console::log('🔴 HTTPS force option is disabled'); } else { Console::log('🟢 HTTPS force option is enabled'); } - if ('enabled' !== App::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled')) { + if ('enabled' !== Http::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled')) { Console::log('🔴 HTTPS force option is disabled for function domains'); } else { Console::log('🟢 HTTPS force option is enabled for function domains'); } - $providerName = App::getEnv('_APP_LOGGING_PROVIDER', ''); - $providerConfig = App::getEnv('_APP_LOGGING_CONFIG', ''); + $providerName = Http::getEnv('_APP_LOGGING_PROVIDER', ''); + $providerConfig = Http::getEnv('_APP_LOGGING_CONFIG', ''); if (empty($providerName) || empty($providerConfig) || !Logger::hasProvider($providerName)) { Console::log('🔴 Logging adapter is disabled'); @@ -162,11 +162,11 @@ class Doctor extends Action } } - if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled + if (Http::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled try { $antivirus = new Network( - App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'), - (int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310) + Http::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'), + (int) Http::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310) ); if ((@$antivirus->ping())) { @@ -249,12 +249,12 @@ class Doctor extends Action } try { - if (App::isProduction()) { + if (Http::isProduction()) { Console::log(''); - $version = \json_decode(@\file_get_contents(App::getEnv('_APP_HOME', 'http://localhost') . '/version'), true); + $version = \json_decode(@\file_get_contents(Http::getEnv('_APP_HOME', 'http://localhost') . '/version'), true); if ($version && isset($version['version'])) { - if (\version_compare($version['version'], App::getEnv('_APP_VERSION', 'UNKNOWN')) === 0) { + if (\version_compare($version['version'], Http::getEnv('_APP_VERSION', 'UNKNOWN')) === 0) { Console::info('You are running the latest version of ' . APP_NAME . '! 🥳'); } else { Console::info('A new version (' . $version['version'] . ') is available! 🥳' . "\n"); diff --git a/src/Appwrite/Platform/Tasks/GetMigrationStats.php b/src/Appwrite/Platform/Tasks/GetMigrationStats.php index dd43f75447..ebd00e8dc1 100644 --- a/src/Appwrite/Platform/Tasks/GetMigrationStats.php +++ b/src/Appwrite/Platform/Tasks/GetMigrationStats.php @@ -5,7 +5,7 @@ namespace Appwrite\Platform\Tasks; use League\Csv\CannotInsertRecord; use League\Csv\Writer; use PHPMailer\PHPMailer\PHPMailer; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Database\Database; @@ -164,8 +164,8 @@ class GetMigrationStats extends Action try { /** Addresses */ - $mail->setFrom(App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), 'Appwrite Cloud Hamster'); - $recipients = explode(',', App::getEnv('_APP_USERS_STATS_RECIPIENTS', '')); + $mail->setFrom(Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), 'Appwrite Cloud Hamster'); + $recipients = explode(',', Http::getEnv('_APP_USERS_STATS_RECIPIENTS', '')); foreach ($recipients as $recipient) { $mail->addAddress($recipient); diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index 7f8ef4041d..95e5cf0e4d 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Hamster as EventHamster; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; @@ -33,9 +33,9 @@ class Hamster extends Action Console::title('Cloud Hamster V1'); Console::success(APP_NAME . ' cloud hamster process has started'); - $sleep = (int) App::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default) + $sleep = (int) Http::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default) - $jobInitTime = App::getEnv('_APP_HAMSTER_TIME', '22:00'); // (hour:minutes) + $jobInitTime = Http::getEnv('_APP_HAMSTER_TIME', '22:00'); // (hour:minutes) $now = new \DateTime(); $now->setTimezone(new \DateTimeZone(date_default_timezone_get())); diff --git a/src/Appwrite/Platform/Tasks/Install.php b/src/Appwrite/Platform/Tasks/Install.php index 4abd267684..8b64002c8d 100644 --- a/src/Appwrite/Platform/Tasks/Install.php +++ b/src/Appwrite/Platform/Tasks/Install.php @@ -9,8 +9,8 @@ use Appwrite\Utopia\View; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Platform\Action; -use Utopia\Validator\Boolean; -use Utopia\Validator\Text; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\Text; class Install extends Action { diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index 55bcd488a0..ec8d3253ca 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -4,7 +4,7 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Certificate; use Appwrite\Event\Delete; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; @@ -35,11 +35,11 @@ class Maintenance extends Action Console::success(APP_NAME . ' maintenance process v1 has started'); // # of days in seconds (1 day = 86400s) - $interval = (int) App::getEnv('_APP_MAINTENANCE_INTERVAL', '86400'); - $delay = (int) App::getEnv('_APP_MAINTENANCE_DELAY', '0'); - $usageStatsRetentionHourly = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_HOURLY', '8640000'); //100 days - $cacheRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_CACHE', '2592000'); // 30 days - $schedulesDeletionRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_SCHEDULES', '86400'); // 1 Day + $interval = (int) Http::getEnv('_APP_MAINTENANCE_INTERVAL', '86400'); + $delay = (int) Http::getEnv('_APP_MAINTENANCE_DELAY', '0'); + $usageStatsRetentionHourly = (int) Http::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_HOURLY', '8640000'); //100 days + $cacheRetention = (int) Http::getEnv('_APP_MAINTENANCE_RETENTION_CACHE', '2592000'); // 30 days + $schedulesDeletionRetention = (int) Http::getEnv('_APP_MAINTENANCE_RETENTION_SCHEDULES', '86400'); // 1 Day Console::loop(function () use ($interval, $cacheRetention, $schedulesDeletionRetention, $usageStatsRetentionHourly, $dbForConsole, $queueForDeletes, $queueForCertificates) { $time = DateTime::now(); diff --git a/src/Appwrite/Platform/Tasks/Migrate.php b/src/Appwrite/Platform/Tasks/Migrate.php index 6e4fe38eb7..c322f17a55 100644 --- a/src/Appwrite/Platform/Tasks/Migrate.php +++ b/src/Appwrite/Platform/Tasks/Migrate.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Migration\Migration; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Database\Database; @@ -12,7 +12,7 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; use Utopia\Registry\Registry; -use Utopia\Validator\Text; +use Utopia\Http\Validator\Text; class Migrate extends Action { diff --git a/src/Appwrite/Platform/Tasks/PatchRecreateRepositoriesDocuments.php b/src/Appwrite/Platform/Tasks/PatchRecreateRepositoriesDocuments.php index 9cf65d05b6..87bd73cc32 100644 --- a/src/Appwrite/Platform/Tasks/PatchRecreateRepositoriesDocuments.php +++ b/src/Appwrite/Platform/Tasks/PatchRecreateRepositoriesDocuments.php @@ -10,7 +10,7 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Platform\Action; -use Utopia\Validator\Text; +use Utopia\Http\Validator\Text; class PatchRecreateRepositoriesDocuments extends Action { diff --git a/src/Appwrite/Platform/Tasks/QueueCount.php b/src/Appwrite/Platform/Tasks/QueueCount.php index 9b7bba82d1..55a2e5ee98 100644 --- a/src/Appwrite/Platform/Tasks/QueueCount.php +++ b/src/Appwrite/Platform/Tasks/QueueCount.php @@ -7,7 +7,7 @@ use Utopia\CLI\Console; use Utopia\Platform\Action; use Utopia\Queue\Client; use Utopia\Queue\Connection; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\WhiteList; class QueueCount extends Action { diff --git a/src/Appwrite/Platform/Tasks/QueueRetry.php b/src/Appwrite/Platform/Tasks/QueueRetry.php index 465152f21d..e1312e7cc5 100644 --- a/src/Appwrite/Platform/Tasks/QueueRetry.php +++ b/src/Appwrite/Platform/Tasks/QueueRetry.php @@ -7,8 +7,8 @@ use Utopia\CLI\Console; use Utopia\Platform\Action; use Utopia\Queue\Client; use Utopia\Queue\Connection; -use Utopia\Validator\WhiteList; -use Utopia\Validator\Wildcard; +use Utopia\Http\Validator\WhiteList; +use Utopia\Http\Validator\Wildcard; class QueueRetry extends Action { diff --git a/src/Appwrite/Platform/Tasks/SSL.php b/src/Appwrite/Platform/Tasks/SSL.php index 251d3a046b..061679b3bf 100644 --- a/src/Appwrite/Platform/Tasks/SSL.php +++ b/src/Appwrite/Platform/Tasks/SSL.php @@ -3,12 +3,12 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Certificate; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\Document; use Utopia\Platform\Action; -use Utopia\Validator\Boolean; -use Utopia\Validator\Hostname; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\Hostname; class SSL extends Action { @@ -21,7 +21,7 @@ class SSL extends Action { $this ->desc('Validate server certificates') - ->param('domain', App::getEnv('_APP_DOMAIN', ''), new Hostname(), 'Domain to generate certificate for. If empty, main domain will be used.', true) + ->param('domain', Http::getEnv('_APP_DOMAIN', ''), new Hostname(), 'Domain to generate certificate for. If empty, main domain will be used.', true) ->param('skip-check', true, new Boolean(true), 'If DNS and renew check should be skipped. Defaults to true, and when true, all jobs will result in certificate generation attempt.', true) ->inject('queueForCertificates') ->callback(fn (string $domain, bool|string $skipCheck, Certificate $queueForCertificates) => $this->action($domain, $skipCheck, $queueForCertificates)); diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index 81110f5216..91b98dd28c 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Tasks; use Swoole\Timer; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; @@ -99,7 +99,7 @@ abstract class ScheduleBase extends Action } $results = $dbForConsole->find('schedules', \array_merge($paginationQueries, [ - Query::equal('region', [App::getEnv('_APP_REGION', 'default')]), + Query::equal('region', [Http::getEnv('_APP_REGION', 'default')]), Query::equal('resourceType', [static::getSupportedResource()]), Query::equal('active', [true]), ])); @@ -153,7 +153,7 @@ abstract class ScheduleBase extends Action } $results = $dbForConsole->find('schedules', \array_merge($paginationQueries, [ - Query::equal('region', [App::getEnv('_APP_REGION', 'default')]), + Query::equal('region', [Http::getEnv('_APP_REGION', 'default')]), Query::equal('resourceType', [static::getSupportedResource()]), Query::greaterThanEqual('resourceUpdatedAt', $lastSyncUpdate), ])); diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 46c83fe2b1..1c79fee034 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -8,7 +8,7 @@ use Appwrite\Specification\Specification; use Appwrite\Utopia\Response; use Exception; use Swoole\Http\Response as HttpResponse; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Adapter\None; use Utopia\Cache\Cache; use Utopia\CLI\Console; @@ -18,8 +18,8 @@ use Utopia\Database\Database; use Utopia\Platform\Action; use Utopia\Registry\Registry; use Utopia\Request; -use Utopia\Validator\Text; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; class Specs extends Action { @@ -40,15 +40,15 @@ class Specs extends Action public function action(string $version, string $mode, Registry $register): void { - $appRoutes = App::getRoutes(); + $appRoutes = Http::getRoutes(); $response = new Response(new HttpResponse()); $mocks = ($mode === 'mocks'); // Mock dependencies - App::setResource('request', fn () => new Request()); - App::setResource('response', fn () => $response); - App::setResource('dbForConsole', fn () => new Database(new MySQL(''), new Cache(new None()))); - App::setResource('dbForProject', fn () => new Database(new MySQL(''), new Cache(new None()))); + Http::setResource('request', fn () => new Request()); + Http::setResource('response', fn () => $response); + Http::setResource('dbForConsole', fn () => new Database(new MySQL(''), new Cache(new None()))); + Http::setResource('dbForProject', fn () => new Database(new MySQL(''), new Cache(new None()))); $platforms = [ 'client' => APP_PLATFORM_CLIENT, @@ -257,8 +257,8 @@ class Specs extends Action }; $specs = new Specification($formatInstance); - $endpoint = App::getEnv('_APP_HOME', '[HOSTNAME]'); - $email = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + $endpoint = Http::getEnv('_APP_HOME', '[HOSTNAME]'); + $email = Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); $formatInstance ->setParam('name', APP_NAME) diff --git a/src/Appwrite/Platform/Tasks/Upgrade.php b/src/Appwrite/Platform/Tasks/Upgrade.php index 341ce42fc4..608b924c06 100644 --- a/src/Appwrite/Platform/Tasks/Upgrade.php +++ b/src/Appwrite/Platform/Tasks/Upgrade.php @@ -3,8 +3,8 @@ namespace Appwrite\Platform\Tasks; use Utopia\CLI\Console; -use Utopia\Validator\Boolean; -use Utopia\Validator\Text; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\Text; class Upgrade extends Install { diff --git a/src/Appwrite/Platform/Tasks/Vars.php b/src/Appwrite/Platform/Tasks/Vars.php index 696073e568..3d2cb2c36a 100644 --- a/src/Appwrite/Platform/Tasks/Vars.php +++ b/src/Appwrite/Platform/Tasks/Vars.php @@ -2,7 +2,7 @@ namespace Appwrite\Platform\Tasks; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Platform\Action; @@ -33,7 +33,7 @@ class Vars extends Action } foreach ($vars as $key => $value) { - Console::log('- ' . $value['name'] . '=' . App::getEnv($value['name'], '')); + Console::log('- ' . $value['name'] . '=' . Http::getEnv($value['name'], '')); } } } diff --git a/src/Appwrite/Platform/Tasks/Version.php b/src/Appwrite/Platform/Tasks/Version.php index 4a9cbf9dcf..8cfe3099c6 100644 --- a/src/Appwrite/Platform/Tasks/Version.php +++ b/src/Appwrite/Platform/Tasks/Version.php @@ -2,7 +2,7 @@ namespace Appwrite\Platform\Tasks; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Platform\Action; @@ -18,7 +18,7 @@ class Version extends Action $this ->desc('Get the server version') ->callback(function () { - Console::log(App::getEnv('_APP_VERSION', 'UNKNOWN')); + Console::log(Http::getEnv('_APP_VERSION', 'UNKNOWN')); }); } } diff --git a/src/Appwrite/Platform/Tasks/VolumeSync.php b/src/Appwrite/Platform/Tasks/VolumeSync.php index 6197b20fbd..17ae9730f9 100644 --- a/src/Appwrite/Platform/Tasks/VolumeSync.php +++ b/src/Appwrite/Platform/Tasks/VolumeSync.php @@ -5,8 +5,8 @@ namespace Appwrite\Platform\Tasks; use Utopia\CLI\Console; use Utopia\Database\DateTime; use Utopia\Platform\Action; -use Utopia\Validator\Integer; -use Utopia\Validator\Text; +use Utopia\Http\Validator\Integer; +use Utopia\Http\Validator\Text; class VolumeSync extends Action { diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 31bf961c30..a0cf080c86 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -10,7 +10,7 @@ use Appwrite\Utopia\Response\Model\Deployment; use Appwrite\Vcs\Comment; use Executor\Executor; use Swoole\Coroutine as Co; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -117,7 +117,7 @@ class Builds extends Action */ protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, Log $log): void { - $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $executor = new Executor(Http::getEnv('_APP_EXECUTOR_HOST')); $functionId = $function->getId(); $log->addTag('functionId', $function->getId()); @@ -195,8 +195,8 @@ class Builds extends Action if ($isVcsEnabled) { $installation = $dbForConsole->getDocument('installations', $installationId); $providerInstallationId = $installation->getAttribute('providerInstallationId'); - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); } @@ -304,7 +304,7 @@ class Builds extends Action } $directorySize = $localDevice->getDirectorySize($tmpDirectory); - $functionsSizeLimit = (int) App::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000'); + $functionsSizeLimit = (int) Http::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000'); if ($directorySize > $functionsSizeLimit) { throw new \Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.'); } @@ -594,8 +594,8 @@ class Builds extends Action $name = "{$functionName} ({$projectName})"; - $protocol = App::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; - $hostname = App::getEnv('_APP_DOMAIN'); + $protocol = Http::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; + $hostname = Http::getEnv('_APP_DOMAIN'); $functionId = $function->getId(); $projectId = $project->getId(); $providerTargetUrl = $protocol . '://' . $hostname . "/console/project-$projectId/functions/function-$functionId"; diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 7cc32ca1c9..5cbb36ed8e 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -11,7 +11,7 @@ use Appwrite\Template\Template; use Appwrite\Utopia\Response\Model\Rule; use Exception; use Throwable; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; @@ -134,7 +134,7 @@ class Certificates extends Action try { // Email for alerts is required by LetsEncrypt - $email = App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'); + $email = Http::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'); if (empty($email)) { throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate.'); } @@ -235,7 +235,7 @@ class Certificates extends Action */ private function getMainDomain(): ?string { - $envDomain = App::getEnv('_APP_DOMAIN', ''); + $envDomain = Http::getEnv('_APP_DOMAIN', ''); if (!empty($envDomain) && $envDomain !== 'localhost') { return $envDomain; } @@ -267,7 +267,7 @@ class Certificates extends Action if (!$isMainDomain) { // TODO: Would be awesome to also support A/AAAA records here. Maybe dry run? // Validate if domain target is properly configured - $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); + $target = new Domain(Http::getEnv('_APP_DOMAIN_TARGET', '')); if (!$target->isKnown() || $target->isTest()) { throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.'); @@ -337,7 +337,7 @@ class Certificates extends Action $stdout = ''; $stderr = ''; - $staging = (App::isProduction()) ? '' : ' --dry-run'; + $staging = (Http::isProduction()) ? '' : ' --dry-run'; $exit = Console::execute("certbot certonly -v --webroot --noninteractive --agree-tos{$staging}" . " --email " . $email . " --cert-name " . $folder @@ -436,7 +436,7 @@ class Certificates extends Action // Log error into console Console::warning('Cannot renew domain (' . $domain . ') on attempt no. ' . $attempt . ' certificate: ' . $errorMessage); - $locale = new Locale(App::getEnv('_APP_LOCALE', 'en')); + $locale = new Locale(Http::getEnv('_APP_LOCALE', 'en')); // Send mail to administratore mail $template = Template::fromFile(__DIR__ . '/../../../../app/config/locale/templates/email-certificate-failed.tpl'); @@ -473,7 +473,7 @@ class Certificates extends Action ->setBody($body) ->setName('Appwrite Administrator') ->setVariables($emailVariables) - ->setRecipient(App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')) + ->setRecipient(Http::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')) ->trigger(); } diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 9204960557..8247e00799 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -8,7 +8,7 @@ use Executor\Executor; use Throwable; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\Cache\Adapter\Filesystem; use Utopia\Cache\Cache; @@ -189,7 +189,7 @@ class Deletes extends Action $this->listByGroup( 'schedules', [ - Query::equal('region', [App::getEnv('_APP_REGION', 'default')]), + Query::equal('region', [Http::getEnv('_APP_REGION', 'default')]), Query::lessThanEqual('resourceUpdatedAt', $datetime), Query::equal('active', [false]), ], @@ -1153,7 +1153,7 @@ class Deletes extends Action */ private function deleteRuntimes(callable $getProjectDB, ?Document $function, Document $project): void { - $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $executor = new Executor(Http::getEnv('_APP_EXECUTOR_HOST')); $deleteByFunction = function (Document $function) use ($getProjectDB, $project, $executor) { $this->listByGroup( diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 05695d9e26..0d325f7bb3 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -9,7 +9,7 @@ use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Utopia\Response\Model\Execution; use Exception; use Executor\Executor; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -383,7 +383,7 @@ class Functions extends Action try { $version = $function->getAttribute('version', 'v2'); $command = $runtime['startCommand']; - $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $executor = new Executor(Http::getEnv('_APP_EXECUTOR_HOST')); $command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"'; $executionResponse = $executor->createExecution( projectId: $project->getId(), diff --git a/src/Appwrite/Platform/Workers/Hamster.php b/src/Appwrite/Platform/Workers/Hamster.php index b72b7f7030..3f7bd86e8a 100644 --- a/src/Appwrite/Platform/Workers/Hamster.php +++ b/src/Appwrite/Platform/Workers/Hamster.php @@ -6,7 +6,7 @@ use Appwrite\Event\Hamster as EventHamster; use Appwrite\Network\Validator\Origin; use Utopia\Analytics\Adapter\Mixpanel; use Utopia\Analytics\Event as AnalyticsEvent; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -67,7 +67,7 @@ class Hamster extends Action */ public function action(Message $message, Group $pools, Cache $cache, Database $dbForConsole): void { - $token = App::getEnv('_APP_MIXPANEL_TOKEN', ''); + $token = Http::getEnv('_APP_MIXPANEL_TOKEN', ''); if (empty($token)) { throw new \Exception('Missing MixPanel Token'); } diff --git a/src/Appwrite/Platform/Workers/Mails.php b/src/Appwrite/Platform/Workers/Mails.php index 57d1baa978..37ce782b8b 100644 --- a/src/Appwrite/Platform/Workers/Mails.php +++ b/src/Appwrite/Platform/Workers/Mails.php @@ -6,7 +6,7 @@ use Appwrite\Template\Template; use Exception; use PHPMailer\PHPMailer\PHPMailer; use Swoole\Runtime; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; @@ -59,14 +59,14 @@ class Mails extends Action $smtp = $payload['smtp']; - if (empty($smtp) && empty(App::getEnv('_APP_SMTP_HOST'))) { + if (empty($smtp) && empty(Http::getEnv('_APP_SMTP_HOST'))) { throw new Exception('Skipped mail processing. No SMTP configuration has been set.'); } $log->addTag('type', empty($smtp) ? 'cloud' : 'smtp'); - $protocol = App::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; - $hostname = App::getEnv('_APP_DOMAIN'); + $protocol = Http::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; + $hostname = Http::getEnv('_APP_DOMAIN'); $recipient = $payload['recipient']; $subject = $payload['subject']; @@ -121,8 +121,8 @@ class Mails extends Action $mail->AltBody = \strip_tags($mail->AltBody); $mail->AltBody = \trim($mail->AltBody); - $replyTo = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - $replyToName = \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); + $replyTo = Http::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + $replyToName = \urldecode(Http::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); if (!empty($smtp)) { $replyTo = !empty($smtp['replyTo']) ? $smtp['replyTo'] : $smtp['senderEmail']; diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index e94f9b495f..19ecc707dd 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -4,7 +4,7 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\Usage; use Appwrite\Messaging\Status as MessageStatus; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -361,7 +361,7 @@ class Messaging extends Action private function sendInternalSMSMessage(Document $message, Document $project, array $recipients, Usage $queueForUsage, Log $log): void { - if (empty(App::getEnv('_APP_SMS_PROVIDER')) || empty(App::getEnv('_APP_SMS_FROM'))) { + if (empty(Http::getEnv('_APP_SMS_PROVIDER')) || empty(Http::getEnv('_APP_SMS_FROM'))) { throw new \Exception('Skipped SMS processing. Missing "_APP_SMS_PROVIDER" or "_APP_SMS_FROM" environment variables.'); } @@ -371,7 +371,7 @@ class Messaging extends Action Console::log('Project: ' . $project->getId()); - $denyList = App::getEnv('_APP_SMS_PROJECTS_DENY_LIST', ''); + $denyList = Http::getEnv('_APP_SMS_PROJECTS_DENY_LIST', ''); $denyList = explode(',', $denyList); if (\in_array($project->getId(), $denyList)) { @@ -379,14 +379,14 @@ class Messaging extends Action return; } - $smsDSN = new DSN(App::getEnv('_APP_SMS_PROVIDER')); + $smsDSN = new DSN(Http::getEnv('_APP_SMS_PROVIDER')); $host = $smsDSN->getHost(); $password = $smsDSN->getPassword(); $user = $smsDSN->getUser(); $log->addTag('type', $host); - $from = App::getEnv('_APP_SMS_FROM'); + $from = Http::getEnv('_APP_SMS_FROM'); $provider = new Document([ '$id' => ID::unique(), diff --git a/src/Appwrite/Platform/Workers/Usage.php b/src/Appwrite/Platform/Workers/Usage.php index 3227153e08..14e004194f 100644 --- a/src/Appwrite/Platform/Workers/Usage.php +++ b/src/Appwrite/Platform/Workers/Usage.php @@ -4,7 +4,7 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\UsageDump; use Exception; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -58,7 +58,7 @@ class Usage extends Action } //Todo Figure out way to preserve keys when the container is being recreated @shimonewman - $aggregationInterval = (int) App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '20'); + $aggregationInterval = (int) Http::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '20'); $project = new Document($payload['project'] ?? []); $projectId = $project->getInternalId(); foreach ($payload['reduce'] ?? [] as $document) { diff --git a/src/Appwrite/Platform/Workers/UsageDump.php b/src/Appwrite/Platform/Workers/UsageDump.php index e1e62feb9f..96cb1e9e0b 100644 --- a/src/Appwrite/Platform/Workers/UsageDump.php +++ b/src/Appwrite/Platform/Workers/UsageDump.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Workers; use Appwrite\Extend\Exception; -use Utopia\App; +use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -81,7 +81,7 @@ class UsageDump extends Action 'time' => $time, 'metric' => $key, 'value' => $value, - 'region' => App::getEnv('_APP_REGION', 'default'), + 'region' => Http::getEnv('_APP_REGION', 'default'), ])); } catch (Duplicate $th) { if ($value < 0) { diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index da5a82999f..88a6dd4c68 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -5,7 +5,7 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\Mail; use Appwrite\Template\Template; use Exception; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; @@ -103,8 +103,8 @@ class Webhooks extends Action \curl_setopt($ch, CURLOPT_MAXFILESIZE, self::MAX_FILE_SIZE); \curl_setopt($ch, CURLOPT_USERAGENT, \sprintf( APP_USERAGENT, - App::getEnv('_APP_VERSION', 'UNKNOWN'), - App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY) + Http::getEnv('_APP_VERSION', 'UNKNOWN'), + Http::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY) )); \curl_setopt( $ch, @@ -156,7 +156,7 @@ class Webhooks extends Action $webhook->setAttribute('logs', $logs); - if ($attempts >= \intval(App::getEnv('_APP_WEBHOOK_MAX_FAILED_ATTEMPTS', '10'))) { + if ($attempts >= \intval(Http::getEnv('_APP_WEBHOOK_MAX_FAILED_ATTEMPTS', '10'))) { $webhook->setAttribute('enabled', false); $this->sendEmailAlert($attempts, $statusCode, $webhook, $project, $dbForConsole, $queueForMails); } diff --git a/src/Appwrite/Specification/Format.php b/src/Appwrite/Specification/Format.php index 30ce6470e1..26d8e5d73f 100644 --- a/src/Appwrite/Specification/Format.php +++ b/src/Appwrite/Specification/Format.php @@ -3,9 +3,9 @@ namespace Appwrite\Specification; use Appwrite\Utopia\Response\Model; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Config\Config; -use Utopia\Route; +use Utopia\Http\Route; abstract class Format { diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 4442dd9a0b..b845bc001b 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -8,10 +8,10 @@ use Appwrite\Utopia\Response\Model; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Validator; -use Utopia\Validator\ArrayList; -use Utopia\Validator\Nullable; -use Utopia\Validator\Range; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\ArrayList; +use Utopia\Http\Validator\Nullable; +use Utopia\Http\Validator\Range; +use Utopia\Http\Validator\WhiteList; class OpenAPI3 extends Format { @@ -292,11 +292,11 @@ class OpenAPI3 extends Format switch ((!empty($validator)) ? \get_class($validator) : '') { case 'Utopia\Database\Validator\UID': - case 'Utopia\Validator\Text': + case 'Utopia\Http\Validator\Text': $node['schema']['type'] = $validator->getType(); $node['schema']['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']'; break; - case 'Utopia\Validator\Boolean': + case 'Utopia\Http\Validator\Boolean': $node['schema']['type'] = $validator->getType(); $node['schema']['x-example'] = false; break; @@ -317,15 +317,15 @@ class OpenAPI3 extends Format $node['schema']['format'] = 'email'; $node['schema']['x-example'] = 'email@example.com'; break; - case 'Utopia\Validator\Host': - case 'Utopia\Validator\URL': + case 'Utopia\Http\Validator\Host': + case 'Utopia\Http\Validator\URL': $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'url'; $node['schema']['x-example'] = 'https://example.com'; break; - case 'Utopia\Validator\JSON': - case 'Utopia\Validator\Mock': - case 'Utopia\Validator\Assoc': + case 'Utopia\Http\Validator\JSON': + case 'Utopia\Http\Validator\Mock': + case 'Utopia\Http\Validator\Assoc': $param['default'] = (empty($param['default'])) ? new \stdClass() : $param['default']; $node['schema']['type'] = 'object'; $node['schema']['x-example'] = '{}'; @@ -335,7 +335,7 @@ class OpenAPI3 extends Format $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'binary'; break; - case 'Utopia\Validator\ArrayList': + case 'Utopia\Http\Validator\ArrayList': /** @var ArrayList $validator */ $node['schema']['type'] = 'array'; $node['schema']['items'] = [ @@ -397,25 +397,25 @@ class OpenAPI3 extends Format $node['schema']['format'] = 'phone'; $node['schema']['x-example'] = '+12065550100'; // In the US, 555 is reserved like example.com break; - case 'Utopia\Validator\Range': + case 'Utopia\Http\Validator\Range': /** @var Range $validator */ $node['schema']['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType(); $node['schema']['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float'; $node['schema']['x-example'] = $validator->getMin(); break; - case 'Utopia\Validator\Numeric': - case 'Utopia\Validator\Integer': + case 'Utopia\Http\Validator\Numeric': + case 'Utopia\Http\Validator\Integer': $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'int32'; break; - case 'Utopia\Validator\FloatValidator': + case 'Utopia\Http\Validator\FloatValidator': $node['schema']['type'] = 'number'; $node['schema']['format'] = 'float'; break; - case 'Utopia\Validator\Length': + case 'Utopia\Http\Validator\Length': $node['schema']['type'] = $validator->getType(); break; - case 'Utopia\Validator\WhiteList': + case 'Utopia\Http\Validator\WhiteList': /** @var WhiteList $validator */ $node['schema']['type'] = $validator->getType(); $node['schema']['x-example'] = $validator->getList()[0]; diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index f923741b45..82dedaef57 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -8,9 +8,9 @@ use Appwrite\Utopia\Response\Model; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Validator; -use Utopia\Validator\ArrayList; -use Utopia\Validator\Nullable; -use Utopia\Validator\Range; +use Utopia\Http\Validator\ArrayList; +use Utopia\Http\Validator\Nullable; +use Utopia\Http\Validator\Range; class Swagger2 extends Format { @@ -287,12 +287,12 @@ class Swagger2 extends Format } switch ((!empty($validator)) ? \get_class($validator) : '') { - case 'Utopia\Validator\Text': + case 'Utopia\Http\Validator\Text': case 'Utopia\Database\Validator\UID': $node['type'] = $validator->getType(); $node['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']'; break; - case 'Utopia\Validator\Boolean': + case 'Utopia\Http\Validator\Boolean': $node['type'] = $validator->getType(); $node['x-example'] = false; break; @@ -313,13 +313,13 @@ class Swagger2 extends Format $node['format'] = 'email'; $node['x-example'] = 'email@example.com'; break; - case 'Utopia\Validator\Host': - case 'Utopia\Validator\URL': + case 'Utopia\Http\Validator\Host': + case 'Utopia\Http\Validator\URL': $node['type'] = $validator->getType(); $node['format'] = 'url'; $node['x-example'] = 'https://example.com'; break; - case 'Utopia\Validator\ArrayList': + case 'Utopia\Http\Validator\ArrayList': /** @var ArrayList $validator */ $node['type'] = 'array'; $node['collectionFormat'] = 'multi'; @@ -327,9 +327,9 @@ class Swagger2 extends Format 'type' => $validator->getValidator()->getType(), ]; break; - case 'Utopia\Validator\JSON': - case 'Utopia\Validator\Mock': - case 'Utopia\Validator\Assoc': + case 'Utopia\Http\Validator\JSON': + case 'Utopia\Http\Validator\Mock': + case 'Utopia\Http\Validator\Assoc': $node['type'] = 'object'; $node['default'] = (empty($param['default'])) ? new \stdClass() : $param['default']; $node['x-example'] = '{}'; @@ -396,26 +396,26 @@ class Swagger2 extends Format $node['format'] = 'phone'; $node['x-example'] = '+12065550100'; break; - case 'Utopia\Validator\Range': + case 'Utopia\Http\Validator\Range': /** @var Range $validator */ $node['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType(); $node['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float'; $node['x-example'] = $validator->getMin(); break; - case 'Utopia\Validator\Numeric': - case 'Utopia\Validator\Integer': + case 'Utopia\Http\Validator\Numeric': + case 'Utopia\Http\Validator\Integer': $node['type'] = $validator->getType(); $node['format'] = 'int32'; break; - case 'Utopia\Validator\FloatValidator': + case 'Utopia\Http\Validator\FloatValidator': $node['type'] = 'number'; $node['format'] = 'float'; break; - case 'Utopia\Validator\Length': + case 'Utopia\Http\Validator\Length': $node['type'] = $validator->getType(); break; - case 'Utopia\Validator\WhiteList': - /** @var \Utopia\Validator\WhiteList $validator */ + case 'Utopia\Http\Validator\WhiteList': + /** @var \Utopia\Http\Validator\WhiteList $validator */ $node['type'] = $validator->getType(); $node['x-example'] = $validator->getList()[0]; diff --git a/src/Appwrite/Utopia/Request.php b/src/Appwrite/Utopia/Request.php index 9ad2f6cb7b..a711afb283 100644 --- a/src/Appwrite/Utopia/Request.php +++ b/src/Appwrite/Utopia/Request.php @@ -3,11 +3,11 @@ namespace Appwrite\Utopia; use Appwrite\Utopia\Request\Filter; -use Swoole\Http\Request as SwooleRequest; -use Utopia\Route; -use Utopia\Swoole\Request as UtopiaRequest; +use Utopia\Http\Adapter\Swoole\Request as SwooleRequest; +use Utopia\Http\Request as HttpRequest; +use Utopia\Http\Route; -class Request extends UtopiaRequest +class Request extends HttpRequest { private static ?Filter $filter = null; private static ?Route $route = null; diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 22043b9f26..4f6ebd3cc5 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -105,7 +105,7 @@ use Exception; use Swoole\Http\Response as SwooleHTTPResponse; // Keep last use Utopia\Database\Document; -use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Http\Adapter\Swoole\Response as SwooleResponse; /** * @method int getStatusCode() diff --git a/src/Appwrite/Vcs/Comment.php b/src/Appwrite/Vcs/Comment.php index 35f76b6fd0..6731365638 100644 --- a/src/Appwrite/Vcs/Comment.php +++ b/src/Appwrite/Vcs/Comment.php @@ -2,7 +2,7 @@ namespace Appwrite\Vcs; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\Document; class Comment @@ -73,8 +73,8 @@ class Comment $text .= "| Function | ID | Status | Action |\n"; $text .= "| :- | :- | :- | :- |\n"; - $protocol = App::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; - $hostname = App::getEnv('_APP_DOMAIN'); + $protocol = Http::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; + $hostname = Http::getEnv('_APP_DOMAIN'); foreach ($project['functions'] as $functionId => $function) { $generateImage = function (string $status) use ($protocol, $hostname) { diff --git a/src/Executor/Executor.php b/src/Executor/Executor.php index 8e73747047..7eae6efb06 100644 --- a/src/Executor/Executor.php +++ b/src/Executor/Executor.php @@ -3,7 +3,7 @@ namespace Executor; use Exception; -use Utopia\App; +use Utopia\Http\Http; class Executor { @@ -34,11 +34,11 @@ class Executor } $this->endpoint = $endpoint; - $this->cpus = \intval(App::getEnv('_APP_FUNCTIONS_CPUS', '1')); - $this->memory = \intval(App::getEnv('_APP_FUNCTIONS_MEMORY', '512')); + $this->cpus = \intval(Http::getEnv('_APP_FUNCTIONS_CPUS', '1')); + $this->memory = \intval(Http::getEnv('_APP_FUNCTIONS_MEMORY', '512')); $this->headers = [ 'content-type' => 'application/json', - 'authorization' => 'Bearer ' . App::getEnv('_APP_EXECUTOR_SECRET', ''), + 'authorization' => 'Bearer ' . Http::getEnv('_APP_EXECUTOR_SECRET', ''), 'x-opr-addressing-method' => 'anycast-efficient' ]; } @@ -72,7 +72,7 @@ class Executor ) { $runtimeId = "$projectId-$deploymentId-build"; $route = "/runtimes"; - $timeout = (int) App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900); + $timeout = (int) Http::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900); $params = [ 'runtimeId' => $runtimeId, 'source' => $source, @@ -111,7 +111,7 @@ class Executor string $projectId, callable $callback ) { - $timeout = (int) App::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900); + $timeout = (int) Http::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900); $runtimeId = "$projectId-$deploymentId-build"; $route = "/runtimes/{$runtimeId}/logs"; @@ -180,7 +180,7 @@ class Executor int $requestTimeout = null ) { if (empty($headers['host'])) { - $headers['host'] = App::getEnv('_APP_DOMAIN', ''); + $headers['host'] = Http::getEnv('_APP_DOMAIN', ''); } $runtimeId = "$projectId-$deploymentId"; diff --git a/tests/e2e/General/AbuseTest.php b/tests/e2e/General/AbuseTest.php index f5a2829974..0c8e363b37 100644 --- a/tests/e2e/General/AbuseTest.php +++ b/tests/e2e/General/AbuseTest.php @@ -7,7 +7,7 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideNone; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -21,7 +21,7 @@ class AbuseTest extends Scope { parent::setUp(); - if (App::getEnv('_APP_OPTIONS_ABUSE') === 'disabled') { + if (Http::getEnv('_APP_OPTIONS_ABUSE') === 'disabled') { $this->markTestSkipped('Abuse is not enabled.'); } } diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 5c1f49399e..8814c70455 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -12,7 +12,7 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Datetime as DatetimeValidator; -use Utopia\Validator\JSON; +use Utopia\Http\Validator\JSON; trait DatabasesBase { diff --git a/tests/e2e/Services/GraphQL/AbuseTest.php b/tests/e2e/Services/GraphQL/AbuseTest.php index 48ee64d141..958baa3b90 100644 --- a/tests/e2e/Services/GraphQL/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/AbuseTest.php @@ -6,7 +6,7 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -21,7 +21,7 @@ class AbuseTest extends Scope { parent::setUp(); - if (App::getEnv('_APP_OPTIONS_ABUSE') === 'disabled') { + if (Http::getEnv('_APP_OPTIONS_ABUSE') === 'disabled') { $this->markTestSkipped('Abuse is not enabled.'); } } @@ -90,7 +90,7 @@ class AbuseTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $max = App::getEnv('_APP_GRAPHQL_MAX_QUERY_COMPLEXITY', 250); + $max = Http::getEnv('_APP_GRAPHQL_MAX_QUERY_COMPLEXITY', 250); $this->assertEquals('Max query complexity should be ' . $max . ' but got 259.', $response['body']['errors'][0]['message']); } @@ -98,7 +98,7 @@ class AbuseTest extends Scope public function testTooManyQueriesBlocked() { $projectId = $this->getProject()['$id']; - $maxQueries = App::getEnv('_APP_GRAPHQL_MAX_QUERIES', 10); + $maxQueries = Http::getEnv('_APP_GRAPHQL_MAX_QUERIES', 10); $query = []; for ($i = 0; $i <= $maxQueries + 1; $i++) { diff --git a/tests/e2e/Services/GraphQL/MessagingTest.php b/tests/e2e/Services/GraphQL/MessagingTest.php index f5e92b9113..839d80b09e 100644 --- a/tests/e2e/Services/GraphQL/MessagingTest.php +++ b/tests/e2e/Services/GraphQL/MessagingTest.php @@ -6,7 +6,7 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\Helpers\ID; use Utopia\DSN\DSN; @@ -547,11 +547,11 @@ class MessagingTest extends Scope public function testSendEmail() { - if (empty(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'))) { + if (empty(Http::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'))) { $this->markTestSkipped('Email DSN not provided'); } - $emailDSN = new DSN(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN')); + $emailDSN = new DSN(Http::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN')); $to = $emailDSN->getParam('to'); $fromName = $emailDSN->getParam('fromName'); $fromEmail = $emailDSN->getParam('fromEmail'); @@ -757,11 +757,11 @@ class MessagingTest extends Scope public function testSendSMS() { - if (empty(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'))) { + if (empty(Http::getEnv('_APP_MESSAGE_SMS_TEST_DSN'))) { $this->markTestSkipped('SMS DSN not provided'); } - $smsDSN = new DSN(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN')); + $smsDSN = new DSN(Http::getEnv('_APP_MESSAGE_SMS_TEST_DSN')); $to = $smsDSN->getParam('to'); $from = $smsDSN->getParam('from'); $authKey = $smsDSN->getPassword(); @@ -960,11 +960,11 @@ class MessagingTest extends Scope public function testSendPushNotification() { - if (empty(App::getEnv('_APP_MESSAGE_PUSH_TEST_DSN'))) { + if (empty(Http::getEnv('_APP_MESSAGE_PUSH_TEST_DSN'))) { $this->markTestSkipped('Push DSN empty'); } - $pushDSN = new DSN(App::getEnv('_APP_MESSAGE_PUSH_TEST_DSN')); + $pushDSN = new DSN(Http::getEnv('_APP_MESSAGE_PUSH_TEST_DSN')); $to = $pushDSN->getParam('to'); $serviceAccountJSON = $pushDSN->getParam('serviceAccountJSON'); diff --git a/tests/e2e/Services/Messaging/MessagingBase.php b/tests/e2e/Services/Messaging/MessagingBase.php index 7d400bf3c1..a0669a1f8d 100644 --- a/tests/e2e/Services/Messaging/MessagingBase.php +++ b/tests/e2e/Services/Messaging/MessagingBase.php @@ -4,7 +4,7 @@ namespace Tests\E2E\Services\Messaging; use Appwrite\Messaging\Status as MessageStatus; use Tests\E2E\Client; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; @@ -1103,11 +1103,11 @@ trait MessagingBase public function testSendEmail() { - if (empty(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'))) { + if (empty(Http::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN'))) { $this->markTestSkipped('Email DSN not provided'); } - $emailDSN = new DSN(App::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN')); + $emailDSN = new DSN(Http::getEnv('_APP_MESSAGE_EMAIL_TEST_DSN')); $to = $emailDSN->getParam('to'); $fromName = $emailDSN->getParam('fromName'); $fromEmail = $emailDSN->getParam('fromEmail'); @@ -1263,11 +1263,11 @@ trait MessagingBase public function testSendSMS() { - if (empty(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN'))) { + if (empty(Http::getEnv('_APP_MESSAGE_SMS_TEST_DSN'))) { $this->markTestSkipped('SMS DSN not provided'); } - $smsDSN = new DSN(App::getEnv('_APP_MESSAGE_SMS_TEST_DSN')); + $smsDSN = new DSN(Http::getEnv('_APP_MESSAGE_SMS_TEST_DSN')); $to = $smsDSN->getParam('to'); $from = $smsDSN->getParam('from'); $senderId = $smsDSN->getUser(); @@ -1427,11 +1427,11 @@ trait MessagingBase public function testSendPushNotification() { - if (empty(App::getEnv('_APP_MESSAGE_PUSH_TEST_DSN'))) { + if (empty(Http::getEnv('_APP_MESSAGE_PUSH_TEST_DSN'))) { $this->markTestSkipped('Push DSN empty'); } - $dsn = new DSN(App::getEnv('_APP_MESSAGE_PUSH_TEST_DSN')); + $dsn = new DSN(Http::getEnv('_APP_MESSAGE_PUSH_TEST_DSN')); $to = $dsn->getParam('to'); $serviceAccountJSON = $dsn->getParam('serviceAccountJSON'); diff --git a/tests/e2e/Services/VCS/VCSBase.php b/tests/e2e/Services/VCS/VCSBase.php index 7531ea3bc8..e9521887ae 100644 --- a/tests/e2e/Services/VCS/VCSBase.php +++ b/tests/e2e/Services/VCS/VCSBase.php @@ -2,7 +2,7 @@ namespace Tests\E2E\Services\VCS; -use Utopia\App; +use Utopia\Http\Http; trait VCSBase { @@ -10,7 +10,7 @@ trait VCSBase { parent::setUp(); - if (App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY') === 'disabled') { + if (Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY') === 'disabled') { $this->markTestSkipped('VCS is not enabled.'); } } diff --git a/tests/e2e/Services/VCS/VCSConsoleClientTest.php b/tests/e2e/Services/VCS/VCSConsoleClientTest.php index 5e04c81a86..37cef56628 100644 --- a/tests/e2e/Services/VCS/VCSConsoleClientTest.php +++ b/tests/e2e/Services/VCS/VCSConsoleClientTest.php @@ -6,7 +6,7 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideConsole; -use Utopia\App; +use Utopia\Http\Http; use Utopia\Cache\Adapter\None; use Utopia\Cache\Cache; use Utopia\Database\Helpers\ID; @@ -278,8 +278,8 @@ class VCSConsoleClientTest extends Scope */ $github = new GitHub(new Cache(new None())); - $privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); - $githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID'); + $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); + $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); $github->initializeVariables($this->providerInstallationId, $privateKey, $githubAppId); $repository = $this->client->call(Client::METHOD_POST, '/vcs/github/installations/' . $installationId . '/providerRepositories', array_merge([ diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index a430a7fdc6..e512797896 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -6,7 +6,7 @@ use Appwrite\Event\Event; use Appwrite\URL\URL; use InvalidArgumentException; use PHPUnit\Framework\TestCase; -use Utopia\App; +use Utopia\Http\Http; use Utopia\DSN\DSN; use Utopia\Queue; use Utopia\Queue\Client; @@ -22,13 +22,13 @@ class EventTest extends TestCase { $fallbackForRedis = URL::unparse([ 'scheme' => 'redis', - 'host' => App::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => App::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => App::getEnv('_APP_REDIS_USER', ''), - 'pass' => App::getEnv('_APP_REDIS_PASS', ''), + 'host' => Http::getEnv('_APP_REDIS_HOST', 'redis'), + 'port' => Http::getEnv('_APP_REDIS_PORT', '6379'), + 'user' => Http::getEnv('_APP_REDIS_USER', ''), + 'pass' => Http::getEnv('_APP_REDIS_PASS', ''), ]); - $dsn = App::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis); + $dsn = Http::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis); $dsn = explode('=', $dsn); $dsn = $dsn[0] ?? ''; $dsn = new DSN($dsn); diff --git a/tests/unit/Usage/StatsTest.php b/tests/unit/Usage/StatsTest.php index c564b31c92..fdef412ac8 100644 --- a/tests/unit/Usage/StatsTest.php +++ b/tests/unit/Usage/StatsTest.php @@ -4,7 +4,7 @@ namespace Tests\Unit\Usage; use Appwrite\URL\URL as AppwriteURL; use PHPUnit\Framework\TestCase; -use Utopia\App; +use Utopia\Http\Http; use Utopia\DSN\DSN; use Utopia\Queue; use Utopia\Queue\Client; @@ -19,12 +19,12 @@ class StatsTest extends TestCase public function setUp(): void { - $env = App::getEnv('_APP_CONNECTIONS_QUEUE', AppwriteURL::unparse([ + $env = Http::getEnv('_APP_CONNECTIONS_QUEUE', AppwriteURL::unparse([ 'scheme' => 'redis', - 'host' => App::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => App::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => App::getEnv('_APP_REDIS_USER', ''), - 'pass' => App::getEnv('_APP_REDIS_PASS', ''), + 'host' => Http::getEnv('_APP_REDIS_HOST', 'redis'), + 'port' => Http::getEnv('_APP_REDIS_PORT', '6379'), + 'user' => Http::getEnv('_APP_REDIS_USER', ''), + 'pass' => Http::getEnv('_APP_REDIS_PASS', ''), ])); $dsn = explode('=', $env); From 005a2399321fb1c07c4088b56cb6af0a26b477ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 7 Mar 2024 18:24:01 +0100 Subject: [PATCH 012/195] Auth fixes --- CHANGES.md | 2 +- app/cli.php | 2 +- app/controllers/api/account.php | 100 ++++---- app/controllers/api/avatars.php | 12 +- app/controllers/api/databases.php | 116 ++++----- app/controllers/api/functions.php | 50 ++-- app/controllers/api/messaging.php | 32 +-- app/controllers/api/project.php | 2 +- app/controllers/api/projects.php | 18 +- app/controllers/api/storage.php | 86 +++---- app/controllers/api/teams.php | 40 +-- app/controllers/api/users.php | 2 +- app/controllers/api/vcs.php | 26 +- app/controllers/general.php | 20 +- app/controllers/shared/api.php | 41 +-- app/controllers/shared/api/auth.php | 7 +- app/http.php | 6 +- app/init.php | 47 ++-- app/realtime.php | 25 +- app/worker.php | 9 +- composer.json | 11 +- composer.lock | 235 ++++++++++++------ src/Appwrite/Auth/Auth.php | 5 +- src/Appwrite/Auth/Validator/Password.php | 2 +- src/Appwrite/Auth/Validator/Phone.php | 2 +- src/Appwrite/Event/Validator/Event.php | 2 +- src/Appwrite/GraphQL/Resolvers.php | 16 +- src/Appwrite/GraphQL/Schema.php | 6 +- src/Appwrite/GraphQL/Types/Mapper.php | 6 +- src/Appwrite/Migration/Migration.php | 6 +- src/Appwrite/Network/Validator/CNAME.php | 2 +- src/Appwrite/Network/Validator/Email.php | 4 +- src/Appwrite/Network/Validator/Origin.php | 2 +- src/Appwrite/Platform/Tasks/CalcTierStats.php | 17 +- .../Platform/Tasks/DeleteOrphanedProjects.php | 5 +- .../Platform/Tasks/GetMigrationStats.php | 5 +- src/Appwrite/Platform/Tasks/Migrate.php | 10 +- src/Appwrite/Platform/Tasks/Specs.php | 5 +- src/Appwrite/Platform/Workers/Builds.php | 13 +- src/Appwrite/Platform/Workers/Hamster.php | 14 +- src/Appwrite/Specification/Format.php | 4 +- .../Specification/Format/OpenAPI3.php | 4 +- .../Specification/Format/Swagger2.php | 2 +- src/Appwrite/Task/Validator/Cron.php | 2 +- .../Utopia/Database/Validator/CompoundUID.php | 2 +- .../Utopia/Database/Validator/ProjectId.php | 2 +- src/Appwrite/Utopia/Request.php | 8 +- src/Appwrite/Utopia/View.php | 2 +- .../DatabasesPermissionsGuestTest.php | 19 +- tests/unit/Auth/AuthTest.php | 27 +- .../unit/Messaging/MessagingChannelsTest.php | 10 +- 51 files changed, 600 insertions(+), 493 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5dd4ba8770..387d7e0f55 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -33,7 +33,7 @@ * Bump console to version 3.2.7 [#7148](https://github.com/appwrite/appwrite/pull/7148) * Chore update database to 0.45.2 [#7138](https://github.com/appwrite/appwrite/pull/7138) * Implement queue thresholds for the health API [#7123](https://github.com/appwrite/appwrite/pull/7123) -* Add Authorization::skip to the usage worker [#7124](https://github.com/appwrite/appwrite/pull/7124) +* Add $auth->skip to the usage worker [#7124](https://github.com/appwrite/appwrite/pull/7124) ## Bug fixes * fix: use queueForDeletes in git installation delete endpoint [#7140](https://github.com/appwrite/appwrite/pull/7140) diff --git a/app/cli.php b/app/cli.php index 95981033b3..86daad2ae3 100644 --- a/app/cli.php +++ b/app/cli.php @@ -25,7 +25,7 @@ use Utopia\Registry\Registry; global $register; -Authorization::disable(); +$auth->disable(); CLI::setResource('register', fn () => $register); diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 60b1e3d48d..2def54db58 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -160,9 +160,9 @@ Http::post('/v1/account') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $auth->skip(fn () => $dbForProject->createDocument('users', $user)); try { - $target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([ + $target = $auth->skip(fn () => $dbForProject->createDocument('targets', new Document([ '$permissions' => [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), @@ -186,9 +186,9 @@ Http::post('/v1/account') throw new Exception(Exception::USER_ALREADY_EXISTS); } - Authorization::unsetRole(Role::guests()->toString()); - Authorization::setRole(Role::user($user->getId())->toString()); - Authorization::setRole(Role::users()->toString()); + $auth->unsetRole(Role::guests()->toString()); + $auth->addRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::users()->toString()); $queueForEvents->setParam('userId', $user->getId()); @@ -243,7 +243,7 @@ Http::post('/v1/account/sessions/email') throw new Exception(Exception::USER_BLOCKED); // User is in status blocked } - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -274,7 +274,7 @@ Http::post('/v1/account/sessions/email') $detector->getDevice() )); - Authorization::setRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::user($user->getId())->toString()); // Re-hash if not using recommended algo if ($user->getAttribute('hash') !== Auth::DEFAULT_ALGO) { @@ -775,7 +775,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $auth->skip(fn () => $dbForProject->createDocument('users', $user)); $dbForProject->createDocument('targets', new Document([ '$permissions' => [ @@ -794,8 +794,8 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') } } - Authorization::setRole(Role::user($user->getId())->toString()); - Authorization::setRole(Role::users()->toString()); + $auth->addRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::users()->toString()); if (false === $user->getAttribute('status')) { // Account is blocked $failureRedirect(Exception::USER_BLOCKED); // User is in status blocked @@ -854,7 +854,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') $dbForProject->updateDocument('users', $user->getId(), $user); - Authorization::setRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::user($user->getId())->toString()); $state['success'] = URLParser::parse($state['success']); $query = URLParser::parseQuery($state['success']['query']); @@ -876,7 +876,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') 'ip' => $request->getIP(), ]); - Authorization::setRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -1108,7 +1108,7 @@ Http::post('/v1/account/tokens/magic-url') $phrase = Phrase::generate(); } - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1163,7 +1163,7 @@ Http::post('/v1/account/tokens/magic-url') ]); $user->removeAttribute('$internalId'); - $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $auth->skip(fn () => $dbForProject->createDocument('users', $user)); } $tokenSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_MAGIC_URL); @@ -1180,7 +1180,7 @@ Http::post('/v1/account/tokens/magic-url') 'ip' => $request->getIP(), ]); - Authorization::setRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -1349,7 +1349,7 @@ Http::post('/v1/account/tokens/email') $phrase = Phrase::generate(); } - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1402,7 +1402,7 @@ Http::post('/v1/account/tokens/email') ]); $user->removeAttribute('$internalId'); - $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $auth->skip(fn () => $dbForProject->createDocument('users', $user)); } $tokenSecret = Auth::codeGenerator(6); @@ -1419,7 +1419,7 @@ Http::post('/v1/account/tokens/email') 'ip' => $request->getIP(), ]); - Authorization::setRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -1542,12 +1542,12 @@ Http::post('/v1/account/tokens/email') }); $createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); /** @var Utopia\Database\Document $user */ - $userFromRequest = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId)); + $userFromRequest = $auth->skip(fn () => $dbForProject->getDocument('users', $userId)); if ($userFromRequest->isEmpty()) { throw new Exception(Exception::USER_INVALID_TOKEN); @@ -1593,7 +1593,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res $detector->getDevice() )); - Authorization::setRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::user($user->getId())->toString()); $session = $dbForProject->createDocument('sessions', $session ->setAttribute('$permissions', [ @@ -1603,7 +1603,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res ])); $dbForProject->purgeCachedDocument('users', $user->getId()); - Authorization::skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId())); + $auth->skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId())); $dbForProject->purgeCachedDocument('users', $user->getId()); if ($verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_MAGIC_URL) { @@ -1770,7 +1770,7 @@ Http::post('/v1/account/tokens/phone') throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1814,9 +1814,9 @@ Http::post('/v1/account/tokens/phone') ]); $user->removeAttribute('$internalId'); - $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $auth->skip(fn () => $dbForProject->createDocument('users', $user)); try { - $target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([ + $target = $auth->skip(fn () => $dbForProject->createDocument('targets', new Document([ '$permissions' => [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), @@ -1851,7 +1851,7 @@ Http::post('/v1/account/tokens/phone') 'ip' => $request->getIP(), ]); - Authorization::setRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -1935,7 +1935,7 @@ Http::post('/v1/account/sessions/anonymous') ->inject('queueForEvents') ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) { $protocol = $request->getProtocol(); - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1981,7 +1981,7 @@ Http::post('/v1/account/sessions/anonymous') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $auth->skip(fn () => $dbForProject->createDocument('users', $user)); // Create session token $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; @@ -2007,7 +2007,7 @@ Http::post('/v1/account/sessions/anonymous') $detector->getDevice() )); - Authorization::setRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::user($user->getId())->toString()); $session = $dbForProject->createDocument('sessions', $session-> setAttribute('$permissions', [ Permission::read(Role::user($user->getId())), @@ -2155,7 +2155,7 @@ Http::get('/v1/account/sessions') ->inject('project') ->action(function (Response $response, Document $user, Locale $locale, Document $project) { - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -2263,7 +2263,7 @@ Http::get('/v1/account/sessions/:sessionId') ->inject('project') ->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Document $project) { - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -2455,7 +2455,7 @@ Http::patch('/v1/account/email') ->setAttribute('passwordUpdate', DateTime::now()); } - $target = Authorization::skip(fn () => $dbForProject->findOne('targets', [ + $target = $auth->skip(fn () => $dbForProject->findOne('targets', [ Query::equal('identifier', [$email]), ])); @@ -2471,7 +2471,7 @@ Http::patch('/v1/account/email') $oldTarget = $user->find('identifier', $oldEmail, 'targets'); if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) { - Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email))); + $auth->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email))); } $dbForProject->purgeCachedDocument('users', $user->getId()); } catch (Duplicate) { @@ -2521,7 +2521,7 @@ Http::patch('/v1/account/phone') $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]); - $target = Authorization::skip(fn () => $dbForProject->findOne('targets', [ + $target = $auth->skip(fn () => $dbForProject->findOne('targets', [ Query::equal('identifier', [$phone]), ])); @@ -2552,7 +2552,7 @@ Http::patch('/v1/account/phone') $oldTarget = $user->find('identifier', $oldPhone, 'targets'); if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) { - Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone))); + $auth->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone))); } $dbForProject->purgeCachedDocument('users', $user->getId()); } catch (Duplicate $th) { @@ -2894,7 +2894,7 @@ Http::post('/v1/account/recovery') throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); } - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -2928,7 +2928,7 @@ Http::post('/v1/account/recovery') 'ip' => $request->getIP(), ]); - Authorization::setRole(Role::user($profile->getId())->toString()); + $auth->addRole(Role::user($profile->getId())->toString()); $recovery = $dbForProject->createDocument('tokens', $recovery ->setAttribute('$permissions', [ @@ -3079,7 +3079,7 @@ Http::put('/v1/account/recovery') throw new Exception(Exception::USER_INVALID_TOKEN); } - Authorization::setRole(Role::user($profile->getId())->toString()); + $auth->addRole(Role::user($profile->getId())->toString()); $newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS); @@ -3159,7 +3159,7 @@ Http::post('/v1/account/verification') throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED); } - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); $verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION); @@ -3176,7 +3176,7 @@ Http::post('/v1/account/verification') 'ip' => $request->getIP(), ]); - Authorization::setRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::user($user->getId())->toString()); $verification = $dbForProject->createDocument('tokens', $verification ->setAttribute('$permissions', [ @@ -3309,7 +3309,7 @@ Http::put('/v1/account/verification') ->inject('queueForEvents') ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { - $profile = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId)); + $profile = $auth->skip(fn () => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); @@ -3322,7 +3322,7 @@ Http::put('/v1/account/verification') throw new Exception(Exception::USER_INVALID_TOKEN); } - Authorization::setRole(Role::user($profile->getId())->toString()); + $auth->addRole(Role::user($profile->getId())->toString()); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true)); @@ -3383,7 +3383,7 @@ Http::post('/v1/account/verification/phone') throw new Exception(Exception::USER_PHONE_ALREADY_VERIFIED); } - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); $secret = Auth::codeGenerator(); @@ -3400,7 +3400,7 @@ Http::post('/v1/account/verification/phone') 'ip' => $request->getIP(), ]); - Authorization::setRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::user($user->getId())->toString()); $verification = $dbForProject->createDocument('tokens', $verification ->setAttribute('$permissions', [ @@ -3481,7 +3481,7 @@ Http::put('/v1/account/verification/phone') ->inject('queueForEvents') ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { - $profile = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId)); + $profile = $auth->skip(fn () => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); @@ -3493,7 +3493,7 @@ Http::put('/v1/account/verification/phone') throw new Exception(Exception::USER_INVALID_TOKEN); } - Authorization::setRole(Role::user($profile->getId())->toString()); + $auth->addRole(Role::user($profile->getId())->toString()); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('phoneVerification', true)); @@ -4230,9 +4230,9 @@ Http::post('/v1/account/targets/push') ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) { $targetId = $targetId == 'unique()' ? ID::unique() : $targetId; - $provider = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId)); + $provider = $auth->skip(fn () => $dbForProject->getDocument('providers', $providerId)); - $target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if (!$target->isEmpty()) { throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS); @@ -4301,7 +4301,7 @@ Http::put('/v1/account/targets/:targetId/push') ->inject('dbForProject') ->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) { - $target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); @@ -4355,7 +4355,7 @@ Http::delete('/v1/account/targets/:targetId/push') ->inject('response') ->inject('dbForProject') ->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject) { - $target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index cd63da8cfe..8a4b0b6afd 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -63,7 +63,7 @@ $avatarCallback = function (string $type, string $code, int $width, int $height, $getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForConsole, ?Logger $logger) { try { - $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId)); + $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); $sessions = $user->getAttribute('sessions', []); @@ -114,7 +114,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro ->setAttribute('providerRefreshToken', $refreshToken) ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry(''))); - Authorization::skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession)); + $auth->skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession)); $dbForProject->purgeCachedDocument('users', $user->getId()); } catch (Throwable $err) { @@ -122,7 +122,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro do { $previousAccessToken = $gitHubSession->getAttribute('providerAccessToken'); - $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId)); + $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); $sessions = $user->getAttribute('sessions', []); $gitHubSession = new Document(); @@ -594,7 +594,7 @@ Http::get('/v1/cards/cloud') ->inject('employees') ->inject('logger') ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) { - $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId)); + $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -801,7 +801,7 @@ Http::get('/v1/cards/cloud-back') ->inject('employees') ->inject('logger') ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) { - $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId)); + $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -879,7 +879,7 @@ Http::get('/v1/cards/cloud-og') ->inject('employees') ->inject('logger') ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) { - $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId)); + $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 4668adc63c..c2fc070cc3 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -88,7 +88,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att $default = $attribute->getAttribute('default'); $options = $attribute->getAttribute('options', []); - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -237,7 +237,7 @@ function updateAttribute( array $elements = null, array $options = [] ): Document { - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -752,7 +752,7 @@ Http::post('/v1/databases/:databaseId/collections') ->inject('queueForEvents') ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -813,7 +813,7 @@ Http::get('/v1/databases/:databaseId/collections') ->inject('mode') ->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -875,7 +875,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId') ->inject('mode') ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, string $mode) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -911,7 +911,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/logs') ->inject('geodb') ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1017,7 +1017,7 @@ Http::put('/v1/databases/:databaseId/collections/:collectionId') ->inject('queueForEvents') ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1081,7 +1081,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId') ->inject('mode') ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1616,7 +1616,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relat $key ??= $relatedCollectionId; $twoWayKey ??= $collectionId; - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1719,7 +1719,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes') ->inject('dbForProject') ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject) { /** @var Document $database */ - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1753,7 +1753,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes') if ($cursor) { $attributeId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn () => $dbForProject->find('attributes', [ + $cursorDocument = $auth->skip(fn () => $dbForProject->find('attributes', [ Query::equal('collectionInternalId', [$collection->getInternalId()]), Query::equal('databaseInternalId', [$database->getInternalId()]), Query::equal('key', [$attributeId]), @@ -1807,7 +1807,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') ->inject('dbForProject') ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2300,7 +2300,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:ke ->inject('queueForEvents') ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2413,7 +2413,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->inject('queueForEvents') ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2575,7 +2575,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes') ->inject('dbForProject') ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject) { /** @var Document $database */ - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2605,7 +2605,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes') if ($cursor) { $indexId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [ + $cursorDocument = $auth->skip(fn () => $dbForProject->find('indexes', [ Query::equal('collectionInternalId', [$collection->getInternalId()]), Query::equal('databaseInternalId', [$database->getInternalId()]), Query::equal('key', [$indexId]), @@ -2645,7 +2645,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->inject('dbForProject') ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2688,7 +2688,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->inject('queueForEvents') ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2771,16 +2771,16 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new documents, try update instead'); } - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -2818,8 +2818,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!Authorization::isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')'); + if (!$auth->isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $auth->getRoles()) . ')'); } } } @@ -2867,7 +2867,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( + $relatedCollection = $auth->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -2881,7 +2881,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') $relation = new Document($relation); } if ($relation instanceof Document) { - $current = Authorization::skip( + $current = $auth->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), $relation->getId()) ); @@ -2941,7 +2941,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( + $relatedCollection = $auth->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -2988,15 +2988,15 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('dbForProject') ->inject('mode') ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -3020,7 +3020,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') if ($cursor) { $documentId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $cursorDocument = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Document '{$documentId}' for the 'cursor' value not found."); @@ -3066,7 +3066,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId)); + $relatedCollection = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId)); foreach ($relations as $index => $doc) { if ($doc instanceof Document) { @@ -3145,16 +3145,16 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume ->inject('dbForProject') ->inject('mode') ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, string $mode) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -3198,7 +3198,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( + $relatedCollection = $auth->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3237,7 +3237,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume ->inject('geodb') ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -3357,16 +3357,16 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD); } - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -3374,7 +3374,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu // Read permission should not be required for update /** @var Document $document */ - $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $document = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); if ($document->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); @@ -3388,7 +3388,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ]); // Users can only manage their own roles, API keys and Admin users can manage any - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -3401,7 +3401,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!Authorization::isRole($role)) { + if (!$auth->isRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -3438,7 +3438,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( + $relatedCollection = $auth->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3453,7 +3453,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $relation = new Document($relation); } if ($relation instanceof Document) { - $oldDocument = Authorization::skip(fn () => $dbForProject->getDocument( + $oldDocument = $auth->skip(fn () => $dbForProject->getDocument( 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), $relation->getId() )); @@ -3522,7 +3522,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( + $relatedCollection = $auth->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3576,23 +3576,23 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:doc ->inject('queueForEvents') ->inject('mode') ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, string $mode) { - $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } // Read permission should not be required for delete - $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $document = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); if ($document->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); @@ -3632,7 +3632,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:doc } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = Authorization::skip( + $relatedCollection = $auth->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3685,7 +3685,7 @@ Http::get('/v1/databases/usage') METRIC_DOCUMENTS, ]; - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -3769,7 +3769,7 @@ Http::get('/v1/databases/:databaseId/usage') str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS), ]; - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -3855,7 +3855,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/usage') str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collectionDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), ]; - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 29d8ddb9da..59c3254d67 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -500,7 +500,7 @@ Http::get('/v1/functions/:functionId/usage') str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), ]; - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -592,7 +592,7 @@ Http::get('/v1/functions/usage') METRIC_EXECUTIONS_COMPUTE, ]; - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -828,7 +828,7 @@ Http::put('/v1/functions/:functionId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForEvents->setParam('functionId', $function->getId()); @@ -973,7 +973,7 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForEvents ->setParam('functionId', $function->getId()) @@ -1018,7 +1018,7 @@ Http::delete('/v1/functions/:functionId') $schedule ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('active', false); - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) @@ -1465,7 +1465,7 @@ Http::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId' throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } - $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $buildId)); + $build = $auth->skip(fn () => $dbForProject->getDocument('builds', $buildId)); if ($build->isEmpty()) { throw new Exception(Exception::BUILD_NOT_FOUND); @@ -1524,10 +1524,10 @@ Http::post('/v1/functions/:functionId/executions') ->inject('geodb') ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode, Func $queueForFunctions, Reader $geodb) { - $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); @@ -1542,7 +1542,7 @@ Http::post('/v1/functions/:functionId/executions') throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } - $deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); + $deployment = $auth->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); if ($deployment->getAttribute('resourceId') !== $function->getId()) { throw new Exception(Exception::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); @@ -1553,7 +1553,7 @@ Http::post('/v1/functions/:functionId/executions') } /** Check if build has completed */ - $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); + $build = $auth->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); if ($build->isEmpty()) { throw new Exception(Exception::BUILD_NOT_FOUND); } @@ -1646,7 +1646,7 @@ Http::post('/v1/functions/:functionId/executions') if ($async) { if ($function->getAttribute('logging')) { /** @var Document $execution */ - $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution)); } $queueForFunctions @@ -1761,10 +1761,10 @@ Http::post('/v1/functions/:functionId/executions') if ($function->getAttribute('logging')) { /** @var Document $execution */ - $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution)); } - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1804,10 +1804,10 @@ Http::get('/v1/functions/:functionId/executions') ->inject('dbForProject') ->inject('mode') ->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { - $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); @@ -1850,7 +1850,7 @@ Http::get('/v1/functions/:functionId/executions') $results = $dbForProject->find('executions', $queries); $total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT); - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); if (!$isPrivilegedUser && !$isAppUser) { @@ -1884,10 +1884,10 @@ Http::get('/v1/functions/:functionId/executions/:executionId') ->inject('dbForProject') ->inject('mode') ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, string $mode) { - $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); @@ -1903,7 +1903,7 @@ Http::get('/v1/functions/:functionId/executions/:executionId') throw new Exception(Exception::EXECUTION_NOT_FOUND); } - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); if (!$isPrivilegedUser && !$isAppUser) { @@ -1973,7 +1973,7 @@ Http::post('/v1/functions/:functionId/variables') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -2102,7 +2102,7 @@ Http::put('/v1/functions/:functionId/variables/:variableId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $response->dynamic($variable, Response::MODEL_VARIABLE); }); @@ -2150,7 +2150,7 @@ Http::delete('/v1/functions/:functionId/variables/:variableId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $response->noContent(); }); diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 1bb08c74a0..5e0d31a294 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -867,7 +867,7 @@ Http::get('/v1/messaging/providers') if ($cursor) { $providerId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId)); + $cursorDocument = $auth->skip(fn () => $dbForProject->getDocument('providers', $providerId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Provider '{$providerId}' for the 'cursor' value not found."); @@ -2001,7 +2001,7 @@ Http::get('/v1/messaging/topics') if ($cursor) { $topicId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $cursorDocument = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Topic '{$topicId}' for the 'cursor' value not found."); @@ -2239,7 +2239,7 @@ Http::post('/v1/messaging/topics/:topicId/subscribers') ->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response) { $subscriberId = $subscriberId == 'unique()' ? ID::unique() : $subscriberId; - $topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $topic = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -2251,13 +2251,13 @@ Http::post('/v1/messaging/topics/:topicId/subscribers') throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription()); } - $target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); } - $user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); + $user = $auth->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); $subscriber = new Document([ '$id' => $subscriberId, @@ -2290,7 +2290,7 @@ Http::post('/v1/messaging/topics/:topicId/subscribers') default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE), }; - Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute( + $auth->skip(fn () => $dbForProject->increaseDocumentAttribute( 'topics', $topicId, $totalAttribute, @@ -2339,7 +2339,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') $queries[] = Query::search('search', $search); } - $topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $topic = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -2357,7 +2357,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') if ($cursor) { $subscriberId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId)); + $cursorDocument = $auth->skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Subscriber '{$subscriberId}' for the 'cursor' value not found."); @@ -2370,8 +2370,8 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') $subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) { return function () use ($subscriber, $dbForProject) { - $target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); - $user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); + $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); + $user = $auth->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); return $subscriber ->setAttribute('target', $target) @@ -2491,7 +2491,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->inject('dbForProject') ->inject('response') ->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response) { - $topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $topic = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -2503,8 +2503,8 @@ Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') throw new Exception(Exception::SUBSCRIBER_NOT_FOUND); } - $target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); - $user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); + $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); + $user = $auth->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); $subscriber ->setAttribute('target', $target) @@ -2534,7 +2534,7 @@ Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->inject('dbForProject') ->inject('response') ->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response) { - $topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $topic = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -2557,7 +2557,7 @@ Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE), }; - Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute( + $auth->skip(fn () => $dbForProject->decreaseDocumentAttribute( 'topics', $topicId, $totalAttribute, @@ -3043,7 +3043,7 @@ Http::get('/v1/messaging/messages') if ($cursor) { $messageId = $cursor->getValue(); - $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('messages', $messageId)); + $cursorDocument = $auth->skip(fn () => $dbForProject->getDocument('messages', $messageId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Message '{$messageId}' for the 'cursor' value not found."); diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index dc84dc060a..15154fbbed 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -70,7 +70,7 @@ Http::get('/v1/project/usage') '1d' => 'Y-m-d\T00:00:00.000P', }; - Authorization::skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, &$total, &$stats) { + $auth->skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, &$total, &$stats) { foreach ($metrics['total'] as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 17f19a926f..2a38e4433f 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -30,15 +30,15 @@ use Utopia\Database\Validator\UID; use Utopia\Domains\Validator\PublicDomain; use Utopia\Locale\Locale; use Utopia\Pools\Group; -use Utopia\Validator\ArrayList; -use Utopia\Validator\Boolean; -use Utopia\Validator\Hostname; -use Utopia\Validator\Integer; -use Utopia\Validator\Multiple; -use Utopia\Validator\Range; -use Utopia\Validator\Text; -use Utopia\Validator\URL; -use Utopia\Validator\WhiteList; +use Utopia\Http\Validator\ArrayList; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\Hostname; +use Utopia\Http\Validator\Integer; +use Utopia\Http\Validator\Multiple; +use Utopia\Http\Validator\Range; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\URL; +use Utopia\Http\Validator\WhiteList; Http::init() ->groups(['projects']) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 591ac5f58d..859daf20f3 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -363,10 +363,10 @@ Http::post('/v1/storage/buckets/:bucketId/files') ->inject('deviceForLocal') ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -397,7 +397,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') } // Users can only manage their own roles, API keys and Admin users can manage any - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -410,7 +410,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!Authorization::isRole($role)) { + if (!$auth->isRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -635,7 +635,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') if (!$validator->isValid($bucket->getCreate())) { throw new Exception(Exception::USER_UNAUTHORIZED); } - $file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = $auth->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); } } catch (AuthorizationException) { throw new Exception(Exception::USER_UNAUTHORIZED); @@ -682,7 +682,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') if (!$validator->isValid($bucket->getCreate())) { throw new Exception(Exception::USER_UNAUTHORIZED); } - $file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = $auth->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); } } catch (AuthorizationException) { throw new Exception(Exception::USER_UNAUTHORIZED); @@ -725,10 +725,10 @@ Http::get('/v1/storage/buckets/:bucketId/files') ->inject('dbForProject') ->inject('mode') ->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -765,7 +765,7 @@ Http::get('/v1/storage/buckets/:bucketId/files') if ($fileSecurity && !$valid) { $cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $cursorDocument = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($cursorDocument->isEmpty()) { @@ -781,8 +781,8 @@ Http::get('/v1/storage/buckets/:bucketId/files') $files = $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries); $total = $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT); } else { - $files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries)); - $total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT)); + $files = $auth->skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries)); + $total = $auth->skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT)); } $response->dynamic(new Document([ @@ -809,10 +809,10 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('dbForProject') ->inject('mode') ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -828,7 +828,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId') if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -879,10 +879,10 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); } - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -898,7 +898,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -1035,10 +1035,10 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->inject('deviceForFiles') ->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Device $deviceForFiles) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1054,7 +1054,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download') if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -1174,10 +1174,10 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->inject('mode') ->inject('deviceForFiles') ->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceForFiles) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1193,7 +1193,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view') if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -1335,10 +1335,10 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('queueForEvents') ->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1352,7 +1352,7 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') } // Read permission should not be required for update - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -1366,7 +1366,7 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') ]); // Users can only manage their own roles, API keys and Admin users can manage any - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles) && !\is_null($permissions)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -1379,7 +1379,7 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!Authorization::isRole($role)) { + if (!$auth->isRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -1403,7 +1403,7 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') throw new Exception(Exception::USER_UNAUTHORIZED); } } else { - $file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = $auth->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); } $queueForEvents @@ -1440,10 +1440,10 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('deviceForFiles') ->inject('queueForDeletes') ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1457,7 +1457,7 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') } // Read permission should not be required for delete - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -1491,7 +1491,7 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') throw new Exception(Exception::USER_UNAUTHORIZED); } } else { - $deleted = Authorization::skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $deleted = $auth->skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if (!$deleted) { @@ -1536,7 +1536,7 @@ Http::get('/v1/storage/usage') ]; $total = []; - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { + $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -1621,7 +1621,7 @@ Http::get('/v1/storage/:bucketId/usage') ]; - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { + $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 8f1e6018e0..c3542645db 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -65,13 +65,13 @@ Http::post('/v1/teams') ->inject('queueForEvents') ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAppUser = Auth::isAppUser($auth->getRoles()); $teamId = $teamId == 'unique()' ? ID::unique() : $teamId; try { - $team = Authorization::skip(fn () => $dbForProject->createDocument('teams', new Document([ + $team = $auth->skip(fn () => $dbForProject->createDocument('teams', new Document([ '$id' => $teamId, '$permissions' => [ Permission::read(Role::team($teamId)), @@ -397,8 +397,8 @@ Http::post('/v1/teams/:teamId/memberships') ->inject('queueForMessaging') ->inject('queueForEvents') ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents) { - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if (empty($url)) { if (!$isAPIKey && !$isPrivilegedUser) { @@ -409,8 +409,8 @@ Http::post('/v1/teams/:teamId/memberships') if (empty($userId) && empty($email) && empty($phone)) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'At least one of userId, email, or phone is required'); } - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAppUser = Auth::isAppUser($auth->getRoles()); if (!$isPrivilegedUser && !$isAppUser && empty(Http::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED); @@ -470,7 +470,7 @@ Http::post('/v1/teams/:teamId/memberships') try { $userId = ID::unique(); - $invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([ + $invitee = $auth->skip(fn () => $dbForProject->createDocument('users', new Document([ '$id' => $userId, '$permissions' => [ Permission::read(Role::any()), @@ -506,7 +506,7 @@ Http::post('/v1/teams/:teamId/memberships') } } - $isOwner = Authorization::isRole('team:' . $team->getId() . '/owner'); + $isOwner = $auth->isRole('team:' . $team->getId() . '/owner'); if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server) throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to send invitations for this team'); @@ -538,12 +538,12 @@ Http::post('/v1/teams/:teamId/memberships') if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership try { - $membership = Authorization::skip(fn () => $dbForProject->createDocument('memberships', $membership)); + $membership = $auth->skip(fn () => $dbForProject->createDocument('memberships', $membership)); } catch (Duplicate $th) { throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS); } - Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); + $auth->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); $dbForProject->purgeCachedDocument('users', $invitee->getId()); } else { @@ -880,9 +880,9 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId') throw new Exception(Exception::USER_NOT_FOUND); } - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAppUser = Auth::isAppUser(Authorization::getRoles()); - $isOwner = Authorization::isRole('team:' . $team->getId() . '/owner'); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAppUser = Auth::isAppUser($auth->getRoles()); + $isOwner = $auth->isRole('team:' . $team->getId() . '/owner'); if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server) throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to modify roles'); @@ -951,7 +951,7 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH); } - $team = Authorization::skip(fn () => $dbForProject->getDocument('teams', $teamId)); + $team = $auth->skip(fn () => $dbForProject->getDocument('teams', $teamId)); if ($team->isEmpty()) { throw new Exception(Exception::TEAM_NOT_FOUND); @@ -982,11 +982,11 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->setAttribute('confirm', true) ; - Authorization::skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true))); + $auth->skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true))); // Log user in - Authorization::setRole(Role::user($user->getId())->toString()); + $auth->addRole(Role::user($user->getId())->toString()); $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); @@ -1016,13 +1016,13 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') $dbForProject->purgeCachedDocument('users', $user->getId()); - Authorization::setRole(Role::user($userId)->toString()); + $auth->addRole(Role::user($userId)->toString()); $membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership); $dbForProject->purgeCachedDocument('users', $user->getId()); - Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); + $auth->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); $queueForEvents ->setParam('teamId', $team->getId()) @@ -1102,7 +1102,7 @@ Http::delete('/v1/teams/:teamId/memberships/:membershipId') $dbForProject->purgeCachedDocument('users', $user->getId()); if ($membership->getAttribute('confirm')) { // Count only confirmed members - Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0)); + $auth->skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0)); } $queueForEvents diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 74b3138313..6bb9d6e555 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -2120,7 +2120,7 @@ Http::get('/v1/users/usage') METRIC_SESSIONS, ]; - Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $count => $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 7be9c2e1ad..d1832a5825 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -46,11 +46,11 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId if ($resourceType === "function") { $projectId = $resource->getAttribute('projectId'); - $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); $dbForProject = $getProjectDB($project); $functionId = $resource->getAttribute('resourceId'); - $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); $functionInternalId = $function->getInternalId(); $deploymentId = ID::unique(); @@ -97,7 +97,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $latestCommentId = ''; if (!empty($providerPullRequestId)) { - $latestComment = Authorization::skip(fn () => $dbForConsole->findOne('vcsComments', [ + $latestComment = $auth->skip(fn () => $dbForConsole->findOne('vcsComments', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::equal('providerPullRequestId', [$providerPullRequestId]), Query::orderDesc('$createdAt'), @@ -118,7 +118,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId if (!empty($latestCommentId)) { $teamId = $project->getAttribute('teamId', ''); - $latestComment = Authorization::skip(fn () => $dbForConsole->createDocument('vcsComments', new Document([ + $latestComment = $auth->skip(fn () => $dbForConsole->createDocument('vcsComments', new Document([ '$id' => ID::unique(), '$permissions' => [ Permission::read(Role::team(ID::custom($teamId))), @@ -139,7 +139,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId } } } elseif (!empty($providerBranch)) { - $latestComments = Authorization::skip(fn () => $dbForConsole->find('vcsComments', [ + $latestComments = $auth->skip(fn () => $dbForConsole->find('vcsComments', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::equal('providerBranch', [$providerBranch]), Query::orderDesc('$createdAt'), @@ -856,7 +856,7 @@ Http::post('/v1/vcs/github/events') $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); //find functionId from functions table - $repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [ + $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::limit(100), ])); @@ -876,13 +876,13 @@ Http::post('/v1/vcs/github/events') ]); foreach ($installations as $installation) { - $repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [ + $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ Query::equal('installationInternalId', [$installation->getInternalId()]), Query::limit(1000) ])); foreach ($repositories as $repository) { - Authorization::skip(fn () => $dbForConsole->deleteDocument('repositories', $repository->getId())); + $auth->skip(fn () => $dbForConsole->deleteDocument('repositories', $repository->getId())); } $dbForConsole->deleteDocument('installations', $installation->getId()); @@ -914,7 +914,7 @@ Http::post('/v1/vcs/github/events') $providerCommitAuthor = $commitDetails["commitAuthor"] ?? ''; $providerCommitMessage = $commitDetails["commitMessage"] ?? ''; - $repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [ + $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::orderDesc('$createdAt') ])); @@ -928,7 +928,7 @@ Http::post('/v1/vcs/github/events') $external = $parsedPayload["external"] ?? true; if ($external) { - $repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [ + $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::orderDesc('$createdAt') ])); @@ -939,7 +939,7 @@ Http::post('/v1/vcs/github/events') if (\in_array($providerPullRequestId, $providerPullRequestIds)) { $providerPullRequestIds = \array_diff($providerPullRequestIds, [$providerPullRequestId]); $repository = $repository->setAttribute('providerPullRequestIds', $providerPullRequestIds); - $repository = Authorization::skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); + $repository = $auth->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); } } } @@ -1099,7 +1099,7 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito throw new Exception(Exception::INSTALLATION_NOT_FOUND); } - $repository = Authorization::skip(fn () => $dbForConsole->getDocument('repositories', $repositoryId, [ + $repository = $auth->skip(fn () => $dbForConsole->getDocument('repositories', $repositoryId, [ Query::equal('projectInternalId', [$project->getInternalId()]) ])); @@ -1116,7 +1116,7 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito // TODO: Delete from array when PR is closed - $repository = Authorization::skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); + $repository = $auth->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); $privateKey = Http::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); $githubAppId = Http::getEnv('_APP_VCS_GITHUB_APP_ID'); diff --git a/app/controllers/general.php b/app/controllers/general.php index d14da35f7c..606cfac9db 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -52,7 +52,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo $host = $request->getHostname() ?? ''; - $route = Authorization::skip( + $route = $auth->skip( fn () => $dbForConsole->find('rules', [ Query::equal('domain', [$host]), Query::limit(1) @@ -80,7 +80,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo } $projectId = $route->getAttribute('projectId'); - $project = Authorization::skip( + $project = $auth->skip( fn () => $dbForConsole->getDocument('projects', $projectId) ); if (array_key_exists('proxy', $project->getAttribute('services', []))) { @@ -124,11 +124,11 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo $requestHeaders = $request->getHeaders(); - $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); $dbForProject = $getProjectDB($project); - $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty() || !$function->getAttribute('enabled')) { throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND); @@ -143,7 +143,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } - $deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); + $deployment = $auth->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); if ($deployment->getAttribute('resourceId') !== $function->getId()) { throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); @@ -154,7 +154,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo } /** Check if build has completed */ - $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); + $build = $auth->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); if ($build->isEmpty()) { throw new AppwriteException(AppwriteException::BUILD_NOT_FOUND); } @@ -316,7 +316,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo if ($function->getAttribute('logging')) { /** @var Document $execution */ - $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution)); } $execution->setAttribute('logs', ''); @@ -444,7 +444,7 @@ Http::init() } elseif (str_starts_with($request->getURI(), '/.well-known/acme-challenge')) { Console::warning('Skipping SSL certificates generation on ACME challenge.'); } else { - Authorization::disable(); + $auth->disable(); $envDomain = Http::getEnv('_APP_DOMAIN', ''); $mainDomain = null; @@ -483,7 +483,7 @@ Http::init() } $domains[$domain->get()] = true; - Authorization::reset(); // ensure authorization is re-enabled + $auth->reset(); // ensure authorization is re-enabled } Config::setParam('domains', $domains); } @@ -701,7 +701,7 @@ Http::error() $log->addExtra('line', $error->getLine()); $log->addExtra('trace', $error->getTraceAsString()); $log->addExtra('detailedTrace', $error->getTrace()); - $log->addExtra('roles', Authorization::getRoles()); + $log->addExtra('roles', $auth->getRoles()); $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); $log->setAction($action); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 3c17f3ae8c..e86215158b 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -156,7 +156,8 @@ Http::init() ->inject('session') ->inject('servers') ->inject('mode') - ->action(function (App $utopia, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode) { + ->inject('auth') + ->action(function (Http $utopia, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode, Authorization $auth) { $route = $utopia->getRoute(); if ($project->isEmpty()) { @@ -220,8 +221,8 @@ Http::init() throw new Exception(Exception::PROJECT_KEY_EXPIRED); } - Authorization::setRole(Auth::USER_ROLE_APPS); - Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. + $auth->addRole(Auth::USER_ROLE_APPS); + $auth->setDefaultStatus(false); // Cancel security segmentation for API keys. $accessedAt = $key->getAttribute('accessedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCCESS)) > $accessedAt) { @@ -247,10 +248,10 @@ Http::init() } } - Authorization::setRole($role); + $auth->addRole($role); - foreach (Auth::getRoles($user) as $authRole) { - Authorization::setRole($authRole); + foreach (Auth::getRoles($user, $auth) as $authRole) { + $auth->addRole($authRole); } $service = $route->getLabel('sdk.namespace', ''); @@ -258,7 +259,7 @@ Http::init() if ( array_key_exists($service, $project->getAttribute('services', [])) && !$project->getAttribute('services', [])[$service] - && !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles())) + && !(Auth::isPrivilegedUser($auth->getRoles()) || Auth::isAppUser($auth->getRoles())) ) { throw new Exception(Exception::GENERAL_SERVICE_DISABLED); } @@ -311,7 +312,8 @@ Http::init() ->inject('queueForUsage') ->inject('dbForProject') ->inject('mode') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode) use ($databaseListener) { + ->inject('auth') + ->action(function (Http $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode, Authorization $auth) use ($databaseListener) { $route = $utopia->getRoute(); @@ -340,7 +342,7 @@ Http::init() $closestLimit = null; - $roles = Authorization::getRoles(); + $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -421,10 +423,10 @@ Http::init() if ($type === 'bucket') { $bucketId = $parts[1] ?? null; - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAPIKey = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -443,7 +445,7 @@ Http::init() if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -490,7 +492,7 @@ Http::shutdown() ->inject('response') ->inject('project') ->inject('dbForProject') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Database $dbForProject) { + ->action(function (Http $utopia, Request $request, Response $response, Document $project, Database $dbForProject) { $sessionLimit = $project->getAttribute('auths', [])['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT; $session = $response->getPayload(); $userId = $session['userId'] ?? ''; @@ -535,9 +537,10 @@ Http::shutdown() ->inject('queueForFunctions') ->inject('mode') ->inject('dbForConsole') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole) use ($parseLabel) { + ->inject('auth') + ->action(function (Http $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole, Authorization $auth) use ($parseLabel) { if (!empty($user) && !$user->isEmpty() && empty($user->getInternalId())) { - $user = Authorization::skip(fn () => $dbForProject->getDocument('users', $user->getId())); + $user = $auth->skip(fn () => $dbForProject->getDocument('users', $user->getId())); } $responsePayload = $response->getPayload(); @@ -675,11 +678,11 @@ Http::shutdown() ]) ; $signature = md5($data); - $cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key)); + $cacheLog = $auth->skip(fn () => $dbForProject->getDocument('cache', $key)); $accessedAt = $cacheLog->getAttribute('accessedAt', ''); $now = DateTime::now(); if ($cacheLog->isEmpty()) { - Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([ + $auth->skip(fn () => $dbForProject->createDocument('cache', new Document([ '$id' => $key, 'resource' => $resource, 'accessedAt' => $now, @@ -687,7 +690,7 @@ Http::shutdown() ]))); } elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) { $cacheLog->setAttribute('accessedAt', $now); - Authorization::skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog)); + $auth->skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog)); } if ($signature !== $cacheLog->getAttribute('signature')) { diff --git a/app/controllers/shared/api/auth.php b/app/controllers/shared/api/auth.php index 6880f73e81..a8a0ac9e84 100644 --- a/app/controllers/shared/api/auth.php +++ b/app/controllers/shared/api/auth.php @@ -34,7 +34,8 @@ Http::init() ->inject('request') ->inject('project') ->inject('geodb') - ->action(function (App $utopia, Request $request, Document $project, Reader $geodb) { + ->inject('auth') + ->action(function (Http $utopia, Request $request, Document $project, Reader $geodb, Authorization $auth) { $denylist = Http::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', ''); if (!empty($denylist && $project->getId() === 'console')) { $countries = explode(',', $denylist); @@ -47,8 +48,8 @@ Http::init() $route = $utopia->match($request); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAppUser = Auth::isAppUser($auth->getRoles()); if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs return; diff --git a/app/http.php b/app/http.php index 34e6b12c1a..54c6374274 100644 --- a/app/http.php +++ b/app/http.php @@ -251,8 +251,8 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo Http::setResource('pools', fn () => $pools); try { - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); + $auth->cleanRoles(); + $auth->addRole(Role::any()->toString()); $app->run($request, $response); } catch (\Throwable $th) { @@ -293,7 +293,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo $log->addExtra('line', $th->getLine()); $log->addExtra('trace', $th->getTraceAsString()); $log->addExtra('detailedTrace', $th->getTrace()); - $log->addExtra('roles', Authorization::getRoles()); + $log->addExtra('roles', $auth->getRoles()); $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); $log->setAction($action); diff --git a/app/init.php b/app/init.php index ed21d599b9..ca6626223e 100644 --- a/app/init.php +++ b/app/init.php @@ -62,6 +62,8 @@ use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Structure; use Utopia\Domains\Validator\PublicDomain; use Utopia\DSN\DSN; +use Utopia\Http\Request; +use Utopia\Http\Response; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Logger\Logger; @@ -436,7 +438,7 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database->find('sessions', [ + return $database->getAuthorization()->skip(fn () => $database->find('sessions', [ Query::equal('userInternalId', [$document->getInternalId()]), Query::limit(APP_LIMIT_SUBQUERY), ])); @@ -449,7 +451,7 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database + return $database->getAuthorization()->skip(fn () => $database ->find('tokens', [ Query::equal('userInternalId', [$document->getInternalId()]), Query::limit(APP_LIMIT_SUBQUERY), @@ -463,7 +465,7 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database + return $database->getAuthorization()->skip(fn () => $database ->find('challenges', [ Query::equal('userInternalId', [$document->getInternalId()]), Query::limit(APP_LIMIT_SUBQUERY), @@ -477,7 +479,7 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database + return $database->getAuthorization()->skip(fn () => $database ->find('authenticators', [ Query::equal('userInternalId', [$document->getInternalId()]), Query::limit(APP_LIMIT_SUBQUERY), @@ -491,7 +493,7 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database + return $database->getAuthorization()->skip(fn () => $database ->find('memberships', [ Query::equal('userInternalId', [$document->getInternalId()]), Query::limit(APP_LIMIT_SUBQUERY), @@ -583,7 +585,7 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database + return $database->getAuthorization()->skip(fn () => $database ->find('targets', [ Query::equal('userInternalId', [$document->getInternalId()]), Query::limit(APP_LIMIT_SUBQUERY) @@ -597,7 +599,7 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - $targetIds = Authorization::skip(fn () => \array_map( + $targetIds = $database->getAuthorization()->skip(fn () => \array_map( fn ($document) => $document->getAttribute('targetInternalId'), $database->find('subscribers', [ Query::equal('topicInternalId', [$document->getInternalId()]), @@ -1129,15 +1131,9 @@ Http::setResource('clients', function ($request, $console, $project) { return $clients; }, ['request', 'console', 'project']); -Http::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $project */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var string $mode */ +Http::setResource('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $auth) { - Authorization::setDefaultStatus(true); + $auth->setDefaultStatus(true); Auth::setCookieName('a_session_' . $project->getId()); @@ -1201,7 +1197,7 @@ Http::setResource('user', function ($mode, $project, $console, $request, $respon if (APP_MODE_ADMIN === $mode) { if ($user->find('teamId', $project->getAttribute('teamId'), 'memberships')) { - Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. + $auth->setDefaultStatus(false); // Cancel security segmentation for admin users. } else { $user = new Document([]); } @@ -1234,12 +1230,9 @@ Http::setResource('user', function ($mode, $project, $console, $request, $respon $dbForConsole->setMetadata('user', $user->getId()); return $user; -}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); +}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole', 'auth']); -Http::setResource('project', function ($dbForConsole, $request, $console) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var Utopia\Database\Document $console */ +Http::setResource('project', function (Database $dbForConsole, Request $request, Document $console, Authorization $auth) { $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); @@ -1247,10 +1240,10 @@ Http::setResource('project', function ($dbForConsole, $request, $console) { return $console; } - $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); return $project; -}, ['dbForConsole', 'request', 'console']); +}, ['dbForConsole', 'request', 'console', 'auth']); Http::setResource('session', function (Document $user, Document $project) { if ($user->isEmpty()) { @@ -1546,7 +1539,7 @@ Http::setResource('promiseAdapter', function ($register) { return $register->get('promiseAdapter'); }, ['register']); -Http::setResource('schema', function ($utopia, $dbForProject) { +Http::setResource('schema', function (Http $utopia, Database $dbForProject, Authorization $auth) { $complexity = function (int $complexity, array $args) { $queries = Query::parseQueries($args['queries'] ?? []); @@ -1556,8 +1549,8 @@ Http::setResource('schema', function ($utopia, $dbForProject) { return $complexity * $limit; }; - $attributes = function (int $limit, int $offset) use ($dbForProject) { - $attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [ + $attributes = function (int $limit, int $offset) use ($dbForProject, $auth) { + $attrs = $auth->skip(fn () => $dbForProject->find('attributes', [ Query::limit($limit), Query::offset($offset), ])); @@ -1630,7 +1623,7 @@ Http::setResource('schema', function ($utopia, $dbForProject) { $urls, $params, ); -}, ['utopia', 'dbForProject']); +}, ['utopia', 'dbForProject', 'auth']); Http::setResource('contributors', function () { $path = 'app/config/contributors.json'; diff --git a/app/realtime.php b/app/realtime.php index 1fa6733575..e38caf488a 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -25,6 +25,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; +use Utopia\Http\Adapter\FPM\Server as FPMServer; use Utopia\Logger\Log; use Utopia\WebSocket\Adapter; use Utopia\WebSocket\Server; @@ -191,7 +192,8 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume 'value' => '{}' ]); - $statsDocument = Authorization::skip(fn () => $database->createDocument('realtime', $document)); + $auth = new Authorization(); + $statsDocument = $auth->skip(fn () => $database->createDocument('realtime', $document)); break; } catch (Throwable) { Console::warning("Collection not ready. Retrying connection ({$attempts})..."); @@ -220,7 +222,8 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume ->setAttribute('timestamp', DateTime::now()) ->setAttribute('value', json_encode($payload)); - Authorization::skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); + $auth = new Authorization(); + $auth->skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); } catch (Throwable $th) { call_user_func($logError, $th, "updateWorkerDocument"); } finally { @@ -244,7 +247,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $payload = []; - $list = Authorization::skip(fn () => $database->find('realtime', [ + $auth = new Authorization(); + $list = $auth->skip(fn () => $database->find('realtime', [ Query::greaterThan('timestamp', DateTime::addSeconds(new \DateTime(), -15)), ])); @@ -340,12 +344,13 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) { $connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId])); $consoleDatabase = getConsoleDB(); - $project = Authorization::skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); + $auth = new Authorization(); + $project = $auth->skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); $database = getProjectDB($project); $user = $database->getDocument('users', $userId); - $roles = Auth::getRoles($user); + $roles = Auth::getRoles($user, $auth); $realtime->subscribe($projectId, $connection, $roles, $realtime->connections[$connection]['channels']); @@ -389,7 +394,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, }); $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime, $logError) { - $app = new App('UTC'); + $app = new Http(new FPMServer(), 'UTC'); $request = new Request($request); $response = new Response(new SwooleResponse()); @@ -442,7 +447,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription()); } - $roles = Auth::getRoles($user); + $auth = new Authorization(); + $roles = Auth::getRoles($user, $auth); $channels = Realtime::convertChannels($request->getQuery('channels', []), $user->getId()); @@ -502,7 +508,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re $database = getConsoleDB(); if ($projectId !== 'console') { - $project = Authorization::skip(fn () => $database->getDocument('projects', $projectId)); + $auth = new Authorization(); + $project = $auth->skip(fn () => $database->getDocument('projects', $projectId)); $database = getProjectDB($project); } else { $project = null; @@ -554,7 +561,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.'); } - $roles = Auth::getRoles($user); + $roles = Auth::getRoles($user, $auth); $channels = Realtime::convertChannels(array_flip($realtime->connections[$connection]['channels']), $user->getId()); $realtime->subscribe($realtime->connections[$connection]['projectId'], $connection, $roles, $channels); diff --git a/app/worker.php b/app/worker.php index ea1c67734c..e81e0f9f5f 100644 --- a/app/worker.php +++ b/app/worker.php @@ -38,7 +38,7 @@ use Utopia\Storage\Device\Local; global $register; -Authorization::disable(); +$auth->disable(); Runtime::enableCoroutine(SWOOLE_HOOK_ALL); Server::setResource('register', fn () => $register); @@ -228,6 +228,8 @@ Server::setResource('deviceForLocalFiles', function (Document $project) { return new Local(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); }, ['project']); +Server::setResource('authorization', fn () => new Authorization()); + $pools = $register->get('pools'); $platform = new Appwrite(); $args = $_SERVER['argv']; @@ -284,7 +286,8 @@ $worker ->inject('log') ->inject('pools') ->inject('project') - ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($queueName) { + ->inject('auth') + ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project, Authorization $auth) use ($queueName) { $pools->reclaim(); $version = Http::getEnv('_APP_VERSION', 'UNKNOWN'); @@ -306,7 +309,7 @@ $worker $log->addExtra('line', $error->getLine()); $log->addExtra('trace', $error->getTraceAsString()); $log->addExtra('detailedTrace', $error->getTrace()); - $log->addExtra('roles', Authorization::getRoles()); + $log->addExtra('roles', $auth->getRoles()); $isProduction = Http::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); diff --git a/composer.json b/composer.json index c95f4012bc..acb933c7e4 100644 --- a/composer.json +++ b/composer.json @@ -46,10 +46,10 @@ "appwrite/php-runtimes": "0.13.*", "appwrite/php-clamav": "2.0.*", "utopia-php/abuse": "0.37.*", - "utopia-php/analytics": "0.10.*", + "utopia-php/analytics": "dev-feat-framework-v2 as 0.10.99", "utopia-php/audit": "0.39.*", "utopia-php/cache": "0.9.*", - "utopia-php/cli": "0.15.*", + "utopia-php/cli": "0.17.*", "utopia-php/config": "0.2.*", "utopia-php/database": "dev-feat-framework-v2 as 0.49.99", "utopia-php/domains": "0.5.*", @@ -60,10 +60,11 @@ "utopia-php/logger": "0.3.*", "utopia-php/messaging": "0.10.*", "utopia-php/migration": "0.4.*", - "utopia-php/orchestration": "0.9.*", - "utopia-php/platform": "0.5.*", + "utopia-php/orchestration": "dev-feat-framework-v2 as 0.9.99", + "utopia-php/platform": "dev-feat-framework-v2 as 0.5.99", "utopia-php/pools": "0.4.*", - "utopia-php/queue": "0.7.*", + "utopia-php/view": "0.1.*", + "utopia-php/queue": "dev-feat-framework-v2-v2 as 0.7.99", "utopia-php/registry": "0.5.*", "utopia-php/storage": "0.18.*", "utopia-php/vcs": "0.6.*", diff --git a/composer.lock b/composer.lock index 3052c56b4a..a90d6b2d3e 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": "f51e2c69cdc67f3a07c4cf5d91bf5de2", + "content-hash": "98a533604587b5d0f5cc3c52ae177aeb", "packages": [ { "name": "adhocore/jwt", @@ -1270,21 +1270,21 @@ }, { "name": "utopia-php/analytics", - "version": "0.10.2", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + "reference": "5d59c2e50381a25adecbca979ed5a7c81307442f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/5d59c2e50381a25adecbca979ed5a7c81307442f", + "reference": "5d59c2e50381a25adecbca979ed5a7c81307442f", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "^0.15.0" + "utopia-php/cli": "0.17.*" }, "require-dev": { "laravel/pint": "dev-main", @@ -1310,9 +1310,9 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + "source": "https://github.com/utopia-php/analytics/tree/feat-framework-v2" }, - "time": "2023-03-22T12:01:09+00:00" + "time": "2024-03-07T15:54:19+00:00" }, { "name": "utopia-php/audit", @@ -1413,21 +1413,21 @@ }, { "name": "utopia-php/cli", - "version": "0.15.0", + "version": "0.17.0", "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + "reference": "0829fd5215afe88f53f3091cedc808da801fd1bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/0829fd5215afe88f53f3091cedc808da801fd1bb", + "reference": "0829fd5215afe88f53f3091cedc808da801fd1bb", "shasum": "" }, "require": { "php": ">=7.4", - "utopia-php/framework": "0.*.*" + "utopia-php/framework": "0.34.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -1456,9 +1456,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" + "source": "https://github.com/utopia-php/cli/tree/0.17.0" }, - "time": "2023-03-01T05:55:14+00:00" + "time": "2024-01-24T11:37:29+00:00" }, { "name": "utopia-php/config", @@ -1517,12 +1517,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "79fd5790227e039832372f2c6c9c284f6f1284bb" + "reference": "bdd9140e40c77faadb0cf2f5050b466c34eef673" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/79fd5790227e039832372f2c6c9c284f6f1284bb", - "reference": "79fd5790227e039832372f2c6c9c284f6f1284bb", + "url": "https://api.github.com/repos/utopia-php/database/zipball/bdd9140e40c77faadb0cf2f5050b466c34eef673", + "reference": "bdd9140e40c77faadb0cf2f5050b466c34eef673", "shasum": "" }, "require": { @@ -1530,6 +1530,7 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.9.*", + "utopia-php/fetch": "0.1.*", "utopia-php/framework": "0.34.*", "utopia-php/mongo": "0.3.*" }, @@ -1565,7 +1566,7 @@ "issues": "https://github.com/utopia-php/database/issues", "source": "https://github.com/utopia-php/database/tree/feat-framework-v2" }, - "time": "2024-02-27T10:05:08+00:00" + "time": "2024-03-07T16:55:44+00:00" }, { "name": "utopia-php/domains", @@ -1674,6 +1675,45 @@ }, "time": "2023-11-02T12:01:43+00:00" }, + { + "name": "utopia-php/fetch", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/fetch.git", + "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/fetch/zipball/2fa214b9262acd1a3583515a364da4f35929d5c5", + "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.5.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Fetch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "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.1.0" + }, + "time": "2023-10-10T11:58:32+00:00" + }, { "name": "utopia-php/framework", "version": "0.34.2", @@ -2034,21 +2074,21 @@ }, { "name": "utopia-php/orchestration", - "version": "0.9.1", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + "reference": "ede2b7a60bd1630ef8bd7fdbbc3cf73275fc9f28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/ede2b7a60bd1630ef8bd7fdbbc3cf73275fc9f28", + "reference": "ede2b7a60bd1630ef8bd7fdbbc3cf73275fc9f28", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*" + "utopia-php/cli": "0.17.*" }, "require-dev": { "laravel/pint": "^1.2", @@ -2078,30 +2118,31 @@ ], "support": { "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + "source": "https://github.com/utopia-php/orchestration/tree/feat-framework-v2" }, - "time": "2023-03-17T15:05:06+00:00" + "time": "2024-03-07T15:56:18+00:00" }, { "name": "utopia-php/platform", - "version": "0.5.1", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "3eceef0b6593fe0f7d2efd36d40402a395a4c285" + "reference": "88711a2992ff0edbf196cdf1f48b5614e1e423f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/3eceef0b6593fe0f7d2efd36d40402a395a4c285", - "reference": "3eceef0b6593fe0f7d2efd36d40402a395a4c285", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/88711a2992ff0edbf196cdf1f48b5614e1e423f7", + "reference": "88711a2992ff0edbf196cdf1f48b5614e1e423f7", "shasum": "" }, "require": { "ext-json": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" + "utopia-php/cli": "0.17.*", + "utopia-php/framework": "0.34.*", + "utopia-php/queue": "dev-feat-framework-v2-v2 as 0.7.99" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2127,9 +2168,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.5.1" + "source": "https://github.com/utopia-php/platform/tree/feat-framework-v2" }, - "time": "2023-12-26T16:14:41+00:00" + "time": "2024-03-07T15:44:09+00:00" }, { "name": "utopia-php/pools", @@ -2184,22 +2225,22 @@ }, { "name": "utopia-php/queue", - "version": "0.7.0", + "version": "dev-feat-framework-v2-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "917565256eb94bcab7246f7a746b1a486813761b" + "reference": "e613ccc1d4da4219b60576ddfe79dadb182bb74e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/917565256eb94bcab7246f7a746b1a486813761b", - "reference": "917565256eb94bcab7246f7a746b1a486813761b", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/e613ccc1d4da4219b60576ddfe79dadb182bb74e", + "reference": "e613ccc1d4da4219b60576ddfe79dadb182bb74e", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" + "utopia-php/cli": "0.17.*", + "utopia-php/framework": "0.34.*" }, "require-dev": { "laravel/pint": "^0.2.3", @@ -2239,9 +2280,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.7.0" + "source": "https://github.com/utopia-php/queue/tree/feat-framework-v2-v2" }, - "time": "2024-01-17T19:00:43+00:00" + "time": "2024-03-07T15:43:35+00:00" }, { "name": "utopia-php/registry", @@ -2455,6 +2496,49 @@ }, "time": "2024-01-08T17:11:12+00:00" }, + { + "name": "utopia-php/view", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/view.git", + "reference": "013a495af4e625df172d9bd534011014cb32bbab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/view/zipball/013a495af4e625df172d9bd534011014cb32bbab", + "reference": "013a495af4e625df172d9bd534011014cb32bbab", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\View\\": "src/View" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP rendering engine", + "keywords": [ + "php", + "view" + ], + "support": { + "issues": "https://github.com/utopia-php/view/issues", + "source": "https://github.com/utopia-php/view/tree/0.1.0" + }, + "time": "2023-09-10T12:07:26+00:00" + }, { "name": "utopia-php/websocket", "version": "0.1.0", @@ -5371,58 +5455,47 @@ } ], "time": "2023-11-21T18:54:41+00:00" - }, - { - "name": "utopia-php/fetch", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/fetch.git", - "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/fetch/zipball/2fa214b9262acd1a3583515a364da4f35929d5c5", - "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "^1.5.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Fetch\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "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.1.0" - }, - "time": "2023-10-10T11:58:32+00:00" } ], "aliases": [ + { + "package": "utopia-php/analytics", + "version": "dev-feat-framework-v2", + "alias": "0.10.99", + "alias_normalized": "0.10.99.0" + }, { "package": "utopia-php/database", "version": "dev-feat-framework-v2", "alias": "0.49.99", "alias_normalized": "0.49.99.0" + }, + { + "package": "utopia-php/orchestration", + "version": "dev-feat-framework-v2", + "alias": "0.9.99", + "alias_normalized": "0.9.99.0" + }, + { + "package": "utopia-php/platform", + "version": "dev-feat-framework-v2", + "alias": "0.5.99", + "alias_normalized": "0.5.99.0" + }, + { + "package": "utopia-php/queue", + "version": "dev-feat-framework-v2-v2", + "alias": "0.7.99", + "alias_normalized": "0.7.99.0" } ], "minimum-stability": "stable", "stability-flags": { + "utopia-php/analytics": 20, "utopia-php/database": 20, + "utopia-php/orchestration": 20, + "utopia-php/platform": 20, + "utopia-php/queue": 20, "appwrite/sdk-generator": 5 }, "prefer-stable": false, diff --git a/src/Appwrite/Auth/Auth.php b/src/Appwrite/Auth/Auth.php index 1e8109622e..291c16bf0d 100644 --- a/src/Appwrite/Auth/Auth.php +++ b/src/Appwrite/Auth/Auth.php @@ -439,13 +439,14 @@ class Auth * Returns all roles for a user. * * @param Document $user + * @param Authorization $auth * @return array */ - public static function getRoles(Document $user): array + public static function getRoles(Document $user, Authorization $auth): array { $roles = []; - if (!self::isPrivilegedUser(Authorization::getRoles()) && !self::isAppUser(Authorization::getRoles())) { + if (!self::isPrivilegedUser($auth->getRoles()) && !self::isAppUser($auth->getRoles())) { if ($user->getId()) { $roles[] = Role::user($user->getId())->toString(); $roles[] = Role::users()->toString(); diff --git a/src/Appwrite/Auth/Validator/Password.php b/src/Appwrite/Auth/Validator/Password.php index bfe5577889..913701f7a3 100644 --- a/src/Appwrite/Auth/Validator/Password.php +++ b/src/Appwrite/Auth/Validator/Password.php @@ -2,7 +2,7 @@ namespace Appwrite\Auth\Validator; -use Utopia\Validator; +use Utopia\Http\Validator; /** * Password. diff --git a/src/Appwrite/Auth/Validator/Phone.php b/src/Appwrite/Auth/Validator/Phone.php index 26aa687278..d5f6df60c8 100644 --- a/src/Appwrite/Auth/Validator/Phone.php +++ b/src/Appwrite/Auth/Validator/Phone.php @@ -2,7 +2,7 @@ namespace Appwrite\Auth\Validator; -use Utopia\Validator; +use Utopia\Http\Validator; /** * Phone. diff --git a/src/Appwrite/Event/Validator/Event.php b/src/Appwrite/Event/Validator/Event.php index 2061d53ed8..a63d6f9da2 100644 --- a/src/Appwrite/Event/Validator/Event.php +++ b/src/Appwrite/Event/Validator/Event.php @@ -3,7 +3,7 @@ namespace Appwrite\Event\Validator; use Utopia\Config\Config; -use Utopia\Validator; +use Utopia\Http\Validator; class Event extends Validator { diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index 69b153ef72..31f1ce45b4 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -20,7 +20,7 @@ class Resolvers * @return callable */ public static function api( - App $utopia, + Http $utopia, ?Route $route, ): callable { return static fn ($type, $args, $context, $info) => new Swoole( @@ -67,7 +67,7 @@ class Resolvers * @return callable */ public static function document( - App $utopia, + Http $utopia, string $databaseId, string $collectionId, string $methodType, @@ -89,7 +89,7 @@ class Resolvers * @return callable */ public static function documentGet( - App $utopia, + Http $utopia, string $databaseId, string $collectionId, callable $url, @@ -119,7 +119,7 @@ class Resolvers * @return callable */ public static function documentList( - App $utopia, + Http $utopia, string $databaseId, string $collectionId, callable $url, @@ -155,7 +155,7 @@ class Resolvers * @return callable */ public static function documentCreate( - App $utopia, + Http $utopia, string $databaseId, string $collectionId, callable $url, @@ -187,7 +187,7 @@ class Resolvers * @return callable */ public static function documentUpdate( - App $utopia, + Http $utopia, string $databaseId, string $collectionId, callable $url, @@ -218,7 +218,7 @@ class Resolvers * @return callable */ public static function documentDelete( - App $utopia, + Http $utopia, string $databaseId, string $collectionId, callable $url, @@ -249,7 +249,7 @@ class Resolvers * @throws Exception */ private static function resolve( - App $utopia, + Http $utopia, Request $request, Response $response, callable $resolve, diff --git a/src/Appwrite/GraphQL/Schema.php b/src/Appwrite/GraphQL/Schema.php index 2399452797..49544a7c93 100644 --- a/src/Appwrite/GraphQL/Schema.php +++ b/src/Appwrite/GraphQL/Schema.php @@ -26,7 +26,7 @@ class Schema * @throws Exception */ public static function build( - App $utopia, + Http $utopia, callable $complexity, callable $attributes, array $urls, @@ -85,7 +85,7 @@ class Schema * @return array * @throws Exception */ - protected static function api(App $utopia, callable $complexity): array + protected static function api(Http $utopia, callable $complexity): array { Mapper::init($utopia ->getResource('response') @@ -143,7 +143,7 @@ class Schema * @throws \Exception */ protected static function collections( - App $utopia, + Http $utopia, callable $complexity, callable $attributes, array $urls, diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index 3f50bd4967..d81d8ea885 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -10,7 +10,7 @@ use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\UnionType; use Utopia\Http\Http; use Utopia\Http\Route; -use Utopia\Validator; +use Utopia\Http\Validator; use Utopia\Http\Validator\Nullable; class Mapper @@ -75,7 +75,7 @@ class Mapper } public static function route( - App $utopia, + Http $utopia, Route $route, callable $complexity ): iterable { @@ -213,7 +213,7 @@ class Mapper * @throws Exception */ public static function param( - App $utopia, + Http $utopia, Validator|callable $validator, bool $required, array $injections diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 646fa8b8fe..ac9fc164d5 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -86,10 +86,10 @@ abstract class Migration */ protected array $collections; - public function __construct() + public function __construct(Authorization $auth) { - Authorization::disable(); - Authorization::setDefaultStatus(false); + $auth->disable(); + $auth->setDefaultStatus(false); $this->collections = Config::getParam('collections', []); diff --git a/src/Appwrite/Network/Validator/CNAME.php b/src/Appwrite/Network/Validator/CNAME.php index e1ae061c84..e9e2b586a5 100644 --- a/src/Appwrite/Network/Validator/CNAME.php +++ b/src/Appwrite/Network/Validator/CNAME.php @@ -2,7 +2,7 @@ namespace Appwrite\Network\Validator; -use Utopia\Validator; +use Utopia\Http\Validator; class CNAME extends Validator { diff --git a/src/Appwrite/Network/Validator/Email.php b/src/Appwrite/Network/Validator/Email.php index 3209a4aada..bae0ff0bbf 100644 --- a/src/Appwrite/Network/Validator/Email.php +++ b/src/Appwrite/Network/Validator/Email.php @@ -2,14 +2,14 @@ namespace Appwrite\Network\Validator; -use Utopia\Validator; +use Utopia\Http\Validator; /** * Email * * Validate that an variable is a valid email address * - * @package Utopia\Validator + * @package Utopia\Http\Validator */ class Email extends Validator { diff --git a/src/Appwrite/Network/Validator/Origin.php b/src/Appwrite/Network/Validator/Origin.php index e64f42ce6d..4d696735bf 100644 --- a/src/Appwrite/Network/Validator/Origin.php +++ b/src/Appwrite/Network/Validator/Origin.php @@ -2,7 +2,7 @@ namespace Appwrite\Network\Validator; -use Utopia\Validator; +use Utopia\Http\Validator; use Utopia\Http\Validator\Hostname; class Origin extends Validator diff --git a/src/Appwrite/Platform/Tasks/CalcTierStats.php b/src/Appwrite/Platform/Tasks/CalcTierStats.php index 7256047c35..137a497ae3 100644 --- a/src/Appwrite/Platform/Tasks/CalcTierStats.php +++ b/src/Appwrite/Platform/Tasks/CalcTierStats.php @@ -73,13 +73,14 @@ class CalcTierStats extends Action ->inject('dbForConsole') ->inject('getProjectDB') ->inject('register') - ->callback(function ($after, $projectId, Group $pools, Cache $cache, Database $dbForConsole, callable $getProjectDB, Registry $register) { - $this->action($after, $projectId, $pools, $cache, $dbForConsole, $getProjectDB, $register); + ->inject('auth') + ->callback(function ($after, $projectId, Group $pools, Cache $cache, Database $dbForConsole, callable $getProjectDB, Registry $register, Authorization $auth) { + $this->action($after, $projectId, $pools, $cache, $dbForConsole, $getProjectDB, $register, $auth); }); } - public function action(string $after, string $projectId, Group $pools, Cache $cache, Database $dbForConsole, callable $getProjectDB, Registry $register): void + public function action(string $after, string $projectId, Group $pools, Cache $cache, Database $dbForConsole, callable $getProjectDB, Registry $register, Authorization $auth): void { //docker compose exec -t appwrite calc-tier-stats @@ -97,7 +98,7 @@ class CalcTierStats extends Action console::log("Project " . $projectId); $project = $dbForConsole->getDocument('projects', $projectId); $dbForProject = call_user_func($getProjectDB, $project); - $data = $this->getData($project, $dbForConsole, $dbForProject); + $data = $this->getData($project, $dbForConsole, $dbForProject, $auth); $csv->insertOne($data); $this->sendMail($register); @@ -121,12 +122,12 @@ class CalcTierStats extends Action Console::info("Iterating all projects"); } - $this->foreachDocument($dbForConsole, 'projects', $queries, function (Document $project) use ($getProjectDB, $dbForConsole, $csv) { + $this->foreachDocument($dbForConsole, 'projects', $queries, function (Document $project) use ($getProjectDB, $dbForConsole, $csv, $auth) { $projectId = $project->getId(); console::log("Project " . $projectId); try { $dbForProject = call_user_func($getProjectDB, $project); - $data = $this->getData($project, $dbForConsole, $dbForProject); + $data = $this->getData($project, $dbForConsole, $dbForProject, $auth); $csv->insertOne($data); } catch (\Throwable $th) { Console::error("Unexpected error occured with Project ID {$projectId}"); @@ -204,7 +205,7 @@ class CalcTierStats extends Action } - private function getData(Document $project, Database $dbForConsole, Database $dbForProject): array + private function getData(Document $project, Database $dbForConsole, Database $dbForProject, Authorization $auth): array { $stats['Project ID'] = $project->getId(); $stats['Organization ID'] = $project->getAttribute('teamId', null); @@ -263,7 +264,7 @@ class CalcTierStats extends Action $tmp = []; $metrics = $this->usageStats; - Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$tmp) { + $auth->skip(function () use ($dbForProject, $periods, $range, $metrics, &$tmp) { foreach ($metrics as $metric => $name) { $limit = $periods[$range]['limit']; $period = $periods[$range]['period']; diff --git a/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php b/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php index c2d4128b72..bcccf8dcb0 100644 --- a/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php +++ b/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php @@ -9,6 +9,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; +use Utopia\Http\Adapter\FPM\Server; use Utopia\Platform\Action; use Utopia\Pools\Group; use Utopia\Registry\Registry; @@ -58,7 +59,7 @@ class DeleteOrphanedProjects extends Action ], $collectionsConfig); /* Initialise new Utopia app */ - $app = new App('UTC'); + $app = new Http(new Server(), 'UTC'); $console = $app->getResource('console'); $projects = [$console]; @@ -123,7 +124,7 @@ class DeleteOrphanedProjects extends Action $dbForConsole->deleteDocument('projects', $project->getId()); $dbForConsole->purgeCachedDocument('projects', $project->getId()); - if ($dbForProject->exists($dbForProject->getDefaultDatabase(), Database::METADATA)) { + if ($dbForProject->exists($dbForProject->getDatabase(), Database::METADATA)) { try { $dbForProject->deleteCollection(Database::METADATA); $dbForProject->purgeCachedCollection(Database::METADATA); diff --git a/src/Appwrite/Platform/Tasks/GetMigrationStats.php b/src/Appwrite/Platform/Tasks/GetMigrationStats.php index ebd00e8dc1..c525a92b20 100644 --- a/src/Appwrite/Platform/Tasks/GetMigrationStats.php +++ b/src/Appwrite/Platform/Tasks/GetMigrationStats.php @@ -10,6 +10,7 @@ use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Query; +use Utopia\Http\Adapter\FPM\Server; use Utopia\Platform\Action; use Utopia\Pools\Group; use Utopia\Registry\Registry; @@ -63,7 +64,7 @@ class GetMigrationStats extends Action Console::success(APP_NAME . ' Migration stats calculation has started'); /* Initialise new Utopia app */ - $app = new App('UTC'); + $app = new Http(new Server(), 'UTC'); $console = $app->getResource('console'); /** CSV stuff */ @@ -101,7 +102,7 @@ class GetMigrationStats extends Action ->getResource(); $dbForProject = new Database($adapter, $cache); - $dbForProject->setDefaultDatabase('appwrite'); + $dbForProject->setDatabase('appwrite'); $dbForProject->setNamespace('_' . $project->getInternalId()); /** Get Project ID */ diff --git a/src/Appwrite/Platform/Tasks/Migrate.php b/src/Appwrite/Platform/Tasks/Migrate.php index c322f17a55..ad158b1b42 100644 --- a/src/Appwrite/Platform/Tasks/Migrate.php +++ b/src/Appwrite/Platform/Tasks/Migrate.php @@ -10,6 +10,7 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; +use Utopia\Http\Adapter\FPM\Server; use Utopia\Platform\Action; use Utopia\Registry\Registry; use Utopia\Http\Validator\Text; @@ -31,7 +32,8 @@ class Migrate extends Action ->inject('dbForConsole') ->inject('getProjectDB') ->inject('register') - ->callback(fn ($version, $cache, $dbForConsole, $getProjectDB, Registry $register) => $this->action($version, $cache, $dbForConsole, $getProjectDB, $register)); + ->inject('auth') + ->callback(fn ($version, $cache, $dbForConsole, $getProjectDB, Registry $register, Authorization $auth) => $this->action($version, $cache, $dbForConsole, $getProjectDB, $register, $auth)); } private function clearProjectsCache(Cache $cache, Document $project) @@ -43,16 +45,16 @@ class Migrate extends Action } } - public function action(string $version, Cache $cache, Database $dbForConsole, callable $getProjectDB, Registry $register) + public function action(string $version, Cache $cache, Database $dbForConsole, callable $getProjectDB, Registry $register, Authorization $auth) { - Authorization::disable(); + $auth->disable(); if (!array_key_exists($version, Migration::$versions)) { Console::error("Version {$version} not found."); Console::exit(1); return; } - $app = new App('UTC'); + $app = new Http(new Server(), 'UTC'); Console::success('Starting Data Migration to version ' . $version); diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 1c79fee034..04c4f225e4 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -7,6 +7,7 @@ use Appwrite\Specification\Format\Swagger2; use Appwrite\Specification\Specification; use Appwrite\Utopia\Response; use Exception; +use Swoole\Http\Request; use Swoole\Http\Response as HttpResponse; use Utopia\Http\Http; use Utopia\Cache\Adapter\None; @@ -15,9 +16,9 @@ use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; +use Utopia\Http\Adapter\FPM\Server; use Utopia\Platform\Action; use Utopia\Registry\Registry; -use Utopia\Request; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; @@ -248,7 +249,7 @@ class Specs extends Action } } - $arguments = [new App('UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0]; + $arguments = [new Http(new Server(), 'UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0]; foreach (['swagger2', 'open-api3'] as $format) { $formatInstance = match ($format) { 'swagger2' => new Swagger2(...$arguments), diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index a0cf080c86..723af17a38 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -52,7 +52,8 @@ class Builds extends Action ->inject('dbForProject') ->inject('deviceForFunctions') ->inject('log') - ->callback(fn ($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $deviceForFunctions, $log)); + ->inject('auth') + ->callback(fn ($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log, Authorization $auth) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $deviceForFunctions, $log, $auth)); } /** @@ -65,10 +66,11 @@ class Builds extends Action * @param Database $dbForProject * @param Device $deviceForFunctions * @param Log $log + * @param Authorization $auth * @return void * @throws \Utopia\Database\Exception */ - public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log): void + public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log, Authorization $auth): void { $payload = $message->getPayload() ?? []; @@ -90,7 +92,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, $queueForUsage, $dbForConsole, $dbForProject, $github, $project, $resource, $deployment, $template, $log); + $this->buildDeployment($deviceForFunctions, $queueForFunctions, $queueForEvents, $queueForUsage, $dbForConsole, $dbForProject, $github, $project, $resource, $deployment, $template, $log, $auth); break; default: @@ -111,11 +113,12 @@ class Builds extends Action * @param Document $deployment * @param Document $template * @param Log $log + * @param Authorization $auth * @return void * @throws \Utopia\Database\Exception * @throws Exception */ - protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, Log $log): void + protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, Log $log, Authorization $auth): void { $executor = new Executor(Http::getEnv('_APP_EXECUTOR_HOST')); @@ -502,7 +505,7 @@ class Builds extends Action ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); } catch (\Throwable $th) { $endTime = DateTime::now(); $durationEnd = \microtime(true); diff --git a/src/Appwrite/Platform/Workers/Hamster.php b/src/Appwrite/Platform/Workers/Hamster.php index 3f7bd86e8a..817291dff1 100644 --- a/src/Appwrite/Platform/Workers/Hamster.php +++ b/src/Appwrite/Platform/Workers/Hamster.php @@ -4,6 +4,7 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\Hamster as EventHamster; use Appwrite\Network\Validator\Origin; +use PharIo\Manifest\Author; use Utopia\Analytics\Adapter\Mixpanel; use Utopia\Analytics\Event as AnalyticsEvent; use Utopia\Http\Http; @@ -53,7 +54,8 @@ class Hamster extends Action ->inject('pools') ->inject('cache') ->inject('dbForConsole') - ->callback(fn (Message $message, Group $pools, Cache $cache, Database $dbForConsole) => $this->action($message, $pools, $cache, $dbForConsole)); + ->inject('auth') + ->callback(fn (Message $message, Group $pools, Cache $cache, Database $dbForConsole, Authorization $auth) => $this->action($message, $pools, $cache, $dbForConsole, $auth)); } /** @@ -65,7 +67,7 @@ class Hamster extends Action * @return void * @throws \Utopia\Database\Exception */ - public function action(Message $message, Group $pools, Cache $cache, Database $dbForConsole): void + public function action(Message $message, Group $pools, Cache $cache, Database $dbForConsole, Authorization $auth): void { $token = Http::getEnv('_APP_MIXPANEL_TOKEN', ''); if (empty($token)) { @@ -83,7 +85,7 @@ class Hamster extends Action switch ($type) { case EventHamster::TYPE_PROJECT: - $this->getStatsForProject(new Document($payload['project']), $pools, $cache, $dbForConsole); + $this->getStatsForProject(new Document($payload['project']), $pools, $cache, $dbForConsole, $auth); break; case EventHamster::TYPE_ORGANISATION: $this->getStatsForOrganization(new Document($payload['organization']), $dbForConsole); @@ -101,7 +103,7 @@ class Hamster extends Action * @param Database $dbForConsole * @throws \Utopia\Database\Exception */ - private function getStatsForProject(Document $project, Group $pools, Cache $cache, Database $dbForConsole): void + private function getStatsForProject(Document $project, Group $pools, Cache $cache, Database $dbForConsole, Authorization $auth): void { /** * Skip user projects with id 'console' @@ -121,7 +123,7 @@ class Hamster extends Action ->getResource(); $dbForProject = new Database($adapter, $cache); - $dbForProject->setDefaultDatabase('appwrite'); + $dbForProject->setDatabase('appwrite'); $dbForProject->setNamespace('_' . $project->getInternalId()); $statsPerProject = []; @@ -279,7 +281,7 @@ class Hamster extends Action ], ]; - Authorization::skip(function () use ($dbForProject, $periods, &$statsPerProject) { + $auth->skip(function () use ($dbForProject, $periods, &$statsPerProject) { foreach ($this->metrics as $key => $metric) { foreach ($periods as $periodKey => $periodValue) { $limit = $periodValue['limit']; diff --git a/src/Appwrite/Specification/Format.php b/src/Appwrite/Specification/Format.php index 26d8e5d73f..fdb4841a93 100644 --- a/src/Appwrite/Specification/Format.php +++ b/src/Appwrite/Specification/Format.php @@ -9,7 +9,7 @@ use Utopia\Http\Route; abstract class Format { - protected App $app; + protected Http $app; /** * @var Route[] @@ -50,7 +50,7 @@ abstract class Format ] ]; - public function __construct(App $app, array $services, array $routes, array $models, array $keys, int $authCount) + public function __construct(Http $app, array $services, array $routes, array $models, array $keys, int $authCount) { $this->app = $app; $this->services = $services; diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index b845bc001b..5ee3011ebd 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -7,7 +7,7 @@ use Appwrite\Template\Template; use Appwrite\Utopia\Response\Model; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; -use Utopia\Validator; +use Utopia\Http\Validator; use Utopia\Http\Validator\ArrayList; use Utopia\Http\Validator\Nullable; use Utopia\Http\Validator\Range; @@ -273,7 +273,7 @@ class OpenAPI3 extends Format foreach ($route->getParams() as $name => $param) { // Set params /** - * @var \Utopia\Validator $validator + * @var \Utopia\Http\Validator $validator */ $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator']; diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 82dedaef57..d75568976c 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -7,7 +7,7 @@ use Appwrite\Template\Template; use Appwrite\Utopia\Response\Model; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; -use Utopia\Validator; +use Utopia\Http\Validator; use Utopia\Http\Validator\ArrayList; use Utopia\Http\Validator\Nullable; use Utopia\Http\Validator\Range; diff --git a/src/Appwrite/Task/Validator/Cron.php b/src/Appwrite/Task/Validator/Cron.php index 03bd1c5220..afa19c50a6 100644 --- a/src/Appwrite/Task/Validator/Cron.php +++ b/src/Appwrite/Task/Validator/Cron.php @@ -3,7 +3,7 @@ namespace Appwrite\Task\Validator; use Cron\CronExpression; -use Utopia\Validator; +use Utopia\Http\Validator; class Cron extends Validator { diff --git a/src/Appwrite/Utopia/Database/Validator/CompoundUID.php b/src/Appwrite/Utopia/Database/Validator/CompoundUID.php index 3f23500952..b851d8ba85 100644 --- a/src/Appwrite/Utopia/Database/Validator/CompoundUID.php +++ b/src/Appwrite/Utopia/Database/Validator/CompoundUID.php @@ -3,7 +3,7 @@ namespace Appwrite\Utopia\Database\Validator; use Utopia\Database\Validator\UID; -use Utopia\Validator; +use Utopia\Http\Validator; class CompoundUID extends Validator { diff --git a/src/Appwrite/Utopia/Database/Validator/ProjectId.php b/src/Appwrite/Utopia/Database/Validator/ProjectId.php index 46b0cdf53e..28abe176fe 100644 --- a/src/Appwrite/Utopia/Database/Validator/ProjectId.php +++ b/src/Appwrite/Utopia/Database/Validator/ProjectId.php @@ -2,7 +2,7 @@ namespace Appwrite\Utopia\Database\Validator; -use Utopia\Validator; +use Utopia\Http\Validator; class ProjectId extends Validator { diff --git a/src/Appwrite/Utopia/Request.php b/src/Appwrite/Utopia/Request.php index a711afb283..ee488d6a13 100644 --- a/src/Appwrite/Utopia/Request.php +++ b/src/Appwrite/Utopia/Request.php @@ -4,19 +4,13 @@ namespace Appwrite\Utopia; use Appwrite\Utopia\Request\Filter; use Utopia\Http\Adapter\Swoole\Request as SwooleRequest; -use Utopia\Http\Request as HttpRequest; use Utopia\Http\Route; -class Request extends HttpRequest +class Request extends SwooleRequest { private static ?Filter $filter = null; private static ?Route $route = null; - public function __construct(SwooleRequest $request) - { - parent::__construct($request); - } - /** * @inheritdoc */ diff --git a/src/Appwrite/Utopia/View.php b/src/Appwrite/Utopia/View.php index e4ed8164c9..60cafb1ca8 100644 --- a/src/Appwrite/Utopia/View.php +++ b/src/Appwrite/Utopia/View.php @@ -2,7 +2,7 @@ namespace Appwrite\Utopia; -use Utopia\View as OldView; +use Utopia\View\View as OldView; class View extends OldView { diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php index ca8753f374..877e66cb03 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php @@ -17,6 +17,13 @@ class DatabasesPermissionsGuestTest extends Scope use SideClient; use DatabasesPermissionsScope; + protected Authorization $auth; + + public function setUp(): void + { + $this->auth = new Authorization(); + } + public function createCollection(): array { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ @@ -111,8 +118,8 @@ class DatabasesPermissionsGuestTest extends Scope $this->assertEquals(201, $publicResponse['headers']['status-code']); $this->assertEquals(201, $privateResponse['headers']['status-code']); - $roles = Authorization::getRoles(); - Authorization::cleanRoles(); + $roles = $this->auth->getRoles(); + $this->auth->cleanRoles(); $publicDocuments = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', [ 'content-type' => 'application/json', @@ -134,7 +141,7 @@ class DatabasesPermissionsGuestTest extends Scope } foreach ($roles as $role) { - Authorization::setRole($role); + $this->auth->addRole($role); } } @@ -145,8 +152,8 @@ class DatabasesPermissionsGuestTest extends Scope $privateCollectionId = $data['privateCollectionId']; $databaseId = $data['databaseId']; - $roles = Authorization::getRoles(); - Authorization::cleanRoles(); + $roles = $this->auth->getRoles(); + $this->auth->cleanRoles(); $publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', [ 'content-type' => 'application/json', @@ -222,7 +229,7 @@ class DatabasesPermissionsGuestTest extends Scope $this->assertEquals(401, $privateDocument['headers']['status-code']); foreach ($roles as $role) { - Authorization::setRole($role); + $this->auth->addRole($role); } } diff --git a/tests/unit/Auth/AuthTest.php b/tests/unit/Auth/AuthTest.php index 705da42879..2bbe690c02 100644 --- a/tests/unit/Auth/AuthTest.php +++ b/tests/unit/Auth/AuthTest.php @@ -13,13 +13,20 @@ use Utopia\Database\Validator\Roles; class AuthTest extends TestCase { + protected Authorization $auth; + + public function setUp(): void + { + $this->auth = new Authorization(); + } + /** * Reset Roles */ public function tearDown(): void { - Authorization::cleanRoles(); - Authorization::setRole(Role::any()->toString()); + $this->auth->cleanRoles(); + $this->auth->addRole(Role::any()->toString()); } public function testCookieName(): void @@ -347,7 +354,7 @@ class AuthTest extends TestCase '$id' => '' ]); - $roles = Auth::getRoles($user); + $roles = Auth::getRoles($user, $this->auth); $this->assertCount(1, $roles); $this->assertContains(Role::guests()->toString(), $roles); } @@ -383,7 +390,7 @@ class AuthTest extends TestCase ] ]); - $roles = Auth::getRoles($user); + $roles = Auth::getRoles($user, $this->auth); $this->assertCount(13, $roles); $this->assertContains(Role::users()->toString(), $roles); @@ -404,21 +411,21 @@ class AuthTest extends TestCase $user['emailVerification'] = false; $user['phoneVerification'] = false; - $roles = Auth::getRoles($user); + $roles = Auth::getRoles($user, $this->auth); $this->assertContains(Role::users(Roles::DIMENSION_UNVERIFIED)->toString(), $roles); $this->assertContains(Role::user(ID::custom('123'), Roles::DIMENSION_UNVERIFIED)->toString(), $roles); // Enable single verification type $user['emailVerification'] = true; - $roles = Auth::getRoles($user); + $roles = Auth::getRoles($user, $this->auth); $this->assertContains(Role::users(Roles::DIMENSION_VERIFIED)->toString(), $roles); $this->assertContains(Role::user(ID::custom('123'), Roles::DIMENSION_VERIFIED)->toString(), $roles); } public function testPrivilegedUserRoles(): void { - Authorization::setRole(Auth::USER_ROLE_OWNER); + $this->auth->addRole(Auth::USER_ROLE_OWNER); $user = new Document([ '$id' => ID::custom('123'), 'emailVerification' => true, @@ -444,7 +451,7 @@ class AuthTest extends TestCase ] ]); - $roles = Auth::getRoles($user); + $roles = Auth::getRoles($user, $this->auth); $this->assertCount(7, $roles); $this->assertNotContains(Role::users()->toString(), $roles); @@ -462,7 +469,7 @@ class AuthTest extends TestCase public function testAppUserRoles(): void { - Authorization::setRole(Auth::USER_ROLE_APPS); + $this->auth->addRole(Auth::USER_ROLE_APPS); $user = new Document([ '$id' => ID::custom('123'), 'memberships' => [ @@ -486,7 +493,7 @@ class AuthTest extends TestCase ] ]); - $roles = Auth::getRoles($user); + $roles = Auth::getRoles($user, $this->auth); $this->assertCount(7, $roles); $this->assertNotContains(Role::users()->toString(), $roles); diff --git a/tests/unit/Messaging/MessagingChannelsTest.php b/tests/unit/Messaging/MessagingChannelsTest.php index 8ba0374093..2f0443c34f 100644 --- a/tests/unit/Messaging/MessagingChannelsTest.php +++ b/tests/unit/Messaging/MessagingChannelsTest.php @@ -6,8 +6,10 @@ use Appwrite\Auth\Auth; use Appwrite\Messaging\Adapter\Realtime; use PHPUnit\Framework\TestCase; use Utopia\Database\Document; +use Utopia\Database\Exception\Authorization; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; +use Utopia\Database\Validator\Authorization as ValidatorAuthorization; class MessagingChannelsTest extends TestCase { @@ -34,8 +36,12 @@ class MessagingChannelsTest extends TestCase 'functions.1', ]; + protected ValidatorAuthorization $auth; + public function setUp(): void { + $this->auth = new ValidatorAuthorization(); + /** * Setup global Counts */ @@ -66,7 +72,7 @@ class MessagingChannelsTest extends TestCase ] ]); - $roles = Auth::getRoles($user); + $roles = Auth::getRoles($user, $this->auth); $parsedChannels = Realtime::convertChannels([0 => $channel], $user->getId()); @@ -90,7 +96,7 @@ class MessagingChannelsTest extends TestCase '$id' => '' ]); - $roles = Auth::getRoles($user); + $roles = Auth::getRoles($user, $this->auth); $parsedChannels = Realtime::convertChannels([0 => $channel], $user->getId()); From db1674811f8cf804b223df336534812deca3a159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Fri, 8 Mar 2024 13:57:20 +0100 Subject: [PATCH 013/195] Finish fixing code QL warnings --- app/cli.php | 25 +- app/controllers/api/account.php | 76 +-- app/controllers/api/avatars.php | 25 +- app/controllers/api/console.php | 2 +- app/controllers/api/databases.php | 197 +++++--- app/controllers/api/functions.php | 57 ++- app/controllers/api/graphql.php | 2 +- app/controllers/api/health.php | 12 +- app/controllers/api/locale.php | 2 +- app/controllers/api/messaging.php | 54 ++- app/controllers/api/migrations.php | 10 +- app/controllers/api/project.php | 5 +- app/controllers/api/projects.php | 15 +- app/controllers/api/proxy.php | 4 +- app/controllers/api/storage.php | 80 ++-- app/controllers/api/teams.php | 24 +- app/controllers/api/users.php | 12 +- app/controllers/api/vcs.php | 16 +- app/controllers/general.php | 23 +- app/controllers/mock.php | 6 +- app/controllers/shared/api.php | 25 +- app/controllers/shared/api/auth.php | 2 +- app/controllers/web/home.php | 2 +- app/http.php | 434 +++++++----------- app/init.php | 35 +- app/realtime.php | 60 ++- app/worker.php | 29 +- composer.json | 10 +- composer.lock | 176 +++++-- src/Appwrite/GraphQL/Resolvers.php | 20 +- src/Appwrite/GraphQL/Schema.php | 8 +- src/Appwrite/GraphQL/Types/Mapper.php | 2 +- src/Appwrite/Messaging/Adapter/Realtime.php | 2 +- src/Appwrite/Migration/Migration.php | 2 +- src/Appwrite/Migration/Version/V15.php | 2 +- src/Appwrite/Migration/Version/V19.php | 2 +- src/Appwrite/Platform/Tasks/CalcTierStats.php | 4 +- .../Platform/Tasks/CreateInfMetric.php | 2 +- .../Platform/Tasks/DeleteOrphanedProjects.php | 17 +- .../Tasks/DevGenerateTranslations.php | 2 +- src/Appwrite/Platform/Tasks/Doctor.php | 2 +- .../Platform/Tasks/GetMigrationStats.php | 16 +- src/Appwrite/Platform/Tasks/Hamster.php | 2 +- src/Appwrite/Platform/Tasks/Install.php | 2 +- src/Appwrite/Platform/Tasks/Maintenance.php | 2 +- src/Appwrite/Platform/Tasks/Migrate.php | 8 +- .../PatchRecreateRepositoriesDocuments.php | 2 +- src/Appwrite/Platform/Tasks/QueueCount.php | 2 +- src/Appwrite/Platform/Tasks/QueueRetry.php | 4 +- src/Appwrite/Platform/Tasks/SSL.php | 4 +- src/Appwrite/Platform/Tasks/ScheduleBase.php | 2 +- src/Appwrite/Platform/Tasks/Specs.php | 6 +- src/Appwrite/Platform/Tasks/Vars.php | 2 +- src/Appwrite/Platform/Tasks/Version.php | 2 +- src/Appwrite/Platform/Tasks/VolumeSync.php | 2 +- src/Appwrite/Platform/Workers/Audits.php | 8 +- src/Appwrite/Platform/Workers/Builds.php | 2 +- .../Platform/Workers/Certificates.php | 2 +- src/Appwrite/Platform/Workers/Deletes.php | 22 +- src/Appwrite/Platform/Workers/Functions.php | 2 +- src/Appwrite/Platform/Workers/Hamster.php | 4 +- src/Appwrite/Platform/Workers/Messaging.php | 2 +- src/Appwrite/Platform/Workers/Usage.php | 2 +- src/Appwrite/Platform/Workers/UsageDump.php | 2 +- src/Appwrite/Platform/Workers/Webhooks.php | 2 +- src/Appwrite/Specification/Format.php | 8 +- .../Specification/Format/OpenAPI3.php | 2 +- .../Specification/Format/Swagger2.php | 2 +- src/Appwrite/Utopia/Request.php | 8 + src/Appwrite/Utopia/Response.php | 5 +- src/Appwrite/Vcs/Comment.php | 2 +- tests/e2e/General/AbuseTest.php | 2 +- tests/e2e/Services/GraphQL/AbuseTest.php | 2 +- tests/e2e/Services/GraphQL/MessagingTest.php | 2 +- .../e2e/Services/Messaging/MessagingBase.php | 2 +- .../e2e/Services/VCS/VCSConsoleClientTest.php | 2 +- tests/unit/Event/EventTest.php | 2 +- .../unit/Messaging/MessagingChannelsTest.php | 1 - tests/unit/Usage/StatsTest.php | 2 +- 79 files changed, 889 insertions(+), 742 deletions(-) diff --git a/app/cli.php b/app/cli.php index 86daad2ae3..744c74e759 100644 --- a/app/cli.php +++ b/app/cli.php @@ -8,7 +8,6 @@ use Appwrite\Event\Delete; use Appwrite\Event\Func; use Appwrite\Event\Hamster; use Appwrite\Platform\Appwrite; -use Utopia\Http\Http; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\CLI; @@ -17,6 +16,7 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; +use Utopia\Http\Http; use Utopia\Logger\Log; use Utopia\Platform\Service; use Utopia\Pools\Group; @@ -25,8 +25,6 @@ use Utopia\Registry\Registry; global $register; -$auth->disable(); - CLI::setResource('register', fn () => $register); CLI::setResource('cache', function ($pools) { @@ -48,7 +46,7 @@ CLI::setResource('pools', function (Registry $register) { return $register->get('pools'); }, ['register']); -CLI::setResource('dbForConsole', function ($pools, $cache) { +CLI::setResource('dbForConsole', function ($pools, $cache, $auth) { $sleep = 3; $maxAttempts = 5; $attempts = 0; @@ -64,6 +62,7 @@ CLI::setResource('dbForConsole', function ($pools, $cache) { ->getResource(); $dbForConsole = new Database($dbAdapter, $cache); + $dbForConsole->setAuthorization($auth); $dbForConsole ->setNamespace('_console') @@ -91,12 +90,12 @@ CLI::setResource('dbForConsole', function ($pools, $cache) { } return $dbForConsole; -}, ['pools', 'cache']); +}, ['pools', 'cache', 'auth']); -CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { +CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, $auth) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } @@ -115,6 +114,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, ->getResource(); $database = new Database($dbAdapter, $cache); + $database->setAuthorization($auth); $databases[$databaseName] = $database; @@ -125,7 +125,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, return $database; }; -}, ['pools', 'dbForConsole', 'cache']); +}, ['pools', 'dbForConsole', 'cache', 'auth']); CLI::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); @@ -178,11 +178,20 @@ CLI::setResource('logError', function (Registry $register) { }; }, ['register']); +CLI::setResource('auth', fn () => new Authorization()); + $platform = new Appwrite(); $platform->init(Service::TYPE_CLI); $cli = $platform->getCli(); +$cli + ->init() + ->inject('auth') + ->action(function (Authorization $auth) { + $auth->disable(); + }); + $cli ->error() ->inject('error') diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 2def54db58..2b1804dce2 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -28,7 +28,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Identities; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; -use Utopia\Http\Http; use Utopia\Audit\Audit as EventAudit; use Utopia\Config\Config; use Utopia\Database\Database; @@ -45,7 +44,7 @@ use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; -use Utopia\Locale\Locale; +use Utopia\Http\Http; use Utopia\Http\Validator\ArrayList; use Utopia\Http\Validator\Assoc; use Utopia\Http\Validator\Boolean; @@ -53,6 +52,7 @@ use Utopia\Http\Validator\Host; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\URL; use Utopia\Http\Validator\WhiteList; +use Utopia\Locale\Locale; $oauthDefaultSuccess = '/auth/oauth2/success'; $oauthDefaultFailure = '/auth/oauth2/failure'; @@ -85,7 +85,8 @@ Http::post('/v1/account') ->inject('dbForProject') ->inject('queueForEvents') ->inject('hooks') - ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks) { + ->inject('auth') + ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks, Authorization $auth) { $email = \strtolower($email); if ('console' === $project->getId()) { @@ -186,7 +187,7 @@ Http::post('/v1/account') throw new Exception(Exception::USER_ALREADY_EXISTS); } - $auth->unsetRole(Role::guests()->toString()); + $auth->removeRole(Role::guests()->toString()); $auth->addRole(Role::user($user->getId())->toString()); $auth->addRole(Role::users()->toString()); @@ -227,7 +228,8 @@ Http::post('/v1/account/sessions/email') ->inject('geodb') ->inject('queueForEvents') ->inject('hooks') - ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks) { + ->inject('auth') + ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks, Authorization $auth) { $email = \strtolower($email); $protocol = $request->getProtocol(); @@ -551,7 +553,8 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('dbForProject') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) { + ->inject('auth') + ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $auth) use ($oauthDefaultSuccess) { $protocol = $request->getProtocol(); $callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId(); $defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => '']; @@ -1098,7 +1101,8 @@ Http::post('/v1/account/tokens/magic-url') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { + ->inject('Auth') + ->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $auth) { if (empty(Http::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); @@ -1340,7 +1344,8 @@ Http::post('/v1/account/tokens/email') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { + ->inject('auth') + ->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $auth) { if (empty(Http::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); } @@ -1541,7 +1546,7 @@ Http::post('/v1/account/tokens/email') ; }); -$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { +$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Authorization $auth) { $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1676,6 +1681,7 @@ Http::put('/v1/account/sessions/magic-url') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') + ->inject('auth') ->action($createSession); Http::put('/v1/account/sessions/phone') @@ -1706,6 +1712,7 @@ Http::put('/v1/account/sessions/phone') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') + ->inject('auth') ->action($createSession); Http::post('/v1/account/sessions/token') @@ -1735,6 +1742,7 @@ Http::post('/v1/account/sessions/token') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') + ->inject('auth') ->action($createSession); Http::post('/v1/account/tokens/phone') @@ -1765,7 +1773,8 @@ Http::post('/v1/account/tokens/phone') ->inject('queueForEvents') ->inject('queueForMessaging') ->inject('locale') - ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale) { + ->inject('auth') + ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, Authorization $auth) { if (empty(Http::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -1933,7 +1942,8 @@ Http::post('/v1/account/sessions/anonymous') ->inject('dbForProject') ->inject('geodb') ->inject('queueForEvents') - ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) { + ->inject('auth') + ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $auth) { $protocol = $request->getProtocol(); $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); @@ -2153,7 +2163,8 @@ Http::get('/v1/account/sessions') ->inject('user') ->inject('locale') ->inject('project') - ->action(function (Response $response, Document $user, Locale $locale, Document $project) { + ->inject('auth') + ->action(function (Response $response, Document $user, Locale $locale, Document $project, Authorization $auth) { $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); @@ -2195,7 +2206,8 @@ Http::get('/v1/account/logs') ->inject('locale') ->inject('geodb') ->inject('dbForProject') - ->action(function (array $queries, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject) { + ->inject('auth') + ->action(function (array $queries, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject, Authorization $auth) { try { $queries = Query::parseQueries($queries); @@ -2207,7 +2219,7 @@ Http::get('/v1/account/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new EventAudit($dbForProject); + $audit = new EventAudit($dbForProject, $auth); $logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset); @@ -2261,7 +2273,8 @@ Http::get('/v1/account/sessions/:sessionId') ->inject('user') ->inject('locale') ->inject('project') - ->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Document $project) { + ->inject('auth') + ->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Document $project, Authorization $auth) { $roles = $auth->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); @@ -2416,7 +2429,8 @@ Http::patch('/v1/account/email') ->inject('queueForEvents') ->inject('project') ->inject('hooks') - ->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks) { + ->inject('auth') + ->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $auth) { // passwordUpdate will be empty if the user has never set a password $passwordUpdate = $user->getAttribute('passwordUpdate'); @@ -2508,7 +2522,8 @@ Http::patch('/v1/account/phone') ->inject('queueForEvents') ->inject('project') ->inject('hooks') - ->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks) { + ->inject('auth') + ->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $auth) { // passwordUpdate will be empty if the user has never set a password $passwordUpdate = $user->getAttribute('passwordUpdate'); @@ -2888,7 +2903,8 @@ Http::post('/v1/account/recovery') ->inject('locale') ->inject('queueForMails') ->inject('queueForEvents') - ->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents, Authorization $auth) { if (empty(Http::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -3065,7 +3081,8 @@ Http::put('/v1/account/recovery') ->inject('project') ->inject('queueForEvents') ->inject('hooks') - ->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks) { + ->inject('auth') + ->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, Authorization $auth) { $profile = $dbForProject->getDocument('users', $userId); if ($profile->isEmpty()) { @@ -3149,7 +3166,8 @@ Http::post('/v1/account/verification') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { + ->inject('auth') + ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $auth) { if (empty(Http::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -3307,7 +3325,8 @@ Http::put('/v1/account/verification') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $profile = $auth->skip(fn () => $dbForProject->getDocument('users', $userId)); @@ -3370,7 +3389,8 @@ Http::post('/v1/account/verification/phone') ->inject('queueForMessaging') ->inject('project') ->inject('locale') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale) { + ->inject('auth') + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, Authorization $auth) { if (empty(Http::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -3479,7 +3499,8 @@ Http::put('/v1/account/verification/phone') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $profile = $auth->skip(fn () => $dbForProject->getDocument('users', $userId)); @@ -4227,7 +4248,8 @@ Http::post('/v1/account/targets/push') ->inject('request') ->inject('response') ->inject('dbForProject') - ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $auth) { $targetId = $targetId == 'unique()' ? ID::unique() : $targetId; $provider = $auth->skip(fn () => $dbForProject->getDocument('providers', $providerId)); @@ -4299,7 +4321,8 @@ Http::put('/v1/account/targets/:targetId/push') ->inject('request') ->inject('response') ->inject('dbForProject') - ->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $auth) { $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId)); @@ -4354,7 +4377,8 @@ Http::delete('/v1/account/targets/:targetId/push') ->inject('request') ->inject('response') ->inject('dbForProject') - ->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $auth) { $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 8a4b0b6afd..b6df369467 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -5,7 +5,6 @@ use Appwrite\URL\URL as URLParse; use Appwrite\Utopia\Response; use chillerlan\QRCode\QRCode; use chillerlan\QRCode\QROptions; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -14,15 +13,16 @@ use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; -use Utopia\Image\Image; -use Utopia\Logger\Log; -use Utopia\Logger\Logger; +use Utopia\Http\Http; use Utopia\Http\Validator\Boolean; use Utopia\Http\Validator\HexColor; use Utopia\Http\Validator\Range; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\URL; use Utopia\Http\Validator\WhiteList; +use Utopia\Image\Image; +use Utopia\Logger\Log; +use Utopia\Logger\Logger; $avatarCallback = function (string $type, string $code, int $width, int $height, int $quality, Response $response) { @@ -61,7 +61,7 @@ $avatarCallback = function (string $type, string $code, int $width, int $height, unset($image); }; -$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForConsole, ?Logger $logger) { +$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForConsole, ?Logger $logger, Authorization $auth) { try { $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); @@ -593,7 +593,8 @@ Http::get('/v1/cards/cloud') ->inject('contributors') ->inject('employees') ->inject('logger') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) { + ->inject('authp') + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { @@ -605,7 +606,7 @@ Http::get('/v1/cards/cloud') $email = $user->getAttribute('email', ''); $createdAt = new \DateTime($user->getCreatedAt()); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $auth); $githubName = $gitHub['name'] ?? ''; $githubId = $gitHub['id'] ?? ''; @@ -800,7 +801,8 @@ Http::get('/v1/cards/cloud-back') ->inject('contributors') ->inject('employees') ->inject('logger') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) { + ->inject('auth') + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { @@ -811,7 +813,7 @@ Http::get('/v1/cards/cloud-back') $userId = $user->getId(); $email = $user->getAttribute('email', ''); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $auth); $githubId = $gitHub['id'] ?? ''; $isHero = \array_key_exists($email, $heroes); @@ -878,7 +880,8 @@ Http::get('/v1/cards/cloud-og') ->inject('contributors') ->inject('employees') ->inject('logger') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) { + ->inject('auth') + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { @@ -894,7 +897,7 @@ Http::get('/v1/cards/cloud-og') $email = $user->getAttribute('email', ''); $createdAt = new \DateTime($user->getCreatedAt()); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $auth); $githubName = $gitHub['name'] ?? ''; $githubId = $gitHub['id'] ?? ''; diff --git a/app/controllers/api/console.php b/app/controllers/api/console.php index 4edec4394a..ef2b283638 100644 --- a/app/controllers/api/console.php +++ b/app/controllers/api/console.php @@ -2,8 +2,8 @@ use Appwrite\Extend\Exception; use Appwrite\Utopia\Response; -use Utopia\Http\Http; use Utopia\Database\Document; +use Utopia\Http\Http; use Utopia\Http\Validator\Text; Http::init() diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index c2fc070cc3..b419e235c1 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -15,7 +15,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Indexes; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; -use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\Config\Config; use Utopia\Database\Database; @@ -32,6 +31,7 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; +use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Index as IndexValidator; use Utopia\Database\Validator\Key; @@ -41,7 +41,7 @@ use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\Structure; use Utopia\Database\Validator\UID; -use Utopia\Locale\Locale; +use Utopia\Http\Http; use Utopia\Http\Validator\ArrayList; use Utopia\Http\Validator\Boolean; use Utopia\Http\Validator\FloatValidator; @@ -53,6 +53,7 @@ use Utopia\Http\Validator\Range; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\URL; use Utopia\Http\Validator\WhiteList; +use Utopia\Locale\Locale; /** * * Create attribute of varying type @@ -74,7 +75,7 @@ use Utopia\Http\Validator\WhiteList; * @throws ConflictException * @throws Exception */ -function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): Document +function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth): Document { $key = $attribute->getAttribute('key'); $type = $attribute->getAttribute('type', ''); @@ -223,6 +224,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att } function updateAttribute( + Authorization $auth, string $databaseId, string $collectionId, string $key, @@ -235,7 +237,7 @@ function updateAttribute( int|float $min = null, int|float $max = null, array $elements = null, - array $options = [] + array $options = [], ): Document { $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -564,7 +566,8 @@ Http::get('/v1/databases/:databaseId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->action(function (string $databaseId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + ->inject('auth') + ->action(function (string $databaseId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -582,7 +585,7 @@ Http::get('/v1/databases/:databaseId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject); + $audit = new Audit($dbForProject, $auth); $resource = 'database/' . $databaseId; $logs = $audit->getLogsByResource($resource, $limit, $offset); @@ -750,7 +753,8 @@ Http::post('/v1/databases/:databaseId/collections') ->inject('dbForProject') ->inject('mode') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents, Authorization $auth) { $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -811,7 +815,8 @@ Http::get('/v1/databases/:databaseId/collections') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { + ->inject('auth') + ->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $auth) { $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -873,7 +878,8 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, string $mode) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, string $mode, Authorization $auth) { $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -909,7 +915,8 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -934,7 +941,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject); + $audit = new Audit($dbForProject, $auth); $resource = 'database/' . $databaseId . '/collection/' . $collectionId; $logs = $audit->getLogsByResource($resource, $limit, $offset); @@ -1015,7 +1022,8 @@ Http::put('/v1/databases/:databaseId/collections/:collectionId') ->inject('dbForProject') ->inject('mode') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents, Authorization $auth) { $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1079,7 +1087,8 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId') ->inject('queueForDatabase') ->inject('queueForEvents') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode, Authorization $auth) { $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1140,7 +1149,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/strin ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { // Ensure attribute default is within required size $validator = new Text($size, 0); @@ -1162,7 +1172,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/strin 'default' => $default, 'array' => $array, 'filters' => $filters, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1194,7 +1204,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1204,7 +1215,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_EMAIL, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1237,7 +1248,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum' ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { if (!is_null($default) && !in_array($default, $elements)) { throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements'); } @@ -1251,7 +1263,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum' 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_ENUM, 'formatOptions' => ['elements' => $elements], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1283,7 +1295,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1293,7 +1306,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_IP, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1325,7 +1338,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1335,7 +1349,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_URL, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1369,7 +1383,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integ ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->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) { + ->inject('auth') + ->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, Authorization $auth) { // Ensure attribute default is within range $min = (is_null($min)) ? PHP_INT_MIN : \intval($min); @@ -1399,7 +1414,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integ 'min' => $min, 'max' => $max, ], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); $formatOptions = $attribute->getAttribute('formatOptions', []); @@ -1440,7 +1455,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->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) { + ->inject('auth') + ->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, Authorization $auth) { // Ensure attribute default is within range $min = (is_null($min)) ? -PHP_FLOAT_MAX : \floatval($min); @@ -1473,7 +1489,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float 'min' => $min, 'max' => $max, ], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); $formatOptions = $attribute->getAttribute('formatOptions', []); @@ -1512,7 +1528,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boole ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1521,7 +1538,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boole 'required' => $required, 'default' => $default, 'array' => $array, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1553,7 +1570,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datet ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { $filters[] = 'datetime'; @@ -1565,7 +1583,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datet 'default' => $default, 'array' => $array, 'filters' => $filters, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1599,6 +1617,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relat ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') + ->inject('auth') ->action(function ( string $databaseId, string $collectionId, @@ -1611,7 +1630,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relat Response $response, Database $dbForProject, EventDatabase $queueForDatabase, - Event $queueForEvents + Event $queueForEvents, + Authorization $auth ) { $key ??= $relatedCollectionId; $twoWayKey ??= $collectionId; @@ -1686,7 +1706,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relat $response, $dbForProject, $queueForDatabase, - $queueForEvents + $queueForEvents, + $auth ); $options = $attribute->getAttribute('options', []); @@ -1717,7 +1738,8 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes') ->param('queries', [], new Attributes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Authorization $auth) { /** @var Document $database */ $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1805,7 +1827,8 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') ->param('key', '', new Key(), 'Attribute Key.') ->inject('response') ->inject('dbForProject') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, Authorization $auth) { $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -1874,9 +1897,11 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/stri ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $attribute = updateAttribute( + auth: $auth, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -1913,8 +1938,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/emai ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $attribute = updateAttribute( + auth: $auth, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -1953,8 +1980,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $attribute = updateAttribute( + auth: $auth, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -1993,8 +2022,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/: ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $attribute = updateAttribute( + auth: $auth, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2032,8 +2063,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/ ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $attribute = updateAttribute( + auth: $auth, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2073,8 +2106,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/inte ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, Response $response, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $attribute = updateAttribute( + auth: $auth, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2122,8 +2157,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/floa ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, Response $response, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $attribute = updateAttribute( + auth: $auth, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2169,8 +2206,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/bool ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, Response $response, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $attribute = updateAttribute( + auth: $auth, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2207,8 +2246,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/date ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $attribute = updateAttribute( + auth: $auth, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2244,6 +2285,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') + ->inject('auth') ->action(function ( string $databaseId, string $collectionId, @@ -2251,9 +2293,11 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ?string $onDelete, Response $response, Database $dbForProject, - Event $queueForEvents + Event $queueForEvents, + Authorization $auth ) { $attribute = updateAttribute( + $auth, $databaseId, $collectionId, $key, @@ -2298,7 +2342,8 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:ke ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2411,7 +2456,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2573,7 +2619,8 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes') ->param('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Authorization $auth) { /** @var Document $database */ $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2643,7 +2690,8 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->param('key', null, new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, Authorization $auth) { $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2686,7 +2734,8 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2759,7 +2808,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('user') ->inject('queueForEvents') ->inject('mode') - ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode) { + ->inject('auth') + ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Authorization $auth) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -2830,17 +2880,16 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') $data['$permissions'] = $permissions; $document = new Document($data); - $checkPermissions = function (Document $collection, Document $document, string $permission) use (&$checkPermissions, $dbForProject, $database) { + $checkPermissions = function (Document $collection, Document $document, string $permission) use (&$checkPermissions, $dbForProject, $database, $auth) { $documentSecurity = $collection->getAttribute('documentSecurity', false); - $validator = new Authorization($permission); - $valid = $validator->isValid($collection->getPermissionsByType($permission)); + $valid = $auth->isValid(new Input($permission, $collection->getPermissionsByType($permission))); if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } if ($permission === Database::PERMISSION_UPDATE) { - $valid = $valid || $validator->isValid($document->getUpdate()); + $valid = $valid || $auth->isValid($document->getUpdate()); if ($documentSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -2921,7 +2970,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') } // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $auth) { $document->setAttribute('$databaseId', $database->getId()); $document->setAttribute('$collectionId', $collection->getId()); @@ -2987,7 +3036,8 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode, Authorization $auth) { $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser($auth->getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); @@ -3039,7 +3089,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') } // Add $collectionId and $databaseId for all documents - $processDocument = (function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database): bool { + $processDocument = (function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $auth): bool { if ($document->isEmpty()) { return false; } @@ -3144,7 +3194,8 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume ->inject('response') ->inject('dbForProject') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, string $mode) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, string $mode, Authorization $auth) { $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser($auth->getRoles()); @@ -3174,7 +3225,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume } // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $auth) { if ($document->isEmpty()) { return; } @@ -3235,7 +3286,8 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -3265,7 +3317,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject); + $audit = new Audit($dbForProject, $auth); $resource = 'database/' . $databaseId . '/collection/' . $collectionId . '/document/' . $document->getId(); $logs = $audit->getLogsByResource($resource, $limit, $offset); @@ -3349,7 +3401,8 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->inject('dbForProject') ->inject('queueForEvents') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, string $mode) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Authorization $auth) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -3416,7 +3469,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $data['$permissions'] = $permissions; $newDocument = new Document($data); - $setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database) { + $setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database, $auth) { $relationships = \array_filter( $collection->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP @@ -3502,7 +3555,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu } // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $auth) { $document->setAttribute('$databaseId', $database->getId()); $document->setAttribute('$collectionId', $collection->getId()); @@ -3575,7 +3628,8 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:doc ->inject('queueForDeletes') ->inject('queueForEvents') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, string $mode) { + ->inject('auth') + ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, string $mode, Authorization $auth) { $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser($auth->getRoles()); @@ -3612,7 +3666,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:doc }); // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $auth) { $document->setAttribute('$databaseId', $database->getId()); $document->setAttribute('$collectionId', $collection->getId()); @@ -3674,7 +3728,8 @@ Http::get('/v1/databases/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $range, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $range, Response $response, Database $dbForProject, Authorization $auth) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -3753,7 +3808,8 @@ Http::get('/v1/databases/:databaseId/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $databaseId, string $range, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $databaseId, string $range, Response $response, Database $dbForProject, Authorization $auth) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -3838,7 +3894,8 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/usage') ->param('collectionId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') - ->action(function (string $databaseId, string $range, string $collectionId, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $databaseId, string $range, string $collectionId, Response $response, Database $dbForProject, Authorization $auth) { $database = $dbForProject->getDocument('databases', $databaseId); $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 59c3254d67..44ac1a926a 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -15,11 +15,11 @@ use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\Queries\Deployments; use Appwrite\Utopia\Database\Validator\Queries\Executions; use Appwrite\Utopia\Database\Validator\Queries\Functions; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model\Rule; use Executor\Executor; use MaxMind\Db\Reader; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -32,20 +32,21 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; +use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Roles; use Utopia\Database\Validator\UID; -use Utopia\Storage\Device; -use Utopia\Storage\Validator\File; -use Utopia\Storage\Validator\FileExt; -use Utopia\Storage\Validator\FileSize; -use Utopia\Storage\Validator\Upload; -use Utopia\Swoole\Request; +use Utopia\Http\Http; use Utopia\Http\Validator\ArrayList; use Utopia\Http\Validator\Assoc; use Utopia\Http\Validator\Boolean; use Utopia\Http\Validator\Range; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; +use Utopia\Storage\Device; +use Utopia\Storage\Validator\File; +use Utopia\Storage\Validator\FileExt; +use Utopia\Storage\Validator\FileSize; +use Utopia\Storage\Validator\Upload; use Utopia\VCS\Adapter\Git\GitHub; use Utopia\VCS\Exception\RepositoryNotFound; @@ -479,7 +480,8 @@ Http::get('/v1/functions/:functionId/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $functionId, string $range, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $functionId, string $range, Response $response, Database $dbForProject, Authorization $auth) { $function = $dbForProject->getDocument('functions', $functionId); @@ -576,7 +578,8 @@ Http::get('/v1/functions/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $range, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $range, Response $response, Database $dbForProject, Authorization $auth) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -694,7 +697,8 @@ Http::put('/v1/functions/:functionId') ->inject('queueForBuilds') ->inject('dbForConsole') ->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, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) { + ->inject('auth') + ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $auth) use ($redeployVcs) { // TODO: If only branch changes, re-deploy $function = $dbForProject->getDocument('functions', $functionId); @@ -940,7 +944,8 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId') ->inject('dbForProject') ->inject('queueForEvents') ->inject('dbForConsole') - ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Event $queueForEvents, Database $dbForConsole) { + ->inject('auth') + ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Event $queueForEvents, Database $dbForConsole, Authorization $auth) { $function = $dbForProject->getDocument('functions', $functionId); $deployment = $dbForProject->getDocument('deployments', $deploymentId); @@ -1001,7 +1006,8 @@ Http::delete('/v1/functions/:functionId') ->inject('queueForDeletes') ->inject('queueForEvents') ->inject('dbForConsole') - ->action(function (string $functionId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Database $dbForConsole) { + ->inject('auth') + ->action(function (string $functionId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Database $dbForConsole, Authorization $auth) { $function = $dbForProject->getDocument('functions', $functionId); @@ -1451,7 +1457,8 @@ Http::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId' ->inject('project') ->inject('queueForEvents') ->inject('queueForBuilds') - ->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds) { + ->inject('auth') + ->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Authorization $auth) { $function = $dbForProject->getDocument('functions', $functionId); @@ -1522,7 +1529,8 @@ Http::post('/v1/functions/:functionId/executions') ->inject('mode') ->inject('queueForFunctions') ->inject('geodb') - ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode, Func $queueForFunctions, Reader $geodb) { + ->inject('auth') + ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode, Func $queueForFunctions, Reader $geodb, Authorization $auth) { $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); @@ -1562,10 +1570,8 @@ Http::post('/v1/functions/:functionId/executions') throw new Exception(Exception::BUILD_NOT_READY); } - $validator = new Authorization('execute'); - - if (!$validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function - throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription()); + if (!$auth->isValid(new Input('execute', $function->getAttribute('execute')))) { // Check if user has write access to execute function + throw new Exception(Exception::USER_UNAUTHORIZED, $auth->getDescription()); } $jwt = ''; // initialize @@ -1803,7 +1809,8 @@ Http::get('/v1/functions/:functionId/executions') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { + ->inject('auth') + ->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $auth) { $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); $isAPIKey = Auth::isAppUser($auth->getRoles()); @@ -1883,7 +1890,8 @@ Http::get('/v1/functions/:functionId/executions/:executionId') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, string $mode) { + ->inject('auth') + ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, string $mode, Authorization $auth) { $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); $isAPIKey = Auth::isAppUser($auth->getRoles()); @@ -1935,7 +1943,8 @@ Http::post('/v1/functions/:functionId/variables') ->inject('response') ->inject('dbForProject') ->inject('dbForConsole') - ->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject, Database $dbForConsole) { + ->inject('auth') + ->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $auth) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -2066,7 +2075,8 @@ Http::put('/v1/functions/:functionId/variables/:variableId') ->inject('response') ->inject('dbForProject') ->inject('dbForConsole') - ->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForConsole) { + ->inject('auth') + ->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $auth) { $function = $dbForProject->getDocument('functions', $functionId); @@ -2124,7 +2134,8 @@ Http::delete('/v1/functions/:functionId/variables/:variableId') ->inject('response') ->inject('dbForProject') ->inject('dbForConsole') - ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject, Database $dbForConsole) { + ->inject('auth') + ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $auth) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index c50811e0a3..4eb858b81c 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -12,8 +12,8 @@ use GraphQL\Validator\Rules\DisableIntrospection; use GraphQL\Validator\Rules\QueryComplexity; use GraphQL\Validator\Rules\QueryDepth; use Swoole\Coroutine\WaitGroup; -use Utopia\Http\Http; use Utopia\Database\Document; +use Utopia\Http\Http; use Utopia\Http\Validator\JSON; use Utopia\Http\Validator\Text; diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 1fc0541d2e..26246db019 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -4,10 +4,15 @@ use Appwrite\ClamAV\Network; use Appwrite\Event\Event; use Appwrite\Extend\Exception; use Appwrite\Utopia\Response; -use Utopia\Http\Http; use Utopia\Config\Config; use Utopia\Database\Document; use Utopia\Domains\Validator\PublicDomain; +use Utopia\Http\Http; +use Utopia\Http\Validator\Domain; +use Utopia\Http\Validator\Integer; +use Utopia\Http\Validator\Multiple; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; use Utopia\Pools\Group; use Utopia\Queue\Client; use Utopia\Queue\Connection; @@ -15,11 +20,6 @@ use Utopia\Registry\Registry; use Utopia\Storage\Device; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; -use Utopia\Http\Validator\Domain; -use Utopia\Http\Validator\Integer; -use Utopia\Http\Validator\Multiple; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; Http::get('/v1/health') ->desc('Get HTTP') diff --git a/app/controllers/api/locale.php b/app/controllers/api/locale.php index e4bf57d5ab..fcaf0c03cb 100644 --- a/app/controllers/api/locale.php +++ b/app/controllers/api/locale.php @@ -3,9 +3,9 @@ use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; -use Utopia\Http\Http; use Utopia\Config\Config; use Utopia\Database\Document; +use Utopia\Http\Http; use Utopia\Locale\Locale; Http::get('/v1/locale') diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 5e0d31a294..1adf08d4bc 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -19,7 +19,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Targets; use Appwrite\Utopia\Database\Validator\Queries\Topics; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; -use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\Database\Database; use Utopia\Database\DateTime; @@ -29,6 +28,7 @@ use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; +use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; @@ -36,7 +36,7 @@ use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\Roles; use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; -use Utopia\Locale\Locale; +use Utopia\Http\Http; use Utopia\Http\Validator\ArrayList; use Utopia\Http\Validator\Boolean; use Utopia\Http\Validator\Integer; @@ -44,6 +44,7 @@ use Utopia\Http\Validator\JSON; use Utopia\Http\Validator\Range; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; +use Utopia\Locale\Locale; use function Swoole\Coroutine\batch; @@ -846,7 +847,8 @@ Http::get('/v1/messaging/providers') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') ->inject('response') - ->action(function (array $queries, string $search, Database $dbForProject, Response $response) { + ->inject('auth') + ->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $auth) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -899,7 +901,8 @@ Http::get('/v1/messaging/providers/:providerId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->action(function (string $providerId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + ->inject('auth') + ->action(function (string $providerId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { $provider = $dbForProject->getDocument('providers', $providerId); if ($provider->isEmpty()) { @@ -916,7 +919,7 @@ Http::get('/v1/messaging/providers/:providerId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject); + $audit = new Audit($dbForProject, $auth); $resource = 'provider/' . $providerId; $logs = $audit->getLogsByResource($resource, $limit, $offset); $output = []; @@ -1980,7 +1983,8 @@ Http::get('/v1/messaging/topics') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') ->inject('response') - ->action(function (array $queries, string $search, Database $dbForProject, Response $response) { + ->inject('auth') + ->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $auth) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -2033,7 +2037,8 @@ Http::get('/v1/messaging/topics/:topicId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->action(function (string $topicId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + ->inject('auth') + ->action(function (string $topicId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { $topic = $dbForProject->getDocument('topics', $topicId); if ($topic->isEmpty()) { @@ -2050,7 +2055,7 @@ Http::get('/v1/messaging/topics/:topicId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject); + $audit = new Audit($dbForProject, $auth); $resource = 'topic/' . $topicId; $logs = $audit->getLogsByResource($resource, $limit, $offset); @@ -2236,7 +2241,8 @@ Http::post('/v1/messaging/topics/:topicId/subscribers') ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') - ->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response) { + ->inject('auth') + ->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response, Authorization $auth) { $subscriberId = $subscriberId == 'unique()' ? ID::unique() : $subscriberId; $topic = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); @@ -2245,9 +2251,9 @@ Http::post('/v1/messaging/topics/:topicId/subscribers') throw new Exception(Exception::TOPIC_NOT_FOUND); } - $validator = new Authorization('subscribe'); + $validator = new Authorization(); - if (!$validator->isValid($topic->getAttribute('subscribe'))) { + if (!$validator->isValid(new Input('subscribe', $topic->getAttribute('subscribe')))) { throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription()); } @@ -2328,7 +2334,8 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') ->inject('response') - ->action(function (string $topicId, array $queries, string $search, Database $dbForProject, Response $response) { + ->inject('auth') + ->action(function (string $topicId, array $queries, string $search, Database $dbForProject, Response $response, Authorization $auth) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -2368,8 +2375,8 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') $subscribers = $dbForProject->find('subscribers', $queries); - $subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) { - return function () use ($subscriber, $dbForProject) { + $subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject, $auth) { + return function () use ($subscriber, $dbForProject, $auth) { $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); $user = $auth->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); @@ -2403,7 +2410,8 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->action(function (string $subscriberId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + ->inject('auth') + ->action(function (string $subscriberId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { $subscriber = $dbForProject->getDocument('subscribers', $subscriberId); if ($subscriber->isEmpty()) { @@ -2420,7 +2428,7 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject); + $audit = new Audit($dbForProject, $auth); $resource = 'subscriber/' . $subscriberId; $logs = $audit->getLogsByResource($resource, $limit, $offset); @@ -2490,7 +2498,8 @@ Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->param('subscriberId', '', new UID(), 'Subscriber ID.') ->inject('dbForProject') ->inject('response') - ->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response) { + ->inject('auth') + ->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response, Authorization $auth) { $topic = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { @@ -2533,7 +2542,8 @@ Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') - ->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response) { + ->inject('auth') + ->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response, Authorization $auth) { $topic = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { @@ -3022,7 +3032,8 @@ Http::get('/v1/messaging/messages') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') ->inject('response') - ->action(function (array $queries, string $search, Database $dbForProject, Response $response) { + ->inject('auth') + ->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $auth) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -3075,7 +3086,8 @@ Http::get('/v1/messaging/messages/:messageId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->action(function (string $messageId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + ->inject('auth') + ->action(function (string $messageId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { $message = $dbForProject->getDocument('messages', $messageId); if ($message->isEmpty()) { @@ -3092,7 +3104,7 @@ Http::get('/v1/messaging/messages/:messageId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject); + $audit = new Audit($dbForProject, $auth); $resource = 'message/' . $messageId; $logs = $audit->getLogsByResource($resource, $limit, $offset); diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index badb482c34..84bad41465 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -9,7 +9,6 @@ use Appwrite\Role; use Appwrite\Utopia\Database\Validator\Queries\Migrations; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Utopia\Http\Http; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -17,16 +16,17 @@ use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\UID; -use Utopia\Migration\Sources\Appwrite; -use Utopia\Migration\Sources\Firebase; -use Utopia\Migration\Sources\NHost; -use Utopia\Migration\Sources\Supabase; +use Utopia\Http\Http; use Utopia\Http\Validator\ArrayList; use Utopia\Http\Validator\Host; use Utopia\Http\Validator\Integer; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\URL; use Utopia\Http\Validator\WhiteList; +use Utopia\Migration\Sources\Appwrite; +use Utopia\Migration\Sources\Firebase; +use Utopia\Migration\Sources\NHost; +use Utopia\Migration\Sources\Supabase; include_once __DIR__ . '/../shared/api.php'; diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 15154fbbed..05a6c46389 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -2,7 +2,6 @@ use Appwrite\Extend\Exception; use Appwrite\Utopia\Response; -use Utopia\Http\Http; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; @@ -13,6 +12,7 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Datetime as DateTimeValidator; use Utopia\Database\Validator\UID; +use Utopia\Http\Http; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; @@ -31,7 +31,8 @@ Http::get('/v1/project/usage') ->param('period', '1d', new WhiteList(['1h', '1d']), 'Period used', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $startDate, string $endDate, string $period, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $startDate, string $endDate, string $period, Response $response, Database $dbForProject, Authorization $auth) { $stats = $total = $usage = []; $format = 'Y-m-d 00:00:00'; $firstDay = (new DateTime($startDate))->format($format); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 2a38e4433f..e868df2eeb 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -13,7 +13,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Projects; use Appwrite\Utopia\Response; use PHPMailer\PHPMailer\PHPMailer; use Utopia\Abuse\Adapters\TimeLimit; -use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\Config\Config; @@ -25,11 +24,11 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\UID; use Utopia\Domains\Validator\PublicDomain; -use Utopia\Locale\Locale; -use Utopia\Pools\Group; +use Utopia\Http\Http; use Utopia\Http\Validator\ArrayList; use Utopia\Http\Validator\Boolean; use Utopia\Http\Validator\Hostname; @@ -39,6 +38,8 @@ use Utopia\Http\Validator\Range; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\URL; use Utopia\Http\Validator\WhiteList; +use Utopia\Locale\Locale; +use Utopia\Pools\Group; Http::init() ->groups(['projects']) @@ -76,7 +77,8 @@ Http::post('/v1/projects') ->inject('dbForConsole') ->inject('cache') ->inject('pools') - ->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole, Cache $cache, Group $pools) { + ->inject('auth') + ->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole, Cache $cache, Group $pools, Authorization $auth) { $team = $dbForConsole->getDocument('teams', $teamId); @@ -177,13 +179,14 @@ Http::post('/v1/projects') } $dbForProject = new Database($pools->get($database)->pop()->getResource(), $cache); + $dbForProject->setAuthorization($auth); $dbForProject->setNamespace("_{$project->getInternalId()}"); $dbForProject->create(); - $audit = new Audit($dbForProject); + $audit = new Audit($dbForProject, $auth); $audit->setup(); - $adapter = new TimeLimit('', 0, 1, $dbForProject); + $adapter = new TimeLimit('', 0, 1, $dbForProject, $auth); $adapter->setup(); /** @var array $collections */ diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php index 7f5564050f..3dff7bd7e9 100644 --- a/app/controllers/api/proxy.php +++ b/app/controllers/api/proxy.php @@ -7,7 +7,6 @@ use Appwrite\Extend\Exception; use Appwrite\Network\Validator\CNAME; use Appwrite\Utopia\Database\Validator\Queries\Rules; use Appwrite\Utopia\Response; -use Utopia\Http\Http; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Query as QueryException; @@ -15,10 +14,11 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; -use Utopia\Logger\Log; +use Utopia\Http\Http; use Utopia\Http\Validator\Domain as ValidatorDomain; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; +use Utopia\Logger\Log; Http::post('/v1/proxy/rules') ->groups(['api', 'proxy']) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 859daf20f3..735773a545 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -9,8 +9,8 @@ use Appwrite\OpenSSL\OpenSSL; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\Queries\Buckets; use Appwrite\Utopia\Database\Validator\Queries\Files; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Utopia\Http\Http; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -24,8 +24,16 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; +use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; +use Utopia\Http\Http; +use Utopia\Http\Validator\ArrayList; +use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\HexColor; +use Utopia\Http\Validator\Range; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; use Utopia\Image\Image; use Utopia\Storage\Compression\Algorithms\GZIP; use Utopia\Storage\Compression\Algorithms\Zstd; @@ -36,13 +44,6 @@ use Utopia\Storage\Validator\File; use Utopia\Storage\Validator\FileExt; use Utopia\Storage\Validator\FileSize; use Utopia\Storage\Validator\Upload; -use Utopia\Swoole\Request; -use Utopia\Http\Validator\ArrayList; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\HexColor; -use Utopia\Http\Validator\Range; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; Http::post('/v1/storage/buckets') ->desc('Create bucket') @@ -361,7 +362,8 @@ Http::post('/v1/storage/buckets/:bucketId/files') ->inject('mode') ->inject('deviceForFiles') ->inject('deviceForLocal') - ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal) { + ->inject('auth') + ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal, Authorization $auth) { $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -372,8 +374,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $validator = new Authorization(Database::PERMISSION_CREATE); - if (!$validator->isValid($bucket->getCreate())) { + if (!$auth->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -631,8 +632,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') * However as with chunk upload even if we are updating, we are essentially creating a file * adding it's new chunk so we validate create permission instead of update */ - $validator = new Authorization(Database::PERMISSION_CREATE); - if (!$validator->isValid($bucket->getCreate())) { + if (!$auth->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { throw new Exception(Exception::USER_UNAUTHORIZED); } $file = $auth->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); @@ -678,8 +678,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') * However as with chunk upload even if we are updating, we are essentially creating a file * adding it's new chunk so we validate create permission instead of update */ - $validator = new Authorization(Database::PERMISSION_CREATE); - if (!$validator->isValid($bucket->getCreate())) { + if (!$auth->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { throw new Exception(Exception::USER_UNAUTHORIZED); } $file = $auth->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); @@ -724,7 +723,8 @@ Http::get('/v1/storage/buckets/:bucketId/files') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { + ->inject('auth') + ->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $auth) { $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $isAPIKey = Auth::isAppUser($auth->getRoles()); @@ -735,8 +735,7 @@ Http::get('/v1/storage/buckets/:bucketId/files') } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $validator = new Authorization(Database::PERMISSION_READ); - $valid = $validator->isValid($bucket->getRead()); + $valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -808,7 +807,8 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode) { + ->inject('auth') + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode, Authorization $auth) { $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $isAPIKey = Auth::isAppUser($auth->getRoles()); @@ -819,8 +819,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId') } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $validator = new Authorization(Database::PERMISSION_READ); - $valid = $validator->isValid($bucket->getRead()); + $valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -873,7 +872,8 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->inject('mode') ->inject('deviceForFiles') ->inject('deviceForLocal') - ->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) { + ->inject('auth') + ->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, Authorization $auth) { if (!\extension_loaded('imagick')) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); @@ -889,8 +889,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $validator = new Authorization(Database::PERMISSION_READ); - $valid = $validator->isValid($bucket->getRead()); + $valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1033,7 +1032,8 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->inject('dbForProject') ->inject('mode') ->inject('deviceForFiles') - ->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Device $deviceForFiles) { + ->inject('auth') + ->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Device $deviceForFiles, Authorization $auth) { $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -1045,8 +1045,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download') } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $validator = new Authorization(Database::PERMISSION_READ); - $valid = $validator->isValid($bucket->getRead()); + $valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1173,7 +1172,8 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->inject('dbForProject') ->inject('mode') ->inject('deviceForFiles') - ->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceForFiles) { + ->inject('auth') + ->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceForFiles, Authorization $auth) { $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $isAPIKey = Auth::isAppUser($auth->getRoles()); @@ -1184,8 +1184,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view') } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $validator = new Authorization(Database::PERMISSION_READ); - $valid = $validator->isValid($bucket->getRead()); + $valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1333,7 +1332,8 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('user') ->inject('mode') ->inject('queueForEvents') - ->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents, Authorization $auth) { $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -1345,8 +1345,7 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $validator = new Authorization(Database::PERMISSION_UPDATE); - $valid = $validator->isValid($bucket->getUpdate()); + $valid = $auth->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1439,7 +1438,8 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('mode') ->inject('deviceForFiles') ->inject('queueForDeletes') - ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes) { + ->inject('auth') + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes, Authorization $auth) { $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $isAPIKey = Auth::isAppUser($auth->getRoles()); @@ -1450,8 +1450,7 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $validator = new Authorization(Database::PERMISSION_DELETE); - $valid = $validator->isValid($bucket->getDelete()); + $valid = $auth->isValid(new Input(Database::PERMISSION_DELETE, $bucket->getDelete())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1464,7 +1463,7 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') } // Make sure we don't delete the file before the document permission check occurs - if ($fileSecurity && !$valid && !$validator->isValid($file->getDelete())) { + if ($fileSecurity && !$valid && !$auth->isValid($file->getDelete())) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1524,7 +1523,8 @@ Http::get('/v1/storage/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $range, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $range, Response $response, Database $dbForProject, Authorization $auth) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -1604,7 +1604,8 @@ Http::get('/v1/storage/:bucketId/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $bucketId, string $range, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $bucketId, string $range, Response $response, Database $dbForProject, Authorization $auth) { $bucket = $dbForProject->getDocument('buckets', $bucketId); @@ -1620,7 +1621,6 @@ Http::get('/v1/storage/:bucketId/usage') str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE), ]; - $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index c3542645db..a60bd2f644 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -17,7 +17,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Teams; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; -use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\Config\Config; use Utopia\Database\Database; @@ -36,11 +35,12 @@ use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; -use Utopia\Locale\Locale; +use Utopia\Http\Http; use Utopia\Http\Validator\ArrayList; use Utopia\Http\Validator\Assoc; use Utopia\Http\Validator\Host; use Utopia\Http\Validator\Text; +use Utopia\Locale\Locale; Http::post('/v1/teams') ->desc('Create team') @@ -63,7 +63,8 @@ Http::post('/v1/teams') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); $isAppUser = Auth::isAppUser($auth->getRoles()); @@ -396,7 +397,8 @@ Http::post('/v1/teams/:teamId/memberships') ->inject('queueForMails') ->inject('queueForMessaging') ->inject('queueForEvents') - ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, Authorization $auth) { $isAPIKey = Auth::isAppUser($auth->getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); @@ -863,7 +865,8 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { @@ -938,7 +941,8 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->inject('project') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Authorization $auth) { $protocol = $request->getProtocol(); $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -1067,7 +1071,8 @@ Http::delete('/v1/teams/:teamId/memberships/:membershipId') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents) { + ->inject('auth') + ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -1131,7 +1136,8 @@ Http::get('/v1/teams/:teamId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + ->inject('auth') + ->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { $team = $dbForProject->getDocument('teams', $teamId); @@ -1149,7 +1155,7 @@ Http::get('/v1/teams/:teamId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject); + $audit = new Audit($dbForProject, $auth); $resource = 'team/' . $team->getId(); $logs = $audit->getLogsByResource($resource, $limit, $offset); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 6bb9d6e555..e6524ff21c 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -21,7 +21,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Users; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; -use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\Config\Config; use Utopia\Database\Database; @@ -38,7 +37,7 @@ use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; -use Utopia\Locale\Locale; +use Utopia\Http\Http; use Utopia\Http\Validator\ArrayList; use Utopia\Http\Validator\Assoc; use Utopia\Http\Validator\Boolean; @@ -46,6 +45,7 @@ use Utopia\Http\Validator\Integer; use Utopia\Http\Validator\Range; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; +use Utopia\Locale\Locale; /** TODO: Remove function when we move to using utopia/platform */ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks): Document @@ -777,7 +777,8 @@ Http::get('/v1/users/:userId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { + ->inject('auth') + ->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { $user = $dbForProject->getDocument('users', $userId); @@ -795,7 +796,7 @@ Http::get('/v1/users/:userId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject); + $audit = new Audit($dbForProject, $auth); $logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset); @@ -2110,7 +2111,8 @@ Http::get('/v1/users/usage') ->inject('response') ->inject('dbForProject') ->inject('register') - ->action(function (string $range, Response $response, Database $dbForProject) { + ->inject('auth') + ->action(function (string $range, Response $response, Database $dbForProject, Authorization $auth) { $periods = Config::getParam('usage', []); $stats = $usage = []; diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index d1832a5825..e2451bc61a 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -8,7 +8,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Installations; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Vcs\Comment; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -32,6 +31,7 @@ use Utopia\Detector\Adapter\Python; use Utopia\Detector\Adapter\Ruby; use Utopia\Detector\Adapter\Swift; use Utopia\Detector\Detector; +use Utopia\Http\Http; use Utopia\Http\Validator\Boolean; use Utopia\Http\Validator\Host; use Utopia\Http\Validator\Text; @@ -40,7 +40,7 @@ use Utopia\VCS\Exception\RepositoryNotFound; use function Swoole\Coroutine\batch; -$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForConsole, Build $queueForBuilds, callable $getProjectDB, Request $request) { +$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForConsole, Build $queueForBuilds, callable $getProjectDB, Request $request, Authorization $auth) { foreach ($repositories as $resource) { $resourceType = $resource->getAttribute('resourceType'); @@ -821,8 +821,9 @@ Http::post('/v1/vcs/github/events') ->inject('dbForConsole') ->inject('getProjectDB') ->inject('queueForBuilds') + ->inject('auth') ->action( - function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) { + function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $auth) use ($createGitDeployments) { $payload = $request->getRawPayload(); $signatureRemote = $request->getHeader('x-hub-signature-256', ''); $signatureLocal = Http::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', ''); @@ -863,7 +864,7 @@ Http::post('/v1/vcs/github/events') // create new deployment only on push and not when branch is created if (!$providerBranchCreated) { - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForConsole, $queueForBuilds, $getProjectDB, $request); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $auth); } } elseif ($event == $github::EVENT_INSTALLATION) { if ($parsedPayload["action"] == "deleted") { @@ -919,7 +920,7 @@ Http::post('/v1/vcs/github/events') Query::orderDesc('$createdAt') ])); - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $queueForBuilds, $getProjectDB, $request); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $auth); } elseif ($parsedPayload["action"] == "closed") { // Allowed external contributions cleanup @@ -1092,7 +1093,8 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito ->inject('dbForConsole') ->inject('getProjectDB') ->inject('queueForBuilds') - ->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) { + ->inject('auth') + ->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $auth) use ($createGitDeployments) { $installation = $dbForConsole->getDocument('installations', $installationId); if ($installation->isEmpty()) { @@ -1140,7 +1142,7 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito $providerBranch = \explode(':', $pullRequestResponse['head']['label'])[1] ?? ''; $providerCommitHash = $pullRequestResponse['head']['sha'] ?? ''; - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForConsole, $queueForBuilds, $getProjectDB, $request); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $auth); $response->noContent(); }); diff --git a/app/controllers/general.php b/app/controllers/general.php index 606cfac9db..138e8f6ee7 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -26,7 +26,6 @@ use Appwrite\Utopia\View; use Executor\Executor; use MaxMind\Db\Reader; use Swoole\Http\Request as SwooleRequest; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -35,18 +34,19 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Domains\Domain; +use Utopia\Http\Http; +use Utopia\Http\Validator\Hostname; +use Utopia\Http\Validator\Text; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Logger\Log\User; use Utopia\Logger\Logger; -use Utopia\Http\Validator\Hostname; -use Utopia\Http\Validator\Text; Config::setParam('domainVerification', false); Config::setParam('cookieDomain', 'localhost'); Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE); -function router(App $utopia, Database $dbForConsole, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) +function router(Http $utopia, Database $dbForConsole, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $auth) { $utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml'); @@ -382,7 +382,8 @@ Http::init() ->inject('queueForUsage') ->inject('queueForEvents') ->inject('queueForCertificates') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates) { + ->inject('auth') + ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Authorization $auth) { /* * Appwrite Router */ @@ -390,7 +391,7 @@ Http::init() $mainDomain = Http::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain if ($host !== $mainDomain) { - if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb)) { + if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb, $auth)) { return; } } @@ -627,7 +628,8 @@ Http::options() ->inject('queueForEvents') ->inject('queueForUsage') ->inject('geodb') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) { + ->inject('auth') + ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $auth) { /* * Appwrite Router */ @@ -635,7 +637,7 @@ Http::options() $mainDomain = Http::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain if ($host !== $mainDomain) { - if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb)) { + if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb, $auth)) { return; } } @@ -660,7 +662,8 @@ Http::error() ->inject('project') ->inject('logger') ->inject('log') - ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log) { + ->inject('auth') + ->action(function (Throwable $error, Http $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $auth) { $version = Http::getEnv('_APP_VERSION', 'UNKNOWN'); $route = $utopia->getRoute(); @@ -734,7 +737,7 @@ Http::error() } /** Handle Utopia Errors */ - if ($error instanceof Utopia\Exception) { + if ($error instanceof Utopia\Http\Exception) { $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); switch ($code) { case 400: diff --git a/app/controllers/mock.php b/app/controllers/mock.php index 544c2a7dc8..1b7565d33d 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -5,16 +5,16 @@ global $utopia, $request, $response; use Appwrite\Extend\Exception; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Utopia\Http\Http; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\UID; -use Utopia\Http\Validator\WhiteList; +use Utopia\Http\Http; use Utopia\Http\Validator\Host; use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub; Http::get('/v1/mock/tests/general/oauth2') @@ -218,7 +218,7 @@ Http::shutdown() ->inject('utopia') ->inject('response') ->inject('request') - ->action(function (App $utopia, Response $response, Request $request) { + ->action(function (Http $utopia, Response $response, Request $request) { $result = []; $route = $utopia->getRoute(); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index e86215158b..fff4a7114c 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -16,7 +16,6 @@ use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; -use Utopia\Http\Http; use Utopia\Cache\Adapter\Filesystem; use Utopia\Cache\Cache; use Utopia\Config\Config; @@ -25,7 +24,11 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; +use Utopia\Database\Validator\Authorization\Input; +use Utopia\Http\Http; use Utopia\Http\Validator\WhiteList; +use Utopia\Pools\Group; +use Utopia\Pools\Pool; $parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) { preg_match_all('/{(.*?)}/', $label, $matches); @@ -328,7 +331,7 @@ Http::init() foreach ($abuseKeyLabel as $abuseKey) { $start = $request->getContentRangeStart(); $end = $request->getContentRangeEnd(); - $timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject); + $timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject, $auth); $timeLimit ->setParam('{projectId}', $project->getId()) ->setParam('{userId}', $user->getId()) @@ -433,8 +436,7 @@ Http::init() } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $validator = new Authorization(Database::PERMISSION_READ); - $valid = $validator->isValid($bucket->getRead()); + $valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -702,8 +704,6 @@ Http::shutdown() } } - - if ($project->getId() !== 'console') { if ($mode !== APP_MODE_ADMIN) { $fileSize = 0; @@ -747,3 +747,16 @@ Http::init() throw new Exception(Exception::GENERAL_USAGE_DISABLED); } }); + +Http::shutdown() + ->inject('pools') + ->action(function (Group $pools) { + $pools->reclaim(); + }); + + +Http::error() + ->inject('pools') + ->action(function (Group $pools) { + $pools->reclaim(); + }); diff --git a/app/controllers/shared/api/auth.php b/app/controllers/shared/api/auth.php index a8a0ac9e84..956847a774 100644 --- a/app/controllers/shared/api/auth.php +++ b/app/controllers/shared/api/auth.php @@ -4,10 +4,10 @@ use Appwrite\Auth\Auth; use Appwrite\Extend\Exception; use Appwrite\Utopia\Request; use MaxMind\Db\Reader; -use Utopia\Http\Http; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; +use Utopia\Http\Http; Http::init() ->groups(['mfaProtected']) diff --git a/app/controllers/web/home.php b/app/controllers/web/home.php index 1d894b95bb..3577061d69 100644 --- a/app/controllers/web/home.php +++ b/app/controllers/web/home.php @@ -1,8 +1,8 @@ desc('Get Version') diff --git a/app/http.php b/app/http.php index 54c6374274..cfd09ce76f 100644 --- a/app/http.php +++ b/app/http.php @@ -4,13 +4,8 @@ require_once __DIR__ . '/../vendor/autoload.php'; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Swoole\Constant; -use Swoole\Http\Request as SwooleRequest; -use Swoole\Http\Response as SwooleResponse; -use Swoole\Http\Server; use Swoole\Process; use Utopia\Abuse\Adapters\TimeLimit; -use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -20,315 +15,198 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; -use Utopia\Logger\Log; -use Utopia\Logger\Log\User; +use Utopia\Http\Adapter\Swoole\Request as SwooleRequest; +use Utopia\Http\Adapter\Swoole\Response as SwooleResponse; +use Utopia\Http\Adapter\Swoole\Server; +use Utopia\Http\Http; use Utopia\Pools\Group; -use Utopia\Swoole\Files; - -$http = new Server( - host: "0.0.0.0", - port: Http::getEnv('PORT', 80), - mode: SWOOLE_PROCESS, -); $payloadSize = 6 * (1024 * 1024); // 6MB $workerNumber = swoole_cpu_num() * intval(Http::getEnv('_APP_WORKER_PER_CORE', 6)); -$http - ->set([ - 'worker_num' => $workerNumber, - 'open_http2_protocol' => true, - 'http_compression' => true, - 'http_compression_level' => 6, - 'package_max_length' => $payloadSize, - 'buffer_output_size' => $payloadSize, - ]); - -$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) { - Console::success('Worker ' . ++$workerId . ' started successfully'); -}); - -$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) { - Console::success('Starting reload...'); -}); - -$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) { - Console::success('Reload completed...'); -}); - -Files::load(__DIR__ . '/../console'); - include __DIR__ . '/controllers/general.php'; -$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) { - $app = new App('UTC'); +$http = new Http(new Server('0.0.0.0', Http::getEnv('PORT', 80), [ + 'open_http2_protocol' => true, + 'http_compression' => true, + 'http_compression_level' => 6, + 'package_max_length' => $payloadSize, + 'buffer_output_size' => $payloadSize, +]), 'UTC'); - go(function () use ($register, $app) { - $pools = $register->get('pools'); - /** @var Group $pools */ - Http::setResource('pools', fn () => $pools); +$http->setRequestClass(Request::class); +$http->setResponseClass(Response::class); - // wait for database to be ready - $attempts = 0; - $max = 10; - $sleep = 1; +$http->loadFiles(__DIR__ . '/../console'); - do { - try { - $attempts++; - $dbForConsole = $app->getResource('dbForConsole'); - $dbForConsole->ping(); - /** @var Utopia\Database\Database $dbForConsole */ - break; // leave the do-while if successful - } catch (\Throwable $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: ' . $e->getMessage()); - } - sleep($sleep); - } - } while ($attempts < $max); +go(function () use ($register, $http, $payloadSize) { + $pools = $register->get('pools'); + /** @var Group $pools */ + Http::setResource('pools', fn () => $pools); + $auth = new Authorization(); - Console::success('[Setup] - Server database init started...'); + // wait for database to be ready + $attempts = 0; + $max = 10; + $sleep = 1; + do { try { - Console::success('[Setup] - Creating database: appwrite...'); - $dbForConsole->create(); + $attempts++; + $dbForConsole = $http->getResource('dbForConsole'); + $dbForConsole->ping(); + /** @var Utopia\Database\Database $dbForConsole */ + break; // leave the do-while if successful } catch (\Throwable $e) { - Console::success('[Setup] - Skip: metadata table already exists'); + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: ' . $e->getMessage()); + } + sleep($sleep); + } + } while ($attempts < $max); + + Console::success('[Setup] - Server database init started...'); + + try { + Console::success('[Setup] - Creating database: appwrite...'); + $dbForConsole->create(); + } catch (\Throwable $e) { + Console::success('[Setup] - Skip: metadata table already exists'); + } + + if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) { + $audit = new Audit($dbForConsole, $auth); + $audit->setup(); + } + + if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { + $adapter = new TimeLimit("", 0, 1, $dbForConsole, $auth); + $adapter->setup(); + } + + /** @var array $collections */ + $collections = Config::getParam('collections', []); + $consoleCollections = $collections['console']; + foreach ($consoleCollections as $key => $collection) { + if (($collection['$collection'] ?? '') !== Database::METADATA) { + continue; + } + if (!$dbForConsole->getCollection($key)->isEmpty()) { + continue; } - if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) { - $audit = new Audit($dbForConsole); - $audit->setup(); + Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); + + $attributes = []; + $indexes = []; + + foreach ($collection['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]); } - if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { - $adapter = new TimeLimit("", 0, 1, $dbForConsole); - $adapter->setup(); + foreach ($collection['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); } - /** @var array $collections */ - $collections = Config::getParam('collections', []); - $consoleCollections = $collections['console']; - foreach ($consoleCollections as $key => $collection) { - if (($collection['$collection'] ?? '') !== Database::METADATA) { - continue; - } - if (!$dbForConsole->getCollection($key)->isEmpty()) { - continue; - } + $dbForConsole->createCollection($key, $attributes, $indexes); + } - Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); + if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) { + Console::success('[Setup] - Creating default bucket...'); + $dbForConsole->createDocument('buckets', new Document([ + '$id' => ID::custom('default'), + '$collection' => ID::custom('buckets'), + 'name' => 'Default', + 'maximumFileSize' => (int) Http::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB + 'allowedFileExtensions' => [], + 'enabled' => true, + 'compression' => 'gzip', + 'encryption' => true, + 'antivirus' => true, + 'fileSecurity' => true, + '$permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'search' => 'buckets Default', + ])); - $attributes = []; - $indexes = []; + $bucket = $dbForConsole->getDocument('buckets', 'default'); - foreach ($collection['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]); - } - - foreach ($collection['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } - - $dbForConsole->createCollection($key, $attributes, $indexes); + Console::success('[Setup] - Creating files collection for default bucket...'); + $files = $collections['buckets']['files'] ?? []; + if (empty($files)) { + throw new Exception('Files collection is not configured.'); } - if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) { - Console::success('[Setup] - Creating default bucket...'); - $dbForConsole->createDocument('buckets', new Document([ - '$id' => ID::custom('default'), - '$collection' => ID::custom('buckets'), - 'name' => 'Default', - 'maximumFileSize' => (int) Http::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB - 'allowedFileExtensions' => [], - 'enabled' => true, - 'compression' => 'gzip', - 'encryption' => true, - 'antivirus' => true, - 'fileSecurity' => true, - '$permissions' => [ - Permission::create(Role::any()), - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'search' => 'buckets Default', - ])); + $attributes = []; + $indexes = []; - $bucket = $dbForConsole->getDocument('buckets', 'default'); - - Console::success('[Setup] - Creating files collection for default bucket...'); - $files = $collections['buckets']['files'] ?? []; - if (empty($files)) { - throw new Exception('Files collection is not configured.'); - } - - $attributes = []; - $indexes = []; - - foreach ($files['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]); - } - - foreach ($files['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } - - $dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); + foreach ($files['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]); } - $pools->reclaim(); + foreach ($files['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); + } - Console::success('[Setup] - Server database init completed...'); - }); + $dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); + } + + $pools->reclaim(); + + Console::success('[Setup] - Server database init completed...'); Console::success('Server started successfully (max payload is ' . number_format($payloadSize) . ' bytes)'); - Console::info("Master pid {$http->master_pid}, manager pid {$http->manager_pid}"); // listen ctrl + c Process::signal(2, function () use ($http) { Console::log('Stop by Ctrl+C'); $http->shutdown(); }); + + Http::init() + ->inject('auth') + ->action(function (Authorization $auth) { + $auth->cleanRoles(); + $auth->addRole(Role::any()->toString()); + }); + + $http->start(); }); -$http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register) { - Http::setResource('swooleRequest', fn () => $swooleRequest); - Http::setResource('swooleResponse', fn () => $swooleResponse); - - $request = new Request($swooleRequest); - $response = new Response($swooleResponse); - - if (Files::isFileLoaded($request->getURI())) { - $time = (60 * 60 * 24 * 365 * 2); // 45 days cache - - $response - ->setContentType(Files::getFileMimeType($request->getURI())) - ->addHeader('Cache-Control', 'public, max-age=' . $time) - ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time) . ' GMT') // 45 days cache - ->send(Files::getFileContents($request->getURI())); - - return; - } - - $app = new App('UTC'); - - $pools = $register->get('pools'); - Http::setResource('pools', fn () => $pools); - - try { - $auth->cleanRoles(); - $auth->addRole(Role::any()->toString()); - - $app->run($request, $response); - } catch (\Throwable $th) { - $version = Http::getEnv('_APP_VERSION', 'UNKNOWN'); - - $logger = $app->getResource("logger"); - if ($logger) { - try { - /** @var Utopia\Database\Document $user */ - $user = $app->getResource('user'); - } catch (\Throwable $_th) { - // All good, user is optional information for logger - } - - $route = $app->getRoute(); - - $log = $app->getResource("log"); - - if (isset($user) && !$user->isEmpty()) { - $log->setUser(new User($user->getId())); - } - - $log->setNamespace("http"); - $log->setServer(\gethostname()); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($th->getMessage()); - - $log->addTag('method', $route->getMethod()); - $log->addTag('url', $route->getPath()); - $log->addTag('verboseType', get_class($th)); - $log->addTag('code', $th->getCode()); - // $log->addTag('projectId', $project->getId()); // TODO: Figure out how to get ProjectID, if it becomes relevant - $log->addTag('hostname', $request->getHostname()); - $log->addTag('locale', (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', ''))); - - $log->addExtra('file', $th->getFile()); - $log->addExtra('line', $th->getLine()); - $log->addExtra('trace', $th->getTraceAsString()); - $log->addExtra('detailedTrace', $th->getTrace()); - $log->addExtra('roles', $auth->getRoles()); - - $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); - $log->setAction($action); - - $isProduction = Http::getEnv('_APP_ENV', 'development') === 'production'; - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - - $responseCode = $logger->addLog($log); - Console::info('Log pushed with status code: ' . $responseCode); - } - - Console::error('[Error] Type: ' . get_class($th)); - Console::error('[Error] Message: ' . $th->getMessage()); - Console::error('[Error] File: ' . $th->getFile()); - Console::error('[Error] Line: ' . $th->getLine()); - - $swooleResponse->setStatusCode(500); - - $output = ((Http::isDevelopment())) ? [ - 'message' => 'Error: ' . $th->getMessage(), - 'code' => 500, - 'file' => $th->getFile(), - 'line' => $th->getLine(), - 'trace' => $th->getTrace(), - 'version' => $version, - ] : [ - 'message' => 'Error: Server Error', - 'code' => 500, - 'version' => $version, - ]; - - $swooleResponse->end(\json_encode($output)); - } finally { - $pools->reclaim(); - } -}); - -$http->start(); diff --git a/app/init.php b/app/init.php index ca6626223e..6773d11236 100644 --- a/app/init.php +++ b/app/init.php @@ -43,7 +43,6 @@ use Appwrite\URL\URL as AppwriteURL; use MaxMind\Db\Reader; use PHPMailer\PHPMailer\PHPMailer; use Swoole\Database\PDOProxy; -use Utopia\Http\Http; use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; @@ -62,8 +61,14 @@ use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Structure; use Utopia\Domains\Validator\PublicDomain; use Utopia\DSN\DSN; +use Utopia\Http\Http; use Utopia\Http\Request; use Utopia\Http\Response; +use Utopia\Http\Validator\Hostname; +use Utopia\Http\Validator\IP; +use Utopia\Http\Validator\Range; +use Utopia\Http\Validator\URL; +use Utopia\Http\Validator\WhiteList; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Logger\Logger; @@ -80,11 +85,6 @@ use Utopia\Storage\Device\Local; use Utopia\Storage\Device\S3; use Utopia\Storage\Device\Wasabi; use Utopia\Storage\Storage; -use Utopia\Http\Validator\Hostname; -use Utopia\Http\Validator\IP; -use Utopia\Http\Validator\Range; -use Utopia\Http\Validator\URL; -use Utopia\Http\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; const APP_NAME = 'Appwrite'; @@ -1307,7 +1307,7 @@ Http::setResource('console', function () { ]); }, []); -Http::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { +Http::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project, Authorization $auth) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } @@ -1318,6 +1318,7 @@ Http::setResource('dbForProject', function (Group $pools, Database $dbForConsole ->getResource(); $database = new Database($dbAdapter, $cache); + $database->setAuthorization($auth); $database ->setNamespace('_' . $project->getInternalId()) @@ -1326,9 +1327,9 @@ Http::setResource('dbForProject', function (Group $pools, Database $dbForConsole ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); return $database; -}, ['pools', 'dbForConsole', 'cache', 'project']); +}, ['pools', 'dbForConsole', 'cache', 'project', 'auth']); -Http::setResource('dbForConsole', function (Group $pools, Cache $cache) { +Http::setResource('dbForConsole', function (Group $pools, Cache $cache, Authorization $auth) { $dbAdapter = $pools ->get('console') ->pop() @@ -1336,6 +1337,7 @@ Http::setResource('dbForConsole', function (Group $pools, Cache $cache) { ; $database = new Database($dbAdapter, $cache); + $database->setAuthorization($auth); $database ->setNamespace('_console') @@ -1344,12 +1346,12 @@ Http::setResource('dbForConsole', function (Group $pools, Cache $cache) { ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); return $database; -}, ['pools', 'cache']); +}, ['pools', 'cache', 'auth']); -Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { +Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - $getProjectDB = function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { + $getProjectDB = function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } @@ -1374,6 +1376,7 @@ Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole ->getResource(); $database = new Database($dbAdapter, $cache); + $database->setAuthorization($auth); $databases[$databaseName] = $database; @@ -1387,7 +1390,7 @@ Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole }; return $getProjectDB; -}, ['pools', 'dbForConsole', 'cache']); +}, ['pools', 'dbForConsole', 'cache', 'auth']); Http::setResource('cache', function (Group $pools) { $list = Config::getParam('pools-cache', []); @@ -1661,4 +1664,8 @@ Http::setResource('requestTimestamp', function ($request) { return $requestTimestamp; }, ['request']); -Http::setResource('auth', fn () => new Authorization()); \ No newline at end of file +Http::setResource('auth', fn () => new Authorization()); + +Http::setResource('pools', function ($register) { + return $register->get('pools'); +}, ['pools']); diff --git a/app/realtime.php b/app/realtime.php index e38caf488a..d8234a691c 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -13,7 +13,6 @@ use Swoole\Table; use Swoole\Timer; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; -use Utopia\Http\Http; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; @@ -26,6 +25,7 @@ use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Http\Adapter\FPM\Server as FPMServer; +use Utopia\Http\Http; use Utopia\Logger\Log; use Utopia\WebSocket\Adapter; use Utopia\WebSocket\Server; @@ -37,7 +37,7 @@ require_once __DIR__ . '/init.php'; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -function getConsoleDB(): Database +function getConsoleDB(Authorization $auth): Database { global $register; @@ -51,6 +51,7 @@ function getConsoleDB(): Database ; $database = new Database($dbAdapter, getCache()); + $database->setAuthorization($auth); $database ->setNamespace('_console') @@ -60,7 +61,7 @@ function getConsoleDB(): Database return $database; } -function getProjectDB(Document $project): Database +function getProjectDB(Document $project, Authorization $auth): Database { global $register; @@ -68,7 +69,7 @@ function getProjectDB(Document $project): Database $pools = $register->get('pools'); if ($project->isEmpty() || $project->getId() === 'console') { - return getConsoleDB(); + return getConsoleDB($auth); } $dbAdapter = $pools @@ -78,6 +79,7 @@ function getProjectDB(Document $project): Database ; $database = new Database($dbAdapter, getCache()); + $database->setAuthorization($auth); $database ->setNamespace('_' . $project->getInternalId()) @@ -170,15 +172,17 @@ $logError = function (Throwable $error, string $action) use ($register) { $server->error($logError); $server->onStart(function () use ($stats, $register, $containerId, &$statsDocument, $logError) { + $auth = new Authorization(); + sleep(5); // wait for the initial database schema to be ready Console::success('Server started successfully'); /** * Create document for this worker to share stats across Containers. */ - go(function () use ($register, $containerId, &$statsDocument) { + go(function () use ($register, $containerId, &$statsDocument, $auth) { $attempts = 0; - $database = getConsoleDB(); + $database = getConsoleDB($auth); do { try { @@ -206,7 +210,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume /** * Save current connections to the Database every 5 seconds. */ - Timer::tick(5000, function () use ($register, $stats, &$statsDocument, $logError) { + Timer::tick(5000, function () use ($register, $stats, &$statsDocument, $logError, $auth) { $payload = []; foreach ($stats as $projectId => $value) { $payload[$projectId] = $stats->get($projectId, 'connectionsTotal'); @@ -216,7 +220,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume } try { - $database = getConsoleDB(); + $database = getConsoleDB($auth); $statsDocument ->setAttribute('timestamp', DateTime::now()) @@ -238,16 +242,17 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $attempts = 0; $start = time(); - Timer::tick(5000, function () use ($server, $register, $realtime, $stats, $logError) { + $auth = new Authorization(); + + Timer::tick(5000, function () use ($server, $register, $realtime, $stats, $logError, $auth) { /** * Sending current connections to project channels on the console project every 5 seconds. */ if ($realtime->hasSubscriber('console', Role::users()->toString(), 'project')) { - $database = getConsoleDB(); + $database = getConsoleDB($auth); $payload = []; - $auth = new Authorization(); $list = $auth->skip(fn () => $database->find('realtime', [ Query::greaterThan('timestamp', DateTime::addSeconds(new \DateTime(), -15)), ])); @@ -334,7 +339,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, Console::error('Pub/sub failed (worker: ' . $workerId . ')'); } - $redis->subscribe(['realtime'], function (Redis $redis, string $channel, string $payload) use ($server, $workerId, $stats, $register, $realtime) { + $redis->subscribe(['realtime'], function (Redis $redis, string $channel, string $payload) use ($server, $workerId, $stats, $register, $realtime, $auth) { $event = json_decode($payload, true); if ($event['permissionsChanged'] && isset($event['userId'])) { @@ -343,10 +348,10 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) { $connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId])); - $consoleDatabase = getConsoleDB(); + $consoleDatabase = getConsoleDB($auth); $auth = new Authorization(); $project = $auth->skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); - $database = getProjectDB($project); + $database = getProjectDB($project, $auth); $user = $database->getDocument('users', $userId); @@ -394,7 +399,9 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, }); $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime, $logError) { - $app = new Http(new FPMServer(), 'UTC'); + $auth = new Authorization(); + + $http = new Http(new FPMServer(), 'UTC'); $request = new Request($request); $response = new Response(new SwooleResponse()); @@ -406,7 +413,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, try { /** @var Document $project */ - $project = $app->getResource('project'); + $project = $http->getResource('project'); /* * Project Check @@ -415,21 +422,22 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, throw new Exception(Exception::REALTIME_POLICY_VIOLATION, 'Missing or unknown project ID'); } - $dbForProject = getProjectDB($project); - $console = $app->getResource('console'); /** @var Document $console */ - $user = $app->getResource('user'); /** @var Document $user */ + $dbForProject = getProjectDB($project, $auth); + $console = $http->getResource('console'); /** @var Document $console */ + $user = $http->getResource('user'); /** @var Document $user */ + $auth = new Authorization(); /* * Abuse Check * * Abuse limits are connecting 128 times per minute and ip address. */ - $timeLimit = new TimeLimit('url:{url},ip:{ip}', 128, 60, $dbForProject); + $timeLimit = new TimeLimit('url:{url},ip:{ip}', 128, 60, $dbForProject, $auth); $timeLimit ->setParam('{ip}', $request->getIP()) ->setParam('{url}', $request->getURI()); - $abuse = new Abuse($timeLimit); + $abuse = new Abuse($timeLimit, $auth); if (Http::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled' && $abuse->check()) { throw new Exception(Exception::REALTIME_TOO_MANY_MESSAGES, 'Too many requests'); @@ -502,15 +510,17 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, }); $server->onMessage(function (int $connection, string $message) use ($server, $register, $realtime, $containerId) { + $auth = new Authorization(); + try { $response = new Response(new SwooleResponse()); $projectId = $realtime->connections[$connection]['projectId']; - $database = getConsoleDB(); + $database = getConsoleDB($auth); if ($projectId !== 'console') { $auth = new Authorization(); $project = $auth->skip(fn () => $database->getDocument('projects', $projectId)); - $database = getProjectDB($project); + $database = getProjectDB($project, $auth); } else { $project = null; } @@ -520,13 +530,13 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re * * Abuse limits are sending 32 times per minute and connection. */ - $timeLimit = new TimeLimit('url:{url},connection:{connection}', 32, 60, $database); + $timeLimit = new TimeLimit('url:{url},connection:{connection}', 32, 60, $database, $auth); $timeLimit ->setParam('{connection}', $connection) ->setParam('{container}', $containerId); - $abuse = new Abuse($timeLimit); + $abuse = new Abuse($timeLimit, $auth); if ($abuse->check() && Http::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') { throw new Exception(Exception::REALTIME_TOO_MANY_MESSAGES, 'Too many messages.'); diff --git a/app/worker.php b/app/worker.php index e81e0f9f5f..61203c2d5a 100644 --- a/app/worker.php +++ b/app/worker.php @@ -17,7 +17,6 @@ use Appwrite\Event\Usage; use Appwrite\Event\UsageDump; use Appwrite\Platform\Appwrite; use Swoole\Runtime; -use Utopia\Http\Http; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; @@ -26,6 +25,7 @@ use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; +use Utopia\Http\Http; use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Platform\Service; @@ -38,12 +38,11 @@ use Utopia\Storage\Device\Local; global $register; -$auth->disable(); Runtime::enableCoroutine(SWOOLE_HOOK_ALL); Server::setResource('register', fn () => $register); -Server::setResource('dbForConsole', function (Cache $cache, Registry $register) { +Server::setResource('dbForConsole', function (Cache $cache, Registry $register, Authorization $auth) { $pools = $register->get('pools'); $database = $pools ->get('console') @@ -51,10 +50,11 @@ Server::setResource('dbForConsole', function (Cache $cache, Registry $register) ->getResource(); $adapter = new Database($database, $cache); + $adapter->setAuthorization($auth); $adapter->setNamespace('_console'); return $adapter; -}, ['cache', 'register']); +}, ['cache', 'register', 'auth']); Server::setResource('project', function (Message $message, Database $dbForConsole) { $payload = $message->getPayload() ?? []; @@ -67,7 +67,7 @@ Server::setResource('project', function (Message $message, Database $dbForConsol return $dbForConsole->getDocument('projects', $project->getId()); }, ['message', 'dbForConsole']); -Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForConsole) { +Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForConsole, Authorization $auth) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } @@ -79,14 +79,15 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, ->getResource(); $adapter = new Database($database, $cache); + $adapter->setAuthorization($auth); $adapter->setNamespace('_' . $project->getInternalId()); return $adapter; -}, ['cache', 'register', 'message', 'project', 'dbForConsole']); +}, ['cache', 'register', 'message', 'project', 'dbForConsole', 'auth']); -Server::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { +Server::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases): Database { + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth): Database { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } @@ -105,6 +106,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForConso ->getResource(); $database = new Database($dbAdapter, $cache); + $database->setAuthorization($auth); $databases[$databaseName] = $database; @@ -112,7 +114,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForConso return $database; }; -}, ['pools', 'dbForConsole', 'cache']); +}, ['pools', 'dbForConsole', 'cache', 'auth']); Server::setResource('abuseRetention', function () { return DateTime::addSeconds(new \DateTime(), -1 * Http::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400)); @@ -228,7 +230,7 @@ Server::setResource('deviceForLocalFiles', function (Document $project) { return new Local(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); }, ['project']); -Server::setResource('authorization', fn () => new Authorization()); +Server::setResource('auth', fn () => new Authorization()); $pools = $register->get('pools'); $platform = new Appwrite(); @@ -272,6 +274,13 @@ try { $worker = $platform->getWorker(); +$worker + ->init() + ->inject('auth') + ->action(function (Authorization $auth) { + $auth->disable(); + }); + $worker ->shutdown() ->inject('pools') diff --git a/composer.json b/composer.json index acb933c7e4..9440299990 100644 --- a/composer.json +++ b/composer.json @@ -45,16 +45,16 @@ "ext-sockets": "*", "appwrite/php-runtimes": "0.13.*", "appwrite/php-clamav": "2.0.*", - "utopia-php/abuse": "0.37.*", + "utopia-php/abuse": "dev-feat-framework-v2 as 0.37.99", "utopia-php/analytics": "dev-feat-framework-v2 as 0.10.99", - "utopia-php/audit": "0.39.*", + "utopia-php/audit": "dev-feat-framework-v2 as 0.39.99", "utopia-php/cache": "0.9.*", "utopia-php/cli": "0.17.*", "utopia-php/config": "0.2.*", "utopia-php/database": "dev-feat-framework-v2 as 0.49.99", - "utopia-php/domains": "0.5.*", + "utopia-php/domains": "dev-feat-framework-v2 as 0.5.99", "utopia-php/dsn": "0.2.*", - "utopia-php/framework": "0.34.*", + "utopia-php/framework": "dev-feat-framework-v2 as 0.34.99", "utopia-php/image": "0.6.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.3.*", @@ -66,7 +66,7 @@ "utopia-php/view": "0.1.*", "utopia-php/queue": "dev-feat-framework-v2-v2 as 0.7.99", "utopia-php/registry": "0.5.*", - "utopia-php/storage": "0.18.*", + "utopia-php/storage": "dev-feat-framework-v2-v2 as 0.18.99", "utopia-php/vcs": "0.6.*", "utopia-php/websocket": "0.1.*", "matomo/device-detector": "6.1.*", diff --git a/composer.lock b/composer.lock index a90d6b2d3e..95d7ddd8fa 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": "98a533604587b5d0f5cc3c52ae177aeb", + "content-hash": "750df68fe066fde9554f4d1ba4a9afb5", "packages": [ { "name": "adhocore/jwt", @@ -482,16 +482,16 @@ }, { "name": "jean85/pretty-package-versions", - "version": "2.0.5", + "version": "2.0.6", "source": { "type": "git", "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af" + "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/ae547e455a3d8babd07b96966b17d7fd21d9c6af", - "reference": "ae547e455a3d8babd07b96966b17d7fd21d9c6af", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", + "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", "shasum": "" }, "require": { @@ -499,9 +499,9 @@ "php": "^7.1|^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.17", + "friendsofphp/php-cs-fixer": "^3.2", "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^0.12.66", + "phpstan/phpstan": "^1.4", "phpunit/phpunit": "^7.5|^8.5|^9.4", "vimeo/psalm": "^4.3" }, @@ -535,9 +535,9 @@ ], "support": { "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.5" + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" }, - "time": "2021-10-08T21:21:46+00:00" + "time": "2024-03-08T09:58:59+00:00" }, { "name": "league/csv", @@ -771,11 +771,54 @@ "version": "0.6.3", "source": { "type": "git", - "url": "https://github.com/mustangostang/spyc", + "url": "https://github.com/mustangostang/spyc.git", "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0" }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0", + "reference": "4627c838b16550b666d15aeae1e5289dd5b77da0", + "shasum": "" + }, + "require": { + "php": ">=5.3.1" + }, + "require-dev": { + "phpunit/phpunit": "4.3.*@dev" + }, "type": "library", - "notification-url": "https://packagist.org/downloads/" + "extra": { + "branch-alias": { + "dev-master": "0.5.x-dev" + } + }, + "autoload": { + "files": [ + "Spyc.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "mustangostang", + "email": "vlad.andersen@gmail.com" + } + ], + "description": "A simple YAML loader/dumper class for PHP", + "homepage": "https://github.com/mustangostang/spyc/", + "keywords": [ + "spyc", + "yaml", + "yml" + ], + "support": { + "issues": "https://github.com/mustangostang/spyc/issues", + "source": "https://github.com/mustangostang/spyc/tree/0.6.3" + }, + "time": "2019-09-10T13:16:29+00:00" }, { "name": "paragonie/constant_time_encoding", @@ -1221,23 +1264,23 @@ }, { "name": "utopia-php/abuse", - "version": "0.37.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "2de5c12886cbd516e511e559afdd9e615d871062" + "reference": "55b34b581c957fc98f912943d94dcdc7079f191e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/2de5c12886cbd516e511e559afdd9e615d871062", - "reference": "2de5c12886cbd516e511e559afdd9e615d871062", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/55b34b581c957fc98f912943d94dcdc7079f191e", + "reference": "55b34b581c957fc98f912943d94dcdc7079f191e", "shasum": "" }, "require": { "ext-curl": "*", "ext-pdo": "*", "php": ">=8.0", - "utopia-php/database": "0.49.*" + "utopia-php/database": "dev-feat-framework-v2 as 0.49.99" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1264,9 +1307,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.37.0" + "source": "https://github.com/utopia-php/abuse/tree/feat-framework-v2" }, - "time": "2024-03-06T21:20:27+00:00" + "time": "2024-03-08T11:27:58+00:00" }, { "name": "utopia-php/analytics", @@ -1316,21 +1359,21 @@ }, { "name": "utopia-php/audit", - "version": "0.39.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "f0bc15012e05cc0b9dde012ab27d25f193768a2c" + "reference": "59f88d71f9d93603393aeda368a975b10b8ddb17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/f0bc15012e05cc0b9dde012ab27d25f193768a2c", - "reference": "f0bc15012e05cc0b9dde012ab27d25f193768a2c", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/59f88d71f9d93603393aeda368a975b10b8ddb17", + "reference": "59f88d71f9d93603393aeda368a975b10b8ddb17", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.49.*" + "utopia-php/database": "dev-feat-framework-v2 as 0.49.99" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1357,9 +1400,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.39.0" + "source": "https://github.com/utopia-php/audit/tree/feat-framework-v2" }, - "time": "2024-03-06T21:20:37+00:00" + "time": "2024-03-08T11:29:31+00:00" }, { "name": "utopia-php/cache", @@ -1570,21 +1613,21 @@ }, { "name": "utopia-php/domains", - "version": "0.5.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c" + "reference": "4e7055f0aaba0c16ae60c972faefb9189fa0db1c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/bf07f60326f8389f378ddf6fcde86217e5cfe18c", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/4e7055f0aaba0c16ae60c972faefb9189fa0db1c", + "reference": "4e7055f0aaba0c16ae60c972faefb9189fa0db1c", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/framework": "0.*.*" + "utopia-php/framework": "0.34.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -1624,9 +1667,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.5.0" + "source": "https://github.com/utopia-php/domains/tree/feat-framework-v2" }, - "time": "2024-01-03T22:04:27+00:00" + "time": "2024-03-08T09:24:35+00:00" }, { "name": "utopia-php/dsn", @@ -1716,16 +1759,16 @@ }, { "name": "utopia-php/framework", - "version": "0.34.2", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "fd126c02b78cc80678c9638f7b335dfb4a841b78" + "reference": "e2e7498aa16cefcdcb474548c3d04ce720ec6430" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/fd126c02b78cc80678c9638f7b335dfb4a841b78", - "reference": "fd126c02b78cc80678c9638f7b335dfb4a841b78", + "url": "https://api.github.com/repos/utopia-php/http/zipball/e2e7498aa16cefcdcb474548c3d04ce720ec6430", + "reference": "e2e7498aa16cefcdcb474548c3d04ce720ec6430", "shasum": "" }, "require": { @@ -1758,9 +1801,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.34.2" + "source": "https://github.com/utopia-php/http/tree/feat-framework-v2" }, - "time": "2024-02-20T11:36:56+00:00" + "time": "2024-03-08T10:38:48+00:00" }, { "name": "utopia-php/image", @@ -2229,12 +2272,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "e613ccc1d4da4219b60576ddfe79dadb182bb74e" + "reference": "0e9ba1b32169d64a78909fd77891db4d64344a63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/e613ccc1d4da4219b60576ddfe79dadb182bb74e", - "reference": "e613ccc1d4da4219b60576ddfe79dadb182bb74e", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/0e9ba1b32169d64a78909fd77891db4d64344a63", + "reference": "0e9ba1b32169d64a78909fd77891db4d64344a63", "shasum": "" }, "require": { @@ -2282,7 +2325,7 @@ "issues": "https://github.com/utopia-php/queue/issues", "source": "https://github.com/utopia-php/queue/tree/feat-framework-v2-v2" }, - "time": "2024-03-07T15:43:35+00:00" + "time": "2024-03-08T09:29:41+00:00" }, { "name": "utopia-php/registry", @@ -2338,16 +2381,16 @@ }, { "name": "utopia-php/storage", - "version": "0.18.3", + "version": "dev-feat-framework-v2-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "faa0279519ac14f3501e8b138e0865ad9d12bff6" + "reference": "80eafa63cb86b33ce35d48c0189bae6ebdc02734" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/faa0279519ac14f3501e8b138e0865ad9d12bff6", - "reference": "faa0279519ac14f3501e8b138e0865ad9d12bff6", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/80eafa63cb86b33ce35d48c0189bae6ebdc02734", + "reference": "80eafa63cb86b33ce35d48c0189bae6ebdc02734", "shasum": "" }, "require": { @@ -2359,7 +2402,7 @@ "ext-zlib": "*", "ext-zstd": "*", "php": ">=8.0", - "utopia-php/framework": "0.*.*", + "utopia-php/framework": "0.34.*", "utopia-php/system": "0.*.*" }, "require-dev": { @@ -2387,9 +2430,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.3" + "source": "https://github.com/utopia-php/storage/tree/feat-framework-v2-v2" }, - "time": "2023-12-31T11:45:12+00:00" + "time": "2024-03-08T09:39:46+00:00" }, { "name": "utopia-php/system", @@ -5458,18 +5501,42 @@ } ], "aliases": [ + { + "package": "utopia-php/abuse", + "version": "dev-feat-framework-v2", + "alias": "0.37.99", + "alias_normalized": "0.37.99.0" + }, { "package": "utopia-php/analytics", "version": "dev-feat-framework-v2", "alias": "0.10.99", "alias_normalized": "0.10.99.0" }, + { + "package": "utopia-php/audit", + "version": "dev-feat-framework-v2", + "alias": "0.39.99", + "alias_normalized": "0.39.99.0" + }, { "package": "utopia-php/database", "version": "dev-feat-framework-v2", "alias": "0.49.99", "alias_normalized": "0.49.99.0" }, + { + "package": "utopia-php/domains", + "version": "dev-feat-framework-v2", + "alias": "0.5.99", + "alias_normalized": "0.5.99.0" + }, + { + "package": "utopia-php/framework", + "version": "dev-feat-framework-v2", + "alias": "0.34.99", + "alias_normalized": "0.34.99.0" + }, { "package": "utopia-php/orchestration", "version": "dev-feat-framework-v2", @@ -5487,15 +5554,26 @@ "version": "dev-feat-framework-v2-v2", "alias": "0.7.99", "alias_normalized": "0.7.99.0" + }, + { + "package": "utopia-php/storage", + "version": "dev-feat-framework-v2-v2", + "alias": "0.18.99", + "alias_normalized": "0.18.99.0" } ], "minimum-stability": "stable", "stability-flags": { + "utopia-php/abuse": 20, "utopia-php/analytics": 20, + "utopia-php/audit": 20, "utopia-php/database": 20, + "utopia-php/domains": 20, + "utopia-php/framework": 20, "utopia-php/orchestration": 20, "utopia-php/platform": 20, "utopia-php/queue": 20, + "utopia-php/storage": 20, "appwrite/sdk-generator": 5 }, "prefer-stable": false, diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index 31f1ce45b4..2c25f163c3 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -6,8 +6,8 @@ use Appwrite\GraphQL\Exception as GQLException; use Appwrite\Promises\Swoole; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Utopia\Http\Http; use Utopia\Exception; +use Utopia\Http\Http; use Utopia\Http\Route; class Resolvers @@ -15,7 +15,7 @@ class Resolvers /** * Create a resolver for a given API {@see Route}. * - * @param App $utopia + * @param Http $utopia * @param ?Route $route * @return callable */ @@ -25,7 +25,7 @@ class Resolvers ): callable { return static fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($utopia, $route, $args, $context, $info) { - /** @var App $utopia */ + /** @var Http $utopia */ /** @var Response $response */ /** @var Request $request */ @@ -60,7 +60,7 @@ class Resolvers /** * Create a resolver for a document in a specified database and collection with a specific method type. * - * @param App $utopia + * @param Http $utopia * @param string $databaseId * @param string $collectionId * @param string $methodType @@ -82,7 +82,7 @@ class Resolvers /** * Create a resolver for getting a document in a specified database and collection. * - * @param App $utopia + * @param Http $utopia * @param string $databaseId * @param string $collectionId * @param callable $url @@ -111,7 +111,7 @@ class Resolvers /** * Create a resolver for listing documents in a specified database and collection. * - * @param App $utopia + * @param Http $utopia * @param string $databaseId * @param string $collectionId * @param callable $url @@ -147,7 +147,7 @@ class Resolvers /** * Create a resolver for creating a document in a specified database and collection. * - * @param App $utopia + * @param Http $utopia * @param string $databaseId * @param string $collectionId * @param callable $url @@ -179,7 +179,7 @@ class Resolvers /** * Create a resolver for updating a document in a specified database and collection. * - * @param App $utopia + * @param Http $utopia * @param string $databaseId * @param string $collectionId * @param callable $url @@ -211,7 +211,7 @@ class Resolvers /** * Create a resolver for deleting a document in a specified database and collection. * - * @param App $utopia + * @param Http $utopia * @param string $databaseId * @param string $collectionId * @param callable $url @@ -238,7 +238,7 @@ class Resolvers } /** - * @param App $utopia + * @param Http $utopia * @param Request $request * @param Response $response * @param callable $resolve diff --git a/src/Appwrite/GraphQL/Schema.php b/src/Appwrite/GraphQL/Schema.php index 49544a7c93..887ea621dd 100644 --- a/src/Appwrite/GraphQL/Schema.php +++ b/src/Appwrite/GraphQL/Schema.php @@ -6,8 +6,8 @@ use Appwrite\GraphQL\Types\Mapper; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; use GraphQL\Type\Schema as GQLSchema; -use Utopia\Http\Http; use Utopia\Exception; +use Utopia\Http\Http; use Utopia\Http\Route; class Schema @@ -17,7 +17,7 @@ class Schema /** * - * @param App $utopia + * @param Http $utopia * @param callable $complexity Function to calculate complexity * @param callable $attributes Function to get attributes * @param array $urls Array of functions to get urls for specific method types @@ -80,7 +80,7 @@ class Schema * This function iterates all API routes and builds a GraphQL * schema defining types and resolvers for all response models. * - * @param App $utopia + * @param Http $utopia * @param callable $complexity * @return array * @throws Exception @@ -134,7 +134,7 @@ class Schema * Iterates all of a projects attributes and builds GraphQL * queries and mutations for the collections they make up. * - * @param App $utopia + * @param Http $utopia * @param callable $complexity * @param callable $attributes * @param array $urls diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index d81d8ea885..41c7de6e56 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -205,7 +205,7 @@ class Mapper /** * Map a {@see Route} parameter to a GraphQL Type * - * @param App $utopia + * @param Http $utopia * @param Validator|callable $validator * @param bool $required * @param array $injections diff --git a/src/Appwrite/Messaging/Adapter/Realtime.php b/src/Appwrite/Messaging/Adapter/Realtime.php index 475b92f2f7..e34b961a4e 100644 --- a/src/Appwrite/Messaging/Adapter/Realtime.php +++ b/src/Appwrite/Messaging/Adapter/Realtime.php @@ -3,11 +3,11 @@ namespace Appwrite\Messaging\Adapter; use Appwrite\Messaging\Adapter; -use Utopia\Http\Http; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; +use Utopia\Http\Http; class Realtime extends Adapter { diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index ac9fc164d5..fc3cc56517 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -4,7 +4,6 @@ namespace Appwrite\Migration; use Exception; use Swoole\Runtime; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -12,6 +11,7 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; +use Utopia\Http\Http; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); diff --git a/src/Appwrite/Migration/Version/V15.php b/src/Appwrite/Migration/Version/V15.php index b724dc33e3..1ea9d26773 100644 --- a/src/Appwrite/Migration/Version/V15.php +++ b/src/Appwrite/Migration/Version/V15.php @@ -4,7 +4,6 @@ namespace Appwrite\Migration\Version; use Appwrite\Migration\Migration; use Appwrite\OpenSSL\OpenSSL; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -12,6 +11,7 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Http\Http; class V15 extends Migration { diff --git a/src/Appwrite/Migration/Version/V19.php b/src/Appwrite/Migration/Version/V19.php index 75e5b31d17..037b3e1ef4 100644 --- a/src/Appwrite/Migration/Version/V19.php +++ b/src/Appwrite/Migration/Version/V19.php @@ -3,7 +3,6 @@ namespace Appwrite\Migration\Version; use Appwrite\Migration\Migration; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -11,6 +10,7 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception; use Utopia\Database\Query; +use Utopia\Http\Http; class V19 extends Migration { diff --git a/src/Appwrite/Platform/Tasks/CalcTierStats.php b/src/Appwrite/Platform/Tasks/CalcTierStats.php index 137a497ae3..665b60519a 100644 --- a/src/Appwrite/Platform/Tasks/CalcTierStats.php +++ b/src/Appwrite/Platform/Tasks/CalcTierStats.php @@ -4,17 +4,17 @@ namespace Appwrite\Platform\Tasks; use League\Csv\Writer; use PHPMailer\PHPMailer\PHPMailer; -use Utopia\Http\Http; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; +use Utopia\Http\Http; +use Utopia\Http\Validator\Text; use Utopia\Platform\Action; use Utopia\Pools\Group; use Utopia\Registry\Registry; -use Utopia\Http\Validator\Text; class CalcTierStats extends Action { diff --git a/src/Appwrite/Platform/Tasks/CreateInfMetric.php b/src/Appwrite/Platform/Tasks/CreateInfMetric.php index 099076fdd5..8f5fb30dfb 100644 --- a/src/Appwrite/Platform/Tasks/CreateInfMetric.php +++ b/src/Appwrite/Platform/Tasks/CreateInfMetric.php @@ -8,8 +8,8 @@ use Utopia\Database\Document; use Utopia\Database\Exception; use Utopia\Database\Exception\Duplicate; use Utopia\Database\Query; -use Utopia\Platform\Action; use Utopia\Http\Validator\Text; +use Utopia\Platform\Action; class CreateInfMetric extends Action { diff --git a/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php b/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php index bcccf8dcb0..92c42be3e1 100644 --- a/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php +++ b/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php @@ -2,18 +2,19 @@ namespace Appwrite\Platform\Tasks; -use Utopia\Http\Http; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Http\Adapter\FPM\Server; +use Utopia\Http\Http; +use Utopia\Http\Validator\Boolean; use Utopia\Platform\Action; use Utopia\Pools\Group; use Utopia\Registry\Registry; -use Utopia\Http\Validator\Boolean; class DeleteOrphanedProjects extends Action { @@ -32,13 +33,14 @@ class DeleteOrphanedProjects extends Action ->inject('cache') ->inject('dbForConsole') ->inject('register') - ->callback(function (bool $commit, Group $pools, Cache $cache, Database $dbForConsole, Registry $register) { - $this->action($commit, $pools, $cache, $dbForConsole, $register); + ->inject('auth') + ->callback(function (bool $commit, Group $pools, Cache $cache, Database $dbForConsole, Registry $register, Authorization $auth) { + $this->action($commit, $pools, $cache, $dbForConsole, $register, $auth); }); } - public function action(bool $commit, Group $pools, Cache $cache, Database $dbForConsole, Registry $register): void + public function action(bool $commit, Group $pools, Cache $cache, Database $dbForConsole, Registry $register, Authorization $auth): void { Console::title('Delete orphaned projects V1'); @@ -59,8 +61,8 @@ class DeleteOrphanedProjects extends Action ], $collectionsConfig); /* Initialise new Utopia app */ - $app = new Http(new Server(), 'UTC'); - $console = $app->getResource('console'); + $http = new Http(new Server(), 'UTC'); + $console = $http->getResource('console'); $projects = [$console]; /** Database connections */ @@ -91,6 +93,7 @@ class DeleteOrphanedProjects extends Action ->getResource(); $dbForProject = new Database($adapter, $cache); + $dbForProject->setAuthorization($auth); $dbForProject->setDatabase('appwrite'); $dbForProject->setNamespace('_' . $project->getInternalId()); diff --git a/src/Appwrite/Platform/Tasks/DevGenerateTranslations.php b/src/Appwrite/Platform/Tasks/DevGenerateTranslations.php index d47d3e990b..e09c69c6ec 100644 --- a/src/Appwrite/Platform/Tasks/DevGenerateTranslations.php +++ b/src/Appwrite/Platform/Tasks/DevGenerateTranslations.php @@ -6,9 +6,9 @@ use Exception; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Fetch\Client; -use Utopia\Platform\Action; use Utopia\Http\Validator\Boolean; use Utopia\Http\Validator\Text; +use Utopia\Platform\Action; class DevGenerateTranslations extends Action { diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index e53f2ba8d7..0f2d12c0a6 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -3,10 +3,10 @@ namespace Appwrite\Platform\Tasks; use Appwrite\ClamAV\Network; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Domains\Domain; +use Utopia\Http\Http; use Utopia\Logger\Logger; use Utopia\Platform\Action; use Utopia\Registry\Registry; diff --git a/src/Appwrite/Platform/Tasks/GetMigrationStats.php b/src/Appwrite/Platform/Tasks/GetMigrationStats.php index c525a92b20..973f55a237 100644 --- a/src/Appwrite/Platform/Tasks/GetMigrationStats.php +++ b/src/Appwrite/Platform/Tasks/GetMigrationStats.php @@ -5,12 +5,14 @@ namespace Appwrite\Platform\Tasks; use League\Csv\CannotInsertRecord; use League\Csv\Writer; use PHPMailer\PHPMailer\PHPMailer; -use Utopia\Http\Http; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Database\Database; +use Utopia\Database\Exception\Authorization; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization as ValidatorAuthorization; use Utopia\Http\Adapter\FPM\Server; +use Utopia\Http\Http; use Utopia\Platform\Action; use Utopia\Pools\Group; use Utopia\Registry\Registry; @@ -47,8 +49,9 @@ class GetMigrationStats extends Action ->inject('cache') ->inject('dbForConsole') ->inject('register') - ->callback(function (Group $pools, Cache $cache, Database $dbForConsole, Registry $register) { - $this->action($pools, $cache, $dbForConsole, $register); + ->inject('auth') + ->callback(function (Group $pools, Cache $cache, Database $dbForConsole, Registry $register, ValidatorAuthorization $auth) { + $this->action($pools, $cache, $dbForConsole, $register, $auth); }); } @@ -56,7 +59,7 @@ class GetMigrationStats extends Action * @throws \Utopia\Exception * @throws CannotInsertRecord */ - public function action(Group $pools, Cache $cache, Database $dbForConsole, Registry $register): void + public function action(Group $pools, Cache $cache, Database $dbForConsole, Registry $register, ValidatorAuthorization $auth): void { //docker compose exec -t appwrite get-migration-stats @@ -64,8 +67,8 @@ class GetMigrationStats extends Action Console::success(APP_NAME . ' Migration stats calculation has started'); /* Initialise new Utopia app */ - $app = new Http(new Server(), 'UTC'); - $console = $app->getResource('console'); + $http = new Http(new Server(), 'UTC'); + $console = $http->getResource('console'); /** CSV stuff */ $this->date = date('Y-m-d'); @@ -102,6 +105,7 @@ class GetMigrationStats extends Action ->getResource(); $dbForProject = new Database($adapter, $cache); + $dbForProject->setAuthorization($auth); $dbForProject->setDatabase('appwrite'); $dbForProject->setNamespace('_' . $project->getInternalId()); diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index 95e5cf0e4d..bf0732a0a3 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -3,11 +3,11 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Hamster as EventHamster; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; +use Utopia\Http\Http; use Utopia\Platform\Action; class Hamster extends Action diff --git a/src/Appwrite/Platform/Tasks/Install.php b/src/Appwrite/Platform/Tasks/Install.php index 8b64002c8d..9e03d10255 100644 --- a/src/Appwrite/Platform/Tasks/Install.php +++ b/src/Appwrite/Platform/Tasks/Install.php @@ -8,9 +8,9 @@ use Appwrite\Docker\Env; use Appwrite\Utopia\View; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Platform\Action; use Utopia\Http\Validator\Boolean; use Utopia\Http\Validator\Text; +use Utopia\Platform\Action; class Install extends Action { diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index ec8d3253ca..ee7ba4b2c0 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -4,12 +4,12 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Certificate; use Appwrite\Event\Delete; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Query; +use Utopia\Http\Http; use Utopia\Platform\Action; class Maintenance extends Action diff --git a/src/Appwrite/Platform/Tasks/Migrate.php b/src/Appwrite/Platform/Tasks/Migrate.php index ad158b1b42..f5a4d66994 100644 --- a/src/Appwrite/Platform/Tasks/Migrate.php +++ b/src/Appwrite/Platform/Tasks/Migrate.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Migration\Migration; -use Utopia\Http\Http; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Database\Database; @@ -11,9 +10,10 @@ use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Http\Adapter\FPM\Server; +use Utopia\Http\Http; +use Utopia\Http\Validator\Text; use Utopia\Platform\Action; use Utopia\Registry\Registry; -use Utopia\Http\Validator\Text; class Migrate extends Action { @@ -54,11 +54,11 @@ class Migrate extends Action return; } - $app = new Http(new Server(), 'UTC'); + $http = new Http(new Server(), 'UTC'); Console::success('Starting Data Migration to version ' . $version); - $console = $app->getResource('console'); + $console = $http->getResource('console'); $limit = 30; $sum = 30; diff --git a/src/Appwrite/Platform/Tasks/PatchRecreateRepositoriesDocuments.php b/src/Appwrite/Platform/Tasks/PatchRecreateRepositoriesDocuments.php index 87bd73cc32..8f4216fed9 100644 --- a/src/Appwrite/Platform/Tasks/PatchRecreateRepositoriesDocuments.php +++ b/src/Appwrite/Platform/Tasks/PatchRecreateRepositoriesDocuments.php @@ -9,8 +9,8 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; -use Utopia\Platform\Action; use Utopia\Http\Validator\Text; +use Utopia\Platform\Action; class PatchRecreateRepositoriesDocuments extends Action { diff --git a/src/Appwrite/Platform/Tasks/QueueCount.php b/src/Appwrite/Platform/Tasks/QueueCount.php index 55a2e5ee98..a8ec9cdacc 100644 --- a/src/Appwrite/Platform/Tasks/QueueCount.php +++ b/src/Appwrite/Platform/Tasks/QueueCount.php @@ -4,10 +4,10 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Event; use Utopia\CLI\Console; +use Utopia\Http\Validator\WhiteList; use Utopia\Platform\Action; use Utopia\Queue\Client; use Utopia\Queue\Connection; -use Utopia\Http\Validator\WhiteList; class QueueCount extends Action { diff --git a/src/Appwrite/Platform/Tasks/QueueRetry.php b/src/Appwrite/Platform/Tasks/QueueRetry.php index e1312e7cc5..3d5f95da0c 100644 --- a/src/Appwrite/Platform/Tasks/QueueRetry.php +++ b/src/Appwrite/Platform/Tasks/QueueRetry.php @@ -4,11 +4,11 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Event; use Utopia\CLI\Console; +use Utopia\Http\Validator\WhiteList; +use Utopia\Http\Validator\Wildcard; use Utopia\Platform\Action; use Utopia\Queue\Client; use Utopia\Queue\Connection; -use Utopia\Http\Validator\WhiteList; -use Utopia\Http\Validator\Wildcard; class QueueRetry extends Action { diff --git a/src/Appwrite/Platform/Tasks/SSL.php b/src/Appwrite/Platform/Tasks/SSL.php index 061679b3bf..0677253a39 100644 --- a/src/Appwrite/Platform/Tasks/SSL.php +++ b/src/Appwrite/Platform/Tasks/SSL.php @@ -3,12 +3,12 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Certificate; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\Document; -use Utopia\Platform\Action; +use Utopia\Http\Http; use Utopia\Http\Validator\Boolean; use Utopia\Http\Validator\Hostname; +use Utopia\Platform\Action; class SSL extends Action { diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index 91b98dd28c..7685ae9f54 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -3,13 +3,13 @@ namespace Appwrite\Platform\Tasks; use Swoole\Timer; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception; use Utopia\Database\Query; +use Utopia\Http\Http; use Utopia\Platform\Action; use Utopia\Pools\Group; diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 04c4f225e4..c97928f5b5 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -9,7 +9,6 @@ use Appwrite\Utopia\Response; use Exception; use Swoole\Http\Request; use Swoole\Http\Response as HttpResponse; -use Utopia\Http\Http; use Utopia\Cache\Adapter\None; use Utopia\Cache\Cache; use Utopia\CLI\Console; @@ -17,10 +16,11 @@ use Utopia\Config\Config; use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; use Utopia\Http\Adapter\FPM\Server; -use Utopia\Platform\Action; -use Utopia\Registry\Registry; +use Utopia\Http\Http; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; +use Utopia\Platform\Action; +use Utopia\Registry\Registry; class Specs extends Action { diff --git a/src/Appwrite/Platform/Tasks/Vars.php b/src/Appwrite/Platform/Tasks/Vars.php index 3d2cb2c36a..6d6866ccd4 100644 --- a/src/Appwrite/Platform/Tasks/Vars.php +++ b/src/Appwrite/Platform/Tasks/Vars.php @@ -2,9 +2,9 @@ namespace Appwrite\Platform\Tasks; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Http\Http; use Utopia\Platform\Action; class Vars extends Action diff --git a/src/Appwrite/Platform/Tasks/Version.php b/src/Appwrite/Platform/Tasks/Version.php index 8cfe3099c6..bac9b91da4 100644 --- a/src/Appwrite/Platform/Tasks/Version.php +++ b/src/Appwrite/Platform/Tasks/Version.php @@ -2,8 +2,8 @@ namespace Appwrite\Platform\Tasks; -use Utopia\Http\Http; use Utopia\CLI\Console; +use Utopia\Http\Http; use Utopia\Platform\Action; class Version extends Action diff --git a/src/Appwrite/Platform/Tasks/VolumeSync.php b/src/Appwrite/Platform/Tasks/VolumeSync.php index 17ae9730f9..272edbd446 100644 --- a/src/Appwrite/Platform/Tasks/VolumeSync.php +++ b/src/Appwrite/Platform/Tasks/VolumeSync.php @@ -4,9 +4,9 @@ namespace Appwrite\Platform\Tasks; use Utopia\CLI\Console; use Utopia\Database\DateTime; -use Utopia\Platform\Action; use Utopia\Http\Validator\Integer; use Utopia\Http\Validator\Text; +use Utopia\Platform\Action; class VolumeSync extends Action { diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 86ca59d3fd..0650906538 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -9,6 +9,7 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization; use Utopia\Database\Exception\Structure; +use Utopia\Database\Validator\Authorization as ValidatorAuthorization; use Utopia\Platform\Action; use Utopia\Queue\Message; @@ -28,7 +29,8 @@ class Audits extends Action ->desc('Audits worker') ->inject('message') ->inject('dbForProject') - ->callback(fn ($message, $dbForProject) => $this->action($message, $dbForProject)); + ->inject('auth') + ->callback(fn ($message, $dbForProject, ValidatorAuthorization $auth) => $this->action($message, $dbForProject, $auth)); } @@ -41,7 +43,7 @@ class Audits extends Action * @throws Authorization * @throws Structure */ - public function action(Message $message, Database $dbForProject): void + public function action(Message $message, Database $dbForProject, ValidatorAuthorization $auth): void { $payload = $message->getPayload() ?? []; @@ -61,7 +63,7 @@ class Audits extends Action $userName = $user->getAttribute('name', ''); $userEmail = $user->getAttribute('email', ''); - $audit = new Audit($dbForProject); + $audit = new Audit($dbForProject, $auth); $audit->log( userId: $user->getInternalId(), // Pass first, most verbose event pattern diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 723af17a38..575ba4d8c8 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -10,7 +10,6 @@ use Appwrite\Utopia\Response\Model\Deployment; use Appwrite\Vcs\Comment; use Executor\Executor; use Swoole\Coroutine as Co; -use Utopia\Http\Http; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -22,6 +21,7 @@ use Utopia\Database\Exception\Restricted; use Utopia\Database\Exception\Structure; use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; +use Utopia\Http\Http; use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 5cbb36ed8e..6fd7af3ffb 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -11,7 +11,6 @@ use Appwrite\Template\Template; use Appwrite\Utopia\Response\Model\Rule; use Exception; use Throwable; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; @@ -22,6 +21,7 @@ use Utopia\Database\Exception\Structure; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Domains\Domain; +use Utopia\Http\Http; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Platform\Action; diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 8247e00799..228a0b8bed 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -8,7 +8,6 @@ use Executor\Executor; use Throwable; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; -use Utopia\Http\Http; use Utopia\Audit\Audit; use Utopia\Cache\Adapter\Filesystem; use Utopia\Cache\Cache; @@ -22,6 +21,8 @@ use Utopia\Database\Exception\Conflict; use Utopia\Database\Exception\Restricted; use Utopia\Database\Exception\Structure; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization as ValidatorAuthorization; +use Utopia\Http\Http; use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; @@ -52,14 +53,15 @@ class Deletes extends Action ->inject('executionRetention') ->inject('auditRetention') ->inject('log') - ->callback(fn ($message, $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log) => $this->action($message, $dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $abuseRetention, $executionRetention, $auditRetention, $log)); + ->inject('auth') + ->callback(fn ($message, $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log, ValidatorAuthorization $auth) => $this->action($message, $dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $abuseRetention, $executionRetention, $auditRetention, $log, $auth)); } /** * @throws Exception * @throws Throwable */ - public function action(Message $message, Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log): void + public function action(Message $message, Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log, ValidatorAuthorization $auth): void { $payload = $message->getPayload() ?? []; @@ -127,7 +129,7 @@ class Deletes extends Action break; case DELETE_TYPE_AUDIT: if (!$project->isEmpty()) { - $this->deleteAuditLogs($project, $getProjectDB, $auditRetention); + $this->deleteAuditLogs($project, $getProjectDB, $auditRetention, $auth); } if (!$document->isEmpty()) { @@ -135,7 +137,7 @@ class Deletes extends Action } break; case DELETE_TYPE_ABUSE: - $this->deleteAbuseLogs($project, $getProjectDB, $abuseRetention); + $this->deleteAbuseLogs($project, $getProjectDB, $abuseRetention, $auth); break; case DELETE_TYPE_REALTIME: $this->deleteRealtimeUsage($dbForConsole, $datetime); @@ -724,12 +726,12 @@ class Deletes extends Action * @return void * @throws Exception */ - private function deleteAbuseLogs(Document $project, callable $getProjectDB, string $abuseRetention): void + private function deleteAbuseLogs(Document $project, callable $getProjectDB, string $abuseRetention, ValidatorAuthorization $auth): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); - $timeLimit = new TimeLimit("", 0, 1, $dbForProject); - $abuse = new Abuse($timeLimit); + $timeLimit = new TimeLimit("", 0, 1, $dbForProject, $auth); + $abuse = new Abuse($timeLimit, $auth); $status = $abuse->cleanup($abuseRetention); if (!$status) { throw new Exception('Failed to delete Abuse logs for project ' . $projectId); @@ -743,11 +745,11 @@ class Deletes extends Action * @return void * @throws Exception */ - private function deleteAuditLogs(Document $project, callable $getProjectDB, string $auditRetention): void + private function deleteAuditLogs(Document $project, callable $getProjectDB, string $auditRetention, ValidatorAuthorization $auth): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); - $audit = new Audit($dbForProject); + $audit = new Audit($dbForProject, $auth); $status = $audit->cleanup($auditRetention); if (!$status) { throw new Exception('Failed to delete Audit logs for project' . $projectId); diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 0d325f7bb3..4cb6cb9f06 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -9,7 +9,6 @@ use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Utopia\Response\Model\Execution; use Exception; use Executor\Executor; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -21,6 +20,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; +use Utopia\Http\Http; use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; diff --git a/src/Appwrite/Platform/Workers/Hamster.php b/src/Appwrite/Platform/Workers/Hamster.php index 817291dff1..84a813d157 100644 --- a/src/Appwrite/Platform/Workers/Hamster.php +++ b/src/Appwrite/Platform/Workers/Hamster.php @@ -4,10 +4,8 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\Hamster as EventHamster; use Appwrite\Network\Validator\Origin; -use PharIo\Manifest\Author; use Utopia\Analytics\Adapter\Mixpanel; use Utopia\Analytics\Event as AnalyticsEvent; -use Utopia\Http\Http; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -15,6 +13,7 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; +use Utopia\Http\Http; use Utopia\Platform\Action; use Utopia\Pools\Group; use Utopia\Queue\Message; @@ -123,6 +122,7 @@ class Hamster extends Action ->getResource(); $dbForProject = new Database($adapter, $cache); + $dbForProject->setAuthorization($auth); $dbForProject->setDatabase('appwrite'); $dbForProject->setNamespace('_' . $project->getInternalId()); diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 19ecc707dd..4535a8d8a8 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -4,7 +4,6 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\Usage; use Appwrite\Messaging\Status as MessageStatus; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -13,6 +12,7 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\DSN\DSN; +use Utopia\Http\Http; use Utopia\Logger\Log; use Utopia\Messaging\Adapter\Email as EmailAdapter; use Utopia\Messaging\Adapter\Email\Mailgun; diff --git a/src/Appwrite/Platform/Workers/Usage.php b/src/Appwrite/Platform/Workers/Usage.php index 14e004194f..284165a476 100644 --- a/src/Appwrite/Platform/Workers/Usage.php +++ b/src/Appwrite/Platform/Workers/Usage.php @@ -4,10 +4,10 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\UsageDump; use Exception; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\DateTime; use Utopia\Database\Document; +use Utopia\Http\Http; use Utopia\Platform\Action; use Utopia\Queue\Message; diff --git a/src/Appwrite/Platform/Workers/UsageDump.php b/src/Appwrite/Platform/Workers/UsageDump.php index 96cb1e9e0b..d33906b24e 100644 --- a/src/Appwrite/Platform/Workers/UsageDump.php +++ b/src/Appwrite/Platform/Workers/UsageDump.php @@ -3,11 +3,11 @@ namespace Appwrite\Platform\Workers; use Appwrite\Extend\Exception; -use Utopia\Http\Http; use Utopia\CLI\Console; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; +use Utopia\Http\Http; use Utopia\Platform\Action; use Utopia\Queue\Message; diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index 88a6dd4c68..1835b16d7d 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -5,10 +5,10 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\Mail; use Appwrite\Template\Template; use Exception; -use Utopia\Http\Http; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; +use Utopia\Http\Http; use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; diff --git a/src/Appwrite/Specification/Format.php b/src/Appwrite/Specification/Format.php index fdb4841a93..e2048971be 100644 --- a/src/Appwrite/Specification/Format.php +++ b/src/Appwrite/Specification/Format.php @@ -3,13 +3,13 @@ namespace Appwrite\Specification; use Appwrite\Utopia\Response\Model; -use Utopia\Http\Http; use Utopia\Config\Config; +use Utopia\Http\Http; use Utopia\Http\Route; abstract class Format { - protected Http $app; + protected Http $http; /** * @var Route[] @@ -50,9 +50,9 @@ abstract class Format ] ]; - public function __construct(Http $app, array $services, array $routes, array $models, array $keys, int $authCount) + public function __construct(Http $http, array $services, array $routes, array $models, array $keys, int $authCount) { - $this->app = $app; + $this->http = $http; $this->services = $services; $this->routes = $routes; $this->models = $models; diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 5ee3011ebd..1da6fb7ef0 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -275,7 +275,7 @@ class OpenAPI3 extends Format /** * @var \Utopia\Http\Validator $validator */ - $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator']; + $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->http->getResources($param['injections'])) : $param['validator']; $node = [ 'name' => $name, diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index d75568976c..d53649db73 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -271,7 +271,7 @@ class Swagger2 extends Format foreach ($parameters as $name => $param) { // Set params /** @var Validator $validator */ - $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator']; + $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->http->getResources($param['injections'])) : $param['validator']; $node = [ 'name' => $name, diff --git a/src/Appwrite/Utopia/Request.php b/src/Appwrite/Utopia/Request.php index ee488d6a13..c5c900d82f 100644 --- a/src/Appwrite/Utopia/Request.php +++ b/src/Appwrite/Utopia/Request.php @@ -11,6 +11,14 @@ class Request extends SwooleRequest private static ?Filter $filter = null; private static ?Route $route = null; + /** + * Request constructor. + */ + public function __construct(SwooleRequest $request) + { + parent::__construct($request->getSwooleRequest()); + } + /** * @inheritdoc */ diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 4f6ebd3cc5..280962e139 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -102,7 +102,6 @@ use Appwrite\Utopia\Response\Model\User; use Appwrite\Utopia\Response\Model\Variable; use Appwrite\Utopia\Response\Model\Webhook; use Exception; -use Swoole\Http\Response as SwooleHTTPResponse; // Keep last use Utopia\Database\Document; use Utopia\Http\Adapter\Swoole\Response as SwooleResponse; @@ -313,8 +312,10 @@ class Response extends SwooleResponse * * @param float $time */ - public function __construct(SwooleHTTPResponse $response) + public function __construct(SwooleResponse $swooleResponse) { + $response = $swooleResponse->getSwooleResponse(); + $this // General ->setModel(new None()) diff --git a/src/Appwrite/Vcs/Comment.php b/src/Appwrite/Vcs/Comment.php index 6731365638..aa2b9ff031 100644 --- a/src/Appwrite/Vcs/Comment.php +++ b/src/Appwrite/Vcs/Comment.php @@ -2,8 +2,8 @@ namespace Appwrite\Vcs; -use Utopia\Http\Http; use Utopia\Database\Document; +use Utopia\Http\Http; class Comment { diff --git a/tests/e2e/General/AbuseTest.php b/tests/e2e/General/AbuseTest.php index 0c8e363b37..5557e52462 100644 --- a/tests/e2e/General/AbuseTest.php +++ b/tests/e2e/General/AbuseTest.php @@ -7,10 +7,10 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideNone; -use Utopia\Http\Http; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Http\Http; class AbuseTest extends Scope { diff --git a/tests/e2e/Services/GraphQL/AbuseTest.php b/tests/e2e/Services/GraphQL/AbuseTest.php index 958baa3b90..92080191d5 100644 --- a/tests/e2e/Services/GraphQL/AbuseTest.php +++ b/tests/e2e/Services/GraphQL/AbuseTest.php @@ -6,10 +6,10 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; -use Utopia\Http\Http; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Http\Http; class AbuseTest extends Scope { diff --git a/tests/e2e/Services/GraphQL/MessagingTest.php b/tests/e2e/Services/GraphQL/MessagingTest.php index 839d80b09e..3e6a3d4b1b 100644 --- a/tests/e2e/Services/GraphQL/MessagingTest.php +++ b/tests/e2e/Services/GraphQL/MessagingTest.php @@ -6,9 +6,9 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; -use Utopia\Http\Http; use Utopia\Database\Helpers\ID; use Utopia\DSN\DSN; +use Utopia\Http\Http; class MessagingTest extends Scope { diff --git a/tests/e2e/Services/Messaging/MessagingBase.php b/tests/e2e/Services/Messaging/MessagingBase.php index a0669a1f8d..30df11ba0f 100644 --- a/tests/e2e/Services/Messaging/MessagingBase.php +++ b/tests/e2e/Services/Messaging/MessagingBase.php @@ -4,13 +4,13 @@ namespace Tests\E2E\Services\Messaging; use Appwrite\Messaging\Status as MessageStatus; use Tests\E2E\Client; -use Utopia\Http\Http; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\DSN\DSN; +use Utopia\Http\Http; trait MessagingBase { diff --git a/tests/e2e/Services/VCS/VCSConsoleClientTest.php b/tests/e2e/Services/VCS/VCSConsoleClientTest.php index 37cef56628..1f783b91b2 100644 --- a/tests/e2e/Services/VCS/VCSConsoleClientTest.php +++ b/tests/e2e/Services/VCS/VCSConsoleClientTest.php @@ -6,11 +6,11 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideConsole; -use Utopia\Http\Http; use Utopia\Cache\Adapter\None; use Utopia\Cache\Cache; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; +use Utopia\Http\Http; use Utopia\VCS\Adapter\Git\GitHub; class VCSConsoleClientTest extends Scope diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index e512797896..3966bf9a3b 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -6,8 +6,8 @@ use Appwrite\Event\Event; use Appwrite\URL\URL; use InvalidArgumentException; use PHPUnit\Framework\TestCase; -use Utopia\Http\Http; use Utopia\DSN\DSN; +use Utopia\Http\Http; use Utopia\Queue; use Utopia\Queue\Client; diff --git a/tests/unit/Messaging/MessagingChannelsTest.php b/tests/unit/Messaging/MessagingChannelsTest.php index 2f0443c34f..77b3cee7d6 100644 --- a/tests/unit/Messaging/MessagingChannelsTest.php +++ b/tests/unit/Messaging/MessagingChannelsTest.php @@ -6,7 +6,6 @@ use Appwrite\Auth\Auth; use Appwrite\Messaging\Adapter\Realtime; use PHPUnit\Framework\TestCase; use Utopia\Database\Document; -use Utopia\Database\Exception\Authorization; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization as ValidatorAuthorization; diff --git a/tests/unit/Usage/StatsTest.php b/tests/unit/Usage/StatsTest.php index fdef412ac8..092c75ba37 100644 --- a/tests/unit/Usage/StatsTest.php +++ b/tests/unit/Usage/StatsTest.php @@ -4,8 +4,8 @@ namespace Tests\Unit\Usage; use Appwrite\URL\URL as AppwriteURL; use PHPUnit\Framework\TestCase; -use Utopia\Http\Http; use Utopia\DSN\DSN; +use Utopia\Http\Http; use Utopia\Queue; use Utopia\Queue\Client; use Utopia\Queue\Connection; From d5f180db4fc82112e2542b0670ea7546e5529485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Fri, 8 Mar 2024 15:10:20 +0100 Subject: [PATCH 014/195] Add debugging --- dev/xdebug.ini | 5 +++-- docker-compose.yml | 4 +++- mariadb-config.cnf | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 mariadb-config.cnf diff --git a/dev/xdebug.ini b/dev/xdebug.ini index e29c8bd46e..30835305f1 100644 --- a/dev/xdebug.ini +++ b/dev/xdebug.ini @@ -1,6 +1,7 @@ zend_extension=xdebug [xdebug] -xdebug.mode=develop,debug +xdebug.mode=develop,debug,profile xdebug.client_host=host.docker.internal -xdebug.start_with_request=yes \ No newline at end of file +xdebug.start_with_request=yes +xdebug.output_dir=/tmp/xdebug \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 5155ba1ad5..596abfab8e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -50,7 +50,7 @@ services: build: context: . args: - DEBUG: false + DEBUG: true TESTING: true VERSION: dev ports: @@ -84,6 +84,7 @@ services: - ./public:/usr/src/code/public - ./src:/usr/src/code/src - ./dev:/usr/src/code/dev + - ./temp-debug:/tmp/xdebug depends_on: - mariadb - redis @@ -959,6 +960,7 @@ services: - database-proxy volumes: - appwrite-mariadb:/var/lib/mysql:rw + - ./mariadb-config.cnf:/etc/mysql/conf.d/mariadb-config.cnf ports: - "3306:3306" environment: diff --git a/mariadb-config.cnf b/mariadb-config.cnf new file mode 100644 index 0000000000..601dccc285 --- /dev/null +++ b/mariadb-config.cnf @@ -0,0 +1,2 @@ +[mysqld] +max_connections=1024 \ No newline at end of file From 513bf7eaca3304ace7a44aaf12e35db114e0bd86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Fri, 8 Mar 2024 15:10:26 +0100 Subject: [PATCH 015/195] Update .env --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index fc58a0781c..a14ff2052e 100644 --- a/.env +++ b/.env @@ -106,4 +106,4 @@ _APP_MESSAGE_PUSH_TEST_DSN= _APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10 _APP_PROJECT_REGIONS=default _APP_DATABASE_PROXY_SECRET=password -_APP_DATABASE_PROXY_CONNECTION=mariadb://user:password@mariadb:3306/appwrite?pool_size=128 \ No newline at end of file +_APP_DATABASE_PROXY_CONNECTION=mariadb://user:password@mariadb:3306/appwrite?pool_size=1000 \ No newline at end of file From 4de95913a59a195abec1e736caaad7ae3d73f9e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Fri, 8 Mar 2024 19:12:53 +0100 Subject: [PATCH 016/195] Disable debug --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 596abfab8e..ff04f03a99 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -50,8 +50,8 @@ services: build: context: . args: - DEBUG: true - TESTING: true + DEBUG: false + TESTING: false VERSION: dev ports: - 9501:80 From 2a0a69f3ed9b7254269cdd7dfe0c5cbe491ed08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sat, 9 Mar 2024 11:59:45 +0100 Subject: [PATCH 017/195] Fix connections pool --- app/cli.php | 60 +++++++++------- app/controllers/api/health.php | 29 +++++--- app/controllers/api/projects.php | 8 ++- app/controllers/shared/api.php | 14 ++-- app/http.php | 3 - app/init.php | 61 ++++++++-------- app/worker.php | 72 ++++++++++--------- .../Platform/Tasks/DeleteOrphanedProjects.php | 15 ++-- src/Appwrite/Platform/Tasks/Doctor.php | 14 ++-- .../Platform/Tasks/GetMigrationStats.php | 18 ++--- .../Platform/Tasks/ScheduleFunctions.php | 2 +- .../Platform/Tasks/ScheduleMessages.php | 2 +- src/Appwrite/Platform/Workers/Hamster.php | 19 ++--- src/Appwrite/Utopia/Queue/Connections.php | 54 ++++++++++++++ 14 files changed, 231 insertions(+), 140 deletions(-) create mode 100644 src/Appwrite/Utopia/Queue/Connections.php diff --git a/app/cli.php b/app/cli.php index 744c74e759..12a8acef06 100644 --- a/app/cli.php +++ b/app/cli.php @@ -8,6 +8,7 @@ use Appwrite\Event\Delete; use Appwrite\Event\Func; use Appwrite\Event\Hamster; use Appwrite\Platform\Appwrite; +use Appwrite\Utopia\Queue\Connections; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\CLI; @@ -27,39 +28,41 @@ global $register; CLI::setResource('register', fn () => $register); -CLI::setResource('cache', function ($pools) { +CLI::setResource('connections', function () { + return new Connections(); +}); + +CLI::setResource('cache', function ($pools, Connections $connections) { $list = Config::getParam('pools-cache', []); $adapters = []; foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource() - ; + $connection = $pools->get($value)->pop(); + $connections->add($connection); + $adapters[] = $connection->getResource(); } return new Cache(new Sharding($adapters)); -}, ['pools']); +}, ['pools', 'connections']); CLI::setResource('pools', function (Registry $register) { return $register->get('pools'); }, ['register']); -CLI::setResource('dbForConsole', function ($pools, $cache, $auth) { +CLI::setResource('dbForConsole', function ($pools, $cache, $auth, Connections $connections) { $sleep = 3; $maxAttempts = 5; $attempts = 0; $ready = false; + $connection = null; + do { $attempts++; try { // Prepare database connection - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource(); + $connection = $pools->get('console')->pop(); + $dbAdapter = $connection->getResource(); $dbForConsole = new Database($dbAdapter, $cache); $dbForConsole->setAuthorization($auth); @@ -79,23 +82,31 @@ CLI::setResource('dbForConsole', function ($pools, $cache, $auth) { $ready = true; } catch (\Throwable $err) { + if($connection !== null) { + $connection->reclaim(); + $connection = null; + } + Console::warning($err->getMessage()); - $pools->get('console')->reclaim(); sleep($sleep); } } while ($attempts < $maxAttempts && !$ready); + if($connection !== null) { + $connections->add($connection); + } + if (!$ready) { throw new Exception("Console is not ready yet. Please try again later."); } return $dbForConsole; -}, ['pools', 'cache', 'auth']); +}, ['pools', 'cache', 'auth', 'connections']); -CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, $auth) { +CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, $auth, Connections $connections) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth) { + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } @@ -108,10 +119,9 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, return $database; } - $dbAdapter = $pools - ->get($databaseName) - ->pop() - ->getResource(); + $connection = $pools->get($databaseName)->pop(); + $connections->add($connection); + $dbAdapter = $connection->getResource(); $database = new Database($dbAdapter, $cache); $database->setAuthorization($auth); @@ -125,11 +135,13 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, return $database; }; -}, ['pools', 'dbForConsole', 'cache', 'auth']); +}, ['pools', 'dbForConsole', 'cache', 'auth', 'connections']); -CLI::setResource('queue', function (Group $pools) { - return $pools->get('queue')->pop()->getResource(); -}, ['pools']); +CLI::setResource('queue', function (Group $pools, Connections $connections) { + $connection = $pools->get('queue')->pop(); + $connections->add($connection); + return $connection->getResource(); +}, ['pools', 'connections']); CLI::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 26246db019..ae8f93def0 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -3,6 +3,7 @@ use Appwrite\ClamAV\Network; use Appwrite\Event\Event; use Appwrite\Extend\Exception; +use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Response; use Utopia\Config\Config; use Utopia\Database\Document; @@ -69,7 +70,8 @@ Http::get('/v1/health/db') ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) ->inject('response') ->inject('pools') - ->action(function (Response $response, Group $pools) { + ->inject('connections') + ->action(function (Response $response, Group $pools, Connections $connections) { $output = []; @@ -81,7 +83,9 @@ Http::get('/v1/health/db') foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $adapter = $pools->get($database)->pop()->getResource(); + $connection = $pools->get($database)->pop(); + $connections->add($connection); + $adapter = $connection->getResource(); $checkStart = \microtime(true); @@ -123,7 +127,8 @@ Http::get('/v1/health/cache') ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) ->inject('response') ->inject('pools') - ->action(function (Response $response, Group $pools) { + ->inject('connections') + ->action(function (Response $response, Group $pools, Connections $connections) { $output = []; @@ -134,7 +139,9 @@ Http::get('/v1/health/cache') foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $adapter = $pools->get($database)->pop()->getResource(); + $connection = $pools->get($database)->pop(); + $connections->add($connection); + $adapter = $connection->getResource(); $checkStart = \microtime(true); @@ -180,7 +187,8 @@ Http::get('/v1/health/queue') ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) ->inject('response') ->inject('pools') - ->action(function (Response $response, Group $pools) { + ->inject('connections') + ->action(function (Response $response, Group $pools, Connections $connections) { $output = []; @@ -191,7 +199,9 @@ Http::get('/v1/health/queue') foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $adapter = $pools->get($database)->pop()->getResource(); + $connection = $pools->get($database)->pop(); + $connections->add($connection); + $adapter = $connection->getResource(); $checkStart = \microtime(true); @@ -237,7 +247,8 @@ Http::get('/v1/health/pubsub') ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) ->inject('response') ->inject('pools') - ->action(function (Response $response, Group $pools) { + ->inject('connections') + ->action(function (Response $response, Group $pools, Connections $connections) { $output = []; @@ -248,7 +259,9 @@ Http::get('/v1/health/pubsub') foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $adapter = $pools->get($database)->pop()->getResource(); + $connection = $pools->get($database)->pop(); + $connections->add($connection); + $adapter = $connection->getResource(); $checkStart = \microtime(true); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index e868df2eeb..6ea31e92a5 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -10,6 +10,7 @@ use Appwrite\Network\Validator\Origin; use Appwrite\Template\Template; use Appwrite\Utopia\Database\Validator\ProjectId; use Appwrite\Utopia\Database\Validator\Queries\Projects; +use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Response; use PHPMailer\PHPMailer\PHPMailer; use Utopia\Abuse\Adapters\TimeLimit; @@ -78,7 +79,8 @@ Http::post('/v1/projects') ->inject('cache') ->inject('pools') ->inject('auth') - ->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole, Cache $cache, Group $pools, Authorization $auth) { + ->inject('connections') + ->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole, Cache $cache, Group $pools, Authorization $auth, Connections $connections) { $team = $dbForConsole->getDocument('teams', $teamId); @@ -178,7 +180,9 @@ Http::post('/v1/projects') throw new Exception(Exception::PROJECT_ALREADY_EXISTS); } - $dbForProject = new Database($pools->get($database)->pop()->getResource(), $cache); + $connection = $pools->get($database)->pop(); + $connections->add($connection); + $dbForProject = new Database($connection->getResource(), $cache); $dbForProject->setAuthorization($auth); $dbForProject->setNamespace("_{$project->getInternalId()}"); $dbForProject->create(); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index fff4a7114c..f924c99f64 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -12,6 +12,7 @@ use Appwrite\Event\Messaging; use Appwrite\Event\Usage; use Appwrite\Extend\Exception; use Appwrite\Messaging\Adapter\Realtime; +use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Abuse\Abuse; @@ -28,7 +29,6 @@ use Utopia\Database\Validator\Authorization\Input; use Utopia\Http\Http; use Utopia\Http\Validator\WhiteList; use Utopia\Pools\Group; -use Utopia\Pools\Pool; $parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) { preg_match_all('/{(.*?)}/', $label, $matches); @@ -749,14 +749,14 @@ Http::init() }); Http::shutdown() - ->inject('pools') - ->action(function (Group $pools) { - $pools->reclaim(); + ->inject('connections') + ->action(function (Connections $connections) { + $connections->reclaim(); }); Http::error() - ->inject('pools') - ->action(function (Group $pools) { - $pools->reclaim(); + ->inject('connections') + ->action(function (Connections $connections) { + $connections->reclaim(); }); diff --git a/app/http.php b/app/http.php index cfd09ce76f..40faabae30 100644 --- a/app/http.php +++ b/app/http.php @@ -15,8 +15,6 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; -use Utopia\Http\Adapter\Swoole\Request as SwooleRequest; -use Utopia\Http\Adapter\Swoole\Response as SwooleResponse; use Utopia\Http\Adapter\Swoole\Server; use Utopia\Http\Http; use Utopia\Pools\Group; @@ -209,4 +207,3 @@ go(function () use ($register, $http, $payloadSize) { $http->start(); }); - diff --git a/app/init.php b/app/init.php index 6773d11236..7fad516cd0 100644 --- a/app/init.php +++ b/app/init.php @@ -40,6 +40,7 @@ use Appwrite\Network\Validator\Email; use Appwrite\Network\Validator\Origin; use Appwrite\OpenSSL\OpenSSL; use Appwrite\URL\URL as AppwriteURL; +use Appwrite\Utopia\Queue\Connections; use MaxMind\Db\Reader; use PHPMailer\PHPMailer\PHPMailer; use Swoole\Database\PDOProxy; @@ -1043,10 +1044,16 @@ Http::setResource('localeCodes', function () { return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); }); +Http::setResource('connections', function () { + return new Connections(); +}); + // Queues -Http::setResource('queue', function (Group $pools) { - return $pools->get('queue')->pop()->getResource(); -}, ['pools']); +Http::setResource('queue', function (Group $pools, Connections $connections) { + $connection = $pools->get('queue')->pop(); + $connections->add($connection); + return $connection->getResource(); +}, ['pools', 'connections']); Http::setResource('queueForMessaging', function (Connection $queue) { return new Messaging($queue); }, ['queue']); @@ -1307,15 +1314,14 @@ Http::setResource('console', function () { ]); }, []); -Http::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project, Authorization $auth) { +Http::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project, Authorization $auth, Connections $connections) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } - $dbAdapter = $pools - ->get($project->getAttribute('database')) - ->pop() - ->getResource(); + $connection = $pools->get($project->getAttribute('database'))->pop(); + $connections->add($connection); + $dbAdapter = $connection->getResource(); $database = new Database($dbAdapter, $cache); $database->setAuthorization($auth); @@ -1327,14 +1333,12 @@ Http::setResource('dbForProject', function (Group $pools, Database $dbForConsole ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); return $database; -}, ['pools', 'dbForConsole', 'cache', 'project', 'auth']); +}, ['pools', 'dbForConsole', 'cache', 'project', 'auth', 'connections']); -Http::setResource('dbForConsole', function (Group $pools, Cache $cache, Authorization $auth) { - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource() - ; +Http::setResource('dbForConsole', function (Group $pools, Cache $cache, Authorization $auth, Connections $connections) { + $connection = $pools->get('console')->pop(); + $connections->add($connection); + $dbAdapter = $connection->getResource(); $database = new Database($dbAdapter, $cache); $database->setAuthorization($auth); @@ -1346,12 +1350,12 @@ Http::setResource('dbForConsole', function (Group $pools, Cache $cache, Authoriz ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); return $database; -}, ['pools', 'cache', 'auth']); +}, ['pools', 'cache', 'auth', 'connections']); -Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth) { +Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth, Connections $connections) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - $getProjectDB = function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth) { + $getProjectDB = function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } @@ -1370,10 +1374,9 @@ Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole return $database; } - $dbAdapter = $pools - ->get($databaseName) - ->pop() - ->getResource(); + $connection = $pools->get($databaseName)->pop(); + $connections->add($connection); + $dbAdapter = $connection->getResource(); $database = new Database($dbAdapter, $cache); $database->setAuthorization($auth); @@ -1390,22 +1393,20 @@ Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole }; return $getProjectDB; -}, ['pools', 'dbForConsole', 'cache', 'auth']); +}, ['pools', 'dbForConsole', 'cache', 'auth', 'connections']); -Http::setResource('cache', function (Group $pools) { +Http::setResource('cache', function (Group $pools, Connections $connections) { $list = Config::getParam('pools-cache', []); $adapters = []; foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource() - ; + $connection = $pools->get($value)->pop(); + $connections->add($connection); + $adapters[] = $connection->getResource(); } return new Cache(new Sharding($adapters)); -}, ['pools']); +}, ['pools', 'connections']); Http::setResource('deviceForLocal', function () { return new Local(); diff --git a/app/worker.php b/app/worker.php index 61203c2d5a..4de03d68b8 100644 --- a/app/worker.php +++ b/app/worker.php @@ -16,6 +16,7 @@ use Appwrite\Event\Migration; use Appwrite\Event\Usage; use Appwrite\Event\UsageDump; use Appwrite\Platform\Appwrite; +use Appwrite\Utopia\Queue\Connections; use Swoole\Runtime; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; @@ -42,19 +43,22 @@ Runtime::enableCoroutine(SWOOLE_HOOK_ALL); Server::setResource('register', fn () => $register); -Server::setResource('dbForConsole', function (Cache $cache, Registry $register, Authorization $auth) { +Server::setResource('connections', function () { + return new Connections(); +}); + +Server::setResource('dbForConsole', function (Cache $cache, Registry $register, Authorization $auth, Connections $connections) { $pools = $register->get('pools'); - $database = $pools - ->get('console') - ->pop() - ->getResource(); + $connection = $pools->get('console')->pop(); + $connections->add($connection); + $database = $connection->getResource(); $adapter = new Database($database, $cache); $adapter->setAuthorization($auth); $adapter->setNamespace('_console'); return $adapter; -}, ['cache', 'register', 'auth']); +}, ['cache', 'register', 'auth', 'connections']); Server::setResource('project', function (Message $message, Database $dbForConsole) { $payload = $message->getPayload() ?? []; @@ -67,27 +71,26 @@ Server::setResource('project', function (Message $message, Database $dbForConsol return $dbForConsole->getDocument('projects', $project->getId()); }, ['message', 'dbForConsole']); -Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForConsole, Authorization $auth) { +Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForConsole, Authorization $auth, Connections $connections) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } $pools = $register->get('pools'); - $database = $pools - ->get($project->getAttribute('database')) - ->pop() - ->getResource(); + $connection = $pools->get($project->getAttribute('database'))->pop(); + $connections->add($connection); + $database = $connection->getResource(); $adapter = new Database($database, $cache); $adapter->setAuthorization($auth); $adapter->setNamespace('_' . $project->getInternalId()); return $adapter; -}, ['cache', 'register', 'message', 'project', 'dbForConsole', 'auth']); +}, ['cache', 'register', 'message', 'project', 'dbForConsole', 'auth', 'connections']); -Server::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth) { +Server::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth, Connections $connections) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth): Database { + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections): Database { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } @@ -100,10 +103,9 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForConso return $database; } - $dbAdapter = $pools - ->get($databaseName) - ->pop() - ->getResource(); + $connection = $pools->get($databaseName)->pop(); + $connections->add($connection); + $dbAdapter = $connection->getResource(); $database = new Database($dbAdapter, $cache); $database->setAuthorization($auth); @@ -114,7 +116,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForConso return $database; }; -}, ['pools', 'dbForConsole', 'cache', 'auth']); +}, ['pools', 'dbForConsole', 'cache', 'auth', 'connections']); Server::setResource('abuseRetention', function () { return DateTime::addSeconds(new \DateTime(), -1 * Http::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400)); @@ -128,21 +130,19 @@ Server::setResource('executionRetention', function () { return DateTime::addSeconds(new \DateTime(), -1 * Http::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); }); -Server::setResource('cache', function (Registry $register) { +Server::setResource('cache', function (Registry $register, Connections $connections) { $pools = $register->get('pools'); $list = Config::getParam('pools-cache', []); $adapters = []; foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource() - ; + $connection = $pools->get($value)->pop(); + $connections->add($connection); + $adapters[] = $connection->getResource(); } return new Cache(new Sharding($adapters)); -}, ['register']); +}, ['register', 'connections']); Server::setResource('log', fn () => new Log()); @@ -154,9 +154,11 @@ Server::setResource('queueForUsageDump', function (Connection $queue) { return new UsageDump($queue); }, ['queue']); -Server::setResource('queue', function (Group $pools) { - return $pools->get('queue')->pop()->getResource(); -}, ['pools']); +Server::setResource('queue', function (Group $pools, Connections $connections) { + $connection = $pools->get('queue')->pop(); + $connections->add($connection); + return $connection->getResource(); +}, ['pools', 'connections']); Server::setResource('queueForDatabase', function (Connection $queue) { return new EventDatabase($queue); @@ -283,9 +285,9 @@ $worker $worker ->shutdown() - ->inject('pools') - ->action(function (Group $pools) { - $pools->reclaim(); + ->inject('connections') + ->action(function (Connections $connections) { + $connections->reclaim(); }); $worker @@ -293,11 +295,11 @@ $worker ->inject('error') ->inject('logger') ->inject('log') - ->inject('pools') + ->inject('connections') ->inject('project') ->inject('auth') - ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project, Authorization $auth) use ($queueName) { - $pools->reclaim(); + ->action(function (Throwable $error, ?Logger $logger, Log $log, Connections $connections, Document $project, Authorization $auth) use ($queueName) { + $connections->reclaim(); $version = Http::getEnv('_APP_VERSION', 'UNKNOWN'); if ($error instanceof PDOException) { diff --git a/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php b/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php index 92c42be3e1..b07e1bed96 100644 --- a/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php +++ b/src/Appwrite/Platform/Tasks/DeleteOrphanedProjects.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Tasks; +use Appwrite\Utopia\Queue\Connections; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -34,13 +35,14 @@ class DeleteOrphanedProjects extends Action ->inject('dbForConsole') ->inject('register') ->inject('auth') - ->callback(function (bool $commit, Group $pools, Cache $cache, Database $dbForConsole, Registry $register, Authorization $auth) { - $this->action($commit, $pools, $cache, $dbForConsole, $register, $auth); + ->inject('connections') + ->callback(function (bool $commit, Group $pools, Cache $cache, Database $dbForConsole, Registry $register, Authorization $auth, Connections $connections) { + $this->action($commit, $pools, $cache, $dbForConsole, $register, $auth, $connections); }); } - public function action(bool $commit, Group $pools, Cache $cache, Database $dbForConsole, Registry $register, Authorization $auth): void + public function action(bool $commit, Group $pools, Cache $cache, Database $dbForConsole, Registry $register, Authorization $auth, Connections $connections): void { Console::title('Delete orphaned projects V1'); @@ -87,10 +89,9 @@ class DeleteOrphanedProjects extends Action try { $db = $project->getAttribute('database'); - $adapter = $pools - ->get($db) - ->pop() - ->getResource(); + $connection = $pools->get($db)->pop(); + $connections->add($connection); + $adapter = $connection->getResource(); $dbForProject = new Database($adapter, $cache); $dbForProject->setAuthorization($auth); diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index 0f2d12c0a6..8a8bdf500c 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Tasks; use Appwrite\ClamAV\Network; +use Appwrite\Utopia\Queue\Connections; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Domains\Domain; @@ -25,10 +26,11 @@ class Doctor extends Action $this ->desc('Validate server health') ->inject('register') - ->callback(fn (Registry $register) => $this->action($register)); + ->inject('connections') + ->callback(fn (Registry $register, Connections $connections) => $this->action($register, $connections)); } - public function action(Registry $register): void + public function action(Registry $register, Connections $connections): void { Console::log(" __ ____ ____ _ _ ____ __ ____ ____ __ __ / _\ ( _ \( _ \/ )( \( _ \( )(_ _)( __) ( )/ \ @@ -126,7 +128,9 @@ class Doctor extends Action foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $adapter = $pools->get($database)->pop()->getResource(); + $connection = $pools->get($database)->pop(); + $connections->add($connection); + $adapter = $connection->getResource(); if ($adapter->ping()) { Console::success('🟢 ' . str_pad("{$key}({$database})", 50, '.') . 'connected'); @@ -149,7 +153,9 @@ class Doctor extends Action foreach ($configs as $key => $config) { foreach ($config as $pool) { try { - $adapter = $pools->get($pool)->pop()->getResource(); + $connection = $pools->get($pool)->pop(); + $connections->add($connection); + $adapter = $connection->getResource(); if ($adapter->ping()) { Console::success('🟢 ' . str_pad("{$key}({$pool})", 50, '.') . 'connected'); diff --git a/src/Appwrite/Platform/Tasks/GetMigrationStats.php b/src/Appwrite/Platform/Tasks/GetMigrationStats.php index 973f55a237..48d8c90a8b 100644 --- a/src/Appwrite/Platform/Tasks/GetMigrationStats.php +++ b/src/Appwrite/Platform/Tasks/GetMigrationStats.php @@ -2,13 +2,13 @@ namespace Appwrite\Platform\Tasks; +use Appwrite\Utopia\Queue\Connections; use League\Csv\CannotInsertRecord; use League\Csv\Writer; use PHPMailer\PHPMailer\PHPMailer; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Database\Database; -use Utopia\Database\Exception\Authorization; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization as ValidatorAuthorization; use Utopia\Http\Adapter\FPM\Server; @@ -50,8 +50,9 @@ class GetMigrationStats extends Action ->inject('dbForConsole') ->inject('register') ->inject('auth') - ->callback(function (Group $pools, Cache $cache, Database $dbForConsole, Registry $register, ValidatorAuthorization $auth) { - $this->action($pools, $cache, $dbForConsole, $register, $auth); + ->inject('connections') + ->callback(function (Group $pools, Cache $cache, Database $dbForConsole, Registry $register, ValidatorAuthorization $auth, Connections $connections) { + $this->action($pools, $cache, $dbForConsole, $register, $auth, $connections); }); } @@ -59,7 +60,7 @@ class GetMigrationStats extends Action * @throws \Utopia\Exception * @throws CannotInsertRecord */ - public function action(Group $pools, Cache $cache, Database $dbForConsole, Registry $register, ValidatorAuthorization $auth): void + public function action(Group $pools, Cache $cache, Database $dbForConsole, Registry $register, ValidatorAuthorization $auth, Connections $connections): void { //docker compose exec -t appwrite get-migration-stats @@ -99,12 +100,11 @@ class GetMigrationStats extends Action try { $db = $project->getAttribute('database'); - $adapter = $pools - ->get($db) - ->pop() - ->getResource(); + $connection = $pools->get($db)->pop(); + $connections->add($connection); + $adapter = $connection->getResource(); - $dbForProject = new Database($adapter, $cache); + $dbForProject = new Database($adapter, $cache); // TODO: Use getProjectDB instead, or reclaim connections properly $dbForProject->setAuthorization($auth); $dbForProject->setDatabase('appwrite'); $dbForProject->setNamespace('_' . $project->getInternalId()); diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index e2c278714f..a5d4918b7a 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -90,7 +90,7 @@ class ScheduleFunctions extends ScheduleBase ->trigger(); } - $queue->reclaim(); + $queue->reclaim(); // TODO: Do in try/catch/finally, or add to connectons resource }); } diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 8e52973a0c..8538a24234 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -51,7 +51,7 @@ class ScheduleMessages extends ScheduleBase $schedule['$id'], ); - $queue->reclaim(); + $queue->reclaim(); // TODO: Do in try/catch/finally, or add to connectons resource unset($this->schedules[$schedule['resourceId']]); }); diff --git a/src/Appwrite/Platform/Workers/Hamster.php b/src/Appwrite/Platform/Workers/Hamster.php index 84a813d157..913d8e10ae 100644 --- a/src/Appwrite/Platform/Workers/Hamster.php +++ b/src/Appwrite/Platform/Workers/Hamster.php @@ -4,6 +4,7 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\Hamster as EventHamster; use Appwrite\Network\Validator\Origin; +use Appwrite\Utopia\Queue\Connections; use Utopia\Analytics\Adapter\Mixpanel; use Utopia\Analytics\Event as AnalyticsEvent; use Utopia\Cache\Cache; @@ -54,7 +55,8 @@ class Hamster extends Action ->inject('cache') ->inject('dbForConsole') ->inject('auth') - ->callback(fn (Message $message, Group $pools, Cache $cache, Database $dbForConsole, Authorization $auth) => $this->action($message, $pools, $cache, $dbForConsole, $auth)); + ->inject('connections') + ->callback(fn (Message $message, Group $pools, Cache $cache, Database $dbForConsole, Authorization $auth, Connections $connections) => $this->action($message, $pools, $cache, $dbForConsole, $auth, $connections)); } /** @@ -66,7 +68,7 @@ class Hamster extends Action * @return void * @throws \Utopia\Database\Exception */ - public function action(Message $message, Group $pools, Cache $cache, Database $dbForConsole, Authorization $auth): void + public function action(Message $message, Group $pools, Cache $cache, Database $dbForConsole, Authorization $auth, Connections $connections): void { $token = Http::getEnv('_APP_MIXPANEL_TOKEN', ''); if (empty($token)) { @@ -84,7 +86,7 @@ class Hamster extends Action switch ($type) { case EventHamster::TYPE_PROJECT: - $this->getStatsForProject(new Document($payload['project']), $pools, $cache, $dbForConsole, $auth); + $this->getStatsForProject(new Document($payload['project']), $pools, $cache, $dbForConsole, $auth, $connections); break; case EventHamster::TYPE_ORGANISATION: $this->getStatsForOrganization(new Document($payload['organization']), $dbForConsole); @@ -102,7 +104,7 @@ class Hamster extends Action * @param Database $dbForConsole * @throws \Utopia\Database\Exception */ - private function getStatsForProject(Document $project, Group $pools, Cache $cache, Database $dbForConsole, Authorization $auth): void + private function getStatsForProject(Document $project, Group $pools, Cache $cache, Database $dbForConsole, Authorization $auth, Connections $connections): void { /** * Skip user projects with id 'console' @@ -116,12 +118,11 @@ class Hamster extends Action try { $db = $project->getAttribute('database'); - $adapter = $pools - ->get($db) - ->pop() - ->getResource(); + $connection = $pools->get($db)->pop(); + $connections->add($connection); + $adapter = $connection->getResource(); - $dbForProject = new Database($adapter, $cache); + $dbForProject = new Database($adapter, $cache); // TODO: Use getProjectDB instead, or reclaim connections properly $dbForProject->setAuthorization($auth); $dbForProject->setDatabase('appwrite'); $dbForProject->setNamespace('_' . $project->getInternalId()); diff --git a/src/Appwrite/Utopia/Queue/Connections.php b/src/Appwrite/Utopia/Queue/Connections.php new file mode 100644 index 0000000000..2aa4d0f69b --- /dev/null +++ b/src/Appwrite/Utopia/Queue/Connections.php @@ -0,0 +1,54 @@ +connections[$connection->getID()] = $connection; + return $this; + } + + /** + * @param string $id + * @return Connection + */ + public function get(string $id): Connection + { + return $this->connections[$id] ?? throw new \Exception("Connection '{$id}' not found"); + } + + /** + * @param string $id + * @return self + */ + public function remove(string $id): self + { + unset($this->connections[$id]); + return $this; + } + + /** + * @return self + */ + public function reclaim(): self + { + foreach ($this->connections as $connection) { + $connection->reclaim(); + } + + return $this; + } +} From 713928cff32d0ec19d51302693b8ddaf6f05374f Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 1 Apr 2024 18:40:35 +0200 Subject: [PATCH 018/195] Couroutines test ## What does this PR do? (Provide a description of what this PR does and why it's needed.) ## Test Plan (Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Screenshots may also be helpful.) ## Related PRs and Issues - (Related PR or issue) ## Checklist - [ ] Have you read the [Contributing Guidelines on issues](https://github.com/appwrite/appwrite/blob/master/CONTRIBUTING.md)? - [ ] If the PR includes a change to an API's metadata (desc, label, params, etc.), does it also include updated API specs and example docs? --- app/console | 2 +- composer.lock | 89 +++++++++++++++++++++++++-------------------------- 2 files changed, 45 insertions(+), 46 deletions(-) diff --git a/app/console b/app/console index 4769c50189..d75ef00fb0 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 4769c5018979b3f00393ce3015ff8bf69d9c1657 +Subproject commit d75ef00fb088c909bf8fdc5b12c2fe25ed270b43 diff --git a/composer.lock b/composer.lock index 95d7ddd8fa..8ef6c37df7 100644 --- a/composer.lock +++ b/composer.lock @@ -156,21 +156,21 @@ }, { "name": "appwrite/php-runtimes", - "version": "0.13.3", + "version": "0.13.5", "source": { "type": "git", "url": "https://github.com/appwrite/runtimes.git", - "reference": "5d93fc578a9a543bcdc9b2c0562d80a51d56c73d" + "reference": "ba24c3a163f1a1da6cd355db92def508d05e59f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/runtimes/zipball/5d93fc578a9a543bcdc9b2c0562d80a51d56c73d", - "reference": "5d93fc578a9a543bcdc9b2c0562d80a51d56c73d", + "url": "https://api.github.com/repos/appwrite/runtimes/zipball/ba24c3a163f1a1da6cd355db92def508d05e59f7", + "reference": "ba24c3a163f1a1da6cd355db92def508d05e59f7", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/system": "0.7.*" + "utopia-php/system": "0.8.*" }, "require-dev": { "phpunit/phpunit": "^9.3", @@ -204,9 +204,9 @@ ], "support": { "issues": "https://github.com/appwrite/runtimes/issues", - "source": "https://github.com/appwrite/runtimes/tree/0.13.3" + "source": "https://github.com/appwrite/runtimes/tree/0.13.5" }, - "time": "2024-03-01T14:47:47+00:00" + "time": "2024-04-01T10:35:02+00:00" }, { "name": "beberlei/assert", @@ -1406,16 +1406,16 @@ }, { "name": "utopia-php/cache", - "version": "0.9.0", + "version": "0.9.1", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "4fc7b4789b5f0ce74835c1ecfec4f3afe6f0e34e" + "reference": "552b4c554bb14d0c529631ce304cdf4a2b9d06a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/4fc7b4789b5f0ce74835c1ecfec4f3afe6f0e34e", - "reference": "4fc7b4789b5f0ce74835c1ecfec4f3afe6f0e34e", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/552b4c554bb14d0c529631ce304cdf4a2b9d06a6", + "reference": "552b4c554bb14d0c529631ce304cdf4a2b9d06a6", "shasum": "" }, "require": { @@ -1450,9 +1450,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.9.0" + "source": "https://github.com/utopia-php/cache/tree/0.9.1" }, - "time": "2024-01-07T18:11:23+00:00" + "time": "2024-03-19T17:07:20+00:00" }, { "name": "utopia-php/cli", @@ -2436,16 +2436,16 @@ }, { "name": "utopia-php/system", - "version": "0.7.2", + "version": "0.8.0", "source": { "type": "git", "url": "https://github.com/utopia-php/system.git", - "reference": "4593d4d334b0c15879c4744a826e0362924c5d66" + "reference": "a2cbfb3c69b9ecb8b6f06c5774f3cf279ea7665e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/system/zipball/4593d4d334b0c15879c4744a826e0362924c5d66", - "reference": "4593d4d334b0c15879c4744a826e0362924c5d66", + "url": "https://api.github.com/repos/utopia-php/system/zipball/a2cbfb3c69b9ecb8b6f06c5774f3cf279ea7665e", + "reference": "a2cbfb3c69b9ecb8b6f06c5774f3cf279ea7665e", "shasum": "" }, "require": { @@ -2486,9 +2486,9 @@ ], "support": { "issues": "https://github.com/utopia-php/system/issues", - "source": "https://github.com/utopia-php/system/tree/0.7.2" + "source": "https://github.com/utopia-php/system/tree/0.8.0" }, - "time": "2023-10-20T01:39:17+00:00" + "time": "2024-04-01T10:22:28+00:00" }, { "name": "utopia-php/vcs", @@ -2935,16 +2935,16 @@ }, { "name": "laravel/pint", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "6b127276e3f263f7bb17d5077e9e0269e61b2a0e" + "reference": "c52de679b3ac01207016c179d7ce173e4be128c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/6b127276e3f263f7bb17d5077e9e0269e61b2a0e", - "reference": "6b127276e3f263f7bb17d5077e9e0269e61b2a0e", + "url": "https://api.github.com/repos/laravel/pint/zipball/c52de679b3ac01207016c179d7ce173e4be128c4", + "reference": "c52de679b3ac01207016c179d7ce173e4be128c4", "shasum": "" }, "require": { @@ -2997,20 +2997,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-02-20T17:38:05+00:00" + "time": "2024-03-26T16:40:24+00:00" }, { "name": "matthiasmullie/minify", - "version": "1.3.71", + "version": "1.3.73", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1" + "reference": "cb7a9297b4ab070909cefade30ee95054d4ae87a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", - "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/cb7a9297b4ab070909cefade30ee95054d4ae87a", + "reference": "cb7a9297b4ab070909cefade30ee95054d4ae87a", "shasum": "" }, "require": { @@ -3060,7 +3060,7 @@ ], "support": { "issues": "https://github.com/matthiasmullie/minify/issues", - "source": "https://github.com/matthiasmullie/minify/tree/1.3.71" + "source": "https://github.com/matthiasmullie/minify/tree/1.3.73" }, "funding": [ { @@ -3068,7 +3068,7 @@ "type": "github" } ], - "time": "2023-04-25T20:33:03+00:00" + "time": "2024-03-15T10:27:10+00:00" }, { "name": "matthiasmullie/path-converter", @@ -3597,16 +3597,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.26.0", + "version": "1.27.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "231e3186624c03d7e7c890ec662b81e6b0405227" + "reference": "86e4d5a4b036f8f0be1464522f4c6b584c452757" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/231e3186624c03d7e7c890ec662b81e6b0405227", - "reference": "231e3186624c03d7e7c890ec662b81e6b0405227", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/86e4d5a4b036f8f0be1464522f4c6b584c452757", + "reference": "86e4d5a4b036f8f0be1464522f4c6b584c452757", "shasum": "" }, "require": { @@ -3638,9 +3638,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.26.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.27.0" }, - "time": "2024-02-23T16:05:55+00:00" + "time": "2024-03-21T13:14:53+00:00" }, { "name": "phpstan/phpstan", @@ -4975,16 +4975,16 @@ }, { "name": "sebastian/resource-operations", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", "shasum": "" }, "require": { @@ -4996,7 +4996,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -5017,8 +5017,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" }, "funding": [ { @@ -5026,7 +5025,7 @@ "type": "github" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2024-03-14T16:00:52+00:00" }, { "name": "sebastian/type", @@ -5599,5 +5598,5 @@ "platform-overrides": { "php": "8.2" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } From 6ce8781af142e0e1e8aa3523294638b349919700 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 1 Apr 2024 19:39:26 +0200 Subject: [PATCH 019/195] Fixed email escaping --- app/console | 2 +- app/controllers/api/account.php | 4 ++-- app/controllers/api/projects.php | 2 +- app/controllers/api/teams.php | 2 +- app/controllers/general.php | 1 + composer.json | 2 +- composer.lock | 14 +++++++------- docker-compose.yml | 2 +- src/Appwrite/Platform/Workers/Certificates.php | 2 +- src/Appwrite/Platform/Workers/Mails.php | 6 +++--- 10 files changed, 19 insertions(+), 18 deletions(-) diff --git a/app/console b/app/console index d75ef00fb0..053a975eb7 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit d75ef00fb088c909bf8fdc5b12c2fe25ed270b43 +Subproject commit 053a975eb7f9b8c28847e7a52852f3b6189b986b diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 2b1804dce2..086f3e8efb 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2966,7 +2966,7 @@ Http::post('/v1/account/recovery') $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl'); $message - ->setParam('{{body}}', $body, escapeHtml: false) + ->setParam('{{body}}', $body, escape: false) ->setParam('{{hello}}', $locale->getText("emails.recovery.hello")) ->setParam('{{footer}}', $locale->getText("emails.recovery.footer")) ->setParam('{{thanks}}', $locale->getText("emails.recovery.thanks")) @@ -3216,7 +3216,7 @@ Http::post('/v1/account/verification') $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl'); $message - ->setParam('{{body}}', $body, escapeHtml: false) + ->setParam('{{body}}', $body, escape: false) ->setParam('{{hello}}', $locale->getText("emails.verification.hello")) ->setParam('{{footer}}', $locale->getText("emails.verification.footer")) ->setParam('{{thanks}}', $locale->getText("emails.verification.thanks")) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 6ea31e92a5..391f881574 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1676,7 +1676,7 @@ Http::get('/v1/projects/:projectId/templates/email/:type/:locale') $message ->setParam('{{hello}}', $localeObj->getText("emails.{$type}.hello")) ->setParam('{{footer}}', $localeObj->getText("emails.{$type}.footer")) - ->setParam('{{body}}', $localeObj->getText('emails.' . $type . '.body'), escapeHtml: false) + ->setParam('{{body}}', $localeObj->getText('emails.' . $type . '.body'), escape: false) ->setParam('{{thanks}}', $localeObj->getText("emails.{$type}.thanks")) ->setParam('{{signature}}', $localeObj->getText("emails.{$type}.signature")) ->setParam('{{direction}}', $localeObj->getText('settings.direction')); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index a60bd2f644..e2ccf7296e 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -567,7 +567,7 @@ Http::post('/v1/teams/:teamId/memberships') $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl'); $message - ->setParam('{{body}}', $body, escapeHtml: false) + ->setParam('{{body}}', $body, escape: false) ->setParam('{{hello}}', $locale->getText("emails.invitation.hello")) ->setParam('{{footer}}', $locale->getText("emails.invitation.footer")) ->setParam('{{thanks}}', $locale->getText("emails.invitation.thanks")) diff --git a/app/controllers/general.php b/app/controllers/general.php index 138e8f6ee7..e6fa8ec5a2 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -730,6 +730,7 @@ Http::error() Console::error('[Error] URL: ' . $route->getPath()); } + Console::error('[Error] Code: ' . $code); Console::error('[Error] Type: ' . get_class($error)); Console::error('[Error] Message: ' . $message); Console::error('[Error] File: ' . $file); diff --git a/composer.json b/composer.json index 9440299990..68125752d0 100644 --- a/composer.json +++ b/composer.json @@ -63,7 +63,7 @@ "utopia-php/orchestration": "dev-feat-framework-v2 as 0.9.99", "utopia-php/platform": "dev-feat-framework-v2 as 0.5.99", "utopia-php/pools": "0.4.*", - "utopia-php/view": "0.1.*", + "utopia-php/view": "0.2.*", "utopia-php/queue": "dev-feat-framework-v2-v2 as 0.7.99", "utopia-php/registry": "0.5.*", "utopia-php/storage": "dev-feat-framework-v2-v2 as 0.18.99", diff --git a/composer.lock b/composer.lock index 8ef6c37df7..832113e6cc 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": "750df68fe066fde9554f4d1ba4a9afb5", + "content-hash": "791b4ede59f656313657d98fc2ed94fc", "packages": [ { "name": "adhocore/jwt", @@ -2541,16 +2541,16 @@ }, { "name": "utopia-php/view", - "version": "0.1.0", + "version": "0.2.0", "source": { "type": "git", "url": "https://github.com/utopia-php/view.git", - "reference": "013a495af4e625df172d9bd534011014cb32bbab" + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/view/zipball/013a495af4e625df172d9bd534011014cb32bbab", - "reference": "013a495af4e625df172d9bd534011014cb32bbab", + "url": "https://api.github.com/repos/utopia-php/view/zipball/6ee55e83bc014c39ed6b69390f6d399116f65e88", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88", "shasum": "" }, "require": { @@ -2578,9 +2578,9 @@ ], "support": { "issues": "https://github.com/utopia-php/view/issues", - "source": "https://github.com/utopia-php/view/tree/0.1.0" + "source": "https://github.com/utopia-php/view/tree/0.2.0" }, - "time": "2023-09-10T12:07:26+00:00" + "time": "2024-04-01T17:21:29+00:00" }, { "name": "utopia-php/websocket", diff --git a/docker-compose.yml b/docker-compose.yml index ff04f03a99..e6a15c7e82 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -51,7 +51,7 @@ services: context: . args: DEBUG: false - TESTING: false + TESTING: true VERSION: dev ports: - 9501:80 diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 6fd7af3ffb..ba7a1a3fcb 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -452,7 +452,7 @@ class Certificates extends Action $message = Template::fromFile(__DIR__ . '/../../../../app/config/locale/templates/email-inner-base.tpl'); $message - ->setParam('{{body}}', $locale->getText("emails.certificate.body"), escapeHtml: false) + ->setParam('{{body}}', $locale->getText("emails.certificate.body"), escape: false) ->setParam('{{hello}}', $locale->getText("emails.certificate.hello")) ->setParam('{{footer}}', $locale->getText("emails.certificate.footer")) ->setParam('{{thanks}}', $locale->getText("emails.certificate.thanks")) diff --git a/src/Appwrite/Platform/Workers/Mails.php b/src/Appwrite/Platform/Workers/Mails.php index 37ce782b8b..d1cf800269 100644 --- a/src/Appwrite/Platform/Workers/Mails.php +++ b/src/Appwrite/Platform/Workers/Mails.php @@ -84,13 +84,13 @@ class Mails extends Action $bodyTemplate = __DIR__ . '/../../../../app/config/locale/templates/email-base.tpl'; } $bodyTemplate = Template::fromFile($bodyTemplate); - $bodyTemplate->setParam('{{body}}', $body, escapeHtml: false); + $bodyTemplate->setParam('{{body}}', $body, escape: false); foreach ($variables as $key => $value) { // TODO: hotfix for redirect param - $bodyTemplate->setParam('{{' . $key . '}}', $value, escapeHtml: $key !== 'redirect'); + $bodyTemplate->setParam('{{' . $key . '}}', $value, escape: $key !== 'redirect'); } foreach ($this->richTextParams as $key => $value) { - $bodyTemplate->setParam('{{' . $key . '}}', $value, escapeHtml: false); + $bodyTemplate->setParam('{{' . $key . '}}', $value, escape: false); } $body = $bodyTemplate->render(); From c73ef2c6492af629daa772480c7529ccaa853b64 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 1 Apr 2024 23:46:00 +0200 Subject: [PATCH 020/195] Fixed 451 status code --- composer.lock | 8 ++++---- src/Appwrite/Utopia/Response.php | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index 832113e6cc..2d778c68ee 100644 --- a/composer.lock +++ b/composer.lock @@ -1763,12 +1763,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "e2e7498aa16cefcdcb474548c3d04ce720ec6430" + "reference": "662f7f93d2cd2b6a8bfc07b5b61dbec57ac51c0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/e2e7498aa16cefcdcb474548c3d04ce720ec6430", - "reference": "e2e7498aa16cefcdcb474548c3d04ce720ec6430", + "url": "https://api.github.com/repos/utopia-php/http/zipball/662f7f93d2cd2b6a8bfc07b5b61dbec57ac51c0c", + "reference": "662f7f93d2cd2b6a8bfc07b5b61dbec57ac51c0c", "shasum": "" }, "require": { @@ -1803,7 +1803,7 @@ "issues": "https://github.com/utopia-php/http/issues", "source": "https://github.com/utopia-php/http/tree/feat-framework-v2" }, - "time": "2024-03-08T10:38:48+00:00" + "time": "2024-04-01T21:28:29+00:00" }, { "name": "utopia-php/image", diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 280962e139..e014087203 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -312,9 +312,9 @@ class Response extends SwooleResponse * * @param float $time */ - public function __construct(SwooleResponse $swooleResponse) + public function __construct(SwooleResponse $response) { - $response = $swooleResponse->getSwooleResponse(); + parent::__construct($response->getSwooleResponse()); $this // General @@ -459,8 +459,6 @@ class Response extends SwooleResponse ->setModel(new MigrationFirebaseProject()) // Tests (keep last) ->setModel(new Mock()); - - parent::__construct($response); } /** From b570368f100933317bd21629c8421c5549f1b9f3 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 1 Apr 2024 23:57:20 +0200 Subject: [PATCH 021/195] Fixed auth error in users usage --- app/controllers/api/users.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index e6524ff21c..29c5df147b 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -2110,7 +2110,6 @@ Http::get('/v1/users/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('register') ->inject('auth') ->action(function (string $range, Response $response, Database $dbForProject, Authorization $auth) { From be7a6e55f87b96f781481e4df1605ab37761831d Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 2 Apr 2024 03:02:06 +0200 Subject: [PATCH 022/195] Fixed whitespace --- app/init.php | 1 - composer.lock | 90 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/app/init.php b/app/init.php index 7f880985d0..09b8c07c31 100644 --- a/app/init.php +++ b/app/init.php @@ -1022,7 +1022,6 @@ foreach ($locales as $locale) { 'method' => 'GET', 'user_agent' => \sprintf( APP_USERAGENT, - System::getEnv('_APP_VERSION', 'UNKNOWN'), System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY) ), diff --git a/composer.lock b/composer.lock index 81a0ab41e5..004e901625 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": "78b9cd75952805a2347578f4fcb96add", + "content-hash": "e29166ee5ac565b8a7ad696a46588abc", "packages": [ { "name": "adhocore/jwt", @@ -1719,17 +1719,17 @@ "time": "2023-11-02T12:01:43+00:00" }, { - "name": "utopia-php/framework", - "version": "0.33.6", + "name": "utopia-php/fetch", + "version": "0.1.0", "source": { "type": "git", - "url": "https://github.com/utopia-php/http.git", - "reference": "8fe57da0cecd57e3b17cd395b4a666a24f4c07a6" + "url": "https://github.com/utopia-php/fetch.git", + "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/8fe57da0cecd57e3b17cd395b4a666a24f4c07a6", - "reference": "8fe57da0cecd57e3b17cd395b4a666a24f4c07a6", + "url": "https://api.github.com/repos/utopia-php/fetch/zipball/2fa214b9262acd1a3583515a364da4f35929d5c5", + "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5", "shasum": "" }, "require": { @@ -1801,9 +1801,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.6" + "source": "https://github.com/utopia-php/http/tree/feat-framework-v2" }, - "time": "2024-03-21T18:10:57+00:00" + "time": "2024-04-01T21:28:29+00:00" }, { "name": "utopia-php/image", @@ -3642,6 +3642,65 @@ }, "time": "2024-03-21T13:14:53+00:00" }, + { + "name": "phpstan/phpstan", + "version": "1.8.11", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "46e223dd68a620da18855c23046ddb00940b4014" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014", + "reference": "46e223dd68a620da18855c23046ddb00940b4014", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.8.11" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-10-24T15:45:13+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "9.2.31", @@ -5503,7 +5562,18 @@ } ], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "utopia-php/abuse": 20, + "utopia-php/analytics": 20, + "utopia-php/audit": 20, + "utopia-php/database": 20, + "utopia-php/domains": 20, + "utopia-php/framework": 20, + "utopia-php/orchestration": 20, + "utopia-php/platform": 20, + "utopia-php/queue": 20, + "utopia-php/storage": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From 378bb5fda3781562fffe50dcc27672701a8f03bf Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 2 Apr 2024 03:14:19 +0200 Subject: [PATCH 023/195] Fixed server --- app/init.php | 18 +++++++++--------- src/Appwrite/GraphQL/Resolvers.php | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/init.php b/app/init.php index 09b8c07c31..74814199c3 100644 --- a/app/init.php +++ b/app/init.php @@ -65,6 +65,11 @@ use Utopia\DSN\DSN; use Utopia\Http\Http; use Utopia\Http\Request; use Utopia\Http\Response; +use Utopia\Http\Validator\Hostname; +use Utopia\Http\Validator\IP; +use Utopia\Http\Validator\Range; +use Utopia\Http\Validator\URL; +use Utopia\Http\Validator\WhiteList; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Logger\Logger; @@ -82,11 +87,6 @@ use Utopia\Storage\Device\S3; use Utopia\Storage\Device\Wasabi; use Utopia\Storage\Storage; use Utopia\System\System; -use Utopia\Http\Validator\Hostname; -use Utopia\Http\Validator\IP; -use Utopia\Http\Validator\Range; -use Utopia\Http\Validator\URL; -use Utopia\Http\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; const APP_NAME = 'Appwrite'; @@ -116,8 +116,8 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours -const APP_CACHE_BUSTER = 405; -const APP_VERSION_STABLE = '1.5.4'; +const APP_CACHE_BUSTER = 331; +const APP_VERSION_STABLE = '1.5.0'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; @@ -262,7 +262,7 @@ if (!Http::isProduction()) { */ 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('apis', __DIR__ . '/config/apis.php'); Config::load('errors', __DIR__ . '/config/errors.php'); Config::load('oAuthProviders', __DIR__ . '/config/oAuthProviders.php'); Config::load('platforms', __DIR__ . '/config/platforms.php'); @@ -744,7 +744,7 @@ $register->set('pools', function () { $group = new Group(); $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ - 'scheme' => 'mariadb', + 'scheme' => System::getEnv('_APP_DB_ADAPTER', 'mariadb'), 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), 'port' => System::getEnv('_APP_DB_PORT', '3306'), 'user' => System::getEnv('_APP_DB_USER', ''), diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index 98c3f0116a..ce1a6ac7c6 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -270,7 +270,7 @@ class Resolvers try { $route = $utopia->match($request, fresh: true); - $utopia->execute($route, $request, xx); + $utopia->execute($route, $request, 'xx'); } catch (\Throwable $e) { if ($beforeReject) { $e = $beforeReject($e); From a1db69b7e70264cb907ac0e741b90bb87b37f633 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 2 Apr 2024 08:13:28 +0200 Subject: [PATCH 024/195] Fixed missing System namespace --- app/controllers/api/teams.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 04166cfb63..5c5013d98c 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -41,6 +41,7 @@ use Utopia\Http\Validator\Assoc; use Utopia\Http\Validator\Host; use Utopia\Http\Validator\Text; use Utopia\Locale\Locale; +use Utopia\System\System; Http::post('/v1/teams') ->desc('Create team') From bd57955168b687764f5aea4aa7fc77b2617a1d09 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 3 Apr 2024 13:43:20 +0200 Subject: [PATCH 025/195] Enabled coroutines --- app/http.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/http.php b/app/http.php index 40faabae30..069767f821 100644 --- a/app/http.php +++ b/app/http.php @@ -5,6 +5,7 @@ require_once __DIR__ . '/../vendor/autoload.php'; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Swoole\Process; +use Swoole\Runtime; use Utopia\Abuse\Adapters\TimeLimit; use Utopia\Audit\Audit; use Utopia\CLI\Console; @@ -22,6 +23,11 @@ use Utopia\Pools\Group; $payloadSize = 6 * (1024 * 1024); // 6MB $workerNumber = swoole_cpu_num() * intval(Http::getEnv('_APP_WORKER_PER_CORE', 6)); +// Unlimited memory limit to handle as many coroutines/requests as possible +ini_set('memory_limit', '-1'); + +Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL); + include __DIR__ . '/controllers/general.php'; $http = new Http(new Server('0.0.0.0', Http::getEnv('PORT', 80), [ From 211bcf8948f6ef7aaf21a8b255ff160e9e4852e7 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 9 Apr 2024 21:02:02 +0200 Subject: [PATCH 026/195] Removed proxy db --- .env | 9 +++----- app/controllers/shared/api.php | 24 +++++++++++++++---- app/init.php | 23 ++++-------------- app/views/install/compose.phtml | 2 +- docker-compose.yml | 41 ++------------------------------- 5 files changed, 30 insertions(+), 69 deletions(-) diff --git a/.env b/.env index cd44aac965..09abb07be2 100644 --- a/.env +++ b/.env @@ -22,9 +22,8 @@ _APP_REDIS_HOST=redis _APP_REDIS_PORT=6379 _APP_REDIS_PASS= _APP_REDIS_USER= -_APP_DB_ADAPTER=mariadb-proxy -_APP_DB_HOST=database-proxy -_APP_DB_PORT=80 +_APP_DB_HOST=mariadb +_APP_DB_PORT=3306 _APP_DB_SCHEMA=appwrite _APP_DB_USER=user _APP_DB_PASS=password @@ -104,6 +103,4 @@ _APP_MESSAGE_SMS_TEST_DSN= _APP_MESSAGE_EMAIL_TEST_DSN= _APP_MESSAGE_PUSH_TEST_DSN= _APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10 -_APP_PROJECT_REGIONS=default -_APP_DATABASE_PROXY_SECRET=password -_APP_DATABASE_PROXY_CONNECTION=mariadb://user:password@mariadb:3306/appwrite?pool_size=1000 \ No newline at end of file +_APP_PROJECT_REGIONS=default \ No newline at end of file diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index b63dc020a2..bf5c3323c7 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -497,12 +497,10 @@ Http::init() */ Http::shutdown() ->groups(['session']) - ->inject('utopia') - ->inject('request') ->inject('response') ->inject('project') ->inject('dbForProject') - ->action(function (Http $utopia, Request $request, Response $response, Document $project, Database $dbForProject) { + ->action(function (Response $response, Document $project, Database $dbForProject) { $sessionLimit = $project->getAttribute('auths', [])['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT; $session = $response->getPayload(); $userId = $session['userId'] ?? ''; @@ -548,7 +546,25 @@ Http::shutdown() ->inject('mode') ->inject('dbForConsole') ->inject('auth') - ->action(function (Http $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole, Authorization $auth) use ($parseLabel) { + ->action(function ( + Http $utopia, + Request $request, + Response $response, + Document $project, + Document $user, + Event $queueForEvents, + Audit $queueForAudits, + Usage $queueForUsage, + Delete $queueForDeletes, + EventDatabase $queueForDatabase, + Build $queueForBuilds, + Messaging $queueForMessaging, + Database $dbForProject, + Func $queueForFunctions, + string $mode, + Database $dbForConsole, + Authorization $auth, + ) use ($parseLabel) { if (!empty($user) && !$user->isEmpty() && empty($user->getInternalId())) { $user = $auth->skip(fn () => $dbForProject->getDocument('users', $user->getId())); } diff --git a/app/init.php b/app/init.php index 74814199c3..f10faf5b70 100644 --- a/app/init.php +++ b/app/init.php @@ -50,7 +50,6 @@ use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Adapter\MariaDB; -use Utopia\Database\Adapter\MariaDBProxy; use Utopia\Database\Adapter\MySQL; use Utopia\Database\Adapter\SQL; use Utopia\Database\Database; @@ -744,7 +743,7 @@ $register->set('pools', function () { $group = new Group(); $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ - 'scheme' => System::getEnv('_APP_DB_ADAPTER', 'mariadb'), + 'scheme' => 'mariadb', 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), 'port' => System::getEnv('_APP_DB_PORT', '3306'), 'user' => System::getEnv('_APP_DB_USER', ''), @@ -764,13 +763,13 @@ $register->set('pools', function () { 'type' => 'database', 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), 'multiple' => false, - 'schemes' => ['mariadb', 'mysql', 'mariadb-proxy'], + 'schemes' => ['mariadb', 'mysql'], ], 'database' => [ 'type' => 'database', 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), 'multiple' => true, - 'schemes' => ['mariadb', 'mysql', 'mariadb-proxy'], + 'schemes' => ['mariadb', 'mysql'], ], 'queue' => [ 'type' => 'queue', @@ -825,7 +824,7 @@ $register->set('pools', function () { //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(); @@ -846,19 +845,6 @@ $register->set('pools', function () { * Resource assignment to an adapter will happen below. */ switch ($dsnScheme) { - case 'mariadb-proxy': - $host = $dsnHost; - if ($dsnPort) { - $host .= ':' . $dsnPort; - } - - // Ignore port and password (user = password) - $resource = [ - 'endpoint' => 'http://' . $host . '/v1', - 'secret' => $dsnPass, - 'database' => $dsnDatabase - ]; - break; case 'mysql': case 'mariadb': $resource = function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { @@ -898,7 +884,6 @@ $register->set('pools', function () { $adapter = match ($dsn->getScheme()) { 'mariadb' => new MariaDB($resource()), 'mysql' => new MySQL($resource()), - 'mariadb-proxy' => new MariaDBProxy($resource['endpoint'], $resource['secret'], $resource['database']), default => null }; diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 354ef4ea49..cd2f4b1548 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -11,7 +11,7 @@ $httpsPort = $this->getParam('httpsPort', ''); $version = $this->getParam('version', ''); $organization = $this->getParam('organization', ''); $image = $this->getParam('image', ''); -?>version: '3' +?> services: traefik: diff --git a/docker-compose.yml b/docker-compose.yml index def84b0466..519af04898 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,8 +10,6 @@ x-logging: &x-logging max-file: "5" max-size: "10m" -version: "3" - services: traefik: image: traefik:2.11 @@ -117,7 +115,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -231,7 +228,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -262,7 +258,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -290,7 +285,6 @@ services: - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -330,7 +324,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -383,7 +376,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -419,7 +411,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -487,7 +478,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -518,7 +508,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -591,7 +580,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -628,7 +616,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -662,7 +649,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -694,7 +680,6 @@ services: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -726,7 +711,6 @@ services: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -762,7 +746,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -790,7 +773,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -822,7 +804,6 @@ services: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -855,7 +836,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -937,27 +917,12 @@ services: - OPR_PROXY_MAX_TIMEOUT=600 - OPR_PROXY_HEALTHCHECK=enabled - database-proxy: - container_name: database-proxy - image: appwrite/database-proxy:0.1.5 - networks: - - appwrite - - database-proxy - ports: - - 9520:80 - environment: - - UTOPIA_DATA_API_ENV=$_APP_ENV - - UTOPIA_DATA_API_SECRET=$_APP_DATABASE_PROXY_SECRET - - UTOPIA_DATA_API_SECRET_CONNECTION=$_APP_DATABASE_PROXY_CONNECTION - - UTOPIA_DATA_API_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER - - UTOPIA_DATA_API_LOGGING_CONFIG=$_APP_LOGGING_CONFIG - mariadb: image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p container_name: appwrite-mariadb <<: *x-logging networks: - - database-proxy + - appwrite volumes: - appwrite-mariadb:/var/lib/mysql:rw - ./mariadb-config.cnf:/etc/mysql/conf.d/mariadb-config.cnf @@ -1045,7 +1010,7 @@ services: ports: - 9506:8080 networks: - - database-proxy + - appwrite redis-insight: image: redis/redisinsight:latest @@ -1075,8 +1040,6 @@ networks: name: gateway appwrite: name: appwrite - database-proxy: - name: database-proxy runtimes: name: runtimes From 08b3182ef3294811027164d20b51b373866d84d5 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sun, 14 Apr 2024 10:56:09 +0200 Subject: [PATCH 027/195] WIP --- composer.json | 13 ++- composer.lock | 165 +++++++++++++++++++++++-------- docker-compose.yml | 98 +++++++++--------- src/Appwrite/Utopia/Request.php | 8 +- src/Appwrite/Utopia/Response.php | 10 +- 5 files changed, 190 insertions(+), 104 deletions(-) diff --git a/composer.json b/composer.json index 43ee25dcf9..e6d1308220 100644 --- a/composer.json +++ b/composer.json @@ -52,9 +52,10 @@ "utopia-php/cli": "0.17.*", "utopia-php/config": "0.2.*", "utopia-php/database": "dev-feat-framework-v2 as 0.49.99", + "utopia-php/di": "dev-main", "utopia-php/domains": "dev-feat-framework-v2 as 0.5.99", "utopia-php/dsn": "0.2.*", - "utopia-php/framework": "dev-feat-framework-v2 as 0.34.99", + "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99", "utopia-php/image": "0.6.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.3.*", @@ -62,7 +63,7 @@ "utopia-php/migration": "0.4.*", "utopia-php/orchestration": "dev-feat-framework-v2 as 0.9.99", "utopia-php/platform": "dev-feat-framework-v2 as 0.5.99", - "utopia-php/pools": "0.4.*", + "utopia-php/pools": "dev-feat-coroutine-support as 0.4.99", "utopia-php/view": "0.2.*", "utopia-php/queue": "dev-feat-framework-v2-v2 as 0.7.99", "utopia-php/registry": "0.5.*", @@ -96,5 +97,11 @@ "platform": { "php": "8.3" } - } + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/utopia-php/di" + } + ] } diff --git a/composer.lock b/composer.lock index 004e901625..fcb2ae8659 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": "e29166ee5ac565b8a7ad696a46588abc", + "content-hash": "42c5379dff348d3525688891a96d1e85", "packages": [ { "name": "adhocore/jwt", @@ -1611,6 +1611,68 @@ }, "time": "2024-03-07T16:55:44+00:00" }, + { + "name": "utopia-php/di", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/di.git", + "reference": "0bb7af5693bc131f4d2ce34d3f732d41e6637679" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/di/zipball/0bb7af5693bc131f4d2ce34d3f732d41e6637679", + "reference": "0bb7af5693bc131f4d2ce34d3f732d41e6637679", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" + }, + "default-branch": true, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/", + "Tests\\E2E\\": "tests/e2e" + } + }, + "scripts": { + "lint": [ + "vendor/bin/pint --test" + ], + "format": [ + "vendor/bin/pint" + ], + "check": [ + "vendor/bin/phpstan analyse -c phpstan.neon" + ], + "test": [ + "vendor/bin/phpunit --configuration phpunit.xml" + ] + }, + "license": [ + "MIT" + ], + "description": "A simple and lite library for managing dependency injections", + "keywords": [ + "framework", + "http", + "php", + "upf" + ], + "support": { + "source": "https://github.com/utopia-php/di/tree/main", + "issues": "https://github.com/utopia-php/di/issues" + }, + "time": "2024-04-08T22:41:41+00:00" + }, { "name": "utopia-php/domains", "version": "dev-feat-framework-v2", @@ -1759,23 +1821,25 @@ }, { "name": "utopia-php/framework", - "version": "dev-feat-framework-v2", + "version": "dev-feat-di-upgrade", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "662f7f93d2cd2b6a8bfc07b5b61dbec57ac51c0c" + "reference": "aef4e9a7bcb3ba21b993f2daad0364657c2fe1aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/662f7f93d2cd2b6a8bfc07b5b61dbec57ac51c0c", - "reference": "662f7f93d2cd2b6a8bfc07b5b61dbec57ac51c0c", + "url": "https://api.github.com/repos/utopia-php/http/zipball/aef4e9a7bcb3ba21b993f2daad0364657c2fe1aa", + "reference": "aef4e9a7bcb3ba21b993f2daad0364657c2fe1aa", "shasum": "" }, "require": { "ext-swoole": "*", - "php": ">=8.0" + "php": ">=8.0", + "utopia-php/di": "dev-main" }, "require-dev": { + "ext-xdebug": "*", "laravel/pint": "^1.2", "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.10", @@ -1785,7 +1849,7 @@ "type": "library", "autoload": { "psr-4": { - "Utopia\\Http\\": "src/Http" + "Utopia\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1801,9 +1865,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/feat-framework-v2" + "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-04-01T21:28:29+00:00" + "time": "2024-04-13T17:20:36+00:00" }, { "name": "utopia-php/image", @@ -2217,16 +2281,16 @@ }, { "name": "utopia-php/pools", - "version": "0.4.2", + "version": "dev-feat-coroutine-support", "source": { "type": "git", "url": "https://github.com/utopia-php/pools.git", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed" + "reference": "ada61e5b86191644e779ea2c71cd7bf172e94fca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/d2870ab74b31b7f4027799f082e85122154f8bed", - "reference": "d2870ab74b31b7f4027799f082e85122154f8bed", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/ada61e5b86191644e779ea2c71cd7bf172e94fca", + "reference": "ada61e5b86191644e779ea2c71cd7bf172e94fca", "shasum": "" }, "require": { @@ -2262,9 +2326,9 @@ ], "support": { "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/0.4.2" + "source": "https://github.com/utopia-php/pools/tree/feat-coroutine-support" }, - "time": "2022-11-22T07:55:45+00:00" + "time": "2024-04-10T21:34:22+00:00" }, { "name": "utopia-php/queue", @@ -2935,16 +2999,16 @@ }, { "name": "laravel/pint", - "version": "v1.15.0", + "version": "v1.15.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "c52de679b3ac01207016c179d7ce173e4be128c4" + "reference": "5f288b5e79938cc72f5c298d384e639de87507c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/c52de679b3ac01207016c179d7ce173e4be128c4", - "reference": "c52de679b3ac01207016c179d7ce173e4be128c4", + "url": "https://api.github.com/repos/laravel/pint/zipball/5f288b5e79938cc72f5c298d384e639de87507c6", + "reference": "5f288b5e79938cc72f5c298d384e639de87507c6", "shasum": "" }, "require": { @@ -2955,13 +3019,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.49.0", - "illuminate/view": "^10.43.0", - "larastan/larastan": "^2.8.1", + "friendsofphp/php-cs-fixer": "^3.52.1", + "illuminate/view": "^10.48.4", + "larastan/larastan": "^2.9.2", "laravel-zero/framework": "^10.3.0", - "mockery/mockery": "^1.6.7", + "mockery/mockery": "^1.6.11", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.33.6" + "pestphp/pest": "^2.34.5" }, "bin": [ "builds/pint" @@ -2997,7 +3061,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-03-26T16:40:24+00:00" + "time": "2024-04-02T14:28:47+00:00" }, { "name": "matthiasmullie/minify", @@ -3413,28 +3477,35 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", + "version": "5.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + "reference": "298d2febfe79d03fe714eb871d5538da55205b1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/298d2febfe79d03fe714eb871d5538da55205b1a", + "reference": "298d2febfe79d03fe714eb871d5538da55205b1a", "shasum": "" }, "require": { + "doctrine/deprecations": "^1.1", "ext-filter": "*", - "php": "^7.2 || ^8.0", + "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7", "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" + "mockery/mockery": "~1.3.5", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^5.13" }, "type": "library", "extra": { @@ -3458,15 +3529,15 @@ }, { "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "email": "opensource@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.0" }, - "time": "2021-10-19T17:43:47+00:00" + "time": "2024-04-09T21:13:58+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -3597,16 +3668,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.27.0", + "version": "1.28.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "86e4d5a4b036f8f0be1464522f4c6b584c452757" + "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/86e4d5a4b036f8f0be1464522f4c6b584c452757", - "reference": "86e4d5a4b036f8f0be1464522f4c6b584c452757", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", + "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", "shasum": "" }, "require": { @@ -3638,9 +3709,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.27.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.28.0" }, - "time": "2024-03-21T13:14:53+00:00" + "time": "2024-04-03T18:51:33+00:00" }, { "name": "phpstan/phpstan", @@ -5532,7 +5603,7 @@ }, { "package": "utopia-php/framework", - "version": "dev-feat-framework-v2", + "version": "dev-feat-di-upgrade", "alias": "0.34.99", "alias_normalized": "0.34.99.0" }, @@ -5548,6 +5619,12 @@ "alias": "0.5.99", "alias_normalized": "0.5.99.0" }, + { + "package": "utopia-php/pools", + "version": "dev-feat-coroutine-support", + "alias": "0.4.99", + "alias_normalized": "0.4.99.0" + }, { "package": "utopia-php/queue", "version": "dev-feat-framework-v2-v2", @@ -5567,10 +5644,12 @@ "utopia-php/analytics": 20, "utopia-php/audit": 20, "utopia-php/database": 20, + "utopia-php/di": 20, "utopia-php/domains": 20, "utopia-php/framework": 20, "utopia-php/orchestration": 20, "utopia-php/platform": 20, + "utopia-php/pools": 20, "utopia-php/queue": 20, "utopia-php/storage": 20 }, diff --git a/docker-compose.yml b/docker-compose.yml index 519af04898..af99151b4c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -188,54 +188,54 @@ services: - _APP_MESSAGE_PUSH_TEST_DSN - _APP_CONSOLE_COUNTRIES_DENYLIST - appwrite-realtime: - entrypoint: realtime - <<: *x-logging - container_name: appwrite-realtime - image: appwrite-dev - restart: unless-stopped - ports: - - 9505:80 - labels: - - "traefik.enable=true" - - "traefik.constraint-label-stack=appwrite" - - "traefik.docker.network=appwrite" - - "traefik.http.services.appwrite_realtime.loadbalancer.server.port=80" - #ws - - traefik.http.routers.appwrite_realtime_ws.entrypoints=appwrite_web - - traefik.http.routers.appwrite_realtime_ws.rule=PathPrefix(`/v1/realtime`) - - traefik.http.routers.appwrite_realtime_ws.service=appwrite_realtime - # wss - - traefik.http.routers.appwrite_realtime_wss.entrypoints=appwrite_websecure - - traefik.http.routers.appwrite_realtime_wss.rule=PathPrefix(`/v1/realtime`) - - traefik.http.routers.appwrite_realtime_wss.service=appwrite_realtime - - traefik.http.routers.appwrite_realtime_wss.tls=true - networks: - - appwrite - volumes: - - ./app:/usr/src/code/app - - ./src:/usr/src/code/src - depends_on: - - mariadb - - redis - environment: - - _APP_ENV - - _APP_WORKER_PER_CORE - - _APP_OPTIONS_ABUSE - - _APP_OPTIONS_ROUTER_PROTECTION - - _APP_OPENSSL_KEY_V1 - - _APP_REDIS_HOST - - _APP_REDIS_PORT - - _APP_REDIS_USER - - _APP_REDIS_PASS - - _APP_DB_HOST - - _APP_DB_PORT - - _APP_DB_SCHEMA - - _APP_DB_USER - - _APP_DB_PASS - - _APP_USAGE_STATS - - _APP_LOGGING_PROVIDER - - _APP_LOGGING_CONFIG + # appwrite-realtime: + # entrypoint: realtime + # <<: *x-logging + # container_name: appwrite-realtime + # image: appwrite-dev + # restart: unless-stopped + # ports: + # - 9505:80 + # labels: + # - "traefik.enable=true" + # - "traefik.constraint-label-stack=appwrite" + # - "traefik.docker.network=appwrite" + # - "traefik.http.services.appwrite_realtime.loadbalancer.server.port=80" + # #ws + # - traefik.http.routers.appwrite_realtime_ws.entrypoints=appwrite_web + # - traefik.http.routers.appwrite_realtime_ws.rule=PathPrefix(`/v1/realtime`) + # - traefik.http.routers.appwrite_realtime_ws.service=appwrite_realtime + # # wss + # - traefik.http.routers.appwrite_realtime_wss.entrypoints=appwrite_websecure + # - traefik.http.routers.appwrite_realtime_wss.rule=PathPrefix(`/v1/realtime`) + # - traefik.http.routers.appwrite_realtime_wss.service=appwrite_realtime + # - traefik.http.routers.appwrite_realtime_wss.tls=true + # networks: + # - appwrite + # volumes: + # - ./app:/usr/src/code/app + # - ./src:/usr/src/code/src + # depends_on: + # - mariadb + # - redis + # environment: + # - _APP_ENV + # - _APP_WORKER_PER_CORE + # - _APP_OPTIONS_ABUSE + # - _APP_OPTIONS_ROUTER_PROTECTION + # - _APP_OPENSSL_KEY_V1 + # - _APP_REDIS_HOST + # - _APP_REDIS_PORT + # - _APP_REDIS_USER + # - _APP_REDIS_PASS + # - _APP_DB_HOST + # - _APP_DB_PORT + # - _APP_DB_SCHEMA + # - _APP_DB_USER + # - _APP_DB_PASS + # - _APP_USAGE_STATS + # - _APP_LOGGING_PROVIDER + # - _APP_LOGGING_CONFIG appwrite-worker-audits: entrypoint: worker-audits @@ -933,7 +933,7 @@ services: - MYSQL_DATABASE=${_APP_DB_SCHEMA} - MYSQL_USER=${_APP_DB_USER} - MYSQL_PASSWORD=${_APP_DB_PASS} - command: "mysqld --innodb-flush-method=fsync" # add ' --query_cache_size=0' for DB tests + command: "mysqld --innodb-flush-method=fsync --max-connections=10000" # add ' --query_cache_size=0' for DB tests # command: mv /var/lib/mysql/ib_logfile0 /var/lib/mysql/ib_logfile0.bu && mv /var/lib/mysql/ib_logfile1 /var/lib/mysql/ib_logfile1.bu # smtp: diff --git a/src/Appwrite/Utopia/Request.php b/src/Appwrite/Utopia/Request.php index 58aa646c63..94a82c6041 100644 --- a/src/Appwrite/Utopia/Request.php +++ b/src/Appwrite/Utopia/Request.php @@ -3,10 +3,10 @@ namespace Appwrite\Utopia; use Appwrite\Utopia\Request\Filter; -use Utopia\Http\Adapter\Swoole\Request as SwooleRequest; +use Utopia\Http\Adapter\Swoole\Request as HttpRequest; use Utopia\Http\Route; -class Request extends SwooleRequest +class Request extends HttpRequest { /** * @var array @@ -17,9 +17,9 @@ class Request extends SwooleRequest /** * Request constructor. */ - public function __construct(SwooleRequest $request) + public function __construct(HttpRequest $request) { - parent::__construct($request->getSwooleRequest()); + parent::__construct($request->swoole); } /** diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 519aa04bf2..9c7543dc4e 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -104,13 +104,13 @@ use Appwrite\Utopia\Response\Model\Webhook; use Exception; // Keep last use Utopia\Database\Document; -use Utopia\Http\Adapter\Swoole\Response as SwooleResponse; +use Utopia\Http\Adapter\Swoole\Response as HttpResponse; /** * @method int getStatusCode() * @method Response setStatusCode(int $code = 200) */ -class Response extends SwooleResponse +class Response extends HttpResponse { // General public const MODEL_NONE = 'none'; @@ -312,10 +312,8 @@ class Response extends SwooleResponse * * @param float $time */ - public function __construct(SwooleResponse $response) + public function __construct(HttpResponse $response) { - parent::__construct($response->getSwooleResponse()); - $this // General ->setModel(new None()) @@ -459,6 +457,8 @@ class Response extends SwooleResponse ->setModel(new MigrationFirebaseProject()) // Tests (keep last) ->setModel(new Mock()); + + parent::__construct($response->swoole); } /** From 9e234b7600864595b833837ef383eb742d08b4ba Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sun, 14 Apr 2024 10:58:05 +0200 Subject: [PATCH 028/195] updated server --- app/controllers/general.php | 663 ++++++++++++++++++------------------ app/http.php | 337 +++++++++--------- 2 files changed, 506 insertions(+), 494 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index acbcd4e204..3890406808 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1,7 +1,5 @@ getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml'); +// function router(Http $utopia, Database $dbForConsole, callable $getProjectDB, Request $request, Response $response, Route $route, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $auth) +// { +// $route?->label('error', __DIR__ . '/../views/general/error.phtml'); - $host = $request->getHostname() ?? ''; +// $host = $request->getHostname() ?? ''; - $route = $auth->skip( - fn () => $dbForConsole->find('rules', [ - Query::equal('domain', [$host]), - Query::limit(1) - ]) - )[0] ?? null; +// $route = $auth->skip( +// fn () => $dbForConsole->find('rules', [ +// Query::equal('domain', [$host]), +// Query::limit(1) +// ]) +// )[0] ?? null; - if ($route === null) { - if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) { - throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.'); - } +// if ($route === null) { +// if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) { +// throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.'); +// } - if (\str_ends_with($host, System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) { - throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.'); - } +// if (\str_ends_with($host, System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) { +// throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.'); +// } - if (System::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') { - if ($host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations - throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.'); - } - } +// if (System::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') { +// if ($host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations +// throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.'); +// } +// } - // Act as API - no Proxy logic - $utopia->getRoute()?->label('error', ''); - return false; - } +// // Act as API - no Proxy logic +// $utopia->getRoute()?->label('error', ''); +// return false; +// } - $projectId = $route->getAttribute('projectId'); - $project = $auth->skip( - fn () => $dbForConsole->getDocument('projects', $projectId) - ); - if (array_key_exists('proxy', $project->getAttribute('services', []))) { - $status = $project->getAttribute('services', [])['proxy']; - if (!$status) { - throw new AppwriteException(AppwriteException::GENERAL_SERVICE_DISABLED); - } - } +// $projectId = $route->getAttribute('projectId'); +// $project = $auth->skip( +// fn () => $dbForConsole->getDocument('projects', $projectId) +// ); +// if (array_key_exists('proxy', $project->getAttribute('services', []))) { +// $status = $project->getAttribute('services', [])['proxy']; +// if (!$status) { +// throw new AppwriteException(AppwriteException::GENERAL_SERVICE_DISABLED); +// } +// } - // Skip Appwrite Router for ACME challenge. Nessessary for certificate generation - $path = ($swooleRequest->server['request_uri'] ?? '/'); - if (\str_starts_with($path, '/.well-known/acme-challenge')) { - return false; - } +// // Skip Appwrite Router for ACME challenge. Nessessary for certificate generation +// $path = ($request->getURI() ?? '/'); +// if (\str_starts_with($path, '/.well-known/acme-challenge')) { +// return false; +// } - $type = $route->getAttribute('resourceType'); +// $type = $route->getAttribute('resourceType'); - if ($type === 'function') { - if (System::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS - if ($request->getProtocol() !== 'https') { - if ($request->getMethod() !== Request::METHOD_GET) { - throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.'); - } +// if ($type === 'function') { +// if (System::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS +// if ($request->getProtocol() !== 'https') { +// if ($request->getMethod() !== Request::METHOD_GET) { +// throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.'); +// } - return $response->redirect('https://' . $request->getHostname() . $request->getURI()); - } - } +// return $response->redirect('https://' . $request->getHostname() . $request->getURI()); +// } +// } - $functionId = $route->getAttribute('resourceId'); - $projectId = $route->getAttribute('projectId'); +// $functionId = $route->getAttribute('resourceId'); +// $projectId = $route->getAttribute('projectId'); - $path = ($swooleRequest->server['request_uri'] ?? '/'); - $query = ($swooleRequest->server['query_string'] ?? ''); - if (!empty($query)) { - $path .= '?' . $query; - } +// $path = ($swooleRequest->server['request_uri'] ?? '/'); +// $query = ($swooleRequest->server['query_string'] ?? ''); +// if (!empty($query)) { +// $path .= '?' . $query; +// } - $body = $swooleRequest->getContent() ?? ''; - $method = $swooleRequest->server['request_method']; +// $body = $swooleRequest->getContent() ?? ''; +// $method = $swooleRequest->server['request_method']; - $requestHeaders = $request->getHeaders(); +// $requestHeaders = $request->getHeaders(); - $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); +// $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - $dbForProject = $getProjectDB($project); +// $dbForProject = $getProjectDB($project); - $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); +// $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - if ($function->isEmpty() || !$function->getAttribute('enabled')) { - throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND); - } +// if ($function->isEmpty() || !$function->getAttribute('enabled')) { +// throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND); +// } - $version = $function->getAttribute('version', 'v2'); - $runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []); +// $version = $function->getAttribute('version', 'v2'); +// $runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []); - $runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null; +// $runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null; - if (\is_null($runtime)) { - throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); - } +// if (\is_null($runtime)) { +// throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); +// } - $deployment = $auth->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); +// $deployment = $auth->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); - if ($deployment->getAttribute('resourceId') !== $function->getId()) { - throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); - } +// if ($deployment->getAttribute('resourceId') !== $function->getId()) { +// throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); +// } - if ($deployment->isEmpty()) { - throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); - } +// if ($deployment->isEmpty()) { +// throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); +// } - /** Check if build has completed */ - $build = $auth->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); - if ($build->isEmpty()) { - throw new AppwriteException(AppwriteException::BUILD_NOT_FOUND); - } +// /** Check if build has completed */ +// $build = $auth->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); +// if ($build->isEmpty()) { +// throw new AppwriteException(AppwriteException::BUILD_NOT_FOUND); +// } - if ($build->getAttribute('status') !== 'ready') { - throw new AppwriteException(AppwriteException::BUILD_NOT_READY); - } +// if ($build->getAttribute('status') !== 'ready') { +// throw new AppwriteException(AppwriteException::BUILD_NOT_READY); +// } - $permissions = $function->getAttribute('execute'); +// $permissions = $function->getAttribute('execute'); - if (!(\in_array('any', $permissions)) && (\in_array('guests', $permissions))) { - throw new AppwriteException(AppwriteException::USER_UNAUTHORIZED, 'To execute function using domain, execute permissions must include "any" or "guests"'); - } +// if (!(\in_array('any', $permissions)) && (\in_array('guests', $permissions))) { +// throw new AppwriteException(AppwriteException::USER_UNAUTHORIZED, 'To execute function using domain, execute permissions must include "any" or "guests"'); +// } - $headers = \array_merge([], $requestHeaders); - $headers['x-appwrite-trigger'] = 'http'; - $headers['x-appwrite-user-id'] = ''; - $headers['x-appwrite-user-jwt'] = ''; - $headers['x-appwrite-country-code'] = ''; - $headers['x-appwrite-continent-code'] = ''; - $headers['x-appwrite-continent-eu'] = 'false'; +// $headers = \array_merge([], $requestHeaders); +// $headers['x-appwrite-trigger'] = 'http'; +// $headers['x-appwrite-user-id'] = ''; +// $headers['x-appwrite-user-jwt'] = ''; +// $headers['x-appwrite-country-code'] = ''; +// $headers['x-appwrite-continent-code'] = ''; +// $headers['x-appwrite-continent-eu'] = 'false'; - $ip = $headers['x-real-ip'] ?? ''; - if (!empty($ip)) { - $record = $geodb->get($ip); +// $ip = $headers['x-real-ip'] ?? ''; +// if (!empty($ip)) { +// $record = $geodb->get($ip); - if ($record) { - $eu = Config::getParam('locale-eu'); +// if ($record) { +// $eu = Config::getParam('locale-eu'); - $headers['x-appwrite-country-code'] = $record['country']['iso_code'] ?? ''; - $headers['x-appwrite-continent-code'] = $record['continent']['code'] ?? ''; - $headers['x-appwrite-continent-eu'] = (\in_array($record['country']['iso_code'], $eu)) ? 'true' : 'false'; - } - } +// $headers['x-appwrite-country-code'] = $record['country']['iso_code'] ?? ''; +// $headers['x-appwrite-continent-code'] = $record['continent']['code'] ?? ''; +// $headers['x-appwrite-continent-eu'] = (\in_array($record['country']['iso_code'], $eu)) ? 'true' : 'false'; +// } +// } - $headersFiltered = []; - foreach ($headers as $key => $value) { - if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_REQUEST)) { - $headersFiltered[] = ['name' => $key, 'value' => $value]; - } - } +// $headersFiltered = []; +// foreach ($headers as $key => $value) { +// if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_REQUEST)) { +// $headersFiltered[] = ['name' => $key, 'value' => $value]; +// } +// } - $executionId = ID::unique(); +// $executionId = ID::unique(); - $execution = new Document([ - '$id' => $executionId, - '$permissions' => [], - 'functionInternalId' => $function->getInternalId(), - 'functionId' => $function->getId(), - 'deploymentInternalId' => $deployment->getInternalId(), - 'deploymentId' => $deployment->getId(), - 'trigger' => 'http', // http / schedule / event - 'status' => 'processing', // waiting / processing / completed / failed - 'responseStatusCode' => 0, - 'responseHeaders' => [], - 'requestPath' => $path, - 'requestMethod' => $method, - 'requestHeaders' => $headersFiltered, - 'errors' => '', - 'logs' => '', - 'duration' => 0.0, - 'search' => implode(' ', [$functionId, $executionId]), - ]); +// $execution = new Document([ +// '$id' => $executionId, +// '$permissions' => [], +// 'functionInternalId' => $function->getInternalId(), +// 'functionId' => $function->getId(), +// 'deploymentInternalId' => $deployment->getInternalId(), +// 'deploymentId' => $deployment->getId(), +// 'trigger' => 'http', // http / schedule / event +// 'status' => 'processing', // waiting / processing / completed / failed +// 'responseStatusCode' => 0, +// 'responseHeaders' => [], +// 'requestPath' => $path, +// 'requestMethod' => $method, +// 'requestHeaders' => $headersFiltered, +// 'errors' => '', +// 'logs' => '', +// 'duration' => 0.0, +// 'search' => implode(' ', [$functionId, $executionId]), +// ]); - $queueForEvents - ->setParam('functionId', $function->getId()) - ->setParam('executionId', $execution->getId()) - ->setContext('function', $function); +// $queueForEvents +// ->setParam('functionId', $function->getId()) +// ->setParam('executionId', $execution->getId()) +// ->setContext('function', $function); - $durationStart = \microtime(true); +// $durationStart = \microtime(true); - $vars = []; +// $vars = []; - // V2 vars - if ($version === 'v2') { - $vars = \array_merge($vars, [ - 'APPWRITE_FUNCTION_TRIGGER' => $headers['x-appwrite-trigger'] ?? '', - 'APPWRITE_FUNCTION_DATA' => $body ?? '', - 'APPWRITE_FUNCTION_USER_ID' => $headers['x-appwrite-user-id'] ?? '', - 'APPWRITE_FUNCTION_JWT' => $headers['x-appwrite-user-jwt'] ?? '' - ]); - } +// // V2 vars +// if ($version === 'v2') { +// $vars = \array_merge($vars, [ +// 'APPWRITE_FUNCTION_TRIGGER' => $headers['x-appwrite-trigger'] ?? '', +// 'APPWRITE_FUNCTION_DATA' => $body ?? '', +// 'APPWRITE_FUNCTION_USER_ID' => $headers['x-appwrite-user-id'] ?? '', +// 'APPWRITE_FUNCTION_JWT' => $headers['x-appwrite-user-jwt'] ?? '' +// ]); +// } - // Shared vars - foreach ($function->getAttribute('varsProject', []) as $var) { - $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); - } +// // Shared vars +// foreach ($function->getAttribute('varsProject', []) as $var) { +// $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); +// } - // Function vars - foreach ($function->getAttribute('vars', []) as $var) { - $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); - } +// // Function vars +// foreach ($function->getAttribute('vars', []) as $var) { +// $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); +// } - // Appwrite vars - $vars = \array_merge($vars, [ - 'APPWRITE_FUNCTION_ID' => $functionId, - 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'), - 'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(), - 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), - 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', - 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', - ]); +// // Appwrite vars +// $vars = \array_merge($vars, [ +// 'APPWRITE_FUNCTION_ID' => $functionId, +// 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'), +// 'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(), +// 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), +// 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', +// 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', +// ]); - /** Execute function */ - $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); - try { - $version = $function->getAttribute('version', 'v2'); - $command = $runtime['startCommand']; - $command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"'; - $executionResponse = $executor->createExecution( - projectId: $project->getId(), - deploymentId: $deployment->getId(), - body: \strlen($body) > 0 ? $body : null, - variables: $vars, - timeout: $function->getAttribute('timeout', 0), - image: $runtime['image'], - source: $build->getAttribute('path', ''), - entrypoint: $deployment->getAttribute('entrypoint', ''), - version: $version, - path: $path, - method: $method, - headers: $headers, - runtimeEntrypoint: $command, - requestTimeout: 30 - ); +// /** Execute function */ +// $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); +// try { +// $version = $function->getAttribute('version', 'v2'); +// $command = $runtime['startCommand']; +// $command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"'; +// $executionResponse = $executor->createExecution( +// projectId: $project->getId(), +// deploymentId: $deployment->getId(), +// body: \strlen($body) > 0 ? $body : null, +// variables: $vars, +// timeout: $function->getAttribute('timeout', 0), +// image: $runtime['image'], +// source: $build->getAttribute('path', ''), +// entrypoint: $deployment->getAttribute('entrypoint', ''), +// version: $version, +// path: $path, +// method: $method, +// headers: $headers, +// runtimeEntrypoint: $command, +// requestTimeout: 30 +// ); - $headersFiltered = []; - foreach ($executionResponse['headers'] as $key => $value) { - if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_RESPONSE)) { - $headersFiltered[] = ['name' => $key, 'value' => $value]; - } - } +// $headersFiltered = []; +// foreach ($executionResponse['headers'] as $key => $value) { +// if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_RESPONSE)) { +// $headersFiltered[] = ['name' => $key, 'value' => $value]; +// } +// } - /** Update execution status */ - $status = $executionResponse['statusCode'] >= 400 ? 'failed' : 'completed'; - $execution->setAttribute('status', $status); - $execution->setAttribute('responseStatusCode', $executionResponse['statusCode']); - $execution->setAttribute('responseHeaders', $headersFiltered); - $execution->setAttribute('logs', $executionResponse['logs']); - $execution->setAttribute('errors', $executionResponse['errors']); - $execution->setAttribute('duration', $executionResponse['duration']); - } catch (\Throwable $th) { - $durationEnd = \microtime(true); +// /** Update execution status */ +// $status = $executionResponse['statusCode'] >= 400 ? 'failed' : 'completed'; +// $execution->setAttribute('status', $status); +// $execution->setAttribute('responseStatusCode', $executionResponse['statusCode']); +// $execution->setAttribute('responseHeaders', $headersFiltered); +// $execution->setAttribute('logs', $executionResponse['logs']); +// $execution->setAttribute('errors', $executionResponse['errors']); +// $execution->setAttribute('duration', $executionResponse['duration']); +// } catch (\Throwable $th) { +// $durationEnd = \microtime(true); - $execution - ->setAttribute('duration', $durationEnd - $durationStart) - ->setAttribute('status', 'failed') - ->setAttribute('responseStatusCode', 500) - ->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode()); - Console::error($th->getMessage()); - } finally { - $queueForUsage - ->addMetric(METRIC_EXECUTIONS, 1) - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1) - ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project - ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function - ; - } +// $execution +// ->setAttribute('duration', $durationEnd - $durationStart) +// ->setAttribute('status', 'failed') +// ->setAttribute('responseStatusCode', 500) +// ->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode()); +// Console::error($th->getMessage()); +// } finally { +// $queueForUsage +// ->addMetric(METRIC_EXECUTIONS, 1) +// ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1) +// ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project +// ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function +// ; +// } - if ($function->getAttribute('logging')) { - /** @var Document $execution */ - $execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution)); - } +// if ($function->getAttribute('logging')) { +// /** @var Document $execution */ +// $execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution)); +// } - $execution->setAttribute('logs', ''); - $execution->setAttribute('errors', ''); +// $execution->setAttribute('logs', ''); +// $execution->setAttribute('errors', ''); - $headers = []; - foreach (($executionResponse['headers'] ?? []) as $key => $value) { - $headers[] = ['name' => $key, 'value' => $value]; - } +// $headers = []; +// foreach (($executionResponse['headers'] ?? []) as $key => $value) { +// $headers[] = ['name' => $key, 'value' => $value]; +// } - $execution->setAttribute('responseBody', $executionResponse['body'] ?? ''); - $execution->setAttribute('responseHeaders', $headers); +// $execution->setAttribute('responseBody', $executionResponse['body'] ?? ''); +// $execution->setAttribute('responseHeaders', $headers); - $body = $execution['responseBody'] ?? ''; +// $body = $execution['responseBody'] ?? ''; - $encodingKey = \array_search('x-open-runtimes-encoding', \array_column($execution['responseHeaders'], 'name')); - if ($encodingKey !== false) { - if (($execution['responseHeaders'][$encodingKey]['value'] ?? '') === 'base64') { - $body = \base64_decode($body); - } - } +// $encodingKey = \array_search('x-open-runtimes-encoding', \array_column($execution['responseHeaders'], 'name')); +// if ($encodingKey !== false) { +// if (($execution['responseHeaders'][$encodingKey]['value'] ?? '') === 'base64') { +// $body = \base64_decode($body); +// } +// } - $contentType = 'text/plain'; - foreach ($execution['responseHeaders'] as $header) { - if (\strtolower($header['name']) === 'content-type') { - $contentType = $header['value']; - } +// $contentType = 'text/plain'; +// foreach ($execution['responseHeaders'] as $header) { +// if (\strtolower($header['name']) === 'content-type') { +// $contentType = $header['value']; +// } - $response->setHeader($header['name'], $header['value']); - } +// $response->setHeader($header['name'], $header['value']); +// } - $response - ->setContentType($contentType) - ->setStatusCode($execution['responseStatusCode'] ?? 200) - ->send($body); +// $response +// ->setContentType($contentType) +// ->setStatusCode($execution['responseStatusCode'] ?? 200) +// ->send($body); - return true; - } elseif ($type === 'api') { - $utopia->getRoute()?->label('error', ''); - return false; - } else { - throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown resource type ' . $type); - } +// return true; +// } elseif ($type === 'api') { +// $utopia->getRoute()?->label('error', ''); +// return false; +// } else { +// throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown resource type ' . $type); +// } - $utopia->getRoute()?->label('error', ''); - return false; -} +// $utopia->getRoute()?->label('error', ''); +// return false; +// } Http::init() ->groups(['api', 'web']) - ->inject('utopia') - ->inject('swooleRequest') ->inject('request') ->inject('response') + ->inject('route') ->inject('console') ->inject('project') ->inject('dbForConsole') - ->inject('getProjectDB') + // ->inject('getProjectDB') ->inject('locale') ->inject('localeCodes') ->inject('clients') @@ -374,25 +374,25 @@ Http::init() ->inject('queueForUsage') ->inject('queueForEvents') ->inject('queueForCertificates') - ->inject('auth') - ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Authorization $auth) { + ->inject('authorization') + ->action(function (Request $request, Response $response, Route $route, Document $console, Document $project, Database $dbForConsole, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Authorization $authorization) { /* * Appwrite Router */ $host = $request->getHostname() ?? ''; $mainDomain = System::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain - if ($host !== $mainDomain) { - if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb, $auth)) { - return; - } - } + // if ($host !== $mainDomain) { + // if (router($utopia, $dbForConsole, $getProjectDB, $request, $response, $queueForEvents, $queueForUsage, $geodb, $auth)) { + // return; + // } + // } /* * Request format */ - $route = $utopia->getRoute(); - Request::setRoute($route); + //$route = $utopia->getRoute(); + //Request::setRoute($route); if ($route === null) { return $response->setStatusCode(404)->send('Not Found'); @@ -419,7 +419,7 @@ Http::init() } elseif (str_starts_with($request->getURI(), '/.well-known/acme-challenge')) { Console::warning('Skipping SSL certificates generation on ACME challenge.'); } else { - $auth->disable(); + $authorization->disable(); $envDomain = System::getEnv('_APP_DOMAIN', ''); $mainDomain = null; @@ -458,7 +458,7 @@ Http::init() } $domains[$domain->get()] = true; - $auth->reset(); // ensure authorization is re-enabled + $authorization->reset(); // ensure authorization is re-enabled } Config::setParam('domains', $domains); } @@ -531,7 +531,9 @@ Http::init() * @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers */ if (System::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS - if ($request->getProtocol() !== 'https' && ($swooleRequest->header['host'] ?? '') !== 'localhost' && ($swooleRequest->header['host'] ?? '') !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations + if ($request->getProtocol() !== 'https' // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations + && ($request->getHeader('host') ?? '') !== 'localhost' + && ($request->getHeader('host') ?? '') !== APP_HOSTNAME_INTERNAL) { if ($request->getMethod() !== Request::METHOD_GET) { throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.'); } @@ -571,54 +573,58 @@ Http::init() } }); -Http::options() - ->inject('utopia') - ->inject('swooleRequest') - ->inject('request') - ->inject('response') - ->inject('dbForConsole') - ->inject('getProjectDB') - ->inject('queueForEvents') - ->inject('queueForUsage') - ->inject('geodb') - ->inject('auth') - ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $auth) { - /* - * Appwrite Router - */ - $host = $request->getHostname() ?? ''; - $mainDomain = System::getEnv('_APP_DOMAIN', ''); - // Only run Router when external domain - if ($host !== $mainDomain) { - if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb, $auth)) { - return; - } - } +// Http::options() +// ->inject('utopia') +// ->inject('swooleRequest') +// ->inject('request') +// ->inject('response') +// ->inject('dbForConsole') +// ->inject('getProjectDB') +// ->inject('queueForEvents') +// ->inject('queueForUsage') +// ->inject('geodb') +// ->inject('auth') +// ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $auth) { +// /* +// * Appwrite Router +// */ +// $host = $request->getHostname() ?? ''; +// $mainDomain = System::getEnv('_APP_DOMAIN', ''); +// // Only run Router when external domain +// if ($host !== $mainDomain) { +// if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb, $auth)) { +// return; +// } +// } - $origin = $request->getOrigin(); +// $origin = $request->getOrigin(); - $response - ->addHeader('Server', 'Appwrite') - ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') - ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent') - ->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies') - ->addHeader('Access-Control-Allow-Origin', $origin) - ->addHeader('Access-Control-Allow-Credentials', 'true') - ->noContent(); - }); +// $response +// ->addHeader('Server', 'Appwrite') +// ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') +// ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent') +// ->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies') +// ->addHeader('Access-Control-Allow-Origin', $origin) +// ->addHeader('Access-Control-Allow-Credentials', 'true') +// ->noContent(); +// }); Http::error() ->inject('error') - ->inject('utopia') + ->inject('user') + ->inject('route') ->inject('request') ->inject('response') ->inject('project') ->inject('logger') ->inject('log') - ->inject('auth') - ->action(function (Throwable $error, Http $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $auth) { + ->inject('authorization') + ->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - $route = $utopia->getRoute(); + + if(is_null($route)) { + $route = new Route($request->getMethod(), $request->getURI()); + } if ($error instanceof AppwriteException) { $publish = $error->isPublishable(); @@ -627,13 +633,6 @@ Http::error() } if ($logger && ($publish || $error->getCode() === 0)) { - try { - /** @var Utopia\Database\Document $user */ - $user = $utopia->getResource('user'); - } catch (\Throwable $th) { - // All good, user is optional information for logger - } - if (isset($user) && !$user->isEmpty()) { $log->setUser(new User($user->getId())); } @@ -657,7 +656,7 @@ Http::error() $log->addExtra('line', $error->getLine()); $log->addExtra('trace', $error->getTraceAsString()); $log->addExtra('detailedTrace', $error->getTrace()); - $log->addExtra('roles', $auth->getRoles()); + $log->addExtra('roles', $authorization->getRoles()); $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); $log->setAction($action); @@ -781,7 +780,7 @@ Http::error() $response->dynamic( new Document($output), - $utopia->isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR + Http::isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR ); }); @@ -855,16 +854,18 @@ Http::get('/.well-known/acme-challenge/*') $response->text($content); }); -include_once __DIR__ . '/shared/api.php'; -include_once __DIR__ . '/shared/api/auth.php'; +//include_once __DIR__ . '/shared/api.php'; +//include_once __DIR__ . '/shared/api/auth.php'; -Http::wildcard() - ->groups(['api']) - ->label('scope', 'global') - ->action(function () { - throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND); - }); +// Http::wildcard() +// ->groups(['api']) +// ->label('scope', 'global') +// ->action(function () { +// throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND); +// }); foreach (Config::getParam('services', []) as $service) { - include_once $service['controller']; + //include_once $service['controller']; } + +include_once 'api/locale.php'; diff --git a/app/http.php b/app/http.php index 069767f821..d6f3322680 100644 --- a/app/http.php +++ b/app/http.php @@ -1,11 +1,15 @@ true, - 'http_compression' => true, - 'http_compression_level' => 6, - 'package_max_length' => $payloadSize, - 'buffer_output_size' => $payloadSize, -]), 'UTC'); + // 'http_compression' => true, + // 'http_compression_level' => 6, + // Server + // 'log_level' => 0, + 'dispatch_mode' => 2, + 'worker_num' => $workerNumber, + 'reactor_num' => swoole_cpu_num() * 2, + // 'task_worker_num' => $workerNumber, + 'open_cpu_affinity' => true, + + // Coroutine + 'enable_coroutine' => true, + 'max_coroutine' => 10000, +]); + +$http = new Http($server, $container, 'UTC'); + +// $http->loadFiles(__DIR__ . '/../console'); $http->setRequestClass(Request::class); $http->setResponseClass(Response::class); -$http->loadFiles(__DIR__ . '/../console'); +require_once __DIR__ . '/init.php'; +require_once __DIR__ . '/init2.php'; +include __DIR__ . '/controllers/general.php'; -go(function () use ($register, $http, $payloadSize) { - $pools = $register->get('pools'); - /** @var Group $pools */ - Http::setResource('pools', fn () => $pools); - $auth = new Authorization(); +global $global; - // wait for database to be ready - $attempts = 0; - $max = 10; - $sleep = 1; +http::onStart() + ->inject('authorization') + ->inject('dbForConsole') + ->inject('connections') + ->action(function (Authorization $authorization, Database $dbForConsole, Connections $connections) { + // wait for database to be ready + $attempts = 0; + $max = 10; + $sleep = 1; - do { - try { - $attempts++; - $dbForConsole = $http->getResource('dbForConsole'); - $dbForConsole->ping(); - /** @var Utopia\Database\Database $dbForConsole */ - break; // leave the do-while if successful - } catch (\Throwable $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: ' . $e->getMessage()); + do { + try { + $attempts++; + $dbForConsole->ping(); + break; // leave the do-while if successful + } catch (\Throwable $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: ' . $e->getMessage()); + } + sleep($sleep); } - sleep($sleep); - } - } while ($attempts < $max); + } while ($attempts < $max); - Console::success('[Setup] - Server database init started...'); + Console::success('[Setup] - Server database init started...'); - try { - Console::success('[Setup] - Creating database: appwrite...'); - $dbForConsole->create(); - } catch (\Throwable $e) { - Console::success('[Setup] - Skip: metadata table already exists'); - } - - if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) { - $audit = new Audit($dbForConsole, $auth); - $audit->setup(); - } - - if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { - $adapter = new TimeLimit("", 0, 1, $dbForConsole, $auth); - $adapter->setup(); - } - - /** @var array $collections */ - $collections = Config::getParam('collections', []); - $consoleCollections = $collections['console']; - foreach ($consoleCollections as $key => $collection) { - if (($collection['$collection'] ?? '') !== Database::METADATA) { - continue; - } - if (!$dbForConsole->getCollection($key)->isEmpty()) { - continue; + try { + Console::success('[Setup] - Creating database: appwrite...'); + $dbForConsole->create(); + } catch (\Throwable $e) { + Console::success('[Setup] - Skip: metadata table already exists'); + return true; } - Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); - - $attributes = []; - $indexes = []; - - foreach ($collection['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]); + if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) { + $audit = new Audit($dbForConsole, $authorization); + $audit->setup(); } - foreach ($collection['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); + if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { + $abuse = new TimeLimit("", 0, 1, $dbForConsole, $authorization); + $abuse->setup(); } - $dbForConsole->createCollection($key, $attributes, $indexes); - } + /** @var array $collections */ + $collections = Config::getParam('collections', []); + $consoleCollections = $collections['console']; + foreach ($consoleCollections as $key => $collection) { + if (($collection['$collection'] ?? '') !== Database::METADATA) { + continue; + } + if (!$dbForConsole->getCollection($key)->isEmpty()) { + continue; + } - if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) { - Console::success('[Setup] - Creating default bucket...'); - $dbForConsole->createDocument('buckets', new Document([ - '$id' => ID::custom('default'), - '$collection' => ID::custom('buckets'), - 'name' => 'Default', - 'maximumFileSize' => (int) Http::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB - 'allowedFileExtensions' => [], - 'enabled' => true, - 'compression' => 'gzip', - 'encryption' => true, - 'antivirus' => true, - 'fileSecurity' => true, - '$permissions' => [ - Permission::create(Role::any()), - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'search' => 'buckets Default', - ])); + Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); - $bucket = $dbForConsole->getDocument('buckets', 'default'); + $attributes = []; + $indexes = []; - Console::success('[Setup] - Creating files collection for default bucket...'); - $files = $collections['buckets']['files'] ?? []; - if (empty($files)) { - throw new Exception('Files collection is not configured.'); + foreach ($collection['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]); + } + + foreach ($collection['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); + } + + $dbForConsole->createCollection($key, $attributes, $indexes); } - $attributes = []; - $indexes = []; + if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) { + Console::success('[Setup] - Creating default bucket...'); + $dbForConsole->createDocument('buckets', new Document([ + '$id' => ID::custom('default'), + '$collection' => ID::custom('buckets'), + 'name' => 'Default', + 'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB + 'allowedFileExtensions' => [], + 'enabled' => true, + 'compression' => 'gzip', + 'encryption' => true, + 'antivirus' => true, + 'fileSecurity' => true, + '$permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'search' => 'buckets Default', + ])); - foreach ($files['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]); + $bucket = $dbForConsole->getDocument('buckets', 'default'); + + Console::success('[Setup] - Creating files collection for default bucket...'); + + $files = $collections['buckets']['files'] ?? []; + if (empty($files)) { + throw new Exception('Files collection is not configured.'); + } + + $attributes = []; + $indexes = []; + + foreach ($files['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]); + } + + foreach ($files['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); + } + + $dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); } - foreach ($files['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } + $connections->reclaim(); - $dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); - } - - $pools->reclaim(); - - Console::success('[Setup] - Server database init completed...'); - - Console::success('Server started successfully (max payload is ' . number_format($payloadSize) . ' bytes)'); - - // listen ctrl + c - Process::signal(2, function () use ($http) { - Console::log('Stop by Ctrl+C'); - $http->shutdown(); + Console::success('[Setup] - Server database init completed...'); + Console::success('Server started successfully'); }); - Http::init() - ->inject('auth') - ->action(function (Authorization $auth) { - $auth->cleanRoles(); - $auth->addRole(Role::any()->toString()); - }); +Http::init() + ->inject('authorization') + ->action(function (Authorization $authorization) { + $authorization->cleanRoles(); + $authorization->addRole(Role::any()->toString()); + }); - $http->start(); -}); +$http->start(); \ No newline at end of file From 6cade893699af44ad61a56640ea36565a36a5983 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sun, 14 Apr 2024 11:22:53 +0200 Subject: [PATCH 029/195] New init structure --- app/controllers/general.php | 24 +- app/http.php | 9 +- app/init/config.php | 35 ++ app/init/constants.php | 159 +++++++ app/init/database/filters.php | 398 +++++++++++++++++ app/init/database/formats.php | 43 ++ app/init/locale.php | 23 + app/init2.php | 798 ++++++++++++++++++++++++++++++++++ 8 files changed, 1483 insertions(+), 6 deletions(-) create mode 100644 app/init/config.php create mode 100644 app/init/constants.php create mode 100644 app/init/database/filters.php create mode 100644 app/init/database/formats.php create mode 100644 app/init/locale.php create mode 100644 app/init2.php diff --git a/app/controllers/general.php b/app/controllers/general.php index 3890406808..6feab6e2f7 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -370,12 +370,28 @@ Http::init() ->inject('locale') ->inject('localeCodes') ->inject('clients') - ->inject('geodb') - ->inject('queueForUsage') - ->inject('queueForEvents') + // ->inject('geodb') + // ->inject('queueForUsage') + // ->inject('queueForEvents') ->inject('queueForCertificates') ->inject('authorization') - ->action(function (Request $request, Response $response, Route $route, Document $console, Document $project, Database $dbForConsole, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Authorization $authorization) { + ->action(function ( + Request $request, + Response $response, + Route $route, + Document $console, + Document $project, + Database $dbForConsole, + Locale $locale, array $localeCodes, + array $clients, + /** + * @disregard P1009 Undefined type + */ + // Reader $geodb, + // Usage $queueForUsage, + // Event $queueForEvents, + Certificate $queueForCertificates, + Authorization $authorization) { /* * Appwrite Router */ diff --git a/app/http.php b/app/http.php index d6f3322680..2fe80777f3 100644 --- a/app/http.php +++ b/app/http.php @@ -56,9 +56,14 @@ $http = new Http($server, $container, 'UTC'); $http->setRequestClass(Request::class); $http->setResponseClass(Response::class); -require_once __DIR__ . '/init.php'; +//require_once __DIR__ . '/init.php'; +require_once __DIR__ . '/init/constants.php'; +require_once __DIR__ . '/init/config.php'; +require_once __DIR__ . '/init/locale.php'; +require_once __DIR__ . '/init/database/filters.php'; +require_once __DIR__ . '/init/database/formats.php'; require_once __DIR__ . '/init2.php'; -include __DIR__ . '/controllers/general.php'; +require_once __DIR__ . '/controllers/general.php'; global $global; diff --git a/app/init/config.php b/app/init/config.php new file mode 100644 index 0000000000..5ae3c82dbc --- /dev/null +++ b/app/init/config.php @@ -0,0 +1,35 @@ + $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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 = $database->getAuthorization()->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->find('targets', [ + Query::equal('$internalId', $targetIds) + ]); + } + return []; + } +); + +Database::addFilter( + 'providerSearch', + function (mixed $value, Document $provider) { + $searchValues = [ + $provider->getId(), + $provider->getAttribute('name', ''), + $provider->getAttribute('provider', ''), + $provider->getAttribute('type', '') + ]; + + $search = \implode(' ', \array_filter($searchValues)); + + return $search; + }, + function (mixed $value) { + return $value; + } +); + +Database::addFilter( + 'topicSearch', + function (mixed $value, Document $topic) { + $searchValues = [ + $topic->getId(), + $topic->getAttribute('name', ''), + $topic->getAttribute('description', ''), + ]; + + $search = \implode(' ', \array_filter($searchValues)); + + return $search; + }, + function (mixed $value) { + return $value; + } +); + +Database::addFilter( + 'messageSearch', + function (mixed $value, Document $message) { + $searchValues = [ + $message->getId(), + $message->getAttribute('description', ''), + $message->getAttribute('status', ''), + ]; + + $data = \json_decode($message->getAttribute('data', []), true); + $providerType = $message->getAttribute('providerType', ''); + + if ($providerType === MESSAGE_TYPE_EMAIL) { + $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); + } elseif ($providerType === MESSAGE_TYPE_SMS) { + $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); + } else { + $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); + } + + $search = \implode(' ', \array_filter($searchValues)); + + return $search; + }, + function (mixed $value) { + return $value; + } +); \ No newline at end of file diff --git a/app/init/database/formats.php b/app/init/database/formats.php new file mode 100644 index 0000000000..f1bf54a456 --- /dev/null +++ b/app/init/database/formats.php @@ -0,0 +1,43 @@ + new MariaDB($resource), + 'mysql' => new MySQL($resource), + default => null + }; + + $adapter->setDatabase($scheme); + break; + case 'pubsub': + $adapter = $resource(); + break; + case 'queue': + $adapter = match ($scheme) { + //'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), + default => null + }; + break; + case 'cache': + $adapter = match ($scheme) { + 'redis' => new RedisCache($resource), + default => null + }; + break; + + default: + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); + } + + return $adapter; +} + +$global = new Registry(); + +$global->set('logger', function () { + // Register error logger + $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); + $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); + + if (empty($providerName) || empty($providerConfig)) { + return; + } + + if (!Logger::hasProvider($providerName)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); + } + + $classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName); + $adapter = new $classname($providerConfig); + return new Logger($adapter); +}); + +$global->set('geodb', function () { + /** + * @disregard P1009 Undefined type + */ + return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.mmdb'); +}); + +$global->set('pools', (function () { + $fallbackForDB = 'db_main=' . URL::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=' . URL::unparse([ + 'scheme' => 'redis', + 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), + 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), + 'user' => System::getEnv('_APP_REDIS_USER', ''), + 'pass' => System::getEnv('_APP_REDIS_PASS', ''), + ]); + + $connections = [ + 'console' => [ + 'type' => 'database', + 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), + 'multiple' => false, + 'schemes' => ['mariadb', 'mysql'], + ], + 'database' => [ + 'type' => 'database', + 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), + 'multiple' => true, + 'schemes' => ['mariadb', 'mysql'], + ], + 'queue' => [ + 'type' => 'queue', + 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'pubsub' => [ + 'type' => 'pubsub', + 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'cache' => [ + 'type' => 'cache', + 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), + 'multiple' => true, + 'schemes' => ['redis'], + ], + ]; + + $pools = []; + $poolSize = (int)System::getEnv('_APP_POOL_CLIENTS', 9000); + $poolSize = 9000; + + foreach ($connections as $key => $connection) { + $dsns = $connection['dsns'] ?? ''; + $multipe = $connection['multiple'] ?? false; + $schemes = $connection['schemes'] ?? []; + $dsns = explode(',', $connection['dsns'] ?? ''); + foreach ($dsns as &$dsn) { + $dsn = explode('=', $dsn); + $name = ($multipe) ? $dsn[0] : 'main'; + $dsn = $dsn[1] ?? ''; + + if (empty($dsn)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); + } + + $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 accross connection types like database, cache, queue, etc. + * + * Resource assignment to an adapter will happen below. + */ + switch ($dsnScheme) { + case 'mysql': + case 'mariadb': + $pool = new PDOPool((new PDOConfig) + ->withHost($dsnHost) + ->withPort($dsnPort) + ->withDbName($dsnDatabase) + ->withCharset('utf8mb4') + ->withUsername($dsnUser) + ->withPassword($dsnPass) + ->withOptions([ + // No need to set PDO::ATTR_ERRMODE it is overwitten in PDOProxy + // 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, + PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, + + ]), + $poolSize + ); + break; + case 'redis': + $pool = new RedisPool((new RedisConfig) + ->withHost($dsnHost) + ->withPort((int)$dsnPort) + ->withAuth($dsnPass) + , $poolSize); + break; + + default: + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid scheme"); + } + + $pools['pools-' . $key . '-' . $name] = [ + 'pool' => $pool, + 'dsn' => $dsn, + ]; + } + } + + return function () use ($pools): array { + return $pools; + }; +})()); + +$mode = new Dependency(); +$mode + ->setName('mode') + ->inject('request') + ->setCallback(function (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)); + }); +$container->set($mode); + +$user = new Dependency(); +$user + ->setName('user') + ->inject('mode') + ->inject('project') + ->inject('console') + ->inject('request') + ->inject('response') + ->inject('dbForProject') + ->inject('dbForConsole') + ->inject('authorization') + ->setCallback(function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization) { + $authorization->setDefaultStatus(true); + + Auth::setCookieName('a_session_' . $project->getId()); + + if (APP_MODE_ADMIN === $mode) { + Auth::setCookieName('a_session_' . $console->getId()); + } + + $session = Auth::decodeSession( + $request->getCookie( + Auth::$cookieName, // Get sessions + $request->getCookie(Auth::$cookieName . '_legacy', '') + ) + ); + + // Get session from header for SSR clients + if (empty($session['id']) && empty($session['secret'])) { + $sessionHeader = $request->getHeader('x-appwrite-session', ''); + + if (!empty($sessionHeader)) { + $session = Auth::decodeSession($sessionHeader); + } + } + + // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies + if ($response) { + $response->addHeader('X-Debug-Fallback', 'false'); + } + + if (empty($session['id']) && empty($session['secret'])) { + if ($response) { + $response->addHeader('X-Debug-Fallback', 'true'); + } + $fallback = $request->getHeader('x-fallback-cookies', ''); + $fallback = \json_decode($fallback, true); + $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); + } + + Auth::$unique = $session['id'] ?? ''; + Auth::$secret = $session['secret'] ?? ''; + + if (APP_MODE_ADMIN !== $mode) { + if ($project->isEmpty()) { + $user = new Document([]); + } else { + if ($project->getId() === 'console') { + $user = $dbForConsole->getDocument('users', Auth::$unique); + } else { + $user = $dbForProject->getDocument('users', Auth::$unique); + } + } + } else { + $user = $dbForConsole->getDocument('users', Auth::$unique); + } + + if ( + $user->isEmpty() // Check a document has been found in the DB + || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) + ) { // Validate user has valid login token + $user = new Document([]); + } + + if (APP_MODE_ADMIN === $mode) { + if ($user->find('teamId', $project->getAttribute('teamId'), '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', 900, 10); // Instantiate with key, algo, maxAge and leeway. + + try { + $payload = $jwt->decode($authJWT); + } catch (JWTException $error) { + throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); + } + + $jwtUserId = $payload['userId'] ?? ''; + $jwtSessionId = $payload['sessionId'] ?? ''; + + if ($jwtUserId && $jwtSessionId) { + $user = $dbForProject->getDocument('users', $jwtUserId); + } + + if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token + $user = new Document([]); + } + } + + // Adds logs to database queries + $dbForProject->setMetadata('user', $user->getId()); + $dbForConsole->setMetadata('user', $user->getId()); + + return $user; + }); +$container->set($user); + +$console = new Dependency(); +$console + ->setName('console') + ->setCallback(function () { + return new Document([ + '$id' => ID::custom('console'), + '$internalId' => ID::custom('console'), + 'name' => 'Appwrite', + '$collection' => ID::custom('projects'), + 'description' => 'Appwrite core engine', + 'logo' => '', + 'teamId' => -1, + 'webhooks' => [], + 'keys' => [], + 'platforms' => [ + [ + '$collection' => ID::custom('platforms'), + 'name' => 'Localhost', + 'type' => Origin::CLIENT_TYPE_WEB, + 'hostname' => 'localhost', + ], // Current host is added on app init + ], + 'legalName' => '', + 'legalCountry' => '', + 'legalState' => '', + 'legalCity' => '', + 'legalAddress' => '', + 'legalTaxId' => '', + 'auths' => [ + '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 + ], + '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', '') + ], + ]); + }); +$container->set($console); + +$project = new Dependency(); +$project + ->setName('project') + ->inject('dbForConsole') + ->inject('request') + ->inject('console') + ->inject('authorization') + ->setCallback(function (Database $dbForConsole, Request $request, Document $console, Authorization $authorization) { + $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); + + if (empty($projectId) || $projectId === 'console') { + return $console; + } + + $project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + + return $project; + }); +$container->set($project); + +$pools = new Dependency(); +$pools + ->setName('pools') + ->inject('registry') + ->setCallback(function (Registry $registry) { + return $registry->get('pools'); + }); +$container->set($pools); + +$dbForProject = new Dependency(); +$dbForProject + ->setName('dbForProject') + ->inject('pools') + ->inject('project') + ->inject('cache') + ->inject('dbForConsole') + ->inject('connections') + ->inject('authorization') + ->setCallback(function(array $pools, Document $project, Cache $cache, Database $dbForConsole, Connections $connections, Authorization $authorization) { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + $pool = $pools['pools-database-'.$project->getAttribute('database')]['pool']; + $dsn = $pools['pools-database-'.$project->getAttribute('database')]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $database = new Database($adapter, $cache); + $database->setAuthorization($authorization); + + $database + ->setNamespace('_' . $project->getInternalId()) + ->setMetadata('host', \gethostname()) + ->setMetadata('project', $project->getId()) + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + return $database; + }); +$container->set($dbForProject); + +$dbForConsole = new Dependency(); +$dbForConsole + ->setName('dbForConsole') + ->inject('pools') + ->inject('cache') + ->inject('authorization') + ->inject('connections') + ->setCallback(function(array $pools, Cache $cache, Authorization $authorization, Connections $connections): Database { + $pool = $pools['pools-console-main']['pool']; + $dsn = $pools['pools-console-main']['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase('appwrite'); + + $database = new Database($adapter, $cache); + $database->setAuthorization($authorization); + + $database + ->setNamespace('_console') + ->setMetadata('host', \gethostname()) + ->setMetadata('project', 'console') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + return $database; + }); +$container->set($dbForConsole); + +$cache = new Dependency(); +$cache + ->setName('cache') + ->setCallback(function (): Cache { + return new Cache(new None()); + }); +$container->set($cache); + +$authorization = new Dependency(); +$authorization + ->setName('authorization') + ->setCallback(function (): Authorization { + return new Authorization(); + }); +$container->set($authorization); + +$registry = new Dependency(); +$registry + ->setName('registry') + ->setCallback(function () use (&$global): Registry { + return $global; + }); +$container->set($registry); + +$pools = new Dependency(); +$pools + ->setName('pools') + ->inject('registry') + ->setCallback(function (Registry $registry) { + return $registry->get('pools'); + }); +$container->set($pools); + +$logger = new Dependency(); +$logger + ->setName('logger') + ->inject('registry') + ->setCallback(function (Registry $registry) { + return $registry->get('logger'); + }); +$container->set($logger); + +$log = new Dependency(); +$log + ->setName('log') + ->setCallback(function () { + return new Log(); + }); +$container->set($log); + +$connections = new Dependency(); +$connections + ->setName('connections') + ->setCallback(function () { + return new Connections(); + }); +$container->set($connections); + +$locale = new Dependency(); +$locale + ->setName('locale') + ->setCallback(fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); +$container->set($locale); + +$localeCodes = new Dependency(); +$localeCodes + ->setName('localeCodes') + ->setCallback(fn () => array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []))); +$container->set($localeCodes); + +$queue = new Dependency(); +$queue + ->setName('queue') + ->inject('pools') + ->inject('connections') + ->setCallback(function (array $pools, Connections $connections) { + $pool = $pools['pools-queue-main']['pool']; + $dsn = $pools['pools-queue-main']['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + return new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()); + }); +$container->set($queue); + +$queueForMessaging = new Dependency(); +$queueForMessaging + ->setName('queueForMessaging') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Messaging($queue); + }); +$container->set($queueForMessaging); + +$queueForMails = new Dependency(); +$queueForMails + ->setName('queueForMails') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Mail($queue); + }); +$container->set($queueForMails); + +$queueForBuilds = new Dependency(); +$queueForBuilds + ->setName('queueForBuilds') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Build($queue); + }); +$container->set($queueForBuilds); + +$queueForDatabase = new Dependency(); +$queueForDatabase + ->setName('queueForDatabase') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new EventDatabase($queue); + }); +$container->set($queueForDatabase); + +$queueForDeletes = new Dependency(); +$queueForDeletes + ->setName('queueForDeletes') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Delete($queue); + }); +$container->set($queueForDeletes); + +$queueForEvents = new Dependency(); +$queueForEvents + ->setName('queueForEvents') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Event($queue); + }); +$container->set($queueForEvents); + +$queueForAudits = new Dependency(); +$queueForAudits + ->setName('queueForAudits') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Audit($queue); + }); +$container->set($queueForAudits); + +$queueForFunctions = new Dependency(); +$queueForFunctions + ->setName('queueForFunctions') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Func($queue); + }); +$container->set($queueForFunctions); + +$queueForUsage = new Dependency(); +$queueForUsage + ->setName('queueForUsage') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Usage($queue); + }); +$container->set($queueForUsage); + +$queueForCertificates = new Dependency(); +$queueForCertificates + ->setName('queueForCertificates') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Certificate($queue); + }); +$container->set($queueForCertificates); + +$queueForMigrations = new Dependency(); +$queueForMigrations + ->setName('queueForMigrations') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Migration($queue); + }); +$container->set($queueForMigrations); + +$clients = new Dependency(); +$clients + ->setName('clients') + ->inject('request') + ->inject('console') + ->inject('project') + ->setCallback(function (Request $request, Document $console, Document $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) && isset($node['hostname']) && !empty($node['hostname'])) + ) + ); + + $clients = \array_unique( + \array_merge( + $clientsConsole, + \array_map( + fn ($node) => $node['hostname'], + \array_filter( + $project->getAttribute('platforms', []), + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) + ) + ) + ) + ); + + return $clients; + }); +$container->set($clients); + +$geodb = new Dependency(); +$geodb + ->setName('geodb') + ->inject('registry') + ->setCallback(function (Registry $register) { + return $register->get('geodb'); + }); +$container->set($geodb); \ No newline at end of file From 766b2ba13e2276ea719961bfaef7113ee10ed559 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sun, 14 Apr 2024 22:17:07 +0200 Subject: [PATCH 030/195] Avatars tests are green! --- app/controllers/api/account.php | 192 +++++++++++----------- app/controllers/api/projects.php | 32 ++-- app/controllers/api/teams.php | 66 ++++---- app/controllers/api/users.php | 12 +- app/controllers/general.php | 27 ++- app/controllers/shared/api.php | 65 ++++---- app/controllers/shared/api/auth.php | 13 +- app/init2.php | 135 +++++++++++---- composer.lock | 8 +- src/Appwrite/Utopia/Queue/Connections.php | 36 +--- 10 files changed, 330 insertions(+), 256 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 9fb55c46ac..bc5efd6aab 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -58,13 +58,13 @@ use Utopia\System\System; $oauthDefaultSuccess = '/auth/oauth2/success'; $oauthDefaultFailure = '/auth/oauth2/failure'; -$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Authorization $auth) { - $roles = $auth->getRoles(); +$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Authorization $authorization) { + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); /** @var Utopia\Database\Document $user */ - $userFromRequest = $auth->skip(fn () => $dbForProject->getDocument('users', $userId)); + $userFromRequest = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId)); if ($userFromRequest->isEmpty()) { throw new Exception(Exception::USER_INVALID_TOKEN); @@ -110,7 +110,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res $detector->getDevice() )); - $auth->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $session = $dbForProject->createDocument('sessions', $session ->setAttribute('$permissions', [ @@ -120,7 +120,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res ])); $dbForProject->purgeCachedDocument('users', $user->getId()); - $auth->skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId())); + $authorization->skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId())); $dbForProject->purgeCachedDocument('users', $user->getId()); if ($verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_MAGIC_URL) { @@ -193,8 +193,8 @@ Http::post('/v1/account') ->inject('dbForProject') ->inject('queueForEvents') ->inject('hooks') - ->inject('auth') - ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks, Authorization $auth) { + ->inject('authorization') + ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks, Authorization $authorization) { $email = \strtolower($email); if ('console' === $project->getId()) { @@ -269,9 +269,9 @@ Http::post('/v1/account') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - $user = $auth->skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); try { - $target = $auth->skip(fn () => $dbForProject->createDocument('targets', new Document([ + $target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([ '$permissions' => [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), @@ -295,9 +295,9 @@ Http::post('/v1/account') throw new Exception(Exception::USER_ALREADY_EXISTS); } - $auth->removeRole(Role::guests()->toString()); - $auth->addRole(Role::user($user->getId())->toString()); - $auth->addRole(Role::users()->toString()); + $authorization->removeRole(Role::guests()->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::users()->toString()); $queueForEvents->setParam('userId', $user->getId()); @@ -392,10 +392,10 @@ Http::get('/v1/account/sessions') ->inject('response') ->inject('user') ->inject('locale') - ->inject('auth') - ->action(function (Response $response, Document $user, Locale $locale, Authorization $auth) { + ->inject('authorization') + ->action(function (Response $response, Document $user, Locale $locale, Authorization $authorization) { - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -500,10 +500,10 @@ Http::get('/v1/account/sessions/:sessionId') ->inject('response') ->inject('user') ->inject('locale') - ->inject('auth') - ->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Authorization $auth) { + ->inject('authorization') + ->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Authorization $authorization) { - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -714,8 +714,8 @@ Http::post('/v1/account/sessions/email') ->inject('geodb') ->inject('queueForEvents') ->inject('hooks') - ->inject('auth') - ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks, Authorization $auth) { + ->inject('authorization') + ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks, Authorization $authorization) { $email = \strtolower($email); $protocol = $request->getProtocol(); @@ -731,7 +731,7 @@ Http::post('/v1/account/sessions/email') throw new Exception(Exception::USER_BLOCKED); // User is in status blocked } - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -762,7 +762,7 @@ Http::post('/v1/account/sessions/email') $detector->getDevice() )); - $auth->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); // Re-hash if not using recommended algo if ($user->getAttribute('hash') !== Auth::DEFAULT_ALGO) { @@ -837,10 +837,10 @@ Http::post('/v1/account/sessions/anonymous') ->inject('dbForProject') ->inject('geodb') ->inject('queueForEvents') - ->inject('auth') - ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization) { $protocol = $request->getProtocol(); - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -886,7 +886,7 @@ Http::post('/v1/account/sessions/anonymous') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - $auth->skip(fn () => $dbForProject->createDocument('users', $user)); + $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); // Create session token $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; @@ -912,7 +912,7 @@ Http::post('/v1/account/sessions/anonymous') $detector->getDevice() )); - $auth->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $session = $dbForProject->createDocument('sessions', $session-> setAttribute('$permissions', [ Permission::read(Role::user($user->getId())), @@ -1138,8 +1138,8 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('dbForProject') ->inject('geodb') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $auth) use ($oauthDefaultSuccess) { + ->inject('authorization') + ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization) use ($oauthDefaultSuccess) { $protocol = $request->getProtocol(); $callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId(); $defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => '']; @@ -1363,7 +1363,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - $user = $auth->skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); $dbForProject->createDocument('targets', new Document([ '$permissions' => [ @@ -1382,8 +1382,8 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') } } - $auth->addRole(Role::user($user->getId())->toString()); - $auth->addRole(Role::users()->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::users()->toString()); if (false === $user->getAttribute('status')) { // Account is blocked $failureRedirect(Exception::USER_BLOCKED); // User is in status blocked @@ -1442,7 +1442,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') $dbForProject->updateDocument('users', $user->getId(), $user); - $auth->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $state['success'] = URLParser::parse($state['success']); $query = URLParser::parseQuery($state['success']['query']); @@ -1464,7 +1464,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') 'ip' => $request->getIP(), ]); - $auth->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -1663,8 +1663,8 @@ Http::post('/v1/account/tokens/magic-url') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->inject('Auth') - ->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $auth) { + ->inject('authorization') + ->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); @@ -1674,7 +1674,7 @@ Http::post('/v1/account/tokens/magic-url') $phrase = Phrase::generate(); } - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1729,7 +1729,7 @@ Http::post('/v1/account/tokens/magic-url') ]); $user->removeAttribute('$internalId'); - $user = $auth->skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); } $tokenSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_MAGIC_URL); @@ -1746,7 +1746,7 @@ Http::post('/v1/account/tokens/magic-url') 'ip' => $request->getIP(), ]); - $auth->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -1906,8 +1906,8 @@ Http::post('/v1/account/tokens/email') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->inject('auth') - ->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $auth) { + ->inject('authorization') + ->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); } @@ -1916,7 +1916,7 @@ Http::post('/v1/account/tokens/email') $phrase = Phrase::generate(); } - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1969,7 +1969,7 @@ Http::post('/v1/account/tokens/email') ]); $user->removeAttribute('$internalId'); - $user = $auth->skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); } $tokenSecret = Auth::codeGenerator(6); @@ -1986,7 +1986,7 @@ Http::post('/v1/account/tokens/email') 'ip' => $request->getIP(), ]); - $auth->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -2136,7 +2136,7 @@ Http::put('/v1/account/sessions/magic-url') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') - ->inject('auth') + ->inject('authorization') ->action($createSession); Http::put('/v1/account/sessions/phone') @@ -2167,7 +2167,7 @@ Http::put('/v1/account/sessions/phone') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') - ->inject('auth') + ->inject('authorization') ->action($createSession); Http::post('/v1/account/tokens/phone') @@ -2198,13 +2198,13 @@ Http::post('/v1/account/tokens/phone') ->inject('queueForEvents') ->inject('queueForMessaging') ->inject('locale') - ->inject('auth') - ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, Authorization $auth) { + ->inject('authorization') + ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, Authorization $authorization) { if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -2248,9 +2248,9 @@ Http::post('/v1/account/tokens/phone') ]); $user->removeAttribute('$internalId'); - $user = $auth->skip(fn () => $dbForProject->createDocument('users', $user)); + $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); try { - $target = $auth->skip(fn () => $dbForProject->createDocument('targets', new Document([ + $target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([ '$permissions' => [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), @@ -2285,7 +2285,7 @@ Http::post('/v1/account/tokens/phone') 'ip' => $request->getIP(), ]); - $auth->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -2427,8 +2427,8 @@ Http::get('/v1/account/logs') ->inject('locale') ->inject('geodb') ->inject('dbForProject') - ->inject('auth') - ->action(function (array $queries, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (array $queries, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject, Authorization $authorization) { try { $queries = Query::parseQueries($queries); @@ -2440,7 +2440,7 @@ Http::get('/v1/account/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new EventAudit($dbForProject, $auth); + $audit = new EventAudit($dbForProject, $authorization); $logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset); @@ -2603,8 +2603,8 @@ Http::patch('/v1/account/email') ->inject('queueForEvents') ->inject('project') ->inject('hooks') - ->inject('auth') - ->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $auth) { + ->inject('authorization') + ->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $authorization) { // passwordUpdate will be empty if the user has never set a password $passwordUpdate = $user->getAttribute('passwordUpdate'); @@ -2643,7 +2643,7 @@ Http::patch('/v1/account/email') ->setAttribute('passwordUpdate', DateTime::now()); } - $target = $auth->skip(fn () => $dbForProject->findOne('targets', [ + $target = $authorization->skip(fn () => $dbForProject->findOne('targets', [ Query::equal('identifier', [$email]), ])); @@ -2659,7 +2659,7 @@ Http::patch('/v1/account/email') $oldTarget = $user->find('identifier', $oldEmail, 'targets'); if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) { - $auth->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email))); + $authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email))); } $dbForProject->purgeCachedDocument('users', $user->getId()); } catch (Duplicate) { @@ -2696,8 +2696,8 @@ Http::patch('/v1/account/phone') ->inject('queueForEvents') ->inject('project') ->inject('hooks') - ->inject('auth') - ->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $auth) { + ->inject('authorization') + ->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $authorization) { // passwordUpdate will be empty if the user has never set a password $passwordUpdate = $user->getAttribute('passwordUpdate'); @@ -2710,7 +2710,7 @@ Http::patch('/v1/account/phone') $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]); - $target = $auth->skip(fn () => $dbForProject->findOne('targets', [ + $target = $authorization->skip(fn () => $dbForProject->findOne('targets', [ Query::equal('identifier', [$phone]), ])); @@ -2741,7 +2741,7 @@ Http::patch('/v1/account/phone') $oldTarget = $user->find('identifier', $oldPhone, 'targets'); if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) { - $auth->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone))); + $authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone))); } $dbForProject->purgeCachedDocument('users', $user->getId()); } catch (Duplicate $th) { @@ -2856,14 +2856,14 @@ Http::post('/v1/account/recovery') ->inject('locale') ->inject('queueForMails') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents, Authorization $authorization) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); } - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -2897,7 +2897,7 @@ Http::post('/v1/account/recovery') 'ip' => $request->getIP(), ]); - $auth->addRole(Role::user($profile->getId())->toString()); + $authorization->addRole(Role::user($profile->getId())->toString()); $recovery = $dbForProject->createDocument('tokens', $recovery ->setAttribute('$permissions', [ @@ -3034,8 +3034,8 @@ Http::put('/v1/account/recovery') ->inject('project') ->inject('queueForEvents') ->inject('hooks') - ->inject('auth') - ->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, Authorization $auth) { + ->inject('authorization') + ->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, Authorization $authorization) { $profile = $dbForProject->getDocument('users', $userId); if ($profile->isEmpty()) { @@ -3049,7 +3049,7 @@ Http::put('/v1/account/recovery') throw new Exception(Exception::USER_INVALID_TOKEN); } - $auth->addRole(Role::user($profile->getId())->toString()); + $authorization->addRole(Role::user($profile->getId())->toString()); $newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS); @@ -3119,8 +3119,8 @@ Http::post('/v1/account/verification') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->inject('auth') - ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $auth) { + ->inject('authorization') + ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -3130,7 +3130,7 @@ Http::post('/v1/account/verification') throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED); } - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); $verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION); @@ -3147,7 +3147,7 @@ Http::post('/v1/account/verification') 'ip' => $request->getIP(), ]); - $auth->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $verification = $dbForProject->createDocument('tokens', $verification ->setAttribute('$permissions', [ @@ -3278,10 +3278,10 @@ Http::put('/v1/account/verification') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { - $profile = $auth->skip(fn () => $dbForProject->getDocument('users', $userId)); + $profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); @@ -3294,7 +3294,7 @@ Http::put('/v1/account/verification') throw new Exception(Exception::USER_INVALID_TOKEN); } - $auth->addRole(Role::user($profile->getId())->toString()); + $authorization->addRole(Role::user($profile->getId())->toString()); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true)); @@ -3342,8 +3342,8 @@ Http::post('/v1/account/verification/phone') ->inject('queueForMessaging') ->inject('project') ->inject('locale') - ->inject('auth') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, Authorization $auth) { + ->inject('authorization') + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, Authorization $authorization) { if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -3356,7 +3356,7 @@ Http::post('/v1/account/verification/phone') throw new Exception(Exception::USER_PHONE_ALREADY_VERIFIED); } - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); $secret = Auth::codeGenerator(); @@ -3373,7 +3373,7 @@ Http::post('/v1/account/verification/phone') 'ip' => $request->getIP(), ]); - $auth->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $verification = $dbForProject->createDocument('tokens', $verification ->setAttribute('$permissions', [ @@ -3452,10 +3452,10 @@ Http::put('/v1/account/verification/phone') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { - $profile = $auth->skip(fn () => $dbForProject->getDocument('users', $userId)); + $profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); @@ -3467,7 +3467,7 @@ Http::put('/v1/account/verification/phone') throw new Exception(Exception::USER_INVALID_TOKEN); } - $auth->addRole(Role::user($profile->getId())->toString()); + $authorization->addRole(Role::user($profile->getId())->toString()); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('phoneVerification', true)); @@ -4153,13 +4153,13 @@ Http::post('/v1/account/targets/push') ->inject('request') ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) { $targetId = $targetId == 'unique()' ? ID::unique() : $targetId; - $provider = $auth->skip(fn () => $dbForProject->getDocument('providers', $providerId)); + $provider = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId)); - $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if (!$target->isEmpty()) { throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS); @@ -4226,10 +4226,10 @@ Http::put('/v1/account/targets/:targetId/push') ->inject('request') ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) { - $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); @@ -4282,9 +4282,9 @@ Http::delete('/v1/account/targets/:targetId/push') ->inject('request') ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $auth) { - $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId)); + ->inject('authorization') + ->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) { + $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 900c0193c7..77052aba2a 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -17,6 +17,8 @@ use Utopia\Abuse\Adapters\TimeLimit; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\Config\Config; +use Utopia\Database\Adapter\MariaDB; +use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; @@ -79,9 +81,9 @@ Http::post('/v1/projects') ->inject('dbForConsole') ->inject('cache') ->inject('pools') - ->inject('auth') + ->inject('authorization') ->inject('connections') - ->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole, Cache $cache, Group $pools, Authorization $auth, Connections $connections) { + ->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole, Cache $cache, array $pools, Authorization $authorization, Connections $connections) { $team = $dbForConsole->getDocument('teams', $teamId); @@ -181,19 +183,27 @@ Http::post('/v1/projects') throw new Exception(Exception::PROJECT_ALREADY_EXISTS); } - $connection = $pools->get($database)->pop(); - $connections->add($connection); - $dbForProject = new Database($connection->getResource(), $cache); - $dbForProject->setAuthorization($auth); + $pool = $pools['pools-database-'.$project->getAttribute('database')]['pool']; + $dsn = $pools['pools-database-'.$project->getAttribute('database')]['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); + + $dbForProject = new Database($adapter, $cache); + $dbForProject->setAuthorization($authorization); $dbForProject->setNamespace("_{$project->getInternalId()}"); $dbForProject->create(); - - $audit = new Audit($dbForProject, $auth); + + $audit = new Audit($dbForProject, $authorization); $audit->setup(); - - $adapter = new TimeLimit('', 0, 1, $dbForProject, $auth); + $adapter = new TimeLimit('', 0, 1, $dbForProject, $authorization); $adapter->setup(); - /** @var array $collections */ $collections = Config::getParam('collections', [])['projects'] ?? []; diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 5c5013d98c..0dd1922f05 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -64,16 +64,16 @@ Http::post('/v1/teams') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); - $isAppUser = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAppUser = Auth::isAppUser($authorization->getRoles()); $teamId = $teamId == 'unique()' ? ID::unique() : $teamId; try { - $team = $auth->skip(fn () => $dbForProject->createDocument('teams', new Document([ + $team = $authorization->skip(fn () => $dbForProject->createDocument('teams', new Document([ '$id' => $teamId, '$permissions' => [ Permission::read(Role::team($teamId)), @@ -398,10 +398,10 @@ Http::post('/v1/teams/:teamId/memberships') ->inject('queueForMails') ->inject('queueForMessaging') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, Authorization $auth) { - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + ->inject('authorization') + ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, Authorization $authorization) { + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if (empty($url)) { if (!$isAPIKey && !$isPrivilegedUser) { @@ -412,8 +412,8 @@ Http::post('/v1/teams/:teamId/memberships') if (empty($userId) && empty($email) && empty($phone)) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'At least one of userId, email, or phone is required'); } - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); - $isAppUser = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAppUser = Auth::isAppUser($authorization->getRoles()); if (!$isPrivilegedUser && !$isAppUser && empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED); @@ -473,7 +473,7 @@ Http::post('/v1/teams/:teamId/memberships') try { $userId = ID::unique(); - $invitee = $auth->skip(fn () => $dbForProject->createDocument('users', new Document([ + $invitee = $authorization->skip(fn () => $dbForProject->createDocument('users', new Document([ '$id' => $userId, '$permissions' => [ Permission::read(Role::any()), @@ -509,7 +509,7 @@ Http::post('/v1/teams/:teamId/memberships') } } - $isOwner = $auth->isRole('team:' . $team->getId() . '/owner'); + $isOwner = $authorization->isRole('team:' . $team->getId() . '/owner'); if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server) throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to send invitations for this team'); @@ -541,12 +541,12 @@ Http::post('/v1/teams/:teamId/memberships') if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership try { - $membership = $auth->skip(fn () => $dbForProject->createDocument('memberships', $membership)); + $membership = $authorization->skip(fn () => $dbForProject->createDocument('memberships', $membership)); } catch (Duplicate $th) { throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS); } - $auth->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); + $authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); $dbForProject->purgeCachedDocument('users', $invitee->getId()); } else { @@ -866,8 +866,8 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { @@ -884,9 +884,9 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId') throw new Exception(Exception::USER_NOT_FOUND); } - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); - $isAppUser = Auth::isAppUser($auth->getRoles()); - $isOwner = $auth->isRole('team:' . $team->getId() . '/owner'); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAppUser = Auth::isAppUser($authorization->getRoles()); + $isOwner = $authorization->isRole('team:' . $team->getId() . '/owner'); if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server) throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to modify roles'); @@ -942,8 +942,8 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->inject('project') ->inject('geodb') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Authorization $authorization) { $protocol = $request->getProtocol(); $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -952,7 +952,7 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') throw new Exception(Exception::MEMBERSHIP_NOT_FOUND); } - $team = $auth->skip(fn () => $dbForProject->getDocument('teams', $teamId)); + $team = $authorization->skip(fn () => $dbForProject->getDocument('teams', $teamId)); if ($team->isEmpty()) { throw new Exception(Exception::TEAM_NOT_FOUND); @@ -987,11 +987,11 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->setAttribute('confirm', true) ; - $auth->skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true))); + $authorization->skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true))); // Log user in - $auth->addRole(Role::user($user->getId())->toString()); + $authorization->addRole(Role::user($user->getId())->toString()); $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); @@ -1021,13 +1021,13 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') $dbForProject->purgeCachedDocument('users', $user->getId()); - $auth->addRole(Role::user($userId)->toString()); + $authorization->addRole(Role::user($userId)->toString()); $membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership); $dbForProject->purgeCachedDocument('users', $user->getId()); - $auth->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); + $authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); $queueForEvents ->setParam('teamId', $team->getId()) @@ -1072,8 +1072,8 @@ Http::delete('/v1/teams/:teamId/memberships/:membershipId') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -1108,7 +1108,7 @@ Http::delete('/v1/teams/:teamId/memberships/:membershipId') $dbForProject->purgeCachedDocument('users', $user->getId()); if ($membership->getAttribute('confirm')) { // Count only confirmed members - $auth->skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0)); + $authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0)); } $queueForEvents @@ -1137,8 +1137,8 @@ Http::get('/v1/teams/:teamId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('auth') - ->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { + ->inject('authorization') + ->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { $team = $dbForProject->getDocument('teams', $teamId); @@ -1156,7 +1156,7 @@ Http::get('/v1/teams/:teamId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $auth); + $audit = new Audit($dbForProject, $authorization); $resource = 'team/' . $team->getId(); $logs = $audit->getLogsByResource($resource, $limit, $offset); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 1849e84c56..fc870045b6 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -769,8 +769,8 @@ Http::get('/v1/users/:userId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('auth') - ->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { + ->inject('authorization') + ->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { $user = $dbForProject->getDocument('users', $userId); @@ -788,7 +788,7 @@ Http::get('/v1/users/:userId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $auth); + $audit = new Audit($dbForProject, $authorization); $logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset); @@ -2105,8 +2105,8 @@ Http::get('/v1/users/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $range, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -2116,7 +2116,7 @@ Http::get('/v1/users/usage') METRIC_SESSIONS, ]; - $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $count => $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/app/controllers/general.php b/app/controllers/general.php index 6feab6e2f7..3855791bac 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -5,6 +5,7 @@ use Appwrite\Event\Event; use Appwrite\Event\Usage; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Network\Validator\Origin; +use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Request\Filters\V16 as RequestV16; use Appwrite\Utopia\Request\Filters\V17 as RequestV17; @@ -635,7 +636,8 @@ Http::error() ->inject('logger') ->inject('log') ->inject('authorization') - ->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization) { + ->inject('connections') + ->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization, Connections $connections) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); if(is_null($route)) { @@ -691,6 +693,7 @@ Http::error() $trace = $error->getTrace(); if (php_sapi_name() === 'cli') { + Console::error('[Error] ------------------'); Console::error('[Error] Timestamp: ' . date('c', time())); if ($route) { @@ -728,7 +731,7 @@ Http::error() /** Wrap all exceptions inside Appwrite\Extend\Exception */ if (!($error instanceof AppwriteException)) { - $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); + $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, (int)$code, $error); } switch ($code) { // Don't show 500 errors! @@ -748,7 +751,7 @@ Http::error() break; default: $code = 500; // All other errors get the generic 500 server error status code - $message = 'Server Error'; + $message = (Http::getMode() === Http::MODE_TYPE_DEVELOPMENT) ? $message : 'Server Error'; } //$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised @@ -794,6 +797,8 @@ Http::error() $response->html($layout->render()); } + $connections->reclaim(); + $response->dynamic( new Document($output), Http::isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR @@ -884,4 +889,20 @@ foreach (Config::getParam('services', []) as $service) { //include_once $service['controller']; } +include_once 'shared/api.php'; +include_once 'shared/api/auth.php'; +include_once 'api/account.php'; +include_once 'api/avatars.php'; +//include_once 'api/database.php'; +//include_once 'api/functions.php'; +//include_once 'api/graphql.php'; +//include_once 'api/health.php'; include_once 'api/locale.php'; +//include_once 'api/messaging.php'; +//include_once 'api/migrations.php'; +include_once 'api/projects.php'; +//include_once 'api/proxy.php'; +//include_once 'api/storage.php'; +include_once 'api/teams.php'; +include_once 'api/users.php'; +//include_once 'api/vcs.php'; diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index bf5c3323c7..39926b7182 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -29,6 +29,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization\Input; use Utopia\System\System; use Utopia\Http\Http; +use Utopia\Http\Route; use Utopia\Http\Validator\WhiteList; $parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) { @@ -152,7 +153,7 @@ $databaseListener = function (string $event, Document $document, Document $proje Http::init() ->groups(['api']) - ->inject('utopia') + ->inject('route') ->inject('request') ->inject('dbForConsole') ->inject('project') @@ -160,10 +161,8 @@ Http::init() ->inject('session') ->inject('servers') ->inject('mode') - ->inject('auth') - ->action(function (Http $utopia, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode, Authorization $auth) { - $route = $utopia->getRoute(); - + ->inject('authorization') + ->action(function (Route $route, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode, Authorization $authorization) { if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } @@ -225,8 +224,8 @@ Http::init() throw new Exception(Exception::PROJECT_KEY_EXPIRED); } - $auth->addRole(Auth::USER_ROLE_APPS); - $auth->setDefaultStatus(false); // Cancel security segmentation for API keys. + $authorization->addRole(Auth::USER_ROLE_APPS); + $authorization->setDefaultStatus(false); // Cancel security segmentation for API keys. $accessedAt = $key->getAttribute('accessedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCCESS)) > $accessedAt) { @@ -252,10 +251,10 @@ Http::init() } } - $auth->addRole($role); + $authorization->addRole($role); - foreach (Auth::getRoles($user, $auth) as $authRole) { - $auth->addRole($authRole); + foreach (Auth::getRoles($user, $authorization) as $authRole) { + $authorization->addRole($authRole); } $service = $route->getLabel('sdk.namespace', ''); @@ -263,7 +262,7 @@ Http::init() if ( array_key_exists($service, $project->getAttribute('services', [])) && !$project->getAttribute('services', [])[$service] - && !(Auth::isPrivilegedUser($auth->getRoles()) || Auth::isAppUser($auth->getRoles())) + && !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles())) ) { throw new Exception(Exception::GENERAL_SERVICE_DISABLED); } @@ -302,7 +301,7 @@ Http::init() Http::init() ->groups(['api']) - ->inject('utopia') + ->inject('route') ->inject('request') ->inject('response') ->inject('project') @@ -316,15 +315,12 @@ Http::init() ->inject('queueForUsage') ->inject('dbForProject') ->inject('mode') - ->inject('auth') - ->action(function (Http $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode, Authorization $auth) use ($databaseListener) { - - $route = $utopia->getRoute(); - + ->inject('authorization') + ->action(function (Route $route, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode, Authorization $authorization) use ($databaseListener) { if ( array_key_exists('rest', $project->getAttribute('apis', [])) && !$project->getAttribute('apis', [])['rest'] - && !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles())) + && !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles())) ) { throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } @@ -340,7 +336,7 @@ Http::init() foreach ($abuseKeyLabel as $abuseKey) { $start = $request->getContentRangeStart(); $end = $request->getContentRangeEnd(); - $timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject, $auth); + $timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject, $authorization); $timeLimit ->setParam('{projectId}', $project->getId()) ->setParam('{userId}', $user->getId()) @@ -354,7 +350,7 @@ Http::init() $closestLimit = null; - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -420,7 +416,7 @@ Http::init() $useCache = $route->getLabel('cache', false); if ($useCache) { $key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER); - $cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key)); + $cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key)); $cache = new Cache( new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId()) ); @@ -434,17 +430,17 @@ Http::init() if ($type === 'bucket') { $bucketId = $parts[1] ?? null; - $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -455,7 +451,7 @@ Http::init() if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -529,7 +525,7 @@ Http::shutdown() Http::shutdown() ->groups(['api']) - ->inject('utopia') + ->inject('route') ->inject('request') ->inject('response') ->inject('project') @@ -545,9 +541,9 @@ Http::shutdown() ->inject('queueForFunctions') ->inject('mode') ->inject('dbForConsole') - ->inject('auth') + ->inject('authorization') ->action(function ( - Http $utopia, + Route $route, Request $request, Response $response, Document $project, @@ -563,10 +559,10 @@ Http::shutdown() Func $queueForFunctions, string $mode, Database $dbForConsole, - Authorization $auth, + Authorization $authorization, ) use ($parseLabel) { if (!empty($user) && !$user->isEmpty() && empty($user->getInternalId())) { - $user = $auth->skip(fn () => $dbForProject->getDocument('users', $user->getId())); + $user = $authorization->skip(fn () => $dbForProject->getDocument('users', $user->getId())); } $responsePayload = $response->getPayload(); @@ -626,7 +622,6 @@ Http::shutdown() } } - $route = $utopia->getRoute(); $requestParams = $route->getParamsValues(); /** @@ -696,11 +691,11 @@ Http::shutdown() $key = md5($request->getURI() . '*' . implode('*', $request->getParams())) . '*' . APP_CACHE_BUSTER; $signature = md5($data['payload']); - $cacheLog = $auth->skip(fn () => $dbForProject->getDocument('cache', $key)); + $cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key)); $accessedAt = $cacheLog->getAttribute('accessedAt', ''); $now = DateTime::now(); if ($cacheLog->isEmpty()) { - $auth->skip(fn () => $dbForProject->createDocument('cache', new Document([ + $authorization->skip(fn () => $dbForProject->createDocument('cache', new Document([ '$id' => $key, 'resource' => $resource, 'resourceType' => $resourceType, @@ -710,7 +705,7 @@ Http::shutdown() ]))); } elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) { $cacheLog->setAttribute('accessedAt', $now); - $auth->skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog)); + $authorization->skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog)); } if ($signature !== $cacheLog->getAttribute('signature')) { diff --git a/app/controllers/shared/api/auth.php b/app/controllers/shared/api/auth.php index 8a7aa30951..32d5d9b1e5 100644 --- a/app/controllers/shared/api/auth.php +++ b/app/controllers/shared/api/auth.php @@ -8,6 +8,7 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; use Utopia\Http\Http; +use Utopia\Http\Route; use Utopia\System\System; Http::init() @@ -31,12 +32,12 @@ Http::init() Http::init() ->groups(['auth']) - ->inject('utopia') + ->inject('route') ->inject('request') ->inject('project') ->inject('geodb') - ->inject('auth') - ->action(function (Http $utopia, Request $request, Document $project, Reader $geodb, Authorization $auth) { + ->inject('authorization') + ->action(function (Route $route, Request $request, Document $project, Reader $geodb, Authorization $authorization) { $denylist = System::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', ''); if (!empty($denylist && $project->getId() === 'console')) { $countries = explode(',', $denylist); @@ -47,10 +48,8 @@ Http::init() } } - $route = $utopia->match($request); - - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); - $isAppUser = Auth::isAppUser($auth->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAppUser = Auth::isAppUser($authorization->getRoles()); if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs return; diff --git a/app/init2.php b/app/init2.php index 16329276e3..af0f0d0a3c 100644 --- a/app/init2.php +++ b/app/init2.php @@ -78,39 +78,12 @@ use Utopia\System\System; use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; use Utopia\Cache\Adapter\None; +Http::setMode(System::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION)); -function getAdapter($type, $scheme, $resource) { - switch ($type) { - case 'database': - $adapter = match ($scheme) { - 'mariadb' => new MariaDB($resource), - 'mysql' => new MySQL($resource), - default => null - }; - - $adapter->setDatabase($scheme); - break; - case 'pubsub': - $adapter = $resource(); - break; - case 'queue': - $adapter = match ($scheme) { - //'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), - default => null - }; - break; - case 'cache': - $adapter = match ($scheme) { - 'redis' => new RedisCache($resource), - default => null - }; - break; - - default: - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); - } - - return $adapter; +if (!Http::isProduction()) { + // Allow specific domains to skip public domain validation in dev environment + // Useful for existing tests involving webhooks + PublicDomain::allow(['request-catcher']); } $global = new Registry(); @@ -140,6 +113,10 @@ $global->set('geodb', function () { return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.mmdb'); }); +$global->set('hooks', function () { + return new Hooks(); +}); + $global->set('pools', (function () { $fallbackForDB = 'db_main=' . URL::unparse([ 'scheme' => 'mariadb', @@ -199,9 +176,12 @@ $global->set('pools', (function () { $multipe = $connection['multiple'] ?? false; $schemes = $connection['schemes'] ?? []; $dsns = explode(',', $connection['dsns'] ?? ''); + $config = []; + foreach ($dsns as &$dsn) { $dsn = explode('=', $dsn); $name = ($multipe) ? $dsn[0] : 'main'; + $config[] = $name; $dsn = $dsn[1] ?? ''; if (empty($dsn)) { @@ -267,6 +247,8 @@ $global->set('pools', (function () { 'dsn' => $dsn, ]; } + + Config::setParam('pools-' . $key, $config); } return function () use ($pools): array { @@ -401,6 +383,34 @@ $user }); $container->set($user); +$session = new Dependency(); +$session + ->setName('session') + ->inject('user') + ->inject('project') + ->setCallback(function (Document $user, Document $project) { + if ($user->isEmpty()) { + return; + } + + $sessions = $user->getAttribute('sessions', []); + $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret, $authDuration); + + if (!$sessionId) { + return; + } + + foreach ($sessions as $session) { + if ($sessionId === $session->getId()) { + return $session; + } + } + + return; + }); +$container->set($session); + $console = new Dependency(); $console ->setName('console') @@ -498,6 +508,8 @@ $dbForProject 'mysql' => new MySQL($connection), default => null }; + + $adapter->setDatabase($dsn->getPath()); $database = new Database($adapter, $cache); $database->setAuthorization($authorization); @@ -531,7 +543,7 @@ $dbForConsole default => null }; - $adapter->setDatabase('appwrite'); + $adapter->setDatabase($dsn->getPath()); $database = new Database($adapter, $cache); $database->setAuthorization($authorization); @@ -788,6 +800,21 @@ $clients }); $container->set($clients); +$servers = new Dependency(); +$servers + ->setName('servers') + ->setCallback(function () { + $platforms = Config::getParam('platforms'); + $server = $platforms[APP_PLATFORM_SERVER]; + + $languages = array_map(function ($language) { + return strtolower($language['name']); + }, $server['sdks']); + + return $languages; + }); +$container->set($servers); + $geodb = new Dependency(); $geodb ->setName('geodb') @@ -795,4 +822,44 @@ $geodb ->setCallback(function (Registry $register) { return $register->get('geodb'); }); -$container->set($geodb); \ No newline at end of file +$container->set($geodb); + +$passwordsDictionary = new Dependency(); +$passwordsDictionary + ->setName('passwordsDictionary') + ->setCallback(function () { + $content = file_get_contents(__DIR__ . '/assets/security/10k-common-passwords'); + $content = explode("\n", $content); + $content = array_flip($content); + return $content; + }); + +$container->set($passwordsDictionary); + +$hooks = new Dependency(); +$hooks + ->setName('hooks') + ->inject('registry') + ->setCallback(function (Registry $registry) { + return $registry->get('hooks'); + }); + +$container->set($hooks); + +$requestTimestamp = new Dependency(); +$requestTimestamp + ->setName('requestTimestamp') + ->inject('request') + ->setCallback(function ($request) { + $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; + }); +$container->set($requestTimestamp); diff --git a/composer.lock b/composer.lock index fcb2ae8659..d7ab08b87e 100644 --- a/composer.lock +++ b/composer.lock @@ -1825,12 +1825,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "aef4e9a7bcb3ba21b993f2daad0364657c2fe1aa" + "reference": "d600ae234780e083c600ab62a8348b7ea506cd1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/aef4e9a7bcb3ba21b993f2daad0364657c2fe1aa", - "reference": "aef4e9a7bcb3ba21b993f2daad0364657c2fe1aa", + "url": "https://api.github.com/repos/utopia-php/http/zipball/d600ae234780e083c600ab62a8348b7ea506cd1d", + "reference": "d600ae234780e083c600ab62a8348b7ea506cd1d", "shasum": "" }, "require": { @@ -1867,7 +1867,7 @@ "issues": "https://github.com/utopia-php/http/issues", "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-04-13T17:20:36+00:00" + "time": "2024-04-14T16:41:37+00:00" }, { "name": "utopia-php/image", diff --git a/src/Appwrite/Utopia/Queue/Connections.php b/src/Appwrite/Utopia/Queue/Connections.php index 2aa4d0f69b..e873373566 100644 --- a/src/Appwrite/Utopia/Queue/Connections.php +++ b/src/Appwrite/Utopia/Queue/Connections.php @@ -2,41 +2,20 @@ namespace Appwrite\Utopia\Queue; -use Utopia\Pools\Connection; - class Connections { /** - * @var Connection[] + * @var array */ protected array $connections = []; /** - * @param Connection $pool + * @param mixed $connection * @return self */ - public function add(Connection $connection): self + public function add(mixed $connection, $pool): self { - $this->connections[$connection->getID()] = $connection; - return $this; - } - - /** - * @param string $id - * @return Connection - */ - public function get(string $id): Connection - { - return $this->connections[$id] ?? throw new \Exception("Connection '{$id}' not found"); - } - - /** - * @param string $id - * @return self - */ - public function remove(string $id): self - { - unset($this->connections[$id]); + $this->connections[] = ['connection' => $connection, 'pool' => $pool]; return $this; } @@ -45,8 +24,11 @@ class Connections */ public function reclaim(): self { - foreach ($this->connections as $connection) { - $connection->reclaim(); + foreach ($this->connections as $id => $resource) { + $pool = $resource['pool']; + $connection = $resource['connection']; + $pool->put($connection); + unset($this->connections[$id]); } return $this; From fd2410d2ae5baa74c14c7e42c2bed72a87940a73 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Sun, 14 Apr 2024 22:42:31 +0200 Subject: [PATCH 031/195] Fixed bootstrap --- app/controllers/api/projects.php | 1 - app/controllers/api/storage.php | 160 +++++++++++++++---------------- app/controllers/general.php | 2 +- app/http.php | 7 +- app/init2.php | 123 ++++++++++++++++++++++++ phpunit.xml | 2 +- 6 files changed, 207 insertions(+), 88 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 77052aba2a..c1889a5696 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -42,7 +42,6 @@ use Utopia\Http\Validator\Text; use Utopia\Http\Validator\URL; use Utopia\Http\Validator\WhiteList; use Utopia\Locale\Locale; -use Utopia\Pools\Group; use Utopia\System\System; Http::init() diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 12be117267..cec4c115a0 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -365,19 +365,19 @@ Http::post('/v1/storage/buckets/:bucketId/files') ->inject('mode') ->inject('deviceForFiles') ->inject('deviceForLocal') - ->inject('auth') - ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal, Authorization $auth) { + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal, Authorization $authorization) { - $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - if (!$auth->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { + if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -401,7 +401,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') } // Users can only manage their own roles, API keys and Admin users can manage any - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -414,7 +414,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$auth->isRole($role)) { + if (!$authorization->isRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -635,10 +635,10 @@ Http::post('/v1/storage/buckets/:bucketId/files') * However as with chunk upload even if we are updating, we are essentially creating a file * adding it's new chunk so we validate create permission instead of update */ - if (!$auth->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { + if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { throw new Exception(Exception::USER_UNAUTHORIZED); } - $file = $auth->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); } } catch (AuthorizationException) { throw new Exception(Exception::USER_UNAUTHORIZED); @@ -681,10 +681,10 @@ Http::post('/v1/storage/buckets/:bucketId/files') * However as with chunk upload even if we are updating, we are essentially creating a file * adding it's new chunk so we validate create permission instead of update */ - if (!$auth->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { + if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { throw new Exception(Exception::USER_UNAUTHORIZED); } - $file = $auth->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); } } catch (AuthorizationException) { throw new Exception(Exception::USER_UNAUTHORIZED); @@ -726,19 +726,19 @@ Http::get('/v1/storage/buckets/:bucketId/files') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('auth') - ->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $auth) { - $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->inject('authorization') + ->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -767,7 +767,7 @@ Http::get('/v1/storage/buckets/:bucketId/files') if ($fileSecurity && !$valid) { $cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $cursorDocument = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($cursorDocument->isEmpty()) { @@ -783,8 +783,8 @@ Http::get('/v1/storage/buckets/:bucketId/files') $files = $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries); $total = $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT); } else { - $files = $auth->skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries)); - $total = $auth->skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT)); + $files = $authorization->skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries)); + $total = $authorization->skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT)); } $response->dynamic(new Document([ @@ -810,19 +810,19 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('auth') - ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode, Authorization $auth) { - $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -830,7 +830,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId') if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -875,24 +875,24 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->inject('mode') ->inject('deviceForFiles') ->inject('deviceForLocal') - ->inject('auth') - ->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, Authorization $auth) { + ->inject('authorization') + ->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, Authorization $authorization) { if (!\extension_loaded('imagick')) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); } - $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -900,7 +900,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -1035,20 +1035,20 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->inject('dbForProject') ->inject('mode') ->inject('deviceForFiles') - ->inject('auth') - ->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Device $deviceForFiles, Authorization $auth) { + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Device $deviceForFiles, Authorization $authorization) { - $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1056,7 +1056,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download') if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -1175,19 +1175,19 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->inject('dbForProject') ->inject('mode') ->inject('deviceForFiles') - ->inject('auth') - ->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceForFiles, Authorization $auth) { - $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceForFiles, Authorization $authorization) { + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $auth->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1195,7 +1195,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view') if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -1324,9 +1324,9 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/push') ->inject('project') ->inject('mode') ->inject('deviceForFiles') - ->inject('auth') - ->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles, Authorization $auth) { - $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles, Authorization $authorization) { + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1')); @@ -1345,14 +1345,14 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/push') throw new Exception(Exception::USER_UNAUTHORIZED); } - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -1490,26 +1490,26 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('user') ->inject('mode') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents, Authorization $authorization) { - $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $auth->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } // Read permission should not be required for update - $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -1523,7 +1523,7 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') ]); // Users can only manage their own roles, API keys and Admin users can manage any - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles) && !\is_null($permissions)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -1536,7 +1536,7 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$auth->isRole($role)) { + if (!$authorization->isRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -1560,7 +1560,7 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') throw new Exception(Exception::USER_UNAUTHORIZED); } } else { - $file = $auth->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); } $queueForEvents @@ -1596,32 +1596,32 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('mode') ->inject('deviceForFiles') ->inject('queueForDeletes') - ->inject('auth') - ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes, Authorization $auth) { - $bucket = $auth->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->inject('authorization') + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes, Authorization $authorization) { + $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $auth->isValid(new Input(Database::PERMISSION_DELETE, $bucket->getDelete())); + $valid = $authorization->isValid(new Input(Database::PERMISSION_DELETE, $bucket->getDelete())); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } // Read permission should not be required for delete - $file = $auth->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } // Make sure we don't delete the file before the document permission check occurs - if ($fileSecurity && !$valid && !$auth->isValid($file->getDelete())) { + if ($fileSecurity && !$valid && !$authorization->isValid($file->getDelete())) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1649,7 +1649,7 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') throw new Exception(Exception::USER_UNAUTHORIZED); } } else { - $deleted = $auth->skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $deleted = $authorization->skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if (!$deleted) { @@ -1682,8 +1682,8 @@ Http::get('/v1/storage/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $range, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -1695,7 +1695,7 @@ Http::get('/v1/storage/usage') ]; $total = []; - $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -1763,8 +1763,8 @@ Http::get('/v1/storage/:bucketId/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $bucketId, string $range, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $bucketId, string $range, Response $response, Database $dbForProject, Authorization $authorization) { $bucket = $dbForProject->getDocument('buckets', $bucketId); @@ -1780,7 +1780,7 @@ Http::get('/v1/storage/:bucketId/usage') str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE), ]; - $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/app/controllers/general.php b/app/controllers/general.php index 3855791bac..b81238a19b 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -902,7 +902,7 @@ include_once 'api/locale.php'; //include_once 'api/migrations.php'; include_once 'api/projects.php'; //include_once 'api/proxy.php'; -//include_once 'api/storage.php'; +include_once 'api/storage.php'; include_once 'api/teams.php'; include_once 'api/users.php'; //include_once 'api/vcs.php'; diff --git a/app/http.php b/app/http.php index 2fe80777f3..2d7a677ae6 100644 --- a/app/http.php +++ b/app/http.php @@ -36,6 +36,8 @@ $server = new Server('0.0.0.0', '80', [ 'open_http2_protocol' => true, // 'http_compression' => true, // 'http_compression_level' => 6, + 'package_max_length' => $payloadSize, + 'buffer_output_size' => $payloadSize, // Server // 'log_level' => 0, @@ -57,11 +59,6 @@ $http->setRequestClass(Request::class); $http->setResponseClass(Response::class); //require_once __DIR__ . '/init.php'; -require_once __DIR__ . '/init/constants.php'; -require_once __DIR__ . '/init/config.php'; -require_once __DIR__ . '/init/locale.php'; -require_once __DIR__ . '/init/database/filters.php'; -require_once __DIR__ . '/init/database/formats.php'; require_once __DIR__ . '/init2.php'; require_once __DIR__ . '/controllers/general.php'; diff --git a/app/init2.php b/app/init2.php index af0f0d0a3c..2d71d9f0f3 100644 --- a/app/init2.php +++ b/app/init2.php @@ -1,5 +1,11 @@ getScheme(); + $accessKey = $dsn->getUser() ?? ''; + $accessSecret = $dsn->getPassword() ?? ''; + $bucket = $dsn->getPath() ?? ''; + $region = $dsn->getParam('region'); + } catch (\Throwable $e) { + Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); + } + + switch ($device) { + case Storage::DEVICE_S3: + return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case STORAGE::DEVICE_DO_SPACES: + return new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_BACKBLAZE: + return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_LINODE: + return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_WASABI: + return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_LOCAL: + default: + return new Local($root); + } + } else { + switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { + case Storage::DEVICE_LOCAL: + default: + return new Local($root); + case Storage::DEVICE_S3: + $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); + $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); + $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); + $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); + $s3Acl = 'private'; + return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); + case Storage::DEVICE_DO_SPACES: + $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); + $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); + $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); + $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); + $doSpacesAcl = 'private'; + return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); + 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); + } + } +} + $global = new Registry(); $global->set('logger', function () { @@ -742,6 +830,41 @@ $queueForMigrations }); $container->set($queueForMigrations); +$deviceForLocal = new Dependency(); +$deviceForLocal + ->setName('deviceForLocal') + ->setCallback(function () { + return new Local(); + }); +$container->set($deviceForLocal); + +$deviceForFiles = new Dependency(); +$deviceForFiles + ->setName('deviceForFiles') + ->inject('project') + ->setCallback(function ($project) { + return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); + }); +$container->set($deviceForFiles); + +$deviceForFunctions = new Dependency(); +$deviceForFunctions + ->setName('deviceForFunctions') + ->inject('project') + ->setCallback(function ($project) { + return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); + }); +$container->set($deviceForFunctions); + +$deviceForBuilds = new Dependency(); +$deviceForBuilds + ->setName('deviceForBuilds') + ->inject('project') + ->setCallback(function ($project) { + return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); + }); +$container->set($deviceForBuilds); + $clients = new Dependency(); $clients ->setName('clients') diff --git a/phpunit.xml b/phpunit.xml index 90ebd4225f..d2bc4610cf 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,6 +1,6 @@ Date: Mon, 15 Apr 2024 05:59:02 +0200 Subject: [PATCH 032/195] Fixed bootstrap file --- app/http.php | 13 ++++--------- app/init2.php | 5 +++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/http.php b/app/http.php index 2d7a677ae6..8b9fae8396 100644 --- a/app/http.php +++ b/app/http.php @@ -20,15 +20,14 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; -use Utopia\DI\Container; use Utopia\Http\Adapter\Swoole\Server; use Utopia\Http\Http; use Utopia\System\System; -// Unlimited memory limit to handle as many coroutines/requests as possible -ini_set('memory_limit', '-1'); +//require_once __DIR__ . '/init.php'; +require_once __DIR__ . '/init2.php'; +require_once __DIR__ . '/controllers/general.php'; -$container = new Container(); $workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); $payloadSize = 6 * (1024 * 1024); // 6MB @@ -58,11 +57,7 @@ $http = new Http($server, $container, 'UTC'); $http->setRequestClass(Request::class); $http->setResponseClass(Response::class); -//require_once __DIR__ . '/init.php'; -require_once __DIR__ . '/init2.php'; -require_once __DIR__ . '/controllers/general.php'; - -global $global; +global $global, $container; http::onStart() ->inject('authorization') diff --git a/app/init2.php b/app/init2.php index 2d71d9f0f3..54a06a210f 100644 --- a/app/init2.php +++ b/app/init2.php @@ -6,6 +6,9 @@ require_once __DIR__ . '/init/locale.php'; require_once __DIR__ . '/init/database/filters.php'; require_once __DIR__ . '/init/database/formats.php'; +// Unlimited memory limit to handle as many coroutines/requests as possible +ini_set('memory_limit', '-1'); + global $http, $container; use Ahc\Jwt\JWT; @@ -83,6 +86,7 @@ use Utopia\Storage\Storage; use Utopia\System\System; use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; use Utopia\Cache\Adapter\None; +use Utopia\DI\Container; Http::setMode(System::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION)); @@ -174,6 +178,7 @@ function getDevice($root): Device } } +$container = new Container(); $global = new Registry(); $global->set('logger', function () { From 49a50b32a745ea954e5b1b08eb878b19a8bf8a35 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 15 Apr 2024 06:29:12 +0200 Subject: [PATCH 033/195] Test DB failure --- app/controllers/general.php | 339 +++++++++++++++++++----------------- 1 file changed, 180 insertions(+), 159 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index b81238a19b..63f3571997 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -628,64 +628,7 @@ Http::init() Http::error() ->inject('error') - ->inject('user') - ->inject('route') - ->inject('request') - ->inject('response') - ->inject('project') - ->inject('logger') - ->inject('log') - ->inject('authorization') - ->inject('connections') - ->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization, Connections $connections) { - $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - - if(is_null($route)) { - $route = new Route($request->getMethod(), $request->getURI()); - } - - if ($error instanceof AppwriteException) { - $publish = $error->isPublishable(); - } else { - $publish = $error->getCode() === 0 || $error->getCode() >= 500; - } - - if ($logger && ($publish || $error->getCode() === 0)) { - if (isset($user) && !$user->isEmpty()) { - $log->setUser(new User($user->getId())); - } - - $log->setNamespace("http"); - $log->setServer(\gethostname()); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($error->getMessage()); - - $log->addTag('database', $project->getAttribute('database', 'console')); - $log->addTag('method', $route->getMethod()); - $log->addTag('url', $route->getPath()); - $log->addTag('verboseType', get_class($error)); - $log->addTag('code', $error->getCode()); - $log->addTag('projectId', $project->getId()); - $log->addTag('hostname', $request->getHostname()); - $log->addTag('locale', (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', ''))); - - $log->addExtra('file', $error->getFile()); - $log->addExtra('line', $error->getLine()); - $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('detailedTrace', $error->getTrace()); - $log->addExtra('roles', $authorization->getRoles()); - - $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); - $log->setAction($action); - - $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - - $responseCode = $logger->addLog($log); - Console::info('Log pushed with status code: ' . $responseCode); - } - + ->action(function($error) { $code = $error->getCode(); $message = $error->getMessage(); $file = $error->getFile(); @@ -696,115 +639,193 @@ Http::error() Console::error('[Error] ------------------'); Console::error('[Error] Timestamp: ' . date('c', time())); - if ($route) { - Console::error('[Error] Method: ' . $route->getMethod()); - Console::error('[Error] URL: ' . $route->getPath()); - } - Console::error('[Error] Code: ' . $code); Console::error('[Error] Type: ' . get_class($error)); Console::error('[Error] Message: ' . $message); Console::error('[Error] File: ' . $file); Console::error('[Error] Line: ' . $line); } - - /** Handle Utopia Errors */ - if ($error instanceof Utopia\Http\Exception) { - $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); - switch ($code) { - case 400: - $error->setType(AppwriteException::GENERAL_ARGUMENT_INVALID); - break; - case 404: - $error->setType(AppwriteException::GENERAL_ROUTE_NOT_FOUND); - break; - } - } elseif ($error instanceof Utopia\Database\Exception\Conflict) { - $error = new AppwriteException(AppwriteException::DOCUMENT_UPDATE_CONFLICT, previous: $error); - $code = $error->getCode(); - $message = $error->getMessage(); - } elseif ($error instanceof Utopia\Database\Exception\Timeout) { - $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); - $code = $error->getCode(); - $message = $error->getMessage(); - } - - /** Wrap all exceptions inside Appwrite\Extend\Exception */ - if (!($error instanceof AppwriteException)) { - $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, (int)$code, $error); - } - - switch ($code) { // Don't show 500 errors! - case 400: // Error allowed publicly - case 401: // Error allowed publicly - case 402: // Error allowed publicly - case 403: // Error allowed publicly - case 404: // Error allowed publicly - case 408: // Error allowed publicly - case 409: // Error allowed publicly - case 412: // Error allowed publicly - case 416: // Error allowed publicly - case 429: // Error allowed publicly - case 451: // Error allowed publicly - case 501: // Error allowed publicly - case 503: // Error allowed publicly - break; - default: - $code = 500; // All other errors get the generic 500 server error status code - $message = (Http::getMode() === Http::MODE_TYPE_DEVELOPMENT) ? $message : 'Server Error'; - } - - //$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised - - $type = $error->getType(); - - $output = ((Http::isDevelopment())) ? [ - 'message' => $message, - 'code' => $code, - 'file' => $file, - 'line' => $line, - 'trace' => \json_encode($trace, JSON_UNESCAPED_UNICODE) === false ? [] : $trace, // check for failing encode - 'version' => $version, - 'type' => $type, - ] : [ - 'message' => $message, - 'code' => $code, - 'version' => $version, - 'type' => $type, - ]; - - $response - ->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate') - ->addHeader('Expires', '0') - ->addHeader('Pragma', 'no-cache') - ->setStatusCode($code); - - $template = ($route) ? $route->getLabel('error', null) : null; - - if ($template) { - $layout = new View($template); - - $layout - ->setParam('title', $project->getAttribute('name') . ' - Error') - ->setParam('development', Http::isDevelopment()) - ->setParam('projectName', $project->getAttribute('name')) - ->setParam('projectURL', $project->getAttribute('url')) - ->setParam('message', $output['message'] ?? '') - ->setParam('type', $output['type'] ?? '') - ->setParam('code', $output['code'] ?? '') - ->setParam('trace', $output['trace'] ?? []); - - $response->html($layout->render()); - } - - $connections->reclaim(); - - $response->dynamic( - new Document($output), - Http::isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR - ); }); +// Http::error() +// ->inject('error') +// ->inject('user') +// ->inject('route') +// ->inject('request') +// ->inject('response') +// ->inject('project') +// ->inject('logger') +// ->inject('log') +// ->inject('authorization') +// ->inject('connections') +// ->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization, Connections $connections) { +// $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); + +// if(is_null($route)) { +// $route = new Route($request->getMethod(), $request->getURI()); +// } + +// if ($error instanceof AppwriteException) { +// $publish = $error->isPublishable(); +// } else { +// $publish = $error->getCode() === 0 || $error->getCode() >= 500; +// } + +// if ($logger && ($publish || $error->getCode() === 0)) { +// if (isset($user) && !$user->isEmpty()) { +// $log->setUser(new User($user->getId())); +// } + +// $log->setNamespace("http"); +// $log->setServer(\gethostname()); +// $log->setVersion($version); +// $log->setType(Log::TYPE_ERROR); +// $log->setMessage($error->getMessage()); + +// $log->addTag('database', $project->getAttribute('database', 'console')); +// $log->addTag('method', $route->getMethod()); +// $log->addTag('url', $route->getPath()); +// $log->addTag('verboseType', get_class($error)); +// $log->addTag('code', $error->getCode()); +// $log->addTag('projectId', $project->getId()); +// $log->addTag('hostname', $request->getHostname()); +// $log->addTag('locale', (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', ''))); + +// $log->addExtra('file', $error->getFile()); +// $log->addExtra('line', $error->getLine()); +// $log->addExtra('trace', $error->getTraceAsString()); +// $log->addExtra('detailedTrace', $error->getTrace()); +// $log->addExtra('roles', $authorization->getRoles()); + +// $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); +// $log->setAction($action); + +// $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; +// $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); + +// $responseCode = $logger->addLog($log); +// Console::info('Log pushed with status code: ' . $responseCode); +// } + +// $code = $error->getCode(); +// $message = $error->getMessage(); +// $file = $error->getFile(); +// $line = $error->getLine(); +// $trace = $error->getTrace(); + +// if (php_sapi_name() === 'cli') { +// Console::error('[Error] ------------------'); +// Console::error('[Error] Timestamp: ' . date('c', time())); + +// if ($route) { +// Console::error('[Error] Method: ' . $route->getMethod()); +// Console::error('[Error] URL: ' . $route->getPath()); +// } + +// Console::error('[Error] Code: ' . $code); +// Console::error('[Error] Type: ' . get_class($error)); +// Console::error('[Error] Message: ' . $message); +// Console::error('[Error] File: ' . $file); +// Console::error('[Error] Line: ' . $line); +// } + +// /** Handle Utopia Errors */ +// if ($error instanceof Utopia\Http\Exception) { +// $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); +// switch ($code) { +// case 400: +// $error->setType(AppwriteException::GENERAL_ARGUMENT_INVALID); +// break; +// case 404: +// $error->setType(AppwriteException::GENERAL_ROUTE_NOT_FOUND); +// break; +// } +// } elseif ($error instanceof Utopia\Database\Exception\Conflict) { +// $error = new AppwriteException(AppwriteException::DOCUMENT_UPDATE_CONFLICT, previous: $error); +// $code = $error->getCode(); +// $message = $error->getMessage(); +// } elseif ($error instanceof Utopia\Database\Exception\Timeout) { +// $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); +// $code = $error->getCode(); +// $message = $error->getMessage(); +// } + +// /** Wrap all exceptions inside Appwrite\Extend\Exception */ +// if (!($error instanceof AppwriteException)) { +// $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, (int)$code, $error); +// } + +// switch ($code) { // Don't show 500 errors! +// case 400: // Error allowed publicly +// case 401: // Error allowed publicly +// case 402: // Error allowed publicly +// case 403: // Error allowed publicly +// case 404: // Error allowed publicly +// case 408: // Error allowed publicly +// case 409: // Error allowed publicly +// case 412: // Error allowed publicly +// case 416: // Error allowed publicly +// case 429: // Error allowed publicly +// case 451: // Error allowed publicly +// case 501: // Error allowed publicly +// case 503: // Error allowed publicly +// break; +// default: +// $code = 500; // All other errors get the generic 500 server error status code +// $message = (Http::getMode() === Http::MODE_TYPE_DEVELOPMENT) ? $message : 'Server Error'; +// } + +// //$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised + +// $type = $error->getType(); + +// $output = ((Http::isDevelopment())) ? [ +// 'message' => $message, +// 'code' => $code, +// 'file' => $file, +// 'line' => $line, +// 'trace' => \json_encode($trace, JSON_UNESCAPED_UNICODE) === false ? [] : $trace, // check for failing encode +// 'version' => $version, +// 'type' => $type, +// ] : [ +// 'message' => $message, +// 'code' => $code, +// 'version' => $version, +// 'type' => $type, +// ]; + +// $response +// ->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate') +// ->addHeader('Expires', '0') +// ->addHeader('Pragma', 'no-cache') +// ->setStatusCode($code); + +// $template = ($route) ? $route->getLabel('error', null) : null; + +// if ($template) { +// $layout = new View($template); + +// $layout +// ->setParam('title', $project->getAttribute('name') . ' - Error') +// ->setParam('development', Http::isDevelopment()) +// ->setParam('projectName', $project->getAttribute('name')) +// ->setParam('projectURL', $project->getAttribute('url')) +// ->setParam('message', $output['message'] ?? '') +// ->setParam('type', $output['type'] ?? '') +// ->setParam('code', $output['code'] ?? '') +// ->setParam('trace', $output['trace'] ?? []); + +// $response->html($layout->render()); +// } + +// $connections->reclaim(); + +// $response->dynamic( +// new Document($output), +// Http::isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR +// ); +// }); + Http::get('/robots.txt') ->desc('Robots.txt File') ->label('scope', 'public') From 87ca2dfb32464799c207ec3746602c12f00d82fd Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 15 Apr 2024 07:29:32 +0200 Subject: [PATCH 034/195] Applied fixes for tests --- app/controllers/general.php | 339 +++++++++++++++++------------------- app/http.php | 323 ++++++++++++++++++---------------- 2 files changed, 336 insertions(+), 326 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 63f3571997..b81238a19b 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -628,7 +628,64 @@ Http::init() Http::error() ->inject('error') - ->action(function($error) { + ->inject('user') + ->inject('route') + ->inject('request') + ->inject('response') + ->inject('project') + ->inject('logger') + ->inject('log') + ->inject('authorization') + ->inject('connections') + ->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization, Connections $connections) { + $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); + + if(is_null($route)) { + $route = new Route($request->getMethod(), $request->getURI()); + } + + if ($error instanceof AppwriteException) { + $publish = $error->isPublishable(); + } else { + $publish = $error->getCode() === 0 || $error->getCode() >= 500; + } + + if ($logger && ($publish || $error->getCode() === 0)) { + if (isset($user) && !$user->isEmpty()) { + $log->setUser(new User($user->getId())); + } + + $log->setNamespace("http"); + $log->setServer(\gethostname()); + $log->setVersion($version); + $log->setType(Log::TYPE_ERROR); + $log->setMessage($error->getMessage()); + + $log->addTag('database', $project->getAttribute('database', 'console')); + $log->addTag('method', $route->getMethod()); + $log->addTag('url', $route->getPath()); + $log->addTag('verboseType', get_class($error)); + $log->addTag('code', $error->getCode()); + $log->addTag('projectId', $project->getId()); + $log->addTag('hostname', $request->getHostname()); + $log->addTag('locale', (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', ''))); + + $log->addExtra('file', $error->getFile()); + $log->addExtra('line', $error->getLine()); + $log->addExtra('trace', $error->getTraceAsString()); + $log->addExtra('detailedTrace', $error->getTrace()); + $log->addExtra('roles', $authorization->getRoles()); + + $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); + $log->setAction($action); + + $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; + $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); + + $responseCode = $logger->addLog($log); + Console::info('Log pushed with status code: ' . $responseCode); + } + $code = $error->getCode(); $message = $error->getMessage(); $file = $error->getFile(); @@ -639,193 +696,115 @@ Http::error() Console::error('[Error] ------------------'); Console::error('[Error] Timestamp: ' . date('c', time())); + if ($route) { + Console::error('[Error] Method: ' . $route->getMethod()); + Console::error('[Error] URL: ' . $route->getPath()); + } + Console::error('[Error] Code: ' . $code); Console::error('[Error] Type: ' . get_class($error)); Console::error('[Error] Message: ' . $message); Console::error('[Error] File: ' . $file); Console::error('[Error] Line: ' . $line); } + + /** Handle Utopia Errors */ + if ($error instanceof Utopia\Http\Exception) { + $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); + switch ($code) { + case 400: + $error->setType(AppwriteException::GENERAL_ARGUMENT_INVALID); + break; + case 404: + $error->setType(AppwriteException::GENERAL_ROUTE_NOT_FOUND); + break; + } + } elseif ($error instanceof Utopia\Database\Exception\Conflict) { + $error = new AppwriteException(AppwriteException::DOCUMENT_UPDATE_CONFLICT, previous: $error); + $code = $error->getCode(); + $message = $error->getMessage(); + } elseif ($error instanceof Utopia\Database\Exception\Timeout) { + $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); + $code = $error->getCode(); + $message = $error->getMessage(); + } + + /** Wrap all exceptions inside Appwrite\Extend\Exception */ + if (!($error instanceof AppwriteException)) { + $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, (int)$code, $error); + } + + switch ($code) { // Don't show 500 errors! + case 400: // Error allowed publicly + case 401: // Error allowed publicly + case 402: // Error allowed publicly + case 403: // Error allowed publicly + case 404: // Error allowed publicly + case 408: // Error allowed publicly + case 409: // Error allowed publicly + case 412: // Error allowed publicly + case 416: // Error allowed publicly + case 429: // Error allowed publicly + case 451: // Error allowed publicly + case 501: // Error allowed publicly + case 503: // Error allowed publicly + break; + default: + $code = 500; // All other errors get the generic 500 server error status code + $message = (Http::getMode() === Http::MODE_TYPE_DEVELOPMENT) ? $message : 'Server Error'; + } + + //$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised + + $type = $error->getType(); + + $output = ((Http::isDevelopment())) ? [ + 'message' => $message, + 'code' => $code, + 'file' => $file, + 'line' => $line, + 'trace' => \json_encode($trace, JSON_UNESCAPED_UNICODE) === false ? [] : $trace, // check for failing encode + 'version' => $version, + 'type' => $type, + ] : [ + 'message' => $message, + 'code' => $code, + 'version' => $version, + 'type' => $type, + ]; + + $response + ->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate') + ->addHeader('Expires', '0') + ->addHeader('Pragma', 'no-cache') + ->setStatusCode($code); + + $template = ($route) ? $route->getLabel('error', null) : null; + + if ($template) { + $layout = new View($template); + + $layout + ->setParam('title', $project->getAttribute('name') . ' - Error') + ->setParam('development', Http::isDevelopment()) + ->setParam('projectName', $project->getAttribute('name')) + ->setParam('projectURL', $project->getAttribute('url')) + ->setParam('message', $output['message'] ?? '') + ->setParam('type', $output['type'] ?? '') + ->setParam('code', $output['code'] ?? '') + ->setParam('trace', $output['trace'] ?? []); + + $response->html($layout->render()); + } + + $connections->reclaim(); + + $response->dynamic( + new Document($output), + Http::isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR + ); }); -// Http::error() -// ->inject('error') -// ->inject('user') -// ->inject('route') -// ->inject('request') -// ->inject('response') -// ->inject('project') -// ->inject('logger') -// ->inject('log') -// ->inject('authorization') -// ->inject('connections') -// ->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization, Connections $connections) { -// $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - -// if(is_null($route)) { -// $route = new Route($request->getMethod(), $request->getURI()); -// } - -// if ($error instanceof AppwriteException) { -// $publish = $error->isPublishable(); -// } else { -// $publish = $error->getCode() === 0 || $error->getCode() >= 500; -// } - -// if ($logger && ($publish || $error->getCode() === 0)) { -// if (isset($user) && !$user->isEmpty()) { -// $log->setUser(new User($user->getId())); -// } - -// $log->setNamespace("http"); -// $log->setServer(\gethostname()); -// $log->setVersion($version); -// $log->setType(Log::TYPE_ERROR); -// $log->setMessage($error->getMessage()); - -// $log->addTag('database', $project->getAttribute('database', 'console')); -// $log->addTag('method', $route->getMethod()); -// $log->addTag('url', $route->getPath()); -// $log->addTag('verboseType', get_class($error)); -// $log->addTag('code', $error->getCode()); -// $log->addTag('projectId', $project->getId()); -// $log->addTag('hostname', $request->getHostname()); -// $log->addTag('locale', (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', ''))); - -// $log->addExtra('file', $error->getFile()); -// $log->addExtra('line', $error->getLine()); -// $log->addExtra('trace', $error->getTraceAsString()); -// $log->addExtra('detailedTrace', $error->getTrace()); -// $log->addExtra('roles', $authorization->getRoles()); - -// $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); -// $log->setAction($action); - -// $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; -// $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - -// $responseCode = $logger->addLog($log); -// Console::info('Log pushed with status code: ' . $responseCode); -// } - -// $code = $error->getCode(); -// $message = $error->getMessage(); -// $file = $error->getFile(); -// $line = $error->getLine(); -// $trace = $error->getTrace(); - -// if (php_sapi_name() === 'cli') { -// Console::error('[Error] ------------------'); -// Console::error('[Error] Timestamp: ' . date('c', time())); - -// if ($route) { -// Console::error('[Error] Method: ' . $route->getMethod()); -// Console::error('[Error] URL: ' . $route->getPath()); -// } - -// Console::error('[Error] Code: ' . $code); -// Console::error('[Error] Type: ' . get_class($error)); -// Console::error('[Error] Message: ' . $message); -// Console::error('[Error] File: ' . $file); -// Console::error('[Error] Line: ' . $line); -// } - -// /** Handle Utopia Errors */ -// if ($error instanceof Utopia\Http\Exception) { -// $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); -// switch ($code) { -// case 400: -// $error->setType(AppwriteException::GENERAL_ARGUMENT_INVALID); -// break; -// case 404: -// $error->setType(AppwriteException::GENERAL_ROUTE_NOT_FOUND); -// break; -// } -// } elseif ($error instanceof Utopia\Database\Exception\Conflict) { -// $error = new AppwriteException(AppwriteException::DOCUMENT_UPDATE_CONFLICT, previous: $error); -// $code = $error->getCode(); -// $message = $error->getMessage(); -// } elseif ($error instanceof Utopia\Database\Exception\Timeout) { -// $error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error); -// $code = $error->getCode(); -// $message = $error->getMessage(); -// } - -// /** Wrap all exceptions inside Appwrite\Extend\Exception */ -// if (!($error instanceof AppwriteException)) { -// $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, (int)$code, $error); -// } - -// switch ($code) { // Don't show 500 errors! -// case 400: // Error allowed publicly -// case 401: // Error allowed publicly -// case 402: // Error allowed publicly -// case 403: // Error allowed publicly -// case 404: // Error allowed publicly -// case 408: // Error allowed publicly -// case 409: // Error allowed publicly -// case 412: // Error allowed publicly -// case 416: // Error allowed publicly -// case 429: // Error allowed publicly -// case 451: // Error allowed publicly -// case 501: // Error allowed publicly -// case 503: // Error allowed publicly -// break; -// default: -// $code = 500; // All other errors get the generic 500 server error status code -// $message = (Http::getMode() === Http::MODE_TYPE_DEVELOPMENT) ? $message : 'Server Error'; -// } - -// //$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised - -// $type = $error->getType(); - -// $output = ((Http::isDevelopment())) ? [ -// 'message' => $message, -// 'code' => $code, -// 'file' => $file, -// 'line' => $line, -// 'trace' => \json_encode($trace, JSON_UNESCAPED_UNICODE) === false ? [] : $trace, // check for failing encode -// 'version' => $version, -// 'type' => $type, -// ] : [ -// 'message' => $message, -// 'code' => $code, -// 'version' => $version, -// 'type' => $type, -// ]; - -// $response -// ->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate') -// ->addHeader('Expires', '0') -// ->addHeader('Pragma', 'no-cache') -// ->setStatusCode($code); - -// $template = ($route) ? $route->getLabel('error', null) : null; - -// if ($template) { -// $layout = new View($template); - -// $layout -// ->setParam('title', $project->getAttribute('name') . ' - Error') -// ->setParam('development', Http::isDevelopment()) -// ->setParam('projectName', $project->getAttribute('name')) -// ->setParam('projectURL', $project->getAttribute('url')) -// ->setParam('message', $output['message'] ?? '') -// ->setParam('type', $output['type'] ?? '') -// ->setParam('code', $output['code'] ?? '') -// ->setParam('trace', $output['trace'] ?? []); - -// $response->html($layout->render()); -// } - -// $connections->reclaim(); - -// $response->dynamic( -// new Document($output), -// Http::isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR -// ); -// }); - Http::get('/robots.txt') ->desc('Robots.txt File') ->label('scope', 'public') diff --git a/app/http.php b/app/http.php index 8b9fae8396..c4b5bcd779 100644 --- a/app/http.php +++ b/app/http.php @@ -12,8 +12,11 @@ use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Abuse\Adapters\TimeLimit; use Utopia\Audit\Audit; +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\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; @@ -61,156 +64,184 @@ global $global, $container; http::onStart() ->inject('authorization') - ->inject('dbForConsole') + ->inject('cache') + ->inject('pools') ->inject('connections') - ->action(function (Authorization $authorization, Database $dbForConsole, Connections $connections) { - // wait for database to be ready - $attempts = 0; - $max = 10; - $sleep = 1; - - do { - try { - $attempts++; - $dbForConsole->ping(); - break; // leave the do-while if successful - } catch (\Throwable $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: ' . $e->getMessage()); - } - sleep($sleep); - } - } while ($attempts < $max); - - Console::success('[Setup] - Server database init started...'); - + ->action(function (Authorization $authorization, Cache $cache, array $pools, Connections $connections) { try { - Console::success('[Setup] - Creating database: appwrite...'); - $dbForConsole->create(); + // wait for database to be ready + $attempts = 0; + $max = 15; + $sleep = 2; + + do { + try { + $attempts++; + $pool = $pools['pools-console-main']['pool']; + $dsn = $pools['pools-console-main']['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); + + $dbForConsole = new Database($adapter, $cache); + $dbForConsole->setAuthorization($authorization); + + $dbForConsole + ->setNamespace('_console') + ->setMetadata('host', \gethostname()) + ->setMetadata('project', 'console') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + $dbForConsole->ping(); + break; // leave the do-while if successful + } catch (\Throwable $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: ' . $e->getMessage()); + } + sleep($sleep); + } + } while ($attempts < $max); + + Console::success('[Setup] - Server database init started...'); + + try { + Console::success('[Setup] - Creating database: appwrite...'); + $dbForConsole->create(); + } catch (\Throwable $e) { + Console::success('[Setup] - Skip: metadata table already exists'); + return true; + } + + if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) { + $audit = new Audit($dbForConsole, $authorization); + $audit->setup(); + } + + if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { + $abuse = new TimeLimit("", 0, 1, $dbForConsole, $authorization); + $abuse->setup(); + } + + /** @var array $collections */ + $collections = Config::getParam('collections', []); + $consoleCollections = $collections['console']; + foreach ($consoleCollections as $key => $collection) { + if (($collection['$collection'] ?? '') !== Database::METADATA) { + continue; + } + if (!$dbForConsole->getCollection($key)->isEmpty()) { + continue; + } + + Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); + + $attributes = []; + $indexes = []; + + foreach ($collection['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]); + } + + foreach ($collection['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); + } + + $dbForConsole->createCollection($key, $attributes, $indexes); + } + + if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) { + Console::success('[Setup] - Creating default bucket...'); + $dbForConsole->createDocument('buckets', new Document([ + '$id' => ID::custom('default'), + '$collection' => ID::custom('buckets'), + 'name' => 'Default', + 'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB + 'allowedFileExtensions' => [], + 'enabled' => true, + 'compression' => 'gzip', + 'encryption' => true, + 'antivirus' => true, + 'fileSecurity' => true, + '$permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'search' => 'buckets Default', + ])); + + $bucket = $dbForConsole->getDocument('buckets', 'default'); + + Console::success('[Setup] - Creating files collection for default bucket...'); + + $files = $collections['buckets']['files'] ?? []; + if (empty($files)) { + throw new Exception('Files collection is not configured.'); + } + + $attributes = []; + $indexes = []; + + foreach ($files['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]); + } + + foreach ($files['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); + } + + $dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); + } + + $connections->reclaim(); + + Console::success('[Setup] - Server database init completed...'); + Console::success('Server started successfully'); } catch (\Throwable $e) { - Console::success('[Setup] - Skip: metadata table already exists'); - return true; + Console::warning('Database not ready: ' . $e->getMessage()); + exit(1); } - - if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) { - $audit = new Audit($dbForConsole, $authorization); - $audit->setup(); - } - - if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { - $abuse = new TimeLimit("", 0, 1, $dbForConsole, $authorization); - $abuse->setup(); - } - - /** @var array $collections */ - $collections = Config::getParam('collections', []); - $consoleCollections = $collections['console']; - foreach ($consoleCollections as $key => $collection) { - if (($collection['$collection'] ?? '') !== Database::METADATA) { - continue; - } - if (!$dbForConsole->getCollection($key)->isEmpty()) { - continue; - } - - Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); - - $attributes = []; - $indexes = []; - - foreach ($collection['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]); - } - - foreach ($collection['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } - - $dbForConsole->createCollection($key, $attributes, $indexes); - } - - if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) { - Console::success('[Setup] - Creating default bucket...'); - $dbForConsole->createDocument('buckets', new Document([ - '$id' => ID::custom('default'), - '$collection' => ID::custom('buckets'), - 'name' => 'Default', - 'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB - 'allowedFileExtensions' => [], - 'enabled' => true, - 'compression' => 'gzip', - 'encryption' => true, - 'antivirus' => true, - 'fileSecurity' => true, - '$permissions' => [ - Permission::create(Role::any()), - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'search' => 'buckets Default', - ])); - - $bucket = $dbForConsole->getDocument('buckets', 'default'); - - Console::success('[Setup] - Creating files collection for default bucket...'); - - $files = $collections['buckets']['files'] ?? []; - if (empty($files)) { - throw new Exception('Files collection is not configured.'); - } - - $attributes = []; - $indexes = []; - - foreach ($files['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]); - } - - foreach ($files['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } - - $dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); - } - - $connections->reclaim(); - - Console::success('[Setup] - Server database init completed...'); - Console::success('Server started successfully'); }); Http::init() From 13eb3bccd0f4dcbdc67f7786d0e932a65ebaaa62 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 15 Apr 2024 12:36:42 +0200 Subject: [PATCH 035/195] Fixed users tests --- app/controllers/api/messaging.php | 88 +++++++++++++++---------------- app/controllers/general.php | 2 +- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 02550d3af3..d98f4ee283 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -848,8 +848,8 @@ Http::get('/v1/messaging/providers') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') ->inject('response') - ->inject('auth') - ->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $auth) { + ->inject('authorization') + ->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $authorization) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -870,7 +870,7 @@ Http::get('/v1/messaging/providers') if ($cursor) { $providerId = $cursor->getValue(); - $cursorDocument = $auth->skip(fn () => $dbForProject->getDocument('providers', $providerId)); + $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Provider '{$providerId}' for the 'cursor' value not found."); @@ -902,8 +902,8 @@ Http::get('/v1/messaging/providers/:providerId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('auth') - ->action(function (string $providerId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { + ->inject('authorization') + ->action(function (string $providerId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { $provider = $dbForProject->getDocument('providers', $providerId); if ($provider->isEmpty()) { @@ -920,7 +920,7 @@ Http::get('/v1/messaging/providers/:providerId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $auth); + $audit = new Audit($dbForProject, $authorization); $resource = 'provider/' . $providerId; $logs = $audit->getLogsByResource($resource, $limit, $offset); $output = []; @@ -1984,8 +1984,8 @@ Http::get('/v1/messaging/topics') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') ->inject('response') - ->inject('auth') - ->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $auth) { + ->inject('authorization') + ->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $authorization) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -2006,7 +2006,7 @@ Http::get('/v1/messaging/topics') if ($cursor) { $topicId = $cursor->getValue(); - $cursorDocument = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Topic '{$topicId}' for the 'cursor' value not found."); @@ -2038,8 +2038,8 @@ Http::get('/v1/messaging/topics/:topicId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('auth') - ->action(function (string $topicId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { + ->inject('authorization') + ->action(function (string $topicId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { $topic = $dbForProject->getDocument('topics', $topicId); if ($topic->isEmpty()) { @@ -2056,7 +2056,7 @@ Http::get('/v1/messaging/topics/:topicId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $auth); + $audit = new Audit($dbForProject, $authorization); $resource = 'topic/' . $topicId; $logs = $audit->getLogsByResource($resource, $limit, $offset); @@ -2240,11 +2240,11 @@ Http::post('/v1/messaging/topics/:topicId/subscribers') ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') - ->inject('auth') - ->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response, Authorization $auth) { + ->inject('authorization') + ->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response, Authorization $authorization) { $subscriberId = $subscriberId == 'unique()' ? ID::unique() : $subscriberId; - $topic = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -2256,13 +2256,13 @@ Http::post('/v1/messaging/topics/:topicId/subscribers') throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription()); } - $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); } - $user = $auth->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); + $user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); $subscriber = new Document([ '$id' => $subscriberId, @@ -2295,7 +2295,7 @@ Http::post('/v1/messaging/topics/:topicId/subscribers') default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE), }; - $auth->skip(fn () => $dbForProject->increaseDocumentAttribute( + $authorization->skip(fn () => $dbForProject->increaseDocumentAttribute( 'topics', $topicId, $totalAttribute, @@ -2333,8 +2333,8 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') ->inject('response') - ->inject('auth') - ->action(function (string $topicId, array $queries, string $search, Database $dbForProject, Response $response, Authorization $auth) { + ->inject('authorization') + ->action(function (string $topicId, array $queries, string $search, Database $dbForProject, Response $response, Authorization $authorization) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -2345,7 +2345,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') $queries[] = Query::search('search', $search); } - $topic = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -2363,7 +2363,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') if ($cursor) { $subscriberId = $cursor->getValue(); - $cursorDocument = $auth->skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId)); + $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Subscriber '{$subscriberId}' for the 'cursor' value not found."); @@ -2374,10 +2374,10 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') $subscribers = $dbForProject->find('subscribers', $queries); - $subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject, $auth) { - return function () use ($subscriber, $dbForProject, $auth) { - $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); - $user = $auth->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); + $subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject, $authorization) { + return function () use ($subscriber, $dbForProject, $authorization) { + $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); + $user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); return $subscriber ->setAttribute('target', $target) @@ -2409,8 +2409,8 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('auth') - ->action(function (string $subscriberId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { + ->inject('authorization') + ->action(function (string $subscriberId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { $subscriber = $dbForProject->getDocument('subscribers', $subscriberId); if ($subscriber->isEmpty()) { @@ -2427,7 +2427,7 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $auth); + $audit = new Audit($dbForProject, $authorization); $resource = 'subscriber/' . $subscriberId; $logs = $audit->getLogsByResource($resource, $limit, $offset); @@ -2497,9 +2497,9 @@ Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->param('subscriberId', '', new UID(), 'Subscriber ID.') ->inject('dbForProject') ->inject('response') - ->inject('auth') - ->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response, Authorization $auth) { - $topic = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + ->inject('authorization') + ->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response, Authorization $authorization) { + $topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -2511,8 +2511,8 @@ Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') throw new Exception(Exception::SUBSCRIBER_NOT_FOUND); } - $target = $auth->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); - $user = $auth->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); + $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); + $user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); $subscriber ->setAttribute('target', $target) @@ -2541,9 +2541,9 @@ Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') - ->inject('auth') - ->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response, Authorization $auth) { - $topic = $auth->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + ->inject('authorization') + ->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response, Authorization $authorization) { + $topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -2566,7 +2566,7 @@ Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE), }; - $auth->skip(fn () => $dbForProject->decreaseDocumentAttribute( + $authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute( 'topics', $topicId, $totalAttribute, @@ -3043,8 +3043,8 @@ Http::get('/v1/messaging/messages') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') ->inject('response') - ->inject('auth') - ->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $auth) { + ->inject('authorization') + ->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $authorization) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -3065,7 +3065,7 @@ Http::get('/v1/messaging/messages') if ($cursor) { $messageId = $cursor->getValue(); - $cursorDocument = $auth->skip(fn () => $dbForProject->getDocument('messages', $messageId)); + $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('messages', $messageId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Message '{$messageId}' for the 'cursor' value not found."); @@ -3097,8 +3097,8 @@ Http::get('/v1/messaging/messages/:messageId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('auth') - ->action(function (string $messageId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { + ->inject('authorization') + ->action(function (string $messageId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { $message = $dbForProject->getDocument('messages', $messageId); if ($message->isEmpty()) { @@ -3115,7 +3115,7 @@ Http::get('/v1/messaging/messages/:messageId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $auth); + $audit = new Audit($dbForProject, $authorization); $resource = 'message/' . $messageId; $logs = $audit->getLogsByResource($resource, $limit, $offset); diff --git a/app/controllers/general.php b/app/controllers/general.php index b81238a19b..531bc36f8c 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -898,7 +898,7 @@ include_once 'api/avatars.php'; //include_once 'api/graphql.php'; //include_once 'api/health.php'; include_once 'api/locale.php'; -//include_once 'api/messaging.php'; +include_once 'api/messaging.php'; //include_once 'api/migrations.php'; include_once 'api/projects.php'; //include_once 'api/proxy.php'; From 07a830e9a6ff66bcd9bd96bf47a6e759c1c43530 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 15 Apr 2024 22:03:07 +0200 Subject: [PATCH 036/195] Work on the main worker --- app/http.php | 5 --- app/init2.php | 5 ++- app/worker.php | 104 +++++++++++++++++++++++++++++-------------------- 3 files changed, 65 insertions(+), 49 deletions(-) diff --git a/app/http.php b/app/http.php index c4b5bcd779..2b983358e1 100644 --- a/app/http.php +++ b/app/http.php @@ -1,9 +1,4 @@ $register); +Server::setResource('register', fn () => $gloabl); Server::setResource('connections', function () { return new Connections(); }); -Server::setResource('dbForConsole', function (Cache $cache, Registry $register, Authorization $auth, Connections $connections) { - $pools = $register->get('pools'); - $connection = $pools->get('console')->pop(); - $connections->add($connection); - $database = $connection->getResource(); +Server::setResource('pools', function () use ($gloabl) { + return $gloabl->get('pools'); +}); - $adapter = new Database($database, $cache); - $adapter->setAuthorization($auth); - $adapter->setNamespace('_console'); +Server::setResource('dbForConsole', function (Cache $cache, array $pools, Authorization $auth, Connections $connections) { + $pool = $pools['pools-console-main']['pool']; + $dsn = $pools['pools-console-main']['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); - return $adapter; -}, ['cache', 'register', 'auth', 'connections']); + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); + + $database = new Database($adapter, $cache); + $database->setAuthorization($auth); + $database->setNamespace('_console'); + + return $database; +}, ['cache', 'pools', 'auth', 'connections']); Server::setResource('project', function (Message $message, Database $dbForConsole) { $payload = $message->getPayload() ?? []; @@ -72,21 +88,31 @@ Server::setResource('project', function (Message $message, Database $dbForConsol return $dbForConsole->getDocument('projects', $project->getId()); }, ['message', 'dbForConsole']); -Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForConsole, Authorization $auth, Connections $connections) { +Server::setResource('dbForProject', function (Cache $cache, array $pools, Message $message, Document $project, Database $dbForConsole, Authorization $auth, Connections $connections) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } - $pools = $register->get('pools'); - $connection = $pools->get($project->getAttribute('database'))->pop(); - $connections->add($connection); - $database = $connection->getResource(); + $pool = $pools['pools-database-'.$project->getAttribute('database')]['pool']; + $dsn = $pools['pools-database-'.$project->getAttribute('database')]['dsn']; - $adapter = new Database($database, $cache); - $adapter->setAuthorization($auth); - $adapter->setNamespace('_' . $project->getInternalId()); - return $adapter; -}, ['cache', 'register', 'message', 'project', 'dbForConsole', 'auth', 'connections']); + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); + + $database = new Database($adapter, $cache); + + $database = new Database($adapter, $cache); + $database->setAuthorization($auth); + $database->setNamespace('_' . $project->getInternalId()); + return $database; +}, ['cache', 'pools', 'message', 'project', 'dbForConsole', 'auth', 'connections']); Server::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth, Connections $connections) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools @@ -131,19 +157,9 @@ Server::setResource('executionRetention', function () { return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); }); -Server::setResource('cache', function (Registry $register, Connections $connections) { - $pools = $register->get('pools'); - $list = Config::getParam('pools-cache', []); - $adapters = []; - - foreach ($list as $value) { - $connection = $pools->get($value)->pop(); - $connections->add($connection); - $adapters[] = $connection->getResource(); - } - - return new Cache(new Sharding($adapters)); -}, ['register', 'connections']); +Server::setResource('cache', function () { + return new Cache(new None()); +}, []); Server::setResource('log', fn () => new Log()); @@ -155,10 +171,13 @@ Server::setResource('queueForUsageDump', function (Connection $queue) { return new UsageDump($queue); }, ['queue']); -Server::setResource('queue', function (Group $pools, Connections $connections) { - $connection = $pools->get('queue')->pop(); - $connections->add($connection); - return $connection->getResource(); +Server::setResource('queue', function (array $pools, Connections $connections) { + $pool = $pools['pools-queue-main']['pool']; + $dsn = $pools['pools-queue-main']['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + return new Redis($dsn->getHost(), $dsn->getPort()); }, ['pools', 'connections']); Server::setResource('queueForDatabase', function (Connection $queue) { @@ -235,7 +254,6 @@ Server::setResource('deviceForLocalFiles', function (Document $project) { Server::setResource('auth', fn () => new Authorization()); -$pools = $register->get('pools'); $platform = new Appwrite(); $args = $_SERVER['argv']; @@ -267,7 +285,7 @@ try { */ $platform->init(Service::TYPE_WORKER, [ 'workersNum' => System::getEnv('_APP_WORKERS_NUM', 1), - 'connection' => $pools->get('queue')->pop()->getResource(), + 'connection' => $global->get('pools')['pools-queue-main']['pool']->get(), 'workerName' => strtolower($workerName) ?? null, 'queueName' => $queueName ]); From 6d6f22788db803c81b06e479077f2fb642000e04 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 15 Apr 2024 23:38:36 +0200 Subject: [PATCH 037/195] Eanble compression --- app/http.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/http.php b/app/http.php index 2b983358e1..679f4ee1a6 100644 --- a/app/http.php +++ b/app/http.php @@ -31,8 +31,8 @@ $payloadSize = 6 * (1024 * 1024); // 6MB $server = new Server('0.0.0.0', '80', [ 'open_http2_protocol' => true, - // 'http_compression' => true, - // 'http_compression_level' => 6, + 'http_compression' => true, + 'http_compression_level' => 6, 'package_max_length' => $payloadSize, 'buffer_output_size' => $payloadSize, From 479f20dabfec0468879888fbc797e4fb65887241 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 07:00:17 +0200 Subject: [PATCH 038/195] Fixed some workers --- app/http.php | 1 - app/worker.php | 560 +++++++++++++++++++++++++++++++++---------------- composer.json | 9 +- composer.lock | 482 ++++++++++++++++++++++-------------------- 4 files changed, 640 insertions(+), 412 deletions(-) diff --git a/app/http.php b/app/http.php index 679f4ee1a6..7bb5ba06be 100644 --- a/app/http.php +++ b/app/http.php @@ -22,7 +22,6 @@ use Utopia\Http\Adapter\Swoole\Server; use Utopia\Http\Http; use Utopia\System\System; -//require_once __DIR__ . '/init.php'; require_once __DIR__ . '/init2.php'; require_once __DIR__ . '/controllers/general.php'; diff --git a/app/worker.php b/app/worker.php index a5d29c295a..c8ba9e7741 100644 --- a/app/worker.php +++ b/app/worker.php @@ -30,6 +30,7 @@ use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; +use Utopia\DI\Dependency; use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Platform\Service; @@ -38,221 +39,417 @@ use Utopia\Queue\Connection; use Utopia\Queue\Connection\Redis; use Utopia\Queue\Message; use Utopia\Queue\Server; +use Utopia\Queue\Worker; use Utopia\Registry\Registry; use Utopia\Storage\Device\Local; use Utopia\System\System; -global $gloabl; +global $gloabl, $container; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -Server::setResource('register', fn () => $gloabl); +$register = new Dependency(); +$register + ->setName('register') + ->setCallback(fn () => $global); +$container->set($register); -Server::setResource('connections', function () { - return new Connections(); -}); +$connections = new Dependency(); +$connections + ->setName('connections') + ->setCallback(function () { + return new Connections(); + }); +$container->set($connections); -Server::setResource('pools', function () use ($gloabl) { - return $gloabl->get('pools'); -}); +$pools = new Dependency(); +$pools + ->setName('pools') + ->inject('register') + ->setCallback(function ($register) { + return $register->get('pools'); + }); +$container->set($pools); -Server::setResource('dbForConsole', function (Cache $cache, array $pools, Authorization $auth, Connections $connections) { - $pool = $pools['pools-console-main']['pool']; - $dsn = $pools['pools-console-main']['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); +$dbForConsole = new Dependency(); +$dbForConsole + ->setName('dbForConsole') + ->inject('cache') + ->inject('pools') + ->inject('auth') + ->inject('connections') + ->setCallback(function (Cache $cache, array $pools, Authorization $auth, Connections $connections) { + $pool = $pools['pools-console-main']['pool']; + $dsn = $pools['pools-console-main']['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; - $adapter->setDatabase($dsn->getPath()); + $adapter->setDatabase($dsn->getPath()); - $database = new Database($adapter, $cache); - $database->setAuthorization($auth); - $database->setNamespace('_console'); + $database = new Database($adapter, $cache); + $database->setAuthorization($auth); + $database->setNamespace('_console'); - return $database; -}, ['cache', 'pools', 'auth', 'connections']); + return $database; + }); +$container->set($dbForConsole); -Server::setResource('project', function (Message $message, Database $dbForConsole) { - $payload = $message->getPayload() ?? []; - $project = new Document($payload['project'] ?? []); - - if ($project->getId() === 'console') { - return $project; - } - - return $dbForConsole->getDocument('projects', $project->getId()); -}, ['message', 'dbForConsole']); - -Server::setResource('dbForProject', function (Cache $cache, array $pools, Message $message, Document $project, Database $dbForConsole, Authorization $auth, Connections $connections) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - $pool = $pools['pools-database-'.$project->getAttribute('database')]['pool']; - $dsn = $pools['pools-database-'.$project->getAttribute('database')]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - - $database = new Database($adapter, $cache); - - $database = new Database($adapter, $cache); - $database->setAuthorization($auth); - $database->setNamespace('_' . $project->getInternalId()); - return $database; -}, ['cache', 'pools', 'message', 'project', 'dbForConsole', 'auth', 'connections']); - -Server::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth, Connections $connections) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections): Database { +$dbForProject = new Dependency(); +$dbForProject + ->setName('dbForProject') + ->inject('cache') + ->inject('pools') + ->inject('message') + ->inject('project') + ->inject('dbForConsole') + ->inject('auth') + ->inject('connections') + ->setCallback(function (Cache $cache, array $pools, Message $message, Document $project, Database $dbForConsole, Authorization $auth, Connections $connections) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } - - $databaseName = $project->getAttribute('database'); - - if (isset($databases[$databaseName])) { - $database = $databases[$databaseName]; - $database->setNamespace('_' . $project->getInternalId()); - return $database; - } - - $connection = $pools->get($databaseName)->pop(); - $connections->add($connection); - $dbAdapter = $connection->getResource(); - - $database = new Database($dbAdapter, $cache); + + $pool = $pools['pools-database-'.$project->getAttribute('database')]['pool']; + $dsn = $pools['pools-database-'.$project->getAttribute('database')]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); + + $database = new Database($adapter, $cache); $database->setAuthorization($auth); - - $databases[$databaseName] = $database; - $database->setNamespace('_' . $project->getInternalId()); - return $database; - }; -}, ['pools', 'dbForConsole', 'cache', 'auth', 'connections']); + }); +$container->set($dbForProject); -Server::setResource('abuseRetention', function () { - return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400)); -}); +$project = new Dependency(); +$project + ->setName('project') + ->inject('message') + ->inject('dbForConsole') + ->setCallback(function (Message $message, Database $dbForConsole) { + $payload = $message->getPayload() ?? []; + $project = new Document($payload['project'] ?? []); + + if ($project->getId() === 'console') { + return $project; + } + + return $dbForConsole->getDocument('projects', $project->getId()); + }); +$container->set($project); -Server::setResource('auditRetention', function () { - return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600)); -}); +$getProjectDB = new Dependency(); +$getProjectDB + ->setName('getProjectDB') + ->inject('pools') + ->inject('dbForConsole') + ->inject('cache') + ->inject('auth') + ->inject('connections') + ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $auth, Connections $connections) { + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections): Database { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } -Server::setResource('executionRetention', function () { - return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); -}); + $databaseName = $project->getAttribute('database'); -Server::setResource('cache', function () { - return new Cache(new None()); -}, []); + $pool = $pools['pools-database-'.$databaseName]['pool']; + $dsn = $pools['pools-database-'.$databaseName]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; -Server::setResource('log', fn () => new Log()); + $database = new Database($adapter, $cache); + $database->setAuthorization($auth); + $database->setNamespace('_' . $project->getInternalId()); -Server::setResource('queueForUsage', function (Connection $queue) { - return new Usage($queue); -}, ['queue']); + return $database; + }; + }); +$container->set($getProjectDB); -Server::setResource('queueForUsageDump', function (Connection $queue) { - return new UsageDump($queue); -}, ['queue']); +// Worker::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth, Connections $connections) { +// $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools -Server::setResource('queue', function (array $pools, Connections $connections) { - $pool = $pools['pools-queue-main']['pool']; - $dsn = $pools['pools-queue-main']['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); +// return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections): Database { +// if ($project->isEmpty() || $project->getId() === 'console') { +// return $dbForConsole; +// } - return new Redis($dsn->getHost(), $dsn->getPort()); -}, ['pools', 'connections']); +// $databaseName = $project->getAttribute('database'); -Server::setResource('queueForDatabase', function (Connection $queue) { - return new EventDatabase($queue); -}, ['queue']); +// if (isset($databases[$databaseName])) { +// $database = $databases[$databaseName]; +// $database->setNamespace('_' . $project->getInternalId()); +// return $database; +// } -Server::setResource('queueForMessaging', function (Connection $queue) { - return new Messaging($queue); -}, ['queue']); +// $connection = $pools->get($databaseName)->pop(); +// $connections->add($connection); +// $dbAdapter = $connection->getResource(); -Server::setResource('queueForMails', function (Connection $queue) { - return new Mail($queue); -}, ['queue']); +// $database = new Database($dbAdapter, $cache); +// $database->setAuthorization($auth); -Server::setResource('queueForBuilds', function (Connection $queue) { - return new Build($queue); -}, ['queue']); +// $databases[$databaseName] = $database; -Server::setResource('queueForDeletes', function (Connection $queue) { - return new Delete($queue); -}, ['queue']); +// $database->setNamespace('_' . $project->getInternalId()); -Server::setResource('queueForEvents', function (Connection $queue) { - return new Event($queue); -}, ['queue']); +// return $database; +// }; +// }, ['pools', 'dbForConsole', 'cache', 'auth', 'connections']); -Server::setResource('queueForAudits', function (Connection $queue) { - return new Audit($queue); -}, ['queue']); +$abuseRetention = new Dependency(); +$abuseRetention + ->setName('abuseRetention') + ->setCallback(function () { + return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400)); + }); +$container->set($abuseRetention); -Server::setResource('queueForFunctions', function (Connection $queue) { - return new Func($queue); -}, ['queue']); +$auditRetention = new Dependency(); +$auditRetention + ->setName('auditRetention') + ->setCallback(function () { + return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600)); + }); +$container->set($auditRetention); -Server::setResource('queueForCertificates', function (Connection $queue) { - return new Certificate($queue); -}, ['queue']); +$executionRetention = new Dependency(); +$executionRetention + ->setName('executionRetention') + ->setCallback(function () { + return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); + }); +$container->set($executionRetention); -Server::setResource('queueForMigrations', function (Connection $queue) { - return new Migration($queue); -}, ['queue']); +$cache = new Dependency(); +$cache + ->setName('cache') + ->setCallback(function () { + return new Cache(new None()); + }); +$container->set($cache); -Server::setResource('queueForHamster', function (Connection $queue) { - return new Hamster($queue); -}, ['queue']); +$log = new Dependency(); +$log + ->setName('log') + ->setCallback(fn () => new Log()); +$container->set($log); -Server::setResource('logger', function (Registry $register) { - return $register->get('logger'); -}, ['register']); +$queue = new Dependency(); +$queue + ->setName('queue') + ->inject('pools') + ->inject('connections') + ->setCallback(function (array $pools, Connections $connections) { + $pool = $pools['pools-queue-main']['pool']; + $dsn = $pools['pools-queue-main']['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + return new Redis($dsn->getHost(), $dsn->getPort()); + }); +$container->set($queue); -Server::setResource('pools', function (Registry $register) { - return $register->get('pools'); -}, ['register']); +$queueForMessaging = new Dependency(); +$queueForMessaging + ->setName('queueForMessaging') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Messaging($queue); + }); +$container->set($queueForMessaging); -Server::setResource('deviceForFunctions', function (Document $project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); -}, ['project']); +$queueForMails = new Dependency(); +$queueForMails + ->setName('queueForMails') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Mail($queue); + }); +$container->set($queueForMails); -Server::setResource('deviceForFiles', function (Document $project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); -}, ['project']); +$queueForBuilds = new Dependency(); +$queueForBuilds + ->setName('queueForBuilds') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Build($queue); + }); +$container->set($queueForBuilds); -Server::setResource('deviceForBuilds', function (Document $project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); -}, ['project']); +$queueForDatabase = new Dependency(); +$queueForDatabase + ->setName('queueForDatabase') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new EventDatabase($queue); + }); +$container->set($queueForDatabase); -Server::setResource('deviceForCache', function (Document $project) { - return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId()); -}, ['project']); +$queueForDeletes = new Dependency(); +$queueForDeletes + ->setName('queueForDeletes') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Delete($queue); + }); +$container->set($queueForDeletes); -Server::setResource('deviceForLocalFiles', function (Document $project) { - return new Local(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); -}, ['project']); +$queueForEvents = new Dependency(); +$queueForEvents + ->setName('queueForEvents') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Event($queue); + }); +$container->set($queueForEvents); -Server::setResource('auth', fn () => new Authorization()); +$queueForAudits = new Dependency(); +$queueForAudits + ->setName('queueForAudits') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Audit($queue); + }); +$container->set($queueForAudits); + +$queueForFunctions = new Dependency(); +$queueForFunctions + ->setName('queueForFunctions') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Func($queue); + }); +$container->set($queueForFunctions); + +$queueForUsage = new Dependency(); +$queueForUsage + ->setName('queueForUsage') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Usage($queue); + }); +$container->set($queueForUsage); + +$queueForUsageDump = new Dependency(); +$queueForUsageDump + ->setName('queueForUsageDump') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new UsageDump($queue); + }); + +$container->set($queueForUsageDump); + +$queueForCertificates = new Dependency(); +$queueForCertificates + ->setName('queueForCertificates') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Certificate($queue); + }); +$container->set($queueForCertificates); + +$queueForMigrations = new Dependency(); +$queueForMigrations + ->setName('queueForMigrations') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Migration($queue); + }); +$container->set($queueForMigrations); + +$queueForHamster = new Dependency(); +$queueForHamster + ->setName('queueForHamster') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Hamster($queue); + }); +$container->set($queueForHamster); + +$logger = new Dependency(); +$logger + ->setName('logger') + ->inject('register') + ->setCallback(function (Registry $register) { + return $register->get('logger'); + }); +$container->set($logger); + +$deviceForFunctions = new Dependency(); +$deviceForFunctions + ->setName('deviceForFunctions') + ->inject('project') + ->setCallback(function (Document $project) { + return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); + }); +$container->set($deviceForFunctions); + +$deviceForFiles = new Dependency(); +$deviceForFiles + ->setName('deviceForFiles') + ->inject('project') + ->setCallback(function (Document $project) { + return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); + }); +$container->set($deviceForFiles); + +$deviceForBuilds = new Dependency(); +$deviceForBuilds + ->setName('deviceForBuilds') + ->inject('project') + ->setCallback(function (Document $project) { + return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); + }); +$container->set($deviceForBuilds); + +$deviceForCache = new Dependency(); +$deviceForCache + ->setName('deviceForCache') + ->inject('project') + ->setCallback(function (Document $project) { + return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId()); + }); +$container->set($deviceForCache); + +$deviceForLocalFiles = new Dependency(); +$deviceForLocalFiles + ->setName('deviceForLocalFiles') + ->inject('project') + ->setCallback(function (Document $project) { + return new Local(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); + }); + +$container->set($deviceForLocalFiles); + +$auth = new Dependency(); +$auth + ->setName('auth') + ->setCallback(fn () => new Authorization()); +$container->set($auth); $platform = new Appwrite(); $args = $_SERVER['argv']; @@ -277,6 +474,14 @@ if (\str_starts_with($workerName, 'databases')) { } try { + + $connection = new Connection\Redis( + System::getEnv('_APP_REDIS_HOST', 'redis'), + System::getEnv('_APP_REDIS_PORT', '6379'), + System::getEnv('_APP_REDIS_USER', ''), + System::getEnv('_APP_REDIS_PASS', '') + ); + /** * Any worker can be configured with the following env vars: * - _APP_WORKERS_NUM The total number of worker processes @@ -285,7 +490,7 @@ try { */ $platform->init(Service::TYPE_WORKER, [ 'workersNum' => System::getEnv('_APP_WORKERS_NUM', 1), - 'connection' => $global->get('pools')['pools-queue-main']['pool']->get(), + 'connection' => $connection, 'workerName' => strtolower($workerName) ?? null, 'queueName' => $queueName ]); @@ -293,24 +498,19 @@ try { Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); } -$worker = $platform->getWorker(); - -$worker - ->init() +Worker::init() ->inject('auth') ->action(function (Authorization $auth) { $auth->disable(); }); -$worker - ->shutdown() +Worker::shutdown() ->inject('connections') ->action(function (Connections $connections) { $connections->reclaim(); }); -$worker - ->error() +Worker::error() ->inject('error') ->inject('logger') ->inject('log') @@ -354,9 +554,7 @@ $worker Console::error('[Error] Line: ' . $error->getLine()); }); -$worker->workerStart() - ->action(function () use ($workerName) { - Console::info("Worker $workerName started"); - }); - -$worker->start(); +$platform + ->getWorker() + ->setContainer($container) + ->start(); \ No newline at end of file diff --git a/composer.json b/composer.json index e6d1308220..2ab6741e1e 100644 --- a/composer.json +++ b/composer.json @@ -4,6 +4,7 @@ "description": "End to end backend server for frontend and mobile apps.", "type": "project", "license": "BSD-3-Clause", + "minimum-stability": "dev", "authors": [ { "name": "Eldad Fux", @@ -52,7 +53,6 @@ "utopia-php/cli": "0.17.*", "utopia-php/config": "0.2.*", "utopia-php/database": "dev-feat-framework-v2 as 0.49.99", - "utopia-php/di": "dev-main", "utopia-php/domains": "dev-feat-framework-v2 as 0.5.99", "utopia-php/dsn": "0.2.*", "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99", @@ -63,9 +63,8 @@ "utopia-php/migration": "0.4.*", "utopia-php/orchestration": "dev-feat-framework-v2 as 0.9.99", "utopia-php/platform": "dev-feat-framework-v2 as 0.5.99", - "utopia-php/pools": "dev-feat-coroutine-support as 0.4.99", "utopia-php/view": "0.2.*", - "utopia-php/queue": "dev-feat-framework-v2-v2 as 0.7.99", + "utopia-php/queue": "dev-feat-coroutine-and-di as 0.7.99", "utopia-php/registry": "0.5.*", "utopia-php/storage": "dev-feat-framework-v2-v2 as 0.18.99", "utopia-php/system": "0.8.*", @@ -102,6 +101,10 @@ { "type": "vcs", "url": "https://github.com/utopia-php/di" + }, + { + "type": "vcs", + "url": "https://github.com/utopia-php/servers" } ] } diff --git a/composer.lock b/composer.lock index d7ab08b87e..17e9c54bae 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": "42c5379dff348d3525688891a96d1e85", + "content-hash": "4ed66c480296d93fe656835801654fed", "packages": [ { "name": "adhocore/jwt", @@ -210,16 +210,16 @@ }, { "name": "beberlei/assert", - "version": "v3.3.2", + "version": "v3.x-dev", "source": { "type": "git", "url": "https://github.com/beberlei/assert.git", - "reference": "cb70015c04be1baee6f5f5c953703347c0ac1655" + "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/beberlei/assert/zipball/cb70015c04be1baee6f5f5c953703347c0ac1655", - "reference": "cb70015c04be1baee6f5f5c953703347c0ac1655", + "url": "https://api.github.com/repos/beberlei/assert/zipball/d63a6943fc4fd1a2aedb65994e3548715105abcf", + "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf", "shasum": "" }, "require": { @@ -227,13 +227,12 @@ "ext-json": "*", "ext-mbstring": "*", "ext-simplexml": "*", - "php": "^7.0 || ^8.0" + "php": "^7" }, "require-dev": { "friendsofphp/php-cs-fixer": "*", - "phpstan/phpstan": "*", - "phpunit/phpunit": ">=6.0.0", - "yoast/phpunit-polyfills": "^0.1.0" + "phpstan/phpstan-shim": "*", + "phpunit/phpunit": ">=6.0.0 <8" }, "suggest": { "ext-intl": "Needed to allow Assertion::count(), Assertion::isCountable(), Assertion::minCount(), and Assertion::maxCount() to operate on ResourceBundles" @@ -271,9 +270,9 @@ ], "support": { "issues": "https://github.com/beberlei/assert/issues", - "source": "https://github.com/beberlei/assert/tree/v3.3.2" + "source": "https://github.com/beberlei/assert/tree/v3" }, - "time": "2021-12-16T21:41:27+00:00" + "time": "2019-12-19T17:51:41+00:00" }, { "name": "chillerlan/php-qrcode", @@ -482,16 +481,16 @@ }, { "name": "jean85/pretty-package-versions", - "version": "2.0.6", + "version": "2.x-dev", "source": { "type": "git", "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" + "reference": "4ea62fbb39a29d65ef6cda413158baa7f1d98550" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4ea62fbb39a29d65ef6cda413158baa7f1d98550", + "reference": "4ea62fbb39a29d65ef6cda413158baa7f1d98550", "shasum": "" }, "require": { @@ -503,8 +502,9 @@ "jean85/composer-provided-replaced-stub-package": "^1.0", "phpstan/phpstan": "^1.4", "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" + "vimeo/psalm": "^4.3 || ^5.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -535,9 +535,9 @@ ], "support": { "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.x" }, - "time": "2024-03-08T09:58:59+00:00" + "time": "2024-04-08T08:58:14+00:00" }, { "name": "league/csv", @@ -970,7 +970,7 @@ }, { "name": "spomky-labs/otphp", - "version": "v10.0.3", + "version": "v10.0.x-dev", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/otphp.git", @@ -999,6 +999,7 @@ "phpunit/phpunit": "^8.0", "thecodingmachine/phpstan-safe-rule": "^1.0 || ^2.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -1045,21 +1046,22 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.29.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" + "reference": "7d191eb4022901cd3d91a816ec5464ca3a08a8aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7d191eb4022901cd3d91a816ec5464ca3a08a8aa", + "reference": "7d191eb4022901cd3d91a816ec5464ca3a08a8aa", "shasum": "" }, "require": { "php": ">=7.1" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -1105,7 +1107,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php80/tree/1.x" }, "funding": [ { @@ -1121,7 +1123,7 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-04-19T06:31:17+00:00" }, { "name": "thecodingmachine/safe", @@ -1268,12 +1270,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "55b34b581c957fc98f912943d94dcdc7079f191e" + "reference": "a2292d71da901ea13129d56f89876626ba92adf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/55b34b581c957fc98f912943d94dcdc7079f191e", - "reference": "55b34b581c957fc98f912943d94dcdc7079f191e", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/a2292d71da901ea13129d56f89876626ba92adf0", + "reference": "a2292d71da901ea13129d56f89876626ba92adf0", "shasum": "" }, "require": { @@ -1309,7 +1311,7 @@ "issues": "https://github.com/utopia-php/abuse/issues", "source": "https://github.com/utopia-php/abuse/tree/feat-framework-v2" }, - "time": "2024-03-08T11:27:58+00:00" + "time": "2024-04-18T17:04:17+00:00" }, { "name": "utopia-php/analytics", @@ -1363,12 +1365,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "59f88d71f9d93603393aeda368a975b10b8ddb17" + "reference": "49c2a113277bfa0d7d1774c150de2d2fa07349e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/59f88d71f9d93603393aeda368a975b10b8ddb17", - "reference": "59f88d71f9d93603393aeda368a975b10b8ddb17", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/49c2a113277bfa0d7d1774c150de2d2fa07349e7", + "reference": "49c2a113277bfa0d7d1774c150de2d2fa07349e7", "shasum": "" }, "require": { @@ -1402,7 +1404,7 @@ "issues": "https://github.com/utopia-php/audit/issues", "source": "https://github.com/utopia-php/audit/tree/feat-framework-v2" }, - "time": "2024-03-08T11:29:31+00:00" + "time": "2024-04-18T17:02:14+00:00" }, { "name": "utopia-php/cache", @@ -1560,12 +1562,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "bdd9140e40c77faadb0cf2f5050b466c34eef673" + "reference": "0d7b914c7770a5c488f104a36147d91db3b71368" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/bdd9140e40c77faadb0cf2f5050b466c34eef673", - "reference": "bdd9140e40c77faadb0cf2f5050b466c34eef673", + "url": "https://api.github.com/repos/utopia-php/database/zipball/0d7b914c7770a5c488f104a36147d91db3b71368", + "reference": "0d7b914c7770a5c488f104a36147d91db3b71368", "shasum": "" }, "require": { @@ -1609,7 +1611,7 @@ "issues": "https://github.com/utopia-php/database/issues", "source": "https://github.com/utopia-php/database/tree/feat-framework-v2" }, - "time": "2024-03-07T16:55:44+00:00" + "time": "2024-04-19T14:37:34+00:00" }, { "name": "utopia-php/di", @@ -1617,12 +1619,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/di.git", - "reference": "0bb7af5693bc131f4d2ce34d3f732d41e6637679" + "reference": "fb3c45b268018b87dcbbf87ad943ced9eea3bbe2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/di/zipball/0bb7af5693bc131f4d2ce34d3f732d41e6637679", - "reference": "0bb7af5693bc131f4d2ce34d3f732d41e6637679", + "url": "https://api.github.com/repos/utopia-php/di/zipball/fb3c45b268018b87dcbbf87ad943ced9eea3bbe2", + "reference": "fb3c45b268018b87dcbbf87ad943ced9eea3bbe2", "shasum": "" }, "require": { @@ -1671,7 +1673,7 @@ "source": "https://github.com/utopia-php/di/tree/main", "issues": "https://github.com/utopia-php/di/issues" }, - "time": "2024-04-08T22:41:41+00:00" + "time": "2024-04-18T20:06:02+00:00" }, { "name": "utopia-php/domains", @@ -1825,12 +1827,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "d600ae234780e083c600ab62a8348b7ea506cd1d" + "reference": "62c2b5b2b1e0e598cd67d79143e4dd210c44d499" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/d600ae234780e083c600ab62a8348b7ea506cd1d", - "reference": "d600ae234780e083c600ab62a8348b7ea506cd1d", + "url": "https://api.github.com/repos/utopia-php/http/zipball/62c2b5b2b1e0e598cd67d79143e4dd210c44d499", + "reference": "62c2b5b2b1e0e598cd67d79143e4dd210c44d499", "shasum": "" }, "require": { @@ -1867,7 +1869,7 @@ "issues": "https://github.com/utopia-php/http/issues", "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-04-14T16:41:37+00:00" + "time": "2024-04-18T20:53:06+00:00" }, { "name": "utopia-php/image", @@ -1970,7 +1972,7 @@ }, { "name": "utopia-php/logger", - "version": "0.3.2", + "version": "0.3.x-dev", "source": { "type": "git", "url": "https://github.com/utopia-php/logger.git", @@ -2017,7 +2019,7 @@ ], "support": { "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.3.2" + "source": "https://github.com/utopia-php/logger/tree/0.3.x" }, "time": "2023-11-22T14:45:43+00:00" }, @@ -2235,12 +2237,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "88711a2992ff0edbf196cdf1f48b5614e1e423f7" + "reference": "e6f2f281b2ad962211b1c6f88650d0230f615a3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/88711a2992ff0edbf196cdf1f48b5614e1e423f7", - "reference": "88711a2992ff0edbf196cdf1f48b5614e1e423f7", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/e6f2f281b2ad962211b1c6f88650d0230f615a3a", + "reference": "e6f2f281b2ad962211b1c6f88650d0230f615a3a", "shasum": "" }, "require": { @@ -2249,7 +2251,7 @@ "php": ">=8.0", "utopia-php/cli": "0.17.*", "utopia-php/framework": "0.34.*", - "utopia-php/queue": "dev-feat-framework-v2-v2 as 0.7.99" + "utopia-php/queue": "dev-feat-coroutine-and-di as 0.7.99" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2277,77 +2279,27 @@ "issues": "https://github.com/utopia-php/platform/issues", "source": "https://github.com/utopia-php/platform/tree/feat-framework-v2" }, - "time": "2024-03-07T15:44:09+00:00" - }, - { - "name": "utopia-php/pools", - "version": "dev-feat-coroutine-support", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/pools.git", - "reference": "ada61e5b86191644e779ea2c71cd7bf172e94fca" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/ada61e5b86191644e779ea2c71cd7bf172e94fca", - "reference": "ada61e5b86191644e779ea2c71cd7bf172e94fca", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Pools\\": "src/Pools" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Team Appwrite", - "email": "team@appwrite.io" - } - ], - "description": "A simple library to manage connection pools", - "keywords": [ - "framework", - "php", - "pools", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/feat-coroutine-support" - }, - "time": "2024-04-10T21:34:22+00:00" + "time": "2024-04-21T18:38:38+00:00" }, { "name": "utopia-php/queue", - "version": "dev-feat-framework-v2-v2", + "version": "dev-feat-coroutine-and-di", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "0e9ba1b32169d64a78909fd77891db4d64344a63" + "reference": "309796b08891eac135540c241d8943dd42eccc9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/0e9ba1b32169d64a78909fd77891db4d64344a63", - "reference": "0e9ba1b32169d64a78909fd77891db4d64344a63", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/309796b08891eac135540c241d8943dd42eccc9e", + "reference": "309796b08891eac135540c241d8943dd42eccc9e", "shasum": "" }, "require": { "php": ">=8.0", "utopia-php/cli": "0.17.*", - "utopia-php/framework": "0.34.*" + "utopia-php/di": "dev-main", + "utopia-php/servers": "dev-dev" }, "require-dev": { "laravel/pint": "^0.2.3", @@ -2357,6 +2309,7 @@ "workerman/workerman": "^4.0" }, "suggest": { + "ext-redis": "Needed to support Redis connections", "ext-swoole": "Needed to support Swoole.", "workerman/workerman": "Needed to support Workerman." }, @@ -2387,9 +2340,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/feat-framework-v2-v2" + "source": "https://github.com/utopia-php/queue/tree/feat-coroutine-and-di" }, - "time": "2024-03-08T09:29:41+00:00" + "time": "2024-04-21T18:59:04+00:00" }, { "name": "utopia-php/registry", @@ -2443,6 +2396,77 @@ }, "time": "2021-03-10T10:45:22+00:00" }, + { + "name": "utopia-php/servers", + "version": "dev-dev", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/servers.git", + "reference": "20bb7ab93c21d0ae37bc309d41b70ddf2a78a6fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/20bb7ab93c21d0ae37bc309d41b70ddf2a78a6fd", + "reference": "20bb7ab93c21d0ae37bc309d41b70ddf2a78a6fd", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/di": "dev-main" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Servers\\": "src/Servers" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\E2E\\": "tests/Servers/Unit" + } + }, + "scripts": { + "test": [ + "phpunit" + ], + "analyse": [ + "vendor/bin/phpstan analyse" + ], + "format": [ + "vendor/bin/pint" + ], + "lint": [ + "vendor/bin/pint --test" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A base library for building Utopia style servers.", + "keywords": [ + "framework", + "php", + "servers", + "upf", + "utopia" + ], + "support": { + "source": "https://github.com/utopia-php/servers/tree/dev", + "issues": "https://github.com/utopia-php/servers/issues" + }, + "time": "2024-04-21T18:53:43+00:00" + }, { "name": "utopia-php/storage", "version": "dev-feat-framework-v2-v2", @@ -2831,16 +2855,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.37.9", + "version": "0.37.12", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "ad80d80e18f0cda981e1bddbf0841a805632caa0" + "reference": "882881934e8014b2135590e7ea5f402ab4513c38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/ad80d80e18f0cda981e1bddbf0841a805632caa0", - "reference": "ad80d80e18f0cda981e1bddbf0841a805632caa0", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/882881934e8014b2135590e7ea5f402ab4513c38", + "reference": "882881934e8014b2135590e7ea5f402ab4513c38", "shasum": "" }, "require": { @@ -2876,13 +2900,13 @@ "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.37.9" + "source": "https://github.com/appwrite/sdk-generator/tree/0.37.12" }, - "time": "2024-03-24T05:40:55+00:00" + "time": "2024-04-17T19:14:43+00:00" }, { "name": "doctrine/deprecations", - "version": "1.1.3", + "version": "1.1.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", @@ -2909,6 +2933,7 @@ "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -2929,29 +2954,29 @@ }, { "name": "doctrine/instantiator", - "version": "1.5.0", + "version": "1.5.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "reference": "12be2483e1f0e850b353e26869e4e6c038459501" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/12be2483e1f0e850b353e26869e4e6c038459501", + "reference": "12be2483e1f0e850b353e26869e4e6c038459501", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", + "doctrine/coding-standard": "^9 || ^12", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.16 || ^1", "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", @@ -2979,7 +3004,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "source": "https://github.com/doctrine/instantiator/tree/1.5.x" }, "funding": [ { @@ -2995,7 +3020,7 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2023-12-09T14:16:53+00:00" }, { "name": "laravel/pint", @@ -3189,16 +3214,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "2f5294676c802a62b0549f6bc8983f14294ce369" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/2f5294676c802a62b0549f6bc8983f14294ce369", + "reference": "2f5294676c802a62b0549f6bc8983f14294ce369", "shasum": "" }, "require": { @@ -3206,13 +3231,15 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, + "default-branch": true, "type": "library", "autoload": { "files": [ @@ -3236,7 +3263,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.x" }, "funding": [ { @@ -3244,20 +3271,20 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2024-02-10T11:10:03+00:00" }, { "name": "nikic/php-parser", - "version": "v5.0.2", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + "reference": "c5ee33df86c06b3278c670f64273b1ba768a0744" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c5ee33df86c06b3278c670f64273b1ba768a0744", + "reference": "c5ee33df86c06b3278c670f64273b1ba768a0744", "shasum": "" }, "require": { @@ -3268,8 +3295,9 @@ }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, + "default-branch": true, "bin": [ "bin/php-parse" ], @@ -3300,13 +3328,13 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + "source": "https://github.com/nikic/PHP-Parser/tree/master" }, - "time": "2024-03-05T20:51:40+00:00" + "time": "2024-04-19T12:04:10+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.4", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", @@ -3326,6 +3354,7 @@ "phar-io/version": "^3.0.1", "php": "^7.2 || ^8.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3424,25 +3453,25 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "reference": "a0eeab580cbdf4414fef6978732510a36ed0a9d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/a0eeab580cbdf4414fef6978732510a36ed0a9d6", + "reference": "a0eeab580cbdf4414fef6978732510a36ed0a9d6", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-2.x": "2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -3471,13 +3500,13 @@ ], "support": { "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master" }, - "time": "2020-06-27T09:03:43+00:00" + "time": "2021-06-25T13:47:51+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.4.0", + "version": "5.x-dev", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", @@ -3507,6 +3536,7 @@ "phpunit/phpunit": "^9.5", "vimeo/psalm": "^5.13" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3541,23 +3571,23 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "reference": "483fb7fe262607b0a5ec32f99bdc42e2212b22fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/483fb7fe262607b0a5ec32f99bdc42e2212b22fe", + "reference": "483fb7fe262607b0a5ec32f99bdc42e2212b22fe", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "phpstan/phpdoc-parser": "^1.18" }, "require-dev": { "ext-tokenizer": "*", @@ -3569,6 +3599,7 @@ "rector/rector": "^0.13.9", "vimeo/psalm": "^4.25" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3593,22 +3624,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.x" }, - "time": "2024-02-23T11:10:43+00:00" + "time": "2024-03-29T20:21:22+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.19.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87" + "reference": "f9e07be0992e7bf1cad210829055b99318df142f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/67a759e7d8746d501c41536ba40cd9c0a07d6a87", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f9e07be0992e7bf1cad210829055b99318df142f", + "reference": "f9e07be0992e7bf1cad210829055b99318df142f", "shasum": "" }, "require": { @@ -3623,6 +3654,7 @@ "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3662,9 +3694,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.19.0" + "source": "https://github.com/phpspec/prophecy/tree/master" }, - "time": "2024-02-29T11:52:51+00:00" + "time": "2024-03-29T09:25:04+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -3715,16 +3747,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.8.11", + "version": "1.8.x-dev", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "46e223dd68a620da18855c23046ddb00940b4014" + "reference": "b20042710baa0d9c07636cc66d4c400f03f1477a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014", - "reference": "46e223dd68a620da18855c23046ddb00940b4014", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b20042710baa0d9c07636cc66d4c400f03f1477a", + "reference": "b20042710baa0d9c07636cc66d4c400f03f1477a", "shasum": "" }, "require": { @@ -3754,7 +3786,7 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.8.11" + "source": "https://github.com/phpstan/phpstan/tree/1.8.x" }, "funding": [ { @@ -3770,20 +3802,20 @@ "type": "tidelift" } ], - "time": "2022-10-24T15:45:13+00:00" + "time": "2022-10-29T12:56:57+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.31", + "version": "9.2.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + "reference": "3352293d9e91513d5508c415835014881b420218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/3352293d9e91513d5508c415835014881b420218", + "reference": "3352293d9e91513d5508c415835014881b420218", "shasum": "" }, "require": { @@ -3840,7 +3872,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2" }, "funding": [ { @@ -3848,20 +3880,20 @@ "type": "github" } ], - "time": "2024-03-02T06:37:42+00:00" + "time": "2024-03-22T05:16:32+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "3.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "38b24367e1b340aa78b96d7cab042942d917bb84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/38b24367e1b340aa78b96d7cab042942d917bb84", + "reference": "38b24367e1b340aa78b96d7cab042942d917bb84", "shasum": "" }, "require": { @@ -3900,7 +3932,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0" }, "funding": [ { @@ -3908,7 +3940,7 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2022-02-11T16:23:04+00:00" }, { "name": "phpunit/php-invoker", @@ -4196,7 +4228,7 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", @@ -4211,6 +4243,7 @@ "require": { "php": ">=8.0.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4246,7 +4279,7 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.2", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", @@ -4413,16 +4446,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "b247957a1c8dc81a671770f74b479c0a78a818f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b247957a1c8dc81a671770f74b479c0a78a818f1", + "reference": "b247957a1c8dc81a671770f74b479c0a78a818f1", "shasum": "" }, "require": { @@ -4475,7 +4508,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0" }, "funding": [ { @@ -4483,11 +4516,11 @@ "type": "github" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2022-09-14T12:46:14+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.3", + "version": "2.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", @@ -4544,7 +4577,7 @@ }, { "name": "sebastian/diff", - "version": "4.0.6", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", @@ -4610,7 +4643,7 @@ }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "5.1.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", @@ -4661,7 +4694,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1" }, "funding": [ { @@ -4673,7 +4706,7 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", @@ -4750,7 +4783,7 @@ }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", @@ -4814,7 +4847,7 @@ }, { "name": "sebastian/lines-of-code", - "version": "1.0.4", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", @@ -4983,7 +5016,7 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", @@ -5046,16 +5079,16 @@ }, { "name": "sebastian/resource-operations", - "version": "3.0.4", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + "reference": "ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25", + "reference": "ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25", "shasum": "" }, "require": { @@ -5064,6 +5097,7 @@ "require-dev": { "phpunit/phpunit": "^9.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -5088,7 +5122,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/main" }, "funding": [ { @@ -5096,11 +5130,11 @@ "type": "github" } ], - "time": "2024-03-14T16:00:52+00:00" + "time": "2024-03-14T18:47:08+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "3.2.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", @@ -5144,7 +5178,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "source": "https://github.com/sebastianbergmann/type/tree/3.2" }, "funding": [ { @@ -5156,7 +5190,7 @@ }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "3.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", @@ -5241,16 +5275,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.29.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + "reference": "c9e59dec962d38cf2e0e4c61c4a1a1312f4dd7fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c9e59dec962d38cf2e0e4c61c4a1a1312f4dd7fe", + "reference": "c9e59dec962d38cf2e0e4c61c4a1a1312f4dd7fe", "shasum": "" }, "require": { @@ -5262,6 +5296,7 @@ "suggest": { "ext-ctype": "For best performance" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -5300,7 +5335,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/1.x" }, "funding": [ { @@ -5316,20 +5351,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-04-19T06:31:17+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + "reference": "e642fbe7a7b73cdb05460555289a9057bfd6ead6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e642fbe7a7b73cdb05460555289a9057bfd6ead6", + "reference": "e642fbe7a7b73cdb05460555289a9057bfd6ead6", "shasum": "" }, "require": { @@ -5341,6 +5376,7 @@ "suggest": { "ext-mbstring": "For best performance" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -5380,7 +5416,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/1.x" }, "funding": [ { @@ -5396,7 +5432,7 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-04-19T06:31:17+00:00" }, { "name": "textalk/websocket", @@ -5619,15 +5655,9 @@ "alias": "0.5.99", "alias_normalized": "0.5.99.0" }, - { - "package": "utopia-php/pools", - "version": "dev-feat-coroutine-support", - "alias": "0.4.99", - "alias_normalized": "0.4.99.0" - }, { "package": "utopia-php/queue", - "version": "dev-feat-framework-v2-v2", + "version": "dev-feat-coroutine-and-di", "alias": "0.7.99", "alias_normalized": "0.7.99.0" }, @@ -5638,18 +5668,16 @@ "alias_normalized": "0.18.99.0" } ], - "minimum-stability": "stable", + "minimum-stability": "dev", "stability-flags": { "utopia-php/abuse": 20, "utopia-php/analytics": 20, "utopia-php/audit": 20, "utopia-php/database": 20, - "utopia-php/di": 20, "utopia-php/domains": 20, "utopia-php/framework": 20, "utopia-php/orchestration": 20, "utopia-php/platform": 20, - "utopia-php/pools": 20, "utopia-php/queue": 20, "utopia-php/storage": 20 }, From 74dc9957f291257442e0ef541441ccec1357afc3 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 07:25:29 +0200 Subject: [PATCH 039/195] Fixed console test --- app/controllers/general.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/controllers/general.php b/app/controllers/general.php index 531bc36f8c..1f7ad48837 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -893,6 +893,7 @@ include_once 'shared/api.php'; include_once 'shared/api/auth.php'; include_once 'api/account.php'; include_once 'api/avatars.php'; +include_once 'api/console.php'; //include_once 'api/database.php'; //include_once 'api/functions.php'; //include_once 'api/graphql.php'; @@ -906,3 +907,5 @@ include_once 'api/storage.php'; include_once 'api/teams.php'; include_once 'api/users.php'; //include_once 'api/vcs.php'; +include_once 'web/console.php'; +include_once 'web/home.php'; From aff328c6604715ff409d67fcb87c481f5b399d2b Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 07:51:24 +0200 Subject: [PATCH 040/195] Fixed test --- app/controllers/api/health.php | 23 +++++++++++++++++------ app/worker.php | 1 + 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index dd106f7d7b..391b286cca 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -6,6 +6,8 @@ use Appwrite\Extend\Exception; use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Response; use Utopia\Config\Config; +use Utopia\Database\Adapter\MariaDB; +use Utopia\Database\Adapter\MySQL; use Utopia\Database\Document; use Utopia\Domains\Validator\PublicDomain; use Utopia\Http\Http; @@ -72,21 +74,30 @@ Http::get('/v1/health/db') ->inject('response') ->inject('pools') ->inject('connections') - ->action(function (Response $response, Group $pools, Connections $connections) { + ->action(function (Response $response, array $pools, Connections $connections) { $output = []; $configs = [ - 'Console.DB' => Config::getParam('pools-console'), - 'Projects.DB' => Config::getParam('pools-database'), + 'console' => Config::getParam('pools-console'), + 'database' => Config::getParam('pools-database'), ]; foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $connection = $pools->get($database)->pop(); - $connections->add($connection); - $adapter = $connection->getResource(); + + $pool = $pools['pools-'.$key.'-'.$database]['pool']; + $dsn = $pools['pools-'.$key.'-'.$database]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + $adapter->setDatabase($dsn->getPath()); $checkStart = \microtime(true); diff --git a/app/worker.php b/app/worker.php index c8ba9e7741..9917164c20 100644 --- a/app/worker.php +++ b/app/worker.php @@ -178,6 +178,7 @@ $getProjectDB 'mysql' => new MySQL($connection), default => null }; + $adapter->setDatabase($dsn->getPath()); $database = new Database($adapter, $cache); $database->setAuthorization($auth); From b97cf78d9dda16d38d91cc2eb2472ee0bad41190 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 07:51:35 +0200 Subject: [PATCH 041/195] Enabled more controllers --- app/controllers/general.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 1f7ad48837..974940cf18 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -897,12 +897,12 @@ include_once 'api/console.php'; //include_once 'api/database.php'; //include_once 'api/functions.php'; //include_once 'api/graphql.php'; -//include_once 'api/health.php'; +include_once 'api/health.php'; include_once 'api/locale.php'; include_once 'api/messaging.php'; //include_once 'api/migrations.php'; include_once 'api/projects.php'; -//include_once 'api/proxy.php'; +include_once 'api/proxy.php'; include_once 'api/storage.php'; include_once 'api/teams.php'; include_once 'api/users.php'; From 3b70ae4d9fe5fa8f9c561dff2fb80c4862a0fdab Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 07:56:36 +0200 Subject: [PATCH 042/195] Fixed formatting --- app/cli.php | 3 +- app/controllers/api/graphql.php | 4 +- app/controllers/api/health.php | 2 +- app/controllers/api/projects.php | 2 +- app/controllers/general.php | 8 +- app/controllers/shared/api.php | 2 +- app/http.php | 2 +- app/init.php | 2 +- app/init/constants.php | 2 +- app/init/database/filters.php | 2 +- app/init/database/formats.php | 2 +- app/init/locale.php | 2 +- app/init2.php | 94 ++++++++------------ app/worker.php | 21 ++--- src/Appwrite/Platform/Tasks/Hamster.php | 1 - src/Appwrite/Platform/Tasks/ScheduleBase.php | 1 - src/Appwrite/Platform/Tasks/Specs.php | 12 +-- src/Appwrite/Platform/Tasks/Version.php | 1 - src/Appwrite/Platform/Workers/Hamster.php | 5 +- src/Appwrite/Platform/Workers/Messaging.php | 1 - src/Appwrite/Platform/Workers/Webhooks.php | 1 - src/Appwrite/Utopia/Response.php | 2 +- tests/unit/Event/EventTest.php | 2 +- tests/unit/Migration/MigrationTest.php | 2 +- tests/unit/Usage/StatsTest.php | 1 - 25 files changed, 74 insertions(+), 103 deletions(-) diff --git a/app/cli.php b/app/cli.php index 55713406e2..9194edd5e1 100644 --- a/app/cli.php +++ b/app/cli.php @@ -17,7 +17,6 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\Http\Http; use Utopia\Logger\Log; use Utopia\Platform\Service; use Utopia\Pools\Group; @@ -180,7 +179,7 @@ CLI::setResource('logError', function (Registry $register) { $log->setAction($action); $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; - + $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); $responseCode = $logger->addLog($log); diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index 19958ae309..7104ba5da2 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -16,10 +16,10 @@ use GraphQL\Validator\Rules\QueryDepth; use Swoole\Coroutine\WaitGroup; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\System\System; +use Utopia\Http\Http; use Utopia\Http\Validator\JSON; use Utopia\Http\Validator\Text; -use Utopia\Http\Http; +use Utopia\System\System; Http::init() ->groups(['graphql']) diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 391b286cca..1c61313ec3 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -89,7 +89,7 @@ Http::get('/v1/health/db') $pool = $pools['pools-'.$key.'-'.$database]['pool']; $dsn = $pools['pools-'.$key.'-'.$database]['dsn']; - + $connection = $pool->get(); $connections->add($connection, $pool); $adapter = match ($dsn->getScheme()) { diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index c1889a5696..6e4415117e 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -198,7 +198,7 @@ Http::post('/v1/projects') $dbForProject->setAuthorization($authorization); $dbForProject->setNamespace("_{$project->getInternalId()}"); $dbForProject->create(); - + $audit = new Audit($dbForProject, $authorization); $audit->setup(); $adapter = new TimeLimit('', 0, 1, $dbForProject, $authorization); diff --git a/app/controllers/general.php b/app/controllers/general.php index 974940cf18..b4fade7b0b 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -23,7 +23,6 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\DI\Dependency; use Utopia\Domains\Domain; use Utopia\Http\Http; use Utopia\Http\Route; @@ -33,7 +32,6 @@ use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Logger\Log\User; use Utopia\Logger\Logger; -use Utopia\Registry\Registry; use Utopia\System\System; Config::setParam('domainVerification', false); @@ -383,7 +381,8 @@ Http::init() Document $console, Document $project, Database $dbForConsole, - Locale $locale, array $localeCodes, + Locale $locale, + array $localeCodes, array $clients, /** * @disregard P1009 Undefined type @@ -392,7 +391,8 @@ Http::init() // Usage $queueForUsage, // Event $queueForEvents, Certificate $queueForCertificates, - Authorization $authorization) { + Authorization $authorization + ) { /* * Appwrite Router */ diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 39926b7182..66dc93f029 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -27,10 +27,10 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization\Input; -use Utopia\System\System; use Utopia\Http\Http; use Utopia\Http\Route; use Utopia\Http\Validator\WhiteList; +use Utopia\System\System; $parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) { preg_match_all('/{(.*?)}/', $label, $matches); diff --git a/app/http.php b/app/http.php index 7bb5ba06be..ec65d7275f 100644 --- a/app/http.php +++ b/app/http.php @@ -245,4 +245,4 @@ Http::init() $authorization->addRole(Role::any()->toString()); }); -$http->start(); \ No newline at end of file +$http->start(); diff --git a/app/init.php b/app/init.php index f10faf5b70..eca05713f0 100644 --- a/app/init.php +++ b/app/init.php @@ -824,7 +824,7 @@ $register->set('pools', function () { //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(); diff --git a/app/init/constants.php b/app/init/constants.php index 615e299776..39eb4a7ad2 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -156,4 +156,4 @@ const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions'; const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute'; const METRIC_NETWORK_REQUESTS = 'network.requests'; const METRIC_NETWORK_INBOUND = 'network.inbound'; -const METRIC_NETWORK_OUTBOUND = 'network.outbound'; \ No newline at end of file +const METRIC_NETWORK_OUTBOUND = 'network.outbound'; diff --git a/app/init/database/filters.php b/app/init/database/filters.php index 84f5adf534..1564d25294 100644 --- a/app/init/database/filters.php +++ b/app/init/database/filters.php @@ -395,4 +395,4 @@ Database::addFilter( function (mixed $value) { return $value; } -); \ No newline at end of file +); diff --git a/app/init/database/formats.php b/app/init/database/formats.php index f1bf54a456..88e46655ac 100644 --- a/app/init/database/formats.php +++ b/app/init/database/formats.php @@ -40,4 +40,4 @@ 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); \ No newline at end of file +}, Database::VAR_FLOAT); diff --git a/app/init/locale.php b/app/init/locale.php index 7f0c267b0e..122dc89692 100644 --- a/app/init/locale.php +++ b/app/init/locale.php @@ -20,4 +20,4 @@ foreach ($locales as $locale) { } Locale::setLanguageFromJSON($code, $path); -} \ No newline at end of file +} diff --git a/app/init2.php b/app/init2.php index 7970441c32..6e123e796d 100644 --- a/app/init2.php +++ b/app/init2.php @@ -29,37 +29,27 @@ use Appwrite\Event\Messaging; use Appwrite\Event\Migration; use Appwrite\Event\Usage; use Appwrite\Extend\Exception; -use Appwrite\GraphQL\Promises\Adapter\Swoole; -use Appwrite\GraphQL\Schema; use Appwrite\Hooks\Hooks; -use Appwrite\Network\Validator\Email; use Appwrite\Network\Validator\Origin; -use Appwrite\OpenSSL\OpenSSL; use Appwrite\URL\URL; use Appwrite\Utopia\Queue\Connections; use MaxMind\Db\Reader; -use PHPMailer\PHPMailer\PHPMailer; -use Swoole\Coroutine; use Swoole\Database\PDOConfig; use Swoole\Database\PDOPool; use Swoole\Database\PDOProxy; use Swoole\Database\RedisConfig; use Swoole\Database\RedisPool; -use Utopia\Cache\Adapter\Redis as RedisCache; -use Utopia\Cache\Adapter\Sharding; +use Utopia\Cache\Adapter\None; 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\DI\Container; use Utopia\DI\Dependency; use Utopia\Domains\Validator\PublicDomain; use Utopia\DSN\DSN; @@ -67,14 +57,9 @@ use Utopia\Http\Http; use Utopia\Http\Request; use Utopia\Http\Response; use Utopia\Http\Validator\Hostname; -use Utopia\Http\Validator\IP; -use Utopia\Http\Validator\Range; -use Utopia\Http\Validator\WhiteList; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Logger\Logger; -use Utopia\Pools\Group; -use Utopia\Pools\Pool; use Utopia\Queue; use Utopia\Queue\Connection; use Utopia\Registry\Registry; @@ -87,9 +72,6 @@ use Utopia\Storage\Device\S3; use Utopia\Storage\Device\Wasabi; use Utopia\Storage\Storage; use Utopia\System\System; -use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; -use Utopia\Cache\Adapter\None; -use Utopia\DI\Container; Http::setMode(System::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION)); @@ -210,7 +192,7 @@ $global->set('geodb', function () { }); $global->set('hooks', function () { - return new Hooks(); + return new Hooks(); }); $global->set('pools', (function () { @@ -279,11 +261,11 @@ $global->set('pools', (function () { $name = ($multipe) ? $dsn[0] : 'main'; $config[] = $name; $dsn = $dsn[1] ?? ''; - + if (empty($dsn)) { throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); } - + $dsn = new DSN($dsn); $dsnHost = $dsn->getHost(); $dsnPort = $dsn->getPort(); @@ -306,7 +288,8 @@ $global->set('pools', (function () { switch ($dsnScheme) { case 'mysql': case 'mariadb': - $pool = new PDOPool((new PDOConfig) + $pool = new PDOPool( + (new PDOConfig()) ->withHost($dsnHost) ->withPort($dsnPort) ->withDbName($dsnDatabase) @@ -327,17 +310,16 @@ $global->set('pools', (function () { ); break; case 'redis': - $pool = new RedisPool((new RedisConfig) + $pool = new RedisPool((new RedisConfig()) ->withHost($dsnHost) ->withPort((int)$dsnPort) - ->withAuth($dsnPass) - , $poolSize); + ->withAuth($dsnPass), $poolSize); break; default: throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid scheme"); } - + $pools['pools-' . $key . '-' . $name] = [ 'pool' => $pool, 'dsn' => $dsn, @@ -381,32 +363,32 @@ $user $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'); @@ -415,10 +397,10 @@ $user $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([]); @@ -432,14 +414,14 @@ $user } else { $user = $dbForConsole->getDocument('users', Auth::$unique); } - + if ( $user->isEmpty() // Check a document has been found in the DB || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) ) { // Validate user has valid login token $user = new Document([]); } - + if (APP_MODE_ADMIN === $mode) { if ($user->find('teamId', $project->getAttribute('teamId'), 'memberships')) { $authorization->setDefaultStatus(false); // Cancel security segmentation for admin users. @@ -447,34 +429,34 @@ $user $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', 900, 10); // Instantiate with key, algo, maxAge and leeway. - + try { $payload = $jwt->decode($authJWT); } catch (JWTException $error) { throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); } - + $jwtUserId = $payload['userId'] ?? ''; $jwtSessionId = $payload['sessionId'] ?? ''; - + if ($jwtUserId && $jwtSessionId) { $user = $dbForProject->getDocument('users', $jwtUserId); } - + if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token $user = new Document([]); } } - + // Adds logs to database queries $dbForProject->setMetadata('user', $user->getId()); $dbForConsole->setMetadata('user', $user->getId()); - + return $user; }); $container->set($user); @@ -589,14 +571,14 @@ $dbForProject ->inject('dbForConsole') ->inject('connections') ->inject('authorization') - ->setCallback(function(array $pools, Document $project, Cache $cache, Database $dbForConsole, Connections $connections, Authorization $authorization) { + ->setCallback(function (array $pools, Document $project, Cache $cache, Database $dbForConsole, Connections $connections, Authorization $authorization) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } $pool = $pools['pools-database-'.$project->getAttribute('database')]['pool']; $dsn = $pools['pools-database-'.$project->getAttribute('database')]['dsn']; - + $connection = $pool->get(); $connections->add($connection, $pool); $adapter = match ($dsn->getScheme()) { @@ -606,16 +588,16 @@ $dbForProject }; $adapter->setDatabase($dsn->getPath()); - + $database = new Database($adapter, $cache); $database->setAuthorization($authorization); - + $database ->setNamespace('_' . $project->getInternalId()) ->setMetadata('host', \gethostname()) ->setMetadata('project', $project->getId()) ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - + return $database; }); $container->set($dbForProject); @@ -627,7 +609,7 @@ $dbForConsole ->inject('cache') ->inject('authorization') ->inject('connections') - ->setCallback(function(array $pools, Cache $cache, Authorization $authorization, Connections $connections): Database { + ->setCallback(function (array $pools, Cache $cache, Authorization $authorization, Connections $connections): Database { $pool = $pools['pools-console-main']['pool']; $dsn = $pools['pools-console-main']['dsn']; $connection = $pool->get(); @@ -886,7 +868,7 @@ $clients '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) { @@ -901,7 +883,7 @@ $clients 'hostname' => $hostname, ], Document::SET_TYPE_APPEND); } - + /** * Get All verified client URLs for both console and current projects * + Filter for duplicated entries @@ -913,7 +895,7 @@ $clients fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ); - + $clients = \array_unique( \array_merge( $clientsConsole, @@ -926,7 +908,7 @@ $clients ) ) ); - + return $clients; }); $container->set($clients); diff --git a/app/worker.php b/app/worker.php index 9917164c20..66538a939d 100644 --- a/app/worker.php +++ b/app/worker.php @@ -20,10 +20,8 @@ use Appwrite\Platform\Appwrite; use Appwrite\Utopia\Queue\Connections; use Swoole\Runtime; use Utopia\Cache\Adapter\None; -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\Database; @@ -38,7 +36,6 @@ use Utopia\Pools\Group; use Utopia\Queue\Connection; use Utopia\Queue\Connection\Redis; use Utopia\Queue\Message; -use Utopia\Queue\Server; use Utopia\Queue\Worker; use Utopia\Registry\Registry; use Utopia\Storage\Device\Local; @@ -114,10 +111,10 @@ $dbForProject if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } - + $pool = $pools['pools-database-'.$project->getAttribute('database')]['pool']; $dsn = $pools['pools-database-'.$project->getAttribute('database')]['dsn']; - + $connection = $pool->get(); $connections->add($connection, $pool); $adapter = match ($dsn->getScheme()) { @@ -125,9 +122,9 @@ $dbForProject 'mysql' => new MySQL($connection), default => null }; - + $adapter->setDatabase($dsn->getPath()); - + $database = new Database($adapter, $cache); $database->setAuthorization($auth); $database->setNamespace('_' . $project->getInternalId()); @@ -143,11 +140,11 @@ $project ->setCallback(function (Message $message, Database $dbForConsole) { $payload = $message->getPayload() ?? []; $project = new Document($payload['project'] ?? []); - + if ($project->getId() === 'console') { return $project; } - + return $dbForConsole->getDocument('projects', $project->getId()); }); $container->set($project); @@ -170,7 +167,7 @@ $getProjectDB $pool = $pools['pools-database-'.$databaseName]['pool']; $dsn = $pools['pools-database-'.$databaseName]['dsn']; - + $connection = $pool->get(); $connections->add($connection, $pool); $adapter = match ($dsn->getScheme()) { @@ -268,7 +265,7 @@ $queue $dsn = $pools['pools-queue-main']['dsn']; $connection = $pool->get(); $connections->add($connection, $pool); - + return new Redis($dsn->getHost(), $dsn->getPort()); }); $container->set($queue); @@ -558,4 +555,4 @@ Worker::error() $platform ->getWorker() ->setContainer($container) - ->start(); \ No newline at end of file + ->start(); diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index 1c5cf73c5a..2cfef1a2c9 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -7,7 +7,6 @@ use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; -use Utopia\Http\Http; use Utopia\Platform\Action; use Utopia\System\System; diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index 009f61e3db..a50fbb2403 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -9,7 +9,6 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception; use Utopia\Database\Query; -use Utopia\Http\Http; use Utopia\Platform\Action; use Utopia\Pools\Group; use Utopia\System\System; diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index a24f10960e..44d2b9904c 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -9,8 +9,6 @@ use Appwrite\Utopia\Response; use Exception; use Swoole\Http\Request as SwooleHttpRequest; use Swoole\Http\Response as SwooleHttpResponse; -use Utopia\Http\Adapter\Swoole\Request; -use Utopia\Http\Adapter\Swoole\Response as HttpResponse; use Utopia\Cache\Adapter\None; use Utopia\Cache\Cache; use Utopia\CLI\Console; @@ -18,12 +16,14 @@ use Utopia\Config\Config; use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; use Utopia\Http\Adapter\FPM\Server; +use Utopia\Http\Adapter\Swoole\Request; +use Utopia\Http\Adapter\Swoole\Response as HttpResponse; use Utopia\Http\Http; +use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; use Utopia\Platform\Action; use Utopia\Registry\Registry; use Utopia\System\System; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; class Specs extends Action { @@ -45,11 +45,11 @@ class Specs extends Action public function action(string $version, string $mode, Registry $register): void { $appRoutes = Http::getRoutes(); - $response = new Response(new HttpResponse(new SwooleHttpResponse)); + $response = new Response(new HttpResponse(new SwooleHttpResponse())); $mocks = ($mode === 'mocks'); // Mock dependencies - Http::setResource('request', fn () => new Request(new SwooleHttpRequest)); + Http::setResource('request', fn () => new Request(new SwooleHttpRequest())); Http::setResource('response', fn () => $response); Http::setResource('dbForConsole', fn () => new Database(new MySQL(''), new Cache(new None()))); Http::setResource('dbForProject', fn () => new Database(new MySQL(''), new Cache(new None()))); diff --git a/src/Appwrite/Platform/Tasks/Version.php b/src/Appwrite/Platform/Tasks/Version.php index c0febe3a31..25e2d13c65 100644 --- a/src/Appwrite/Platform/Tasks/Version.php +++ b/src/Appwrite/Platform/Tasks/Version.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Tasks; use Utopia\CLI\Console; -use Utopia\Http\Http; use Utopia\Platform\Action; use Utopia\System\System; diff --git a/src/Appwrite/Platform/Workers/Hamster.php b/src/Appwrite/Platform/Workers/Hamster.php index 6ae28b25cd..70bfc6e298 100644 --- a/src/Appwrite/Platform/Workers/Hamster.php +++ b/src/Appwrite/Platform/Workers/Hamster.php @@ -14,7 +14,6 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\Http\Http; use Utopia\Platform\Action; use Utopia\Pools\Group; use Utopia\Queue\Message; @@ -72,11 +71,11 @@ class Hamster extends Action public function action(Message $message, Group $pools, Cache $cache, Database $dbForConsole, Authorization $auth, Connections $connections): void { $token = System::getEnv('_APP_MIXPANEL_TOKEN', ''); - + if (empty($token)) { throw new \Exception('Missing MixPanel Token'); } - + $this->mixpanel = new Mixpanel($token); $payload = $message->getPayload() ?? []; diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index c5e83ee091..c29853d43d 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -12,7 +12,6 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\DSN\DSN; -use Utopia\Http\Http; use Utopia\Logger\Log; use Utopia\Messaging\Adapter\Email as EmailAdapter; use Utopia\Messaging\Adapter\Email\Mailgun; diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index caab96c4f5..d60946f709 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -8,7 +8,6 @@ use Exception; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; -use Utopia\Http\Http; use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 9c7543dc4e..0f23874fc7 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -458,7 +458,7 @@ class Response extends HttpResponse // Tests (keep last) ->setModel(new Mock()); - parent::__construct($response->swoole); + parent::__construct($response->swoole); } /** diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index 0bdc00da62..1990fddd8d 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -11,7 +11,7 @@ use Utopia\Queue; use Utopia\Queue\Client; use Utopia\System\System; -require_once __DIR__ . '/../../../app/init.php'; +//require_once __DIR__ . '/../../../app/init.php'; class EventTest extends TestCase { diff --git a/tests/unit/Migration/MigrationTest.php b/tests/unit/Migration/MigrationTest.php index 536278d55b..8043ec9f94 100644 --- a/tests/unit/Migration/MigrationTest.php +++ b/tests/unit/Migration/MigrationTest.php @@ -36,7 +36,7 @@ abstract class MigrationTest extends TestCase */ public function testMigrationVersions(): void { - require_once __DIR__ . '/../../../app/init.php'; + //require_once __DIR__ . '/../../../app/init.php'; foreach (Migration::$versions as $class) { $this->assertTrue(class_exists('Appwrite\\Migration\\Version\\' . $class)); diff --git a/tests/unit/Usage/StatsTest.php b/tests/unit/Usage/StatsTest.php index 743321397f..534d5beda0 100644 --- a/tests/unit/Usage/StatsTest.php +++ b/tests/unit/Usage/StatsTest.php @@ -5,7 +5,6 @@ namespace Tests\Unit\Usage; use Appwrite\URL\URL as AppwriteURL; use PHPUnit\Framework\TestCase; use Utopia\DSN\DSN; -use Utopia\Http\Http; use Utopia\Queue; use Utopia\Queue\Client; use Utopia\Queue\Connection; From c413a6cab5538749fd0eb5ac1861e80c803d1305 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 09:01:48 +0200 Subject: [PATCH 043/195] Updated database service --- app/controllers/api/databases.php | 356 +++++++++++++++--------------- app/controllers/general.php | 2 +- app/worker.php | 33 +-- 3 files changed, 180 insertions(+), 211 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index b419e235c1..470f3df084 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -75,7 +75,7 @@ use Utopia\Locale\Locale; * @throws ConflictException * @throws Exception */ -function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth): Document +function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): Document { $key = $attribute->getAttribute('key'); $type = $attribute->getAttribute('type', ''); @@ -89,7 +89,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att $default = $attribute->getAttribute('default'); $options = $attribute->getAttribute('options', []); - $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -224,7 +224,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att } function updateAttribute( - Authorization $auth, + Authorization $authorization, string $databaseId, string $collectionId, string $key, @@ -239,7 +239,7 @@ function updateAttribute( array $elements = null, array $options = [], ): Document { - $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -566,8 +566,8 @@ Http::get('/v1/databases/:databaseId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('auth') - ->action(function (string $databaseId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -585,7 +585,7 @@ Http::get('/v1/databases/:databaseId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $auth); + $audit = new Audit($dbForProject); $resource = 'database/' . $databaseId; $logs = $audit->getLogsByResource($resource, $limit, $offset); @@ -753,10 +753,10 @@ Http::post('/v1/databases/:databaseId/collections') ->inject('dbForProject') ->inject('mode') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents, Authorization $authorization) { - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -815,10 +815,10 @@ Http::get('/v1/databases/:databaseId/collections') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('auth') - ->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -878,10 +878,10 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, string $mode, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -915,10 +915,10 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -941,7 +941,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $auth); + $audit = new Audit($dbForProject); $resource = 'database/' . $databaseId . '/collection/' . $collectionId; $logs = $audit->getLogsByResource($resource, $limit, $offset); @@ -1022,10 +1022,10 @@ Http::put('/v1/databases/:databaseId/collections/:collectionId') ->inject('dbForProject') ->inject('mode') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents, Authorization $authorization) { - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1087,10 +1087,10 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId') ->inject('queueForDatabase') ->inject('queueForEvents') ->inject('mode') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode, Authorization $authorization) { - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1149,8 +1149,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/strin ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { // Ensure attribute default is within required size $validator = new Text($size, 0); @@ -1172,7 +1172,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/strin 'default' => $default, 'array' => $array, 'filters' => $filters, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1204,8 +1204,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1215,7 +1215,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_EMAIL, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1248,8 +1248,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum' ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { if (!is_null($default) && !in_array($default, $elements)) { throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements'); } @@ -1263,7 +1263,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum' 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_ENUM, 'formatOptions' => ['elements' => $elements], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1295,8 +1295,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1306,7 +1306,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_IP, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1338,8 +1338,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1349,7 +1349,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_URL, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1383,8 +1383,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integ ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') - ->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, Authorization $auth) { + ->inject('authorization') + ->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, Authorization $authorization) { // Ensure attribute default is within range $min = (is_null($min)) ? PHP_INT_MIN : \intval($min); @@ -1414,7 +1414,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integ 'min' => $min, 'max' => $max, ], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); $formatOptions = $attribute->getAttribute('formatOptions', []); @@ -1455,8 +1455,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') - ->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, Authorization $auth) { + ->inject('authorization') + ->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, Authorization $authorization) { // Ensure attribute default is within range $min = (is_null($min)) ? -PHP_FLOAT_MAX : \floatval($min); @@ -1489,7 +1489,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float 'min' => $min, 'max' => $max, ], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); $formatOptions = $attribute->getAttribute('formatOptions', []); @@ -1528,8 +1528,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boole ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1538,7 +1538,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boole 'required' => $required, 'default' => $default, 'array' => $array, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1570,8 +1570,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datet ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { $filters[] = 'datetime'; @@ -1583,7 +1583,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datet 'default' => $default, 'array' => $array, 'filters' => $filters, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $auth); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) @@ -1617,7 +1617,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relat ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') + ->inject('authorization') ->action(function ( string $databaseId, string $collectionId, @@ -1631,12 +1631,12 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relat Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, - Authorization $auth + Authorization $authorization ) { $key ??= $relatedCollectionId; $twoWayKey ??= $collectionId; - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1707,7 +1707,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relat $dbForProject, $queueForDatabase, $queueForEvents, - $auth + $authorization ); $options = $attribute->getAttribute('options', []); @@ -1738,10 +1738,10 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes') ->param('queries', [], new Attributes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Authorization $authorization) { /** @var Document $database */ - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1775,7 +1775,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes') if ($cursor) { $attributeId = $cursor->getValue(); - $cursorDocument = $auth->skip(fn () => $dbForProject->find('attributes', [ + $cursorDocument = $authorization->skip(fn () => $dbForProject->find('attributes', [ Query::equal('collectionInternalId', [$collection->getInternalId()]), Query::equal('databaseInternalId', [$database->getInternalId()]), Query::equal('key', [$attributeId]), @@ -1827,10 +1827,10 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') ->param('key', '', new Key(), 'Attribute Key.') ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, Authorization $authorization) { - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1897,11 +1897,11 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/stri ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { $attribute = updateAttribute( - auth: $auth, + authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -1938,10 +1938,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/emai ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { $attribute = updateAttribute( - auth: $auth, + authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -1980,10 +1980,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { $attribute = updateAttribute( - auth: $auth, + authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2022,10 +2022,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/: ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { $attribute = updateAttribute( - auth: $auth, + authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2063,10 +2063,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/ ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { $attribute = updateAttribute( - auth: $auth, + authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2106,10 +2106,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/inte ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { $attribute = updateAttribute( - auth: $auth, + authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2157,10 +2157,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/floa ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { $attribute = updateAttribute( - auth: $auth, + authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2206,10 +2206,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/bool ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { $attribute = updateAttribute( - auth: $auth, + authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2246,10 +2246,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/date ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { $attribute = updateAttribute( - auth: $auth, + authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2285,7 +2285,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('auth') + ->inject('authorization') ->action(function ( string $databaseId, string $collectionId, @@ -2294,10 +2294,10 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key Response $response, Database $dbForProject, Event $queueForEvents, - Authorization $auth + Authorization $authorization ) { $attribute = updateAttribute( - $auth, + $authorization, $databaseId, $collectionId, $key, @@ -2342,10 +2342,10 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:ke ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { - $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2456,10 +2456,10 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { - $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2619,10 +2619,10 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes') ->param('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Authorization $authorization) { /** @var Document $database */ - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2652,7 +2652,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes') if ($cursor) { $indexId = $cursor->getValue(); - $cursorDocument = $auth->skip(fn () => $dbForProject->find('indexes', [ + $cursorDocument = $authorization->skip(fn () => $dbForProject->find('indexes', [ Query::equal('collectionInternalId', [$collection->getInternalId()]), Query::equal('databaseInternalId', [$database->getInternalId()]), Query::equal('key', [$indexId]), @@ -2690,10 +2690,10 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->param('key', null, new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, Authorization $authorization) { - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2734,10 +2734,10 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { - $db = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2808,8 +2808,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('user') ->inject('queueForEvents') ->inject('mode') - ->inject('auth') - ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Authorization $authorization) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -2821,16 +2821,16 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new documents, try update instead'); } - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -2868,8 +2868,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$auth->isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $auth->getRoles()) . ')'); + if (!$authorization->isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $authorization->getRoles()) . ')'); } } } @@ -2880,16 +2880,16 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') $data['$permissions'] = $permissions; $document = new Document($data); - $checkPermissions = function (Document $collection, Document $document, string $permission) use (&$checkPermissions, $dbForProject, $database, $auth) { + $checkPermissions = function (Document $collection, Document $document, string $permission) use (&$checkPermissions, $dbForProject, $database, $authorization) { $documentSecurity = $collection->getAttribute('documentSecurity', false); - $valid = $auth->isValid(new Input($permission, $collection->getPermissionsByType($permission))); + $valid = $authorization->isValid(new Input($permission, $collection->getPermissionsByType($permission))); if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } if ($permission === Database::PERMISSION_UPDATE) { - $valid = $valid || $auth->isValid($document->getUpdate()); + $valid = $valid || $authorization->isValid($document->getUpdate()); if ($documentSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -2916,7 +2916,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $auth->skip( + $relatedCollection = $authorization->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -2930,7 +2930,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') $relation = new Document($relation); } if ($relation instanceof Document) { - $current = $auth->skip( + $current = $authorization->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), $relation->getId()) ); @@ -2970,7 +2970,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') } // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $auth) { + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $authorization) { $document->setAttribute('$databaseId', $database->getId()); $document->setAttribute('$collectionId', $collection->getId()); @@ -2990,7 +2990,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $auth->skip( + $relatedCollection = $authorization->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3036,17 +3036,17 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode, Authorization $auth) { - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -3070,7 +3070,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') if ($cursor) { $documentId = $cursor->getValue(); - $cursorDocument = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Document '{$documentId}' for the 'cursor' value not found."); @@ -3089,7 +3089,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') } // Add $collectionId and $databaseId for all documents - $processDocument = (function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $auth): bool { + $processDocument = (function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $authorization): bool { if ($document->isEmpty()) { return false; } @@ -3116,7 +3116,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId)); + $relatedCollection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId)); foreach ($relations as $index => $doc) { if ($doc instanceof Document) { @@ -3194,18 +3194,18 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, string $mode, Authorization $auth) { - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -3225,7 +3225,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume } // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $auth) { + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $authorization) { if ($document->isEmpty()) { return; } @@ -3249,7 +3249,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $auth->skip( + $relatedCollection = $authorization->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3286,10 +3286,10 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -3317,7 +3317,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $auth); + $audit = new Audit($dbForProject); $resource = 'database/' . $databaseId . '/collection/' . $collectionId . '/document/' . $document->getId(); $logs = $audit->getLogsByResource($resource, $limit, $offset); @@ -3401,8 +3401,8 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->inject('dbForProject') ->inject('queueForEvents') ->inject('mode') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Authorization $authorization) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -3410,16 +3410,16 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD); } - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -3427,7 +3427,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu // Read permission should not be required for update /** @var Document $document */ - $document = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $document = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); if ($document->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); @@ -3441,7 +3441,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ]); // Users can only manage their own roles, API keys and Admin users can manage any - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -3454,7 +3454,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$auth->isRole($role)) { + if (!$authorization->isRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -3469,7 +3469,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $data['$permissions'] = $permissions; $newDocument = new Document($data); - $setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database, $auth) { + $setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database, $authorization) { $relationships = \array_filter( $collection->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP @@ -3491,7 +3491,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $auth->skip( + $relatedCollection = $authorization->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3506,7 +3506,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $relation = new Document($relation); } if ($relation instanceof Document) { - $oldDocument = $auth->skip(fn () => $dbForProject->getDocument( + $oldDocument = $authorization->skip(fn () => $dbForProject->getDocument( 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), $relation->getId() )); @@ -3555,7 +3555,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu } // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $auth) { + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $authorization) { $document->setAttribute('$databaseId', $database->getId()); $document->setAttribute('$collectionId', $collection->getId()); @@ -3575,7 +3575,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $auth->skip( + $relatedCollection = $authorization->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3628,25 +3628,25 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:doc ->inject('queueForDeletes') ->inject('queueForEvents') ->inject('mode') - ->inject('auth') - ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, string $mode, Authorization $auth) { - $database = $auth->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + ->inject('authorization') + ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, string $mode, Authorization $authorization) { + $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } // Read permission should not be required for delete - $document = $auth->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $document = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); if ($document->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); @@ -3666,7 +3666,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:doc }); // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $auth) { + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $authorization) { $document->setAttribute('$databaseId', $database->getId()); $document->setAttribute('$collectionId', $collection->getId()); @@ -3686,7 +3686,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:doc } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $auth->skip( + $relatedCollection = $authorization->skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3728,8 +3728,8 @@ Http::get('/v1/databases/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $range, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -3740,7 +3740,7 @@ Http::get('/v1/databases/usage') METRIC_DOCUMENTS, ]; - $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -3808,8 +3808,8 @@ Http::get('/v1/databases/:databaseId/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $databaseId, string $range, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $range, Response $response, Database $dbForProject, Authorization $authorization) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -3825,7 +3825,7 @@ Http::get('/v1/databases/:databaseId/usage') str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS), ]; - $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -3894,8 +3894,8 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/usage') ->param('collectionId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $databaseId, string $range, string $collectionId, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $databaseId, string $range, string $collectionId, Response $response, Database $dbForProject, Authorization $authorization) { $database = $dbForProject->getDocument('databases', $databaseId); $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); @@ -3912,7 +3912,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/usage') str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collectionDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), ]; - $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/app/controllers/general.php b/app/controllers/general.php index b4fade7b0b..a5e2134dcc 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -894,7 +894,7 @@ include_once 'shared/api/auth.php'; include_once 'api/account.php'; include_once 'api/avatars.php'; include_once 'api/console.php'; -//include_once 'api/database.php'; +include_once 'api/databases.php'; //include_once 'api/functions.php'; //include_once 'api/graphql.php'; include_once 'api/health.php'; diff --git a/app/worker.php b/app/worker.php index 66538a939d..25a07bf5df 100644 --- a/app/worker.php +++ b/app/worker.php @@ -186,37 +186,6 @@ $getProjectDB }); $container->set($getProjectDB); -// Worker::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth, Connections $connections) { -// $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - -// return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections): Database { -// if ($project->isEmpty() || $project->getId() === 'console') { -// return $dbForConsole; -// } - -// $databaseName = $project->getAttribute('database'); - -// if (isset($databases[$databaseName])) { -// $database = $databases[$databaseName]; -// $database->setNamespace('_' . $project->getInternalId()); -// return $database; -// } - -// $connection = $pools->get($databaseName)->pop(); -// $connections->add($connection); -// $dbAdapter = $connection->getResource(); - -// $database = new Database($dbAdapter, $cache); -// $database->setAuthorization($auth); - -// $databases[$databaseName] = $database; - -// $database->setNamespace('_' . $project->getInternalId()); - -// return $database; -// }; -// }, ['pools', 'dbForConsole', 'cache', 'auth', 'connections']); - $abuseRetention = new Dependency(); $abuseRetention ->setName('abuseRetention') @@ -466,7 +435,7 @@ if (!empty($workerIndex)) { } if (\str_starts_with($workerName, 'databases')) { - $queueName = System::getEnv('_APP_QUEUE_NAME', 'database_db_main'); + $queueName = System::getEnv('_APP_QUEUE_NAME', 'db_main'); } else { $queueName = System::getEnv('_APP_QUEUE_NAME', 'v1-' . strtolower($workerName)); } From 5d54f567cd3f0c2ba481345123eb84255f59dcc7 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 09:45:31 +0200 Subject: [PATCH 044/195] Fixed client overwriting --- tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php | 1 + tests/unit/Auth/AuthTest.php | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php index 877e66cb03..4288b92613 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php @@ -21,6 +21,7 @@ class DatabasesPermissionsGuestTest extends Scope public function setUp(): void { + parent::setUp(); $this->auth = new Authorization(); } diff --git a/tests/unit/Auth/AuthTest.php b/tests/unit/Auth/AuthTest.php index 2bbe690c02..54cce740a7 100644 --- a/tests/unit/Auth/AuthTest.php +++ b/tests/unit/Auth/AuthTest.php @@ -17,6 +17,7 @@ class AuthTest extends TestCase public function setUp(): void { + parent::setUp(); $this->auth = new Authorization(); } From ab6744df3a79b4be3926312bac34fb61665a9b45 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 14:12:09 +0200 Subject: [PATCH 045/195] Fixed webhooks tests --- app/controllers/api/account.php | 1 + app/controllers/api/functions.php | 110 +++++++++--------- app/controllers/general.php | 4 +- app/init2.php | 41 ++++++- .../Webhooks/WebhooksCustomServerTest.php | 5 +- 5 files changed, 100 insertions(+), 61 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index bc5efd6aab..c21237edce 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -977,6 +977,7 @@ Http::post('/v1/account/sessions/token') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') + ->inject('authorization') ->action($createSession); Http::get('/v1/account/sessions/oauth2/:provider') diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 904d010d13..e08ca3bfd1 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -169,8 +169,8 @@ Http::post('/v1/functions') ->inject('queueForBuilds') ->inject('dbForConsole') ->inject('gitHub') - ->inject('auth') - ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateBranch, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $auth) use ($redeployVcs) { + ->inject('authorization') + ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateBranch, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $authorization) use ($redeployVcs) { $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId; $allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', ''))); @@ -232,7 +232,7 @@ Http::post('/v1/functions') 'providerSilentMode' => $providerSilentMode, ])); - $schedule = $auth->skip( + $schedule = $authorization->skip( fn () => $dbForConsole->createDocument('schedules', new Document([ 'region' => System::getEnv('_APP_REGION', 'default'), // Todo replace with projects region 'resourceType' => 'function', @@ -289,7 +289,7 @@ Http::post('/v1/functions') $routeSubdomain = ID::unique(); $domain = "{$routeSubdomain}.{$functionsDomain}"; - $rule = $auth->skip( + $rule = $authorization->skip( fn () => $dbForConsole->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), @@ -481,8 +481,8 @@ Http::get('/v1/functions/:functionId/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $functionId, string $range, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $functionId, string $range, Response $response, Database $dbForProject, Authorization $authorization) { $function = $dbForProject->getDocument('functions', $functionId); @@ -503,7 +503,7 @@ Http::get('/v1/functions/:functionId/usage') str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), ]; - $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -579,8 +579,8 @@ Http::get('/v1/functions/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $range, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -596,7 +596,7 @@ Http::get('/v1/functions/usage') METRIC_EXECUTIONS_COMPUTE, ]; - $auth->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -698,8 +698,8 @@ Http::put('/v1/functions/:functionId') ->inject('queueForBuilds') ->inject('dbForConsole') ->inject('gitHub') - ->inject('auth') - ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $auth) use ($redeployVcs) { + ->inject('authorization') + ->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $authorization) use ($redeployVcs) { // TODO: If only branch changes, re-deploy $function = $dbForProject->getDocument('functions', $functionId); @@ -833,7 +833,7 @@ Http::put('/v1/functions/:functionId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForEvents->setParam('functionId', $function->getId()); @@ -945,8 +945,8 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId') ->inject('dbForProject') ->inject('queueForEvents') ->inject('dbForConsole') - ->inject('auth') - ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Event $queueForEvents, Database $dbForConsole, Authorization $auth) { + ->inject('authorization') + ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Event $queueForEvents, Database $dbForConsole, Authorization $authorization) { $function = $dbForProject->getDocument('functions', $functionId); $deployment = $dbForProject->getDocument('deployments', $deploymentId); @@ -979,7 +979,7 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForEvents ->setParam('functionId', $function->getId()) @@ -1007,8 +1007,8 @@ Http::delete('/v1/functions/:functionId') ->inject('queueForDeletes') ->inject('queueForEvents') ->inject('dbForConsole') - ->inject('auth') - ->action(function (string $functionId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Database $dbForConsole, Authorization $auth) { + ->inject('authorization') + ->action(function (string $functionId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Database $dbForConsole, Authorization $authorization) { $function = $dbForProject->getDocument('functions', $functionId); @@ -1025,7 +1025,7 @@ Http::delete('/v1/functions/:functionId') $schedule ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('active', false); - $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) @@ -1458,8 +1458,8 @@ Http::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId' ->inject('project') ->inject('queueForEvents') ->inject('queueForBuilds') - ->inject('auth') - ->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Authorization $auth) { + ->inject('authorization') + ->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Authorization $authorization) { $function = $dbForProject->getDocument('functions', $functionId); @@ -1473,7 +1473,7 @@ Http::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId' throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } - $build = $auth->skip(fn () => $dbForProject->getDocument('builds', $buildId)); + $build = $authorization->skip(fn () => $dbForProject->getDocument('builds', $buildId)); if ($build->isEmpty()) { throw new Exception(Exception::BUILD_NOT_FOUND); @@ -1530,13 +1530,13 @@ Http::post('/v1/functions/:functionId/executions') ->inject('mode') ->inject('queueForFunctions') ->inject('geodb') - ->inject('auth') - ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode, Func $queueForFunctions, Reader $geodb, Authorization $auth) { + ->inject('authorization') + ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode, Func $queueForFunctions, Reader $geodb, Authorization $authorization) { - $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); @@ -1551,7 +1551,7 @@ Http::post('/v1/functions/:functionId/executions') throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } - $deployment = $auth->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); + $deployment = $authorization->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); if ($deployment->getAttribute('resourceId') !== $function->getId()) { throw new Exception(Exception::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); @@ -1562,7 +1562,7 @@ Http::post('/v1/functions/:functionId/executions') } /** Check if build has completed */ - $build = $auth->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); + $build = $authorization->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); if ($build->isEmpty()) { throw new Exception(Exception::BUILD_NOT_FOUND); } @@ -1571,8 +1571,8 @@ Http::post('/v1/functions/:functionId/executions') throw new Exception(Exception::BUILD_NOT_READY); } - if (!$auth->isValid(new Input('execute', $function->getAttribute('execute')))) { // Check if user has write access to execute function - throw new Exception(Exception::USER_UNAUTHORIZED, $auth->getDescription()); + if (!$authorization->isValid(new Input('execute', $function->getAttribute('execute')))) { // Check if user has write access to execute function + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } $jwt = ''; // initialize @@ -1653,7 +1653,7 @@ Http::post('/v1/functions/:functionId/executions') if ($async) { if ($function->getAttribute('logging')) { /** @var Document $execution */ - $execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); } $queueForFunctions @@ -1768,10 +1768,10 @@ Http::post('/v1/functions/:functionId/executions') if ($function->getAttribute('logging')) { /** @var Document $execution */ - $execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); } - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1810,12 +1810,12 @@ Http::get('/v1/functions/:functionId/executions') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('auth') - ->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $auth) { - $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + ->inject('authorization') + ->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { + $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); @@ -1858,7 +1858,7 @@ Http::get('/v1/functions/:functionId/executions') $results = $dbForProject->find('executions', $queries); $total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT); - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); if (!$isPrivilegedUser && !$isAppUser) { @@ -1891,12 +1891,12 @@ Http::get('/v1/functions/:functionId/executions/:executionId') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('auth') - ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, string $mode, Authorization $auth) { - $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + ->inject('authorization') + ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { + $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser($auth->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($auth->getRoles()); + $isAPIKey = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); @@ -1912,7 +1912,7 @@ Http::get('/v1/functions/:functionId/executions/:executionId') throw new Exception(Exception::EXECUTION_NOT_FOUND); } - $roles = $auth->getRoles(); + $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); if (!$isPrivilegedUser && !$isAppUser) { @@ -1944,8 +1944,8 @@ Http::post('/v1/functions/:functionId/variables') ->inject('response') ->inject('dbForProject') ->inject('dbForConsole') - ->inject('auth') - ->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $auth) { + ->inject('authorization') + ->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -1983,7 +1983,7 @@ Http::post('/v1/functions/:functionId/variables') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -2076,8 +2076,8 @@ Http::put('/v1/functions/:functionId/variables/:variableId') ->inject('response') ->inject('dbForProject') ->inject('dbForConsole') - ->inject('auth') - ->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $auth) { + ->inject('authorization') + ->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization) { $function = $dbForProject->getDocument('functions', $functionId); @@ -2113,7 +2113,7 @@ Http::put('/v1/functions/:functionId/variables/:variableId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $response->dynamic($variable, Response::MODEL_VARIABLE); }); @@ -2135,8 +2135,8 @@ Http::delete('/v1/functions/:functionId/variables/:variableId') ->inject('response') ->inject('dbForProject') ->inject('dbForConsole') - ->inject('auth') - ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $auth) { + ->inject('authorization') + ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -2162,7 +2162,7 @@ Http::delete('/v1/functions/:functionId/variables/:variableId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $response->noContent(); }); diff --git a/app/controllers/general.php b/app/controllers/general.php index a5e2134dcc..0c029237a1 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -895,7 +895,7 @@ include_once 'api/account.php'; include_once 'api/avatars.php'; include_once 'api/console.php'; include_once 'api/databases.php'; -//include_once 'api/functions.php'; +include_once 'api/functions.php'; //include_once 'api/graphql.php'; include_once 'api/health.php'; include_once 'api/locale.php'; @@ -906,6 +906,6 @@ include_once 'api/proxy.php'; include_once 'api/storage.php'; include_once 'api/teams.php'; include_once 'api/users.php'; -//include_once 'api/vcs.php'; +include_once 'api/vcs.php'; include_once 'web/console.php'; include_once 'web/home.php'; diff --git a/app/init2.php b/app/init2.php index 6e123e796d..8697ccbfc2 100644 --- a/app/init2.php +++ b/app/init2.php @@ -1,4 +1,6 @@ set('pools', (function () { }; })()); +$global->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; +}); + $mode = new Dependency(); $mode ->setName('mode') @@ -956,9 +987,17 @@ $hooks ->setCallback(function (Registry $registry) { return $registry->get('hooks'); }); - $container->set($hooks); +$github = new Dependency(); +$github + ->setName('gitHub') + ->inject('cache') + ->setCallback(function (Cache $cache) { + return new GitHub($cache); + }); +$container->set($github); + $requestTimestamp = new Dependency(); $requestTimestamp ->setName('requestTimestamp') diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index d31c981a03..64167a5991 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -486,11 +486,10 @@ class WebhooksCustomServerTest extends Scope /** * Test for SUCCESS */ - $stderr = ''; - $stdout = ''; + $output = ''; $folder = 'timeout'; $code = realpath(__DIR__ . '/../../../resources/functions') . "/{$folder}/code.tar.gz"; - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/{$folder} && tar --exclude code.tar.gz -czf code.tar.gz .", '', $stdout, $stderr); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/{$folder} && tar --exclude code.tar.gz -czf code.tar.gz .", '', $output); $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge([ 'content-type' => 'multipart/form-data', From 19f8b13cfe97e3f228fe0c1ddce62374c34c640a Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 14:19:37 +0200 Subject: [PATCH 046/195] Fixed formatting --- app/init2.php | 1 + app/worker.php | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/init2.php b/app/init2.php index 8697ccbfc2..703e2a0a73 100644 --- a/app/init2.php +++ b/app/init2.php @@ -1,4 +1,5 @@ Date: Mon, 22 Apr 2024 14:41:46 +0200 Subject: [PATCH 047/195] Fixed execute to match new signature --- src/Appwrite/Platform/Tasks/Install.php | 7 ++-- src/Appwrite/Platform/Tasks/VolumeSync.php | 10 +++--- src/Appwrite/Platform/Workers/Builds.php | 33 +++++++++---------- .../Platform/Workers/Certificates.php | 26 +++++++-------- .../e2e/Services/Functions/FunctionsBase.php | 5 ++- .../Realtime/RealtimeCustomClientTest.php | 5 ++- 6 files changed, 38 insertions(+), 48 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Install.php b/src/Appwrite/Platform/Tasks/Install.php index 9e03d10255..a1a73e6385 100644 --- a/src/Appwrite/Platform/Tasks/Install.php +++ b/src/Appwrite/Platform/Tasks/Install.php @@ -213,8 +213,7 @@ class Install extends Action } $env = ''; - $stdout = ''; - $stderr = ''; + $output = ''; foreach ($input as $key => $value) { if ($value) { @@ -225,13 +224,13 @@ class Install extends Action $exit = 0; if (!$noStart) { Console::log("Running \"docker compose up -d --remove-orphans --renew-anon-volumes\""); - $exit = Console::execute("$env docker compose --project-directory $this->path up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr); + $exit = Console::execute("$env docker compose --project-directory $this->path up -d --remove-orphans --renew-anon-volumes", '', $output); } if ($exit !== 0) { $message = 'Failed to install Appwrite dockers'; Console::error($message); - Console::error($stderr); + Console::error($output); Console::exit($exit); } else { $message = 'Appwrite installed successfully'; diff --git a/src/Appwrite/Platform/Tasks/VolumeSync.php b/src/Appwrite/Platform/Tasks/VolumeSync.php index 272edbd446..178895fca7 100644 --- a/src/Appwrite/Platform/Tasks/VolumeSync.php +++ b/src/Appwrite/Platform/Tasks/VolumeSync.php @@ -47,13 +47,11 @@ class VolumeSync extends Action return; } - $stdin = ""; - $stdout = ""; - $stderr = ""; + $input = ""; + $output = ""; - Console::execute("rsync -av $source $destination", $stdin, $stdout, $stderr); - Console::success($stdout); - Console::error($stderr); + Console::execute("rsync -av $source $destination", $input, $output); + Console::log($output); }, $interval); } } diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 37ed979d50..f9fec274a5 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -221,13 +221,12 @@ class Builds extends Action $branchName = $deployment->getAttribute('providerBranch'); $commitHash = $deployment->getAttribute('providerCommitHash', ''); $gitCloneCommand = $github->generateCloneCommand($cloneOwner, $cloneRepository, $branchName, $tmpDirectory, $rootDirectory, $commitHash); - $stdout = ''; - $stderr = ''; - Console::execute('mkdir -p /tmp/builds/' . \escapeshellcmd($buildId), '', $stdout, $stderr); - $exit = Console::execute($gitCloneCommand, '', $stdout, $stderr); + $output = ''; + Console::execute('mkdir -p /tmp/builds/' . \escapeshellcmd($buildId), '', $output); + $exit = Console::execute($gitCloneCommand, '', $output); if ($exit !== 0) { - throw new \Exception('Unable to clone code repository: ' . $stderr); + throw new \Exception('Unable to clone code repository: ' . $output); } // Build from template @@ -244,33 +243,33 @@ class Builds extends Action // Clone template repo $tmpTemplateDirectory = '/tmp/builds/' . \escapeshellcmd($buildId) . '/template'; $gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateBranch, $tmpTemplateDirectory, $templateRootDirectory); - $exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr); + $exit = Console::execute($gitCloneCommandForTemplate, '', $output); if ($exit !== 0) { - throw new \Exception('Unable to clone code repository: ' . $stderr); + throw new \Exception('Unable to clone code repository: ' . $output); } // Ensure directories - Console::execute('mkdir -p ' . $tmpTemplateDirectory . '/' . $templateRootDirectory, '', $stdout, $stderr); - Console::execute('mkdir -p ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr); + Console::execute('mkdir -p ' . $tmpTemplateDirectory . '/' . $templateRootDirectory, '', $output); + Console::execute('mkdir -p ' . $tmpDirectory . '/' . $rootDirectory, '', $output); // Merge template into user repo - Console::execute('cp -rfn ' . $tmpTemplateDirectory . '/' . $templateRootDirectory . '/* ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr); + Console::execute('cp -rfn ' . $tmpTemplateDirectory . '/' . $templateRootDirectory . '/* ' . $tmpDirectory . '/' . $rootDirectory, '', $output); // Commit and push - $exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . $tmpDirectory . ' && git add . && git commit -m "Create \'' . \escapeshellcmd($function->getAttribute('name', '')) . '\' function" && git push origin ' . \escapeshellcmd($branchName), '', $stdout, $stderr); + $exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . $tmpDirectory . ' && git add . && git commit -m "Create \'' . \escapeshellcmd($function->getAttribute('name', '')) . '\' function" && git push origin ' . \escapeshellcmd($branchName), '', $output); if ($exit !== 0) { - throw new \Exception('Unable to push code repository: ' . $stderr); + throw new \Exception('Unable to push code repository: ' . $output); } - $exit = Console::execute('cd ' . $tmpDirectory . ' && git rev-parse HEAD', '', $stdout, $stderr); + $exit = Console::execute('cd ' . $tmpDirectory . ' && git rev-parse HEAD', '', $output); if ($exit !== 0) { - throw new \Exception('Unable to get vcs commit SHA: ' . $stderr); + throw new \Exception('Unable to get vcs commit SHA: ' . $output); } - $providerCommitHash = \trim($stdout); + $providerCommitHash = \trim($output); $authorUrl = "https://github.com/$cloneOwner"; $deployment->setAttribute('providerCommitHash', $providerCommitHash ?? ''); @@ -312,7 +311,7 @@ class Builds extends Action throw new \Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.'); } - Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr); + Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $output); $path = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION)); $result = $localDevice->transfer($tmpPathFile, $path, $deviceForFunctions); @@ -321,7 +320,7 @@ class Builds extends Action throw new \Exception("Unable to move file"); } - Console::execute('rm -rf ' . $tmpPath, '', $stdout, $stderr); + Console::execute('rm -rf ' . $tmpPath, '', $output); $source = $path; diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 531e36ae1c..c4a6cd7a04 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -330,30 +330,26 @@ class Certificates extends Action * * @param string $folder Folder into which certificates should be generated * @param string $domain Domain to generate certificate for - * @return array Named array with keys 'stdout' and 'stderr', both string + * @return string output * @throws Exception */ - private function issueCertificate(string $folder, string $domain, string $email): array + private function issueCertificate(string $folder, string $domain, string $email): string { - $stdout = ''; - $stderr = ''; + $output = ''; $staging = (Http::isProduction()) ? '' : ' --dry-run'; $exit = Console::execute("certbot certonly -v --webroot --noninteractive --agree-tos{$staging}" . " --email " . $email . " --cert-name " . $folder . " -w " . APP_STORAGE_CERTIFICATES - . " -d {$domain}", '', $stdout, $stderr); + . " -d {$domain}", '', $output); // Unexpected error, usually 5XX, API limits, ... if ($exit !== 0) { - throw new Exception('Failed to issue a certificate with message: ' . $stderr); + throw new Exception('Failed to issue a certificate with message: ' . $output); } - return [ - 'stdout' => $stdout, - 'stderr' => $stderr - ]; + return $output; } /** @@ -381,7 +377,7 @@ class Certificates extends Action * @return void * @throws Exception */ - private function applyCertificateFiles(string $folder, string $domain, array $letsEncryptData): void + private function applyCertificateFiles(string $folder, string $domain, string $letsEncryptData): void { // Prepare folder in storage for domain @@ -394,19 +390,19 @@ class Certificates extends Action // Move generated files if (!@\rename('/etc/letsencrypt/live/' . $folder . '/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) { - throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData); } if (!@\rename('/etc/letsencrypt/live/' . $folder . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) { - throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData); } if (!@\rename('/etc/letsencrypt/live/' . $folder . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) { - throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData); } if (!@\rename('/etc/letsencrypt/live/' . $folder . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) { - throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); + throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData); } $config = \implode(PHP_EOL, [ diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index c45ebbe068..1aad158375 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -7,12 +7,11 @@ use Utopia\CLI\Console; trait FunctionsBase { - protected string $stdout = ''; - protected string $stderr = ''; + protected string $output = ''; protected function packageCode($folder) { - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output); } // /** diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index 9b73566bda..019b2fda51 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -1272,11 +1272,10 @@ class RealtimeCustomClientTest extends Scope $this->assertNotEmpty($function['body']['$id']); $folder = 'timeout'; - $stderr = ''; - $stdout = ''; + $output = ''; $code = realpath(__DIR__ . '/../../../resources/functions') . "/{$folder}/code.tar.gz"; - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/{$folder} && tar --exclude code.tar.gz -czf code.tar.gz .", '', $stdout, $stderr); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/{$folder} && tar --exclude code.tar.gz -czf code.tar.gz .", '', $output); $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ 'content-type' => 'multipart/form-data', From 351b9318d4154f54f59e7b13c63d55c2616b81f9 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 17:58:40 +0200 Subject: [PATCH 048/195] Fixed router, and options --- app/controllers/general.php | 609 ++++++++++++++--------------- app/init2.php | 37 ++ composer.lock | 10 +- src/Appwrite/GraphQL/Resolvers.php | 4 +- tests/unit/Utopia/RequestTest.php | 2 +- 5 files changed, 348 insertions(+), 314 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 0c029237a1..d53be96186 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -38,324 +38,320 @@ Config::setParam('domainVerification', false); Config::setParam('cookieDomain', 'localhost'); Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE); -// function router(Http $utopia, Database $dbForConsole, callable $getProjectDB, Request $request, Response $response, Route $route, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $auth) -// { -// $route?->label('error', __DIR__ . '/../views/general/error.phtml'); +function router(Database $dbForConsole, callable $getProjectDB, Request $request, Response $response, Route $route, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $auth) +{ + $route?->label('error', __DIR__ . '/../views/general/error.phtml'); -// $host = $request->getHostname() ?? ''; + $host = $request->getHostname() ?? ''; -// $route = $auth->skip( -// fn () => $dbForConsole->find('rules', [ -// Query::equal('domain', [$host]), -// Query::limit(1) -// ]) -// )[0] ?? null; + $rule = $auth->skip( + fn () => $dbForConsole->find('rules', [ + Query::equal('domain', [$host]), + Query::limit(1) + ]) + )[0] ?? null; -// if ($route === null) { -// if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) { -// throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.'); -// } + if ($rule === null) { + if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) { + throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.'); + } -// if (\str_ends_with($host, System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) { -// throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.'); -// } + if (\str_ends_with($host, System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) { + throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.'); + } -// if (System::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') { -// if ($host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations -// throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.'); -// } -// } + if (System::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') { + if ($host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations + throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.'); + } + } -// // Act as API - no Proxy logic -// $utopia->getRoute()?->label('error', ''); -// return false; -// } + // Act as API - no Proxy logic + $route?->label('error', ''); + return false; + } -// $projectId = $route->getAttribute('projectId'); -// $project = $auth->skip( -// fn () => $dbForConsole->getDocument('projects', $projectId) -// ); -// if (array_key_exists('proxy', $project->getAttribute('services', []))) { -// $status = $project->getAttribute('services', [])['proxy']; -// if (!$status) { -// throw new AppwriteException(AppwriteException::GENERAL_SERVICE_DISABLED); -// } -// } + $projectId = $rule->getAttribute('projectId'); + $project = $auth->skip( + fn () => $dbForConsole->getDocument('projects', $projectId) + ); + if (array_key_exists('proxy', $project->getAttribute('services', []))) { + $status = $project->getAttribute('services', [])['proxy']; + if (!$status) { + throw new AppwriteException(AppwriteException::GENERAL_SERVICE_DISABLED); + } + } -// // Skip Appwrite Router for ACME challenge. Nessessary for certificate generation -// $path = ($request->getURI() ?? '/'); -// if (\str_starts_with($path, '/.well-known/acme-challenge')) { -// return false; -// } + // Skip Appwrite Router for ACME challenge. Nessessary for certificate generation + $path = ($request->getURI() ?? '/'); + if (\str_starts_with($path, '/.well-known/acme-challenge')) { + return false; + } -// $type = $route->getAttribute('resourceType'); + $type = $rule->getAttribute('resourceType'); -// if ($type === 'function') { -// if (System::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS -// if ($request->getProtocol() !== 'https') { -// if ($request->getMethod() !== Request::METHOD_GET) { -// throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.'); -// } + if ($type === 'function') { + if (System::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS + if ($request->getProtocol() !== 'https') { + if ($request->getMethod() !== Request::METHOD_GET) { + throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.'); + } -// return $response->redirect('https://' . $request->getHostname() . $request->getURI()); -// } -// } + return $response->redirect('https://' . $request->getHostname() . $request->getURI()); + } + } -// $functionId = $route->getAttribute('resourceId'); -// $projectId = $route->getAttribute('projectId'); + $functionId = $rule->getAttribute('resourceId'); + $projectId = $rule->getAttribute('projectId'); -// $path = ($swooleRequest->server['request_uri'] ?? '/'); -// $query = ($swooleRequest->server['query_string'] ?? ''); -// if (!empty($query)) { -// $path .= '?' . $query; -// } + $path = ($request->getURI() ?? '/'); + $query = ($request->getQueryString() ?? ''); + if (!empty($query)) { + $path .= '?' . $query; + } + $body = $request->getRawPayload() ?? ''; + $method = $request->getMethod(); -// $body = $swooleRequest->getContent() ?? ''; -// $method = $swooleRequest->server['request_method']; + $requestHeaders = $request->getHeaders(); -// $requestHeaders = $request->getHeaders(); + $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); -// $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $dbForProject = $getProjectDB($project); -// $dbForProject = $getProjectDB($project); + $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); -// $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + if ($function->isEmpty() || !$function->getAttribute('enabled')) { + throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND); + } -// if ($function->isEmpty() || !$function->getAttribute('enabled')) { -// throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND); -// } + $version = $function->getAttribute('version', 'v2'); + $runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []); -// $version = $function->getAttribute('version', 'v2'); -// $runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []); + $runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null; -// $runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null; + if (\is_null($runtime)) { + throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); + } -// if (\is_null($runtime)) { -// throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); -// } + $deployment = $auth->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); -// $deployment = $auth->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); + if ($deployment->getAttribute('resourceId') !== $function->getId()) { + throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); + } -// if ($deployment->getAttribute('resourceId') !== $function->getId()) { -// throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); -// } + if ($deployment->isEmpty()) { + throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); + } -// if ($deployment->isEmpty()) { -// throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); -// } + /** Check if build has completed */ + $build = $auth->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); + if ($build->isEmpty()) { + throw new AppwriteException(AppwriteException::BUILD_NOT_FOUND); + } -// /** Check if build has completed */ -// $build = $auth->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); -// if ($build->isEmpty()) { -// throw new AppwriteException(AppwriteException::BUILD_NOT_FOUND); -// } + if ($build->getAttribute('status') !== 'ready') { + throw new AppwriteException(AppwriteException::BUILD_NOT_READY); + } -// if ($build->getAttribute('status') !== 'ready') { -// throw new AppwriteException(AppwriteException::BUILD_NOT_READY); -// } + $permissions = $function->getAttribute('execute'); -// $permissions = $function->getAttribute('execute'); + if (!(\in_array('any', $permissions)) && (\in_array('guests', $permissions))) { + throw new AppwriteException(AppwriteException::USER_UNAUTHORIZED, 'To execute function using domain, execute permissions must include "any" or "guests"'); + } -// if (!(\in_array('any', $permissions)) && (\in_array('guests', $permissions))) { -// throw new AppwriteException(AppwriteException::USER_UNAUTHORIZED, 'To execute function using domain, execute permissions must include "any" or "guests"'); -// } + $headers = \array_merge([], $requestHeaders); + $headers['x-appwrite-trigger'] = 'http'; + $headers['x-appwrite-user-id'] = ''; + $headers['x-appwrite-user-jwt'] = ''; + $headers['x-appwrite-country-code'] = ''; + $headers['x-appwrite-continent-code'] = ''; + $headers['x-appwrite-continent-eu'] = 'false'; -// $headers = \array_merge([], $requestHeaders); -// $headers['x-appwrite-trigger'] = 'http'; -// $headers['x-appwrite-user-id'] = ''; -// $headers['x-appwrite-user-jwt'] = ''; -// $headers['x-appwrite-country-code'] = ''; -// $headers['x-appwrite-continent-code'] = ''; -// $headers['x-appwrite-continent-eu'] = 'false'; + $ip = $headers['x-real-ip'] ?? ''; + if (!empty($ip)) { + $record = $geodb->get($ip); -// $ip = $headers['x-real-ip'] ?? ''; -// if (!empty($ip)) { -// $record = $geodb->get($ip); + if ($record) { + $eu = Config::getParam('locale-eu'); -// if ($record) { -// $eu = Config::getParam('locale-eu'); + $headers['x-appwrite-country-code'] = $record['country']['iso_code'] ?? ''; + $headers['x-appwrite-continent-code'] = $record['continent']['code'] ?? ''; + $headers['x-appwrite-continent-eu'] = (\in_array($record['country']['iso_code'], $eu)) ? 'true' : 'false'; + } + } -// $headers['x-appwrite-country-code'] = $record['country']['iso_code'] ?? ''; -// $headers['x-appwrite-continent-code'] = $record['continent']['code'] ?? ''; -// $headers['x-appwrite-continent-eu'] = (\in_array($record['country']['iso_code'], $eu)) ? 'true' : 'false'; -// } -// } + $headersFiltered = []; + foreach ($headers as $key => $value) { + if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_REQUEST)) { + $headersFiltered[] = ['name' => $key, 'value' => $value]; + } + } -// $headersFiltered = []; -// foreach ($headers as $key => $value) { -// if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_REQUEST)) { -// $headersFiltered[] = ['name' => $key, 'value' => $value]; -// } -// } + $executionId = ID::unique(); -// $executionId = ID::unique(); + $execution = new Document([ + '$id' => $executionId, + '$permissions' => [], + 'functionInternalId' => $function->getInternalId(), + 'functionId' => $function->getId(), + 'deploymentInternalId' => $deployment->getInternalId(), + 'deploymentId' => $deployment->getId(), + 'trigger' => 'http', // http / schedule / event + 'status' => 'processing', // waiting / processing / completed / failed + 'responseStatusCode' => 0, + 'responseHeaders' => [], + 'requestPath' => $path, + 'requestMethod' => $method, + 'requestHeaders' => $headersFiltered, + 'errors' => '', + 'logs' => '', + 'duration' => 0.0, + 'search' => implode(' ', [$functionId, $executionId]), + ]); -// $execution = new Document([ -// '$id' => $executionId, -// '$permissions' => [], -// 'functionInternalId' => $function->getInternalId(), -// 'functionId' => $function->getId(), -// 'deploymentInternalId' => $deployment->getInternalId(), -// 'deploymentId' => $deployment->getId(), -// 'trigger' => 'http', // http / schedule / event -// 'status' => 'processing', // waiting / processing / completed / failed -// 'responseStatusCode' => 0, -// 'responseHeaders' => [], -// 'requestPath' => $path, -// 'requestMethod' => $method, -// 'requestHeaders' => $headersFiltered, -// 'errors' => '', -// 'logs' => '', -// 'duration' => 0.0, -// 'search' => implode(' ', [$functionId, $executionId]), -// ]); + $queueForEvents + ->setParam('functionId', $function->getId()) + ->setParam('executionId', $execution->getId()) + ->setContext('function', $function); -// $queueForEvents -// ->setParam('functionId', $function->getId()) -// ->setParam('executionId', $execution->getId()) -// ->setContext('function', $function); + $durationStart = \microtime(true); -// $durationStart = \microtime(true); + $vars = []; -// $vars = []; + // V2 vars + if ($version === 'v2') { + $vars = \array_merge($vars, [ + 'APPWRITE_FUNCTION_TRIGGER' => $headers['x-appwrite-trigger'] ?? '', + 'APPWRITE_FUNCTION_DATA' => $body ?? '', + 'APPWRITE_FUNCTION_USER_ID' => $headers['x-appwrite-user-id'] ?? '', + 'APPWRITE_FUNCTION_JWT' => $headers['x-appwrite-user-jwt'] ?? '' + ]); + } -// // V2 vars -// if ($version === 'v2') { -// $vars = \array_merge($vars, [ -// 'APPWRITE_FUNCTION_TRIGGER' => $headers['x-appwrite-trigger'] ?? '', -// 'APPWRITE_FUNCTION_DATA' => $body ?? '', -// 'APPWRITE_FUNCTION_USER_ID' => $headers['x-appwrite-user-id'] ?? '', -// 'APPWRITE_FUNCTION_JWT' => $headers['x-appwrite-user-jwt'] ?? '' -// ]); -// } + // Shared vars + foreach ($function->getAttribute('varsProject', []) as $var) { + $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); + } -// // Shared vars -// foreach ($function->getAttribute('varsProject', []) as $var) { -// $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); -// } + // Function vars + foreach ($function->getAttribute('vars', []) as $var) { + $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); + } -// // Function vars -// foreach ($function->getAttribute('vars', []) as $var) { -// $vars[$var->getAttribute('key')] = $var->getAttribute('value', ''); -// } + // Appwrite vars + $vars = \array_merge($vars, [ + 'APPWRITE_FUNCTION_ID' => $functionId, + 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'), + 'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(), + 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), + 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', + 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', + ]); -// // Appwrite vars -// $vars = \array_merge($vars, [ -// 'APPWRITE_FUNCTION_ID' => $functionId, -// 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'), -// 'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(), -// 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), -// 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', -// 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', -// ]); + /** Execute function */ + $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); + try { + $version = $function->getAttribute('version', 'v2'); + $command = $runtime['startCommand']; + $command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"'; + $executionResponse = $executor->createExecution( + projectId: $project->getId(), + deploymentId: $deployment->getId(), + body: \strlen($body) > 0 ? $body : null, + variables: $vars, + timeout: $function->getAttribute('timeout', 0), + image: $runtime['image'], + source: $build->getAttribute('path', ''), + entrypoint: $deployment->getAttribute('entrypoint', ''), + version: $version, + path: $path, + method: $method, + headers: $headers, + runtimeEntrypoint: $command, + requestTimeout: 30 + ); -// /** Execute function */ -// $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); -// try { -// $version = $function->getAttribute('version', 'v2'); -// $command = $runtime['startCommand']; -// $command = $version === 'v2' ? '' : 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $command . '"'; -// $executionResponse = $executor->createExecution( -// projectId: $project->getId(), -// deploymentId: $deployment->getId(), -// body: \strlen($body) > 0 ? $body : null, -// variables: $vars, -// timeout: $function->getAttribute('timeout', 0), -// image: $runtime['image'], -// source: $build->getAttribute('path', ''), -// entrypoint: $deployment->getAttribute('entrypoint', ''), -// version: $version, -// path: $path, -// method: $method, -// headers: $headers, -// runtimeEntrypoint: $command, -// requestTimeout: 30 -// ); + $headersFiltered = []; + foreach ($executionResponse['headers'] as $key => $value) { + if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_RESPONSE)) { + $headersFiltered[] = ['name' => $key, 'value' => $value]; + } + } -// $headersFiltered = []; -// foreach ($executionResponse['headers'] as $key => $value) { -// if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_RESPONSE)) { -// $headersFiltered[] = ['name' => $key, 'value' => $value]; -// } -// } + /** Update execution status */ + $status = $executionResponse['statusCode'] >= 400 ? 'failed' : 'completed'; + $execution->setAttribute('status', $status); + $execution->setAttribute('responseStatusCode', $executionResponse['statusCode']); + $execution->setAttribute('responseHeaders', $headersFiltered); + $execution->setAttribute('logs', $executionResponse['logs']); + $execution->setAttribute('errors', $executionResponse['errors']); + $execution->setAttribute('duration', $executionResponse['duration']); + } catch (\Throwable $th) { + $durationEnd = \microtime(true); -// /** Update execution status */ -// $status = $executionResponse['statusCode'] >= 400 ? 'failed' : 'completed'; -// $execution->setAttribute('status', $status); -// $execution->setAttribute('responseStatusCode', $executionResponse['statusCode']); -// $execution->setAttribute('responseHeaders', $headersFiltered); -// $execution->setAttribute('logs', $executionResponse['logs']); -// $execution->setAttribute('errors', $executionResponse['errors']); -// $execution->setAttribute('duration', $executionResponse['duration']); -// } catch (\Throwable $th) { -// $durationEnd = \microtime(true); + $execution + ->setAttribute('duration', $durationEnd - $durationStart) + ->setAttribute('status', 'failed') + ->setAttribute('responseStatusCode', 500) + ->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode()); + Console::error($th->getMessage()); + } finally { + $queueForUsage + ->addMetric(METRIC_EXECUTIONS, 1) + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1) + ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project + ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function + ; + } -// $execution -// ->setAttribute('duration', $durationEnd - $durationStart) -// ->setAttribute('status', 'failed') -// ->setAttribute('responseStatusCode', 500) -// ->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode()); -// Console::error($th->getMessage()); -// } finally { -// $queueForUsage -// ->addMetric(METRIC_EXECUTIONS, 1) -// ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1) -// ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project -// ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function -// ; -// } + if ($function->getAttribute('logging')) { + /** @var Document $execution */ + $execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution)); + } -// if ($function->getAttribute('logging')) { -// /** @var Document $execution */ -// $execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution)); -// } + $execution->setAttribute('logs', ''); + $execution->setAttribute('errors', ''); -// $execution->setAttribute('logs', ''); -// $execution->setAttribute('errors', ''); + $headers = []; + foreach (($executionResponse['headers'] ?? []) as $key => $value) { + $headers[] = ['name' => $key, 'value' => $value]; + } -// $headers = []; -// foreach (($executionResponse['headers'] ?? []) as $key => $value) { -// $headers[] = ['name' => $key, 'value' => $value]; -// } + $execution->setAttribute('responseBody', $executionResponse['body'] ?? ''); + $execution->setAttribute('responseHeaders', $headers); -// $execution->setAttribute('responseBody', $executionResponse['body'] ?? ''); -// $execution->setAttribute('responseHeaders', $headers); + $body = $execution['responseBody'] ?? ''; -// $body = $execution['responseBody'] ?? ''; + $encodingKey = \array_search('x-open-runtimes-encoding', \array_column($execution['responseHeaders'], 'name')); + if ($encodingKey !== false) { + if (($execution['responseHeaders'][$encodingKey]['value'] ?? '') === 'base64') { + $body = \base64_decode($body); + } + } -// $encodingKey = \array_search('x-open-runtimes-encoding', \array_column($execution['responseHeaders'], 'name')); -// if ($encodingKey !== false) { -// if (($execution['responseHeaders'][$encodingKey]['value'] ?? '') === 'base64') { -// $body = \base64_decode($body); -// } -// } + $contentType = 'text/plain'; + foreach ($execution['responseHeaders'] as $header) { + if (\strtolower($header['name']) === 'content-type') { + $contentType = $header['value']; + } -// $contentType = 'text/plain'; -// foreach ($execution['responseHeaders'] as $header) { -// if (\strtolower($header['name']) === 'content-type') { -// $contentType = $header['value']; -// } + $response->setHeader($header['name'], $header['value']); + } -// $response->setHeader($header['name'], $header['value']); -// } + $response + ->setContentType($contentType) + ->setStatusCode($execution['responseStatusCode'] ?? 200) + ->send($body); -// $response -// ->setContentType($contentType) -// ->setStatusCode($execution['responseStatusCode'] ?? 200) -// ->send($body); - -// return true; -// } elseif ($type === 'api') { -// $utopia->getRoute()?->label('error', ''); -// return false; -// } else { -// throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown resource type ' . $type); -// } - -// $utopia->getRoute()?->label('error', ''); -// return false; -// } + return true; + } elseif ($type === 'api') { + $route?->label('error', ''); + return false; + } else { + throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown resource type ' . $type); + } +} Http::init() ->groups(['api', 'web']) @@ -365,13 +361,13 @@ Http::init() ->inject('console') ->inject('project') ->inject('dbForConsole') - // ->inject('getProjectDB') + ->inject('getProjectDB') ->inject('locale') ->inject('localeCodes') ->inject('clients') - // ->inject('geodb') - // ->inject('queueForUsage') - // ->inject('queueForEvents') + ->inject('geodb') + ->inject('queueForUsage') + ->inject('queueForEvents') ->inject('queueForCertificates') ->inject('authorization') ->action(function ( @@ -381,15 +377,16 @@ Http::init() Document $console, Document $project, Database $dbForConsole, + $getProjectDB, Locale $locale, array $localeCodes, array $clients, /** * @disregard P1009 Undefined type */ - // Reader $geodb, - // Usage $queueForUsage, - // Event $queueForEvents, + Reader $geodb, + Usage $queueForUsage, + Event $queueForEvents, Certificate $queueForCertificates, Authorization $authorization ) { @@ -399,11 +396,11 @@ Http::init() $host = $request->getHostname() ?? ''; $mainDomain = System::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain - // if ($host !== $mainDomain) { - // if (router($utopia, $dbForConsole, $getProjectDB, $request, $response, $queueForEvents, $queueForUsage, $geodb, $auth)) { - // return; - // } - // } + if ($host !== $mainDomain) { + if (router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization)) { + return; + } + } /* * Request format @@ -590,41 +587,41 @@ Http::init() } }); -// Http::options() -// ->inject('utopia') -// ->inject('swooleRequest') -// ->inject('request') -// ->inject('response') -// ->inject('dbForConsole') -// ->inject('getProjectDB') -// ->inject('queueForEvents') -// ->inject('queueForUsage') -// ->inject('geodb') -// ->inject('auth') -// ->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $auth) { -// /* -// * Appwrite Router -// */ -// $host = $request->getHostname() ?? ''; -// $mainDomain = System::getEnv('_APP_DOMAIN', ''); -// // Only run Router when external domain -// if ($host !== $mainDomain) { -// if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb, $auth)) { -// return; -// } -// } +Http::options() + ->inject('route') + ->inject('swooleRequest') + ->inject('request') + ->inject('response') + ->inject('dbForConsole') + ->inject('getProjectDB') + ->inject('queueForEvents') + ->inject('queueForUsage') + ->inject('geodb') + ->inject('auth') + ->action(function (Route $route, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $auth) { + /* + * Appwrite Router + */ + $host = $request->getHostname() ?? ''; + $mainDomain = System::getEnv('_APP_DOMAIN', ''); + // Only run Router when external domain + if ($host !== $mainDomain) { + if (router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $auth)) { + return; + } + } -// $origin = $request->getOrigin(); + $origin = $request->getOrigin(); -// $response -// ->addHeader('Server', 'Appwrite') -// ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') -// ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent') -// ->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies') -// ->addHeader('Access-Control-Allow-Origin', $origin) -// ->addHeader('Access-Control-Allow-Credentials', 'true') -// ->noContent(); -// }); + $response + ->addHeader('Server', 'Appwrite') + ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') + ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent') + ->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies') + ->addHeader('Access-Control-Allow-Origin', $origin) + ->addHeader('Access-Control-Allow-Credentials', 'true') + ->noContent(); + }); Http::error() ->inject('error') diff --git a/app/init2.php b/app/init2.php index 703e2a0a73..70ab15b397 100644 --- a/app/init2.php +++ b/app/init2.php @@ -1016,3 +1016,40 @@ $requestTimestamp return $requestTimestamp; }); $container->set($requestTimestamp); + +$getProjectDB = new Dependency(); +$getProjectDB + ->setName('getProjectDB') + ->inject('pools') + ->inject('dbForConsole') + ->inject('cache') + ->inject('authorization') + ->inject('connections') + ->setCallback(function (array $pools, Database $dbForConsole, $cache, Authorization $authorization, Connections $connections) { + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $authorization, $connections): Database { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + $databaseName = $project->getAttribute('database'); + + $pool = $pools['pools-database-'.$databaseName]['pool']; + $dsn = $pools['pools-database-'.$databaseName]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + $adapter->setDatabase($dsn->getPath()); + + $database = new Database($adapter, $cache); + $database->setAuthorization($authorization); + $database->setNamespace('_' . $project->getInternalId()); + + return $database; + }; + }); +$container->set($getProjectDB); diff --git a/composer.lock b/composer.lock index 17e9c54bae..9c6618c9ce 100644 --- a/composer.lock +++ b/composer.lock @@ -1827,18 +1827,18 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "62c2b5b2b1e0e598cd67d79143e4dd210c44d499" + "reference": "787b884c306f67b67bd0834b3cabddce1652fd29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/62c2b5b2b1e0e598cd67d79143e4dd210c44d499", - "reference": "62c2b5b2b1e0e598cd67d79143e4dd210c44d499", + "url": "https://api.github.com/repos/utopia-php/http/zipball/787b884c306f67b67bd0834b3cabddce1652fd29", + "reference": "787b884c306f67b67bd0834b3cabddce1652fd29", "shasum": "" }, "require": { "ext-swoole": "*", "php": ">=8.0", - "utopia-php/di": "dev-main" + "utopia-php/servers": "dev-dev" }, "require-dev": { "ext-xdebug": "*", @@ -1869,7 +1869,7 @@ "issues": "https://github.com/utopia-php/http/issues", "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-04-18T20:53:06+00:00" + "time": "2024-04-22T14:38:43+00:00" }, { "name": "utopia-php/image", diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index ce1a6ac7c6..cd62cc3776 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -46,7 +46,7 @@ class Resolvers switch ($route->getMethod()) { case 'GET': - $request->setQueryString($args); + $request->setQuery($args); break; default: $request->setPayload($args); @@ -134,7 +134,7 @@ class Resolvers $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); - $request->setQueryString($params($databaseId, $collectionId, $args)); + $request->setQuery($params($databaseId, $collectionId, $args)); $beforeResolve = function ($payload) { return $payload['documents']; diff --git a/tests/unit/Utopia/RequestTest.php b/tests/unit/Utopia/RequestTest.php index 73daaa88bc..ab067b81d7 100644 --- a/tests/unit/Utopia/RequestTest.php +++ b/tests/unit/Utopia/RequestTest.php @@ -36,7 +36,7 @@ class RequestTest extends TestCase // set test header to prevent header populaten inside the request class $this->request->addHeader('EXAMPLE', 'VALUE'); $this->request->setRoute($route); - $this->request->setQueryString([ + $this->request->setQuery([ 'initial' => true, 'first' => false ]); From 09e9483d94ac8361f47e6ff9af16ac519ed039bb Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 21:38:48 +0200 Subject: [PATCH 049/195] Changed autoloading order --- app/cli.php | 2 +- app/http.php | 7 ++----- app/init2.php | 2 ++ app/worker.php | 1 - 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/cli.php b/app/cli.php index 9194edd5e1..423dff72b5 100644 --- a/app/cli.php +++ b/app/cli.php @@ -1,6 +1,6 @@ decode($authJWT); } catch (JWTException $error) { + $request->removeHeader('x-appwrite-jwt'); throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); } diff --git a/app/worker.php b/app/worker.php index 7763843955..33db5fa6a8 100644 --- a/app/worker.php +++ b/app/worker.php @@ -1,6 +1,5 @@ Date: Mon, 22 Apr 2024 21:43:57 +0200 Subject: [PATCH 050/195] Fixed storage tests --- app/controllers/api/storage.php | 2 +- composer.lock | 8 ++++---- tests/e2e/Services/Storage/StorageCustomClientTest.php | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index cec4c115a0..3390a963a2 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -1621,7 +1621,7 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') } // Make sure we don't delete the file before the document permission check occurs - if ($fileSecurity && !$valid && !$authorization->isValid($file->getDelete())) { + if ($fileSecurity && !$valid && !$authorization->isValid(new Input(Database::PERMISSION_DELETE, $file->getDelete()))) { throw new Exception(Exception::USER_UNAUTHORIZED); } diff --git a/composer.lock b/composer.lock index 9c6618c9ce..2024225af8 100644 --- a/composer.lock +++ b/composer.lock @@ -2287,12 +2287,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "309796b08891eac135540c241d8943dd42eccc9e" + "reference": "8749796c05bf9a0abc9c949af5ceb2efa8531960" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/309796b08891eac135540c241d8943dd42eccc9e", - "reference": "309796b08891eac135540c241d8943dd42eccc9e", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/8749796c05bf9a0abc9c949af5ceb2efa8531960", + "reference": "8749796c05bf9a0abc9c949af5ceb2efa8531960", "shasum": "" }, "require": { @@ -2342,7 +2342,7 @@ "issues": "https://github.com/utopia-php/queue/issues", "source": "https://github.com/utopia-php/queue/tree/feat-coroutine-and-di" }, - "time": "2024-04-21T18:59:04+00:00" + "time": "2024-04-22T18:32:34+00:00" }, { "name": "utopia-php/registry", diff --git a/tests/e2e/Services/Storage/StorageCustomClientTest.php b/tests/e2e/Services/Storage/StorageCustomClientTest.php index c723fba50a..55340ab849 100644 --- a/tests/e2e/Services/Storage/StorageCustomClientTest.php +++ b/tests/e2e/Services/Storage/StorageCustomClientTest.php @@ -1089,7 +1089,7 @@ class StorageCustomClientTest extends Scope $this->assertEquals(200, $file['headers']['status-code']); - // Team 1 view success + // Team 2 view success $file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/view', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1112,6 +1112,8 @@ class StorageCustomClientTest extends Scope 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'), ]); + $this->assertEquals($file['headers']['status-code'], 401); + // Team 2 create failure $file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', [ 'content-type' => 'multipart/form-data', From 86b46353ad514f6403f4632ad9938cb1af4eb7cc Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 22:35:38 +0200 Subject: [PATCH 051/195] Fixed for general tests --- app/controllers/api/project.php | 6 +++--- app/controllers/general.php | 8 ++++---- composer.lock | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 05a6c46389..c6ac657d28 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -31,8 +31,8 @@ Http::get('/v1/project/usage') ->param('period', '1d', new WhiteList(['1h', '1d']), 'Period used', true) ->inject('response') ->inject('dbForProject') - ->inject('auth') - ->action(function (string $startDate, string $endDate, string $period, Response $response, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->action(function (string $startDate, string $endDate, string $period, Response $response, Database $dbForProject, Authorization $authorization) { $stats = $total = $usage = []; $format = 'Y-m-d 00:00:00'; $firstDay = (new DateTime($startDate))->format($format); @@ -71,7 +71,7 @@ Http::get('/v1/project/usage') '1d' => 'Y-m-d\T00:00:00.000P', }; - $auth->skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, &$total, &$stats) { + $authorization->skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, &$total, &$stats) { foreach ($metrics['total'] as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/app/controllers/general.php b/app/controllers/general.php index d53be96186..d58efef3dc 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -589,7 +589,6 @@ Http::init() Http::options() ->inject('route') - ->inject('swooleRequest') ->inject('request') ->inject('response') ->inject('dbForConsole') @@ -597,8 +596,8 @@ Http::options() ->inject('queueForEvents') ->inject('queueForUsage') ->inject('geodb') - ->inject('auth') - ->action(function (Route $route, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $auth) { + ->inject('authorization') + ->action(function (Route $route, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $authorization) { /* * Appwrite Router */ @@ -606,7 +605,7 @@ Http::options() $mainDomain = System::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain if ($host !== $mainDomain) { - if (router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $auth)) { + if (router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization)) { return; } } @@ -898,6 +897,7 @@ include_once 'api/health.php'; include_once 'api/locale.php'; include_once 'api/messaging.php'; //include_once 'api/migrations.php'; +include_once 'api/project.php'; include_once 'api/projects.php'; include_once 'api/proxy.php'; include_once 'api/storage.php'; diff --git a/composer.lock b/composer.lock index 2024225af8..0050b28008 100644 --- a/composer.lock +++ b/composer.lock @@ -1827,12 +1827,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "787b884c306f67b67bd0834b3cabddce1652fd29" + "reference": "db782cedc80a639d1608d6dca7771fcff4031d88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/787b884c306f67b67bd0834b3cabddce1652fd29", - "reference": "787b884c306f67b67bd0834b3cabddce1652fd29", + "url": "https://api.github.com/repos/utopia-php/http/zipball/db782cedc80a639d1608d6dca7771fcff4031d88", + "reference": "db782cedc80a639d1608d6dca7771fcff4031d88", "shasum": "" }, "require": { @@ -1869,7 +1869,7 @@ "issues": "https://github.com/utopia-php/http/issues", "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-04-22T14:38:43+00:00" + "time": "2024-04-22T20:19:36+00:00" }, { "name": "utopia-php/image", From d67df5feb13fc55ddf3ef9e35d1cb92c1fcd40d9 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 22:45:55 +0200 Subject: [PATCH 052/195] Fixed formatting --- app/controllers/api/messaging.php | 8 ++++---- app/controllers/api/projects.php | 4 ++-- app/controllers/api/teams.php | 2 +- app/controllers/api/users.php | 2 +- app/controllers/general.php | 3 +-- app/controllers/shared/api.php | 2 +- app/http.php | 5 +++-- app/init2.php | 1 + app/realtime.php | 8 ++++---- src/Appwrite/Platform/Workers/Audits.php | 2 +- src/Appwrite/Platform/Workers/Deletes.php | 6 +++--- 11 files changed, 22 insertions(+), 21 deletions(-) diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index d98f4ee283..8c72a9b62f 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -920,7 +920,7 @@ Http::get('/v1/messaging/providers/:providerId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $authorization); + $audit = new Audit($dbForProject); $resource = 'provider/' . $providerId; $logs = $audit->getLogsByResource($resource, $limit, $offset); $output = []; @@ -2056,7 +2056,7 @@ Http::get('/v1/messaging/topics/:topicId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $authorization); + $audit = new Audit($dbForProject); $resource = 'topic/' . $topicId; $logs = $audit->getLogsByResource($resource, $limit, $offset); @@ -2427,7 +2427,7 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $authorization); + $audit = new Audit($dbForProject); $resource = 'subscriber/' . $subscriberId; $logs = $audit->getLogsByResource($resource, $limit, $offset); @@ -3115,7 +3115,7 @@ Http::get('/v1/messaging/messages/:messageId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $authorization); + $audit = new Audit($dbForProject); $resource = 'message/' . $messageId; $logs = $audit->getLogsByResource($resource, $limit, $offset); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 6e4415117e..d6e9ca58ff 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -199,9 +199,9 @@ Http::post('/v1/projects') $dbForProject->setNamespace("_{$project->getInternalId()}"); $dbForProject->create(); - $audit = new Audit($dbForProject, $authorization); + $audit = new Audit($dbForProject); $audit->setup(); - $adapter = new TimeLimit('', 0, 1, $dbForProject, $authorization); + $adapter = new TimeLimit('', 0, 1, $dbForProject); $adapter->setup(); /** @var array $collections */ $collections = Config::getParam('collections', [])['projects'] ?? []; diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 0dd1922f05..f55b140815 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -1156,7 +1156,7 @@ Http::get('/v1/teams/:teamId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $authorization); + $audit = new Audit($dbForProject); $resource = 'team/' . $team->getId(); $logs = $audit->getLogsByResource($resource, $limit, $offset); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index fc870045b6..27f605e571 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -788,7 +788,7 @@ Http::get('/v1/users/:userId/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new Audit($dbForProject, $authorization); + $audit = new Audit($dbForProject); $logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset); diff --git a/app/controllers/general.php b/app/controllers/general.php index d58efef3dc..cf727a2a05 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -15,7 +15,6 @@ use Appwrite\Utopia\Response\Filters\V17 as ResponseV17; use Appwrite\Utopia\View; use Executor\Executor; use MaxMind\Db\Reader; -use Swoole\Http\Request as SwooleRequest; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -597,7 +596,7 @@ Http::options() ->inject('queueForUsage') ->inject('geodb') ->inject('authorization') - ->action(function (Route $route, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $authorization) { + ->action(function (Route $route, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $authorization) { /* * Appwrite Router */ diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 66dc93f029..c46148cfb3 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -336,7 +336,7 @@ Http::init() foreach ($abuseKeyLabel as $abuseKey) { $start = $request->getContentRangeStart(); $end = $request->getContentRangeEnd(); - $timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject, $authorization); + $timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject); $timeLimit ->setParam('{projectId}', $project->getId()) ->setParam('{userId}', $user->getId()) diff --git a/app/http.php b/app/http.php index 0ba12219ee..307b9252fe 100644 --- a/app/http.php +++ b/app/http.php @@ -1,4 +1,5 @@ getCollection(Audit::COLLECTION)->isEmpty()) { - $audit = new Audit($dbForConsole, $authorization); + $audit = new Audit($dbForConsole); $audit->setup(); } if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { - $abuse = new TimeLimit("", 0, 1, $dbForConsole, $authorization); + $abuse = new TimeLimit("", 0, 1, $dbForConsole); $abuse->setup(); } diff --git a/app/init2.php b/app/init2.php index 7561fa7cd3..25161d0c31 100644 --- a/app/init2.php +++ b/app/init2.php @@ -1,4 +1,5 @@ onOpen(function (int $connection, SwooleRequest $request) use ($server, * * Abuse limits are connecting 128 times per minute and ip address. */ - $timeLimit = new TimeLimit('url:{url},ip:{ip}', 128, 60, $dbForProject, $auth); + $timeLimit = new TimeLimit('url:{url},ip:{ip}', 128, 60, $dbForProject); $timeLimit ->setParam('{ip}', $request->getIP()) ->setParam('{url}', $request->getURI()); - $abuse = new Abuse($timeLimit, $auth); + $abuse = new Abuse($timeLimit); if (System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled' && $abuse->check()) { throw new Exception(Exception::REALTIME_TOO_MANY_MESSAGES, 'Too many requests'); @@ -540,13 +540,13 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re * * Abuse limits are sending 32 times per minute and connection. */ - $timeLimit = new TimeLimit('url:{url},connection:{connection}', 32, 60, $database, $auth); + $timeLimit = new TimeLimit('url:{url},connection:{connection}', 32, 60, $database); $timeLimit ->setParam('{connection}', $connection) ->setParam('{container}', $containerId); - $abuse = new Abuse($timeLimit, $auth); + $abuse = new Abuse($timeLimit); if ($abuse->check() && System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') { throw new Exception(Exception::REALTIME_TOO_MANY_MESSAGES, 'Too many messages.'); diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 0650906538..4712e58cbe 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -63,7 +63,7 @@ class Audits extends Action $userName = $user->getAttribute('name', ''); $userEmail = $user->getAttribute('email', ''); - $audit = new Audit($dbForProject, $auth); + $audit = new Audit($dbForProject); $audit->log( userId: $user->getInternalId(), // Pass first, most verbose event pattern diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 2b474234f8..ec3f142d37 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -660,8 +660,8 @@ class Deletes extends Action { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); - $timeLimit = new TimeLimit("", 0, 1, $dbForProject, $auth); - $abuse = new Abuse($timeLimit, $auth); + $timeLimit = new TimeLimit("", 0, 1, $dbForProject); + $abuse = new Abuse($timeLimit); $status = $abuse->cleanup($abuseRetention); if (!$status) { throw new Exception('Failed to delete Abuse logs for project ' . $projectId); @@ -679,7 +679,7 @@ class Deletes extends Action { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); - $audit = new Audit($dbForProject, $auth); + $audit = new Audit($dbForProject); $status = $audit->cleanup($auditRetention); if (!$status) { throw new Exception('Failed to delete Audit logs for project' . $projectId); From 69a43aa6671903a187c6c0afd0efaf425d1061ad Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 23:28:40 +0200 Subject: [PATCH 053/195] Fixed projects tests --- composer.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/composer.lock b/composer.lock index 0050b28008..7e6c800328 100644 --- a/composer.lock +++ b/composer.lock @@ -1619,12 +1619,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/di.git", - "reference": "fb3c45b268018b87dcbbf87ad943ced9eea3bbe2" + "reference": "a0c8be65c19570c80e904d58f54bdd901d1d5d9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/di/zipball/fb3c45b268018b87dcbbf87ad943ced9eea3bbe2", - "reference": "fb3c45b268018b87dcbbf87ad943ced9eea3bbe2", + "url": "https://api.github.com/repos/utopia-php/di/zipball/a0c8be65c19570c80e904d58f54bdd901d1d5d9c", + "reference": "a0c8be65c19570c80e904d58f54bdd901d1d5d9c", "shasum": "" }, "require": { @@ -1673,7 +1673,7 @@ "source": "https://github.com/utopia-php/di/tree/main", "issues": "https://github.com/utopia-php/di/issues" }, - "time": "2024-04-18T20:06:02+00:00" + "time": "2024-04-22T21:22:44+00:00" }, { "name": "utopia-php/domains", @@ -1827,12 +1827,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "db782cedc80a639d1608d6dca7771fcff4031d88" + "reference": "cc3341f13d6b90f3104f197acb9136abe2fc708a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/db782cedc80a639d1608d6dca7771fcff4031d88", - "reference": "db782cedc80a639d1608d6dca7771fcff4031d88", + "url": "https://api.github.com/repos/utopia-php/http/zipball/cc3341f13d6b90f3104f197acb9136abe2fc708a", + "reference": "cc3341f13d6b90f3104f197acb9136abe2fc708a", "shasum": "" }, "require": { @@ -1869,7 +1869,7 @@ "issues": "https://github.com/utopia-php/http/issues", "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-04-22T20:19:36+00:00" + "time": "2024-04-22T21:25:06+00:00" }, { "name": "utopia-php/image", @@ -2287,12 +2287,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "8749796c05bf9a0abc9c949af5ceb2efa8531960" + "reference": "2133eb6da85156ff4abf0d0940715fa3975424f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/8749796c05bf9a0abc9c949af5ceb2efa8531960", - "reference": "8749796c05bf9a0abc9c949af5ceb2efa8531960", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/2133eb6da85156ff4abf0d0940715fa3975424f2", + "reference": "2133eb6da85156ff4abf0d0940715fa3975424f2", "shasum": "" }, "require": { @@ -2342,7 +2342,7 @@ "issues": "https://github.com/utopia-php/queue/issues", "source": "https://github.com/utopia-php/queue/tree/feat-coroutine-and-di" }, - "time": "2024-04-22T18:32:34+00:00" + "time": "2024-04-22T21:24:21+00:00" }, { "name": "utopia-php/registry", @@ -2402,12 +2402,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/servers.git", - "reference": "20bb7ab93c21d0ae37bc309d41b70ddf2a78a6fd" + "reference": "e7eee7f82399c89adc28cf79912a9a41d7bb3233" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/servers/zipball/20bb7ab93c21d0ae37bc309d41b70ddf2a78a6fd", - "reference": "20bb7ab93c21d0ae37bc309d41b70ddf2a78a6fd", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/e7eee7f82399c89adc28cf79912a9a41d7bb3233", + "reference": "e7eee7f82399c89adc28cf79912a9a41d7bb3233", "shasum": "" }, "require": { @@ -2465,7 +2465,7 @@ "source": "https://github.com/utopia-php/servers/tree/dev", "issues": "https://github.com/utopia-php/servers/issues" }, - "time": "2024-04-21T18:53:43+00:00" + "time": "2024-04-22T21:23:28+00:00" }, { "name": "utopia-php/storage", From 46ab7b1a36b4d9302601960297cb9d88777e6656 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Mon, 22 Apr 2024 23:33:18 +0200 Subject: [PATCH 054/195] Fixed functions test --- app/controllers/general.php | 1 + app/controllers/mock.php | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index cf727a2a05..2d02696a3b 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -884,6 +884,7 @@ foreach (Config::getParam('services', []) as $service) { //include_once $service['controller']; } +include_once 'mock.php'; include_once 'shared/api.php'; include_once 'shared/api/auth.php'; include_once 'api/account.php'; diff --git a/app/controllers/mock.php b/app/controllers/mock.php index 4f927897c3..edea7b220d 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -3,7 +3,6 @@ global $utopia, $request, $response; use Appwrite\Extend\Exception; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Document; @@ -12,6 +11,7 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\UID; use Utopia\Http\Http; +use Utopia\Http\Route; use Utopia\Http\Validator\Host; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; @@ -216,13 +216,11 @@ Http::get('/v1/mock/github/callback') Http::shutdown() ->groups(['mock']) - ->inject('utopia') + ->inject('route') ->inject('response') - ->inject('request') - ->action(function (Http $utopia, Response $response, Request $request) { + ->action(function (Route $route, Response $response) { $result = []; - $route = $utopia->getRoute(); $path = APP_STORAGE_CACHE . '/tests.json'; $tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : []; From beeb66e66d73f50c00f88e9237ae4454c03ca193 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 23 Apr 2024 00:17:07 +0200 Subject: [PATCH 055/195] Fixed account tests --- app/controllers/general.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 2d02696a3b..c0756eca14 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -704,7 +704,7 @@ Http::error() } /** Handle Utopia Errors */ - if ($error instanceof Utopia\Http\Exception) { + if ($error instanceof Utopia\Http\Exception || $error instanceof Utopia\Servers\Exception) { $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); switch ($code) { case 400: From 9fc0342cf1bc01113ba70fa6daf5faa45c5aa092 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 23 Apr 2024 13:30:20 +0200 Subject: [PATCH 056/195] Updated injections --- app/controllers/api/graphql.php | 6 +- app/controllers/api/messaging.php | 3 +- app/controllers/general.php | 4 +- app/init2.php | 107 +++++++++++++++++++++++++++++- 4 files changed, 112 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index 7104ba5da2..6be785f5a0 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -24,12 +24,12 @@ use Utopia\System\System; Http::init() ->groups(['graphql']) ->inject('project') - ->inject('auth') - ->action(function (Document $project, Authorization $auth) { + ->inject('authorization') + ->action(function (Document $project, Authorization $authorization) { if ( array_key_exists('graphql', $project->getAttribute('apis', [])) && !$project->getAttribute('apis', [])['graphql'] - && !(Auth::isPrivilegedUser($auth->getRoles()) || Auth::isAppUser($auth->getRoles())) + && !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles())) ) { throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 8c72a9b62f..d19286a2ff 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -902,8 +902,7 @@ Http::get('/v1/messaging/providers/:providerId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('authorization') - ->action(function (string $providerId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { + ->action(function (string $providerId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { $provider = $dbForProject->getDocument('providers', $providerId); if ($provider->isEmpty()) { diff --git a/app/controllers/general.php b/app/controllers/general.php index c0756eca14..348fb1d825 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -892,11 +892,11 @@ include_once 'api/avatars.php'; include_once 'api/console.php'; include_once 'api/databases.php'; include_once 'api/functions.php'; -//include_once 'api/graphql.php'; +include_once 'api/graphql.php'; include_once 'api/health.php'; include_once 'api/locale.php'; include_once 'api/messaging.php'; -//include_once 'api/migrations.php'; +include_once 'api/migrations.php'; include_once 'api/project.php'; include_once 'api/projects.php'; include_once 'api/proxy.php'; diff --git a/app/init2.php b/app/init2.php index 25161d0c31..812957602b 100644 --- a/app/init2.php +++ b/app/init2.php @@ -34,6 +34,8 @@ use Appwrite\Event\Messaging; use Appwrite\Event\Migration; use Appwrite\Event\Usage; use Appwrite\Extend\Exception; +use Appwrite\GraphQL\Promises\Adapter\Swoole; +use Appwrite\GraphQL\Schema; use Appwrite\Hooks\Hooks; use Appwrite\Network\Validator\Origin; use Appwrite\URL\URL; @@ -41,7 +43,6 @@ use Appwrite\Utopia\Queue\Connections; use MaxMind\Db\Reader; use Swoole\Database\PDOConfig; use Swoole\Database\PDOPool; -use Swoole\Database\PDOProxy; use Swoole\Database\RedisConfig; use Swoole\Database\RedisPool; use Utopia\Cache\Adapter\None; @@ -52,6 +53,7 @@ use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Query; use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; use Utopia\DI\Container; @@ -368,6 +370,10 @@ $global->set('smtp', function () { return $mail; }); +$global->set('promiseAdapter', function () { + return new Swoole(); +}); + $mode = new Dependency(); $mode ->setName('mode') @@ -1056,3 +1062,102 @@ $getProjectDB }; }); $container->set($getProjectDB); + +$promiseAdapter = new Dependency(); +$promiseAdapter + ->setName('promiseAdapter') + ->inject('register') + ->setCallback(function ($register) { + return $register->get('promiseAdapter'); + }); +$container->set($promiseAdapter); + +$schema = new Dependency(); +$schema + ->setName('schema') + ->inject('utopia') + ->inject('dbForProject') + ->inject('auth') + ->setCallback(function (Http $utopia, Database $dbForProject, Authorization $auth) { + $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, $auth) { + $attrs = $auth->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']); + + 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']); + + return [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'documentId' => $documentId, + 'data' => $args, + 'permissions' => $permissions, + ]; + }, + ]; + + return Schema::build( + $utopia, + $complexity, + $attributes, + $urls, + $params, + ); + }); +$container->set($schema); \ No newline at end of file From 55fe725f4b13288cd6c3e1068359a47edeba995f Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 24 Apr 2024 23:16:43 +0200 Subject: [PATCH 057/195] Fixed format --- app/init2.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/init2.php b/app/init2.php index 812957602b..f0432f5cee 100644 --- a/app/init2.php +++ b/app/init2.php @@ -53,8 +53,8 @@ use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Database\Query; use Utopia\Database\Helpers\ID; +use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\DI\Container; use Utopia\DI\Dependency; @@ -1160,4 +1160,4 @@ $schema $params, ); }); -$container->set($schema); \ No newline at end of file +$container->set($schema); From 90ef64c63e772c6387759bfb78a965ef8dccc8b1 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 8 May 2024 18:16:43 +0100 Subject: [PATCH 058/195] Fixed namespace --- tests/unit/Utopia/RequestTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/Utopia/RequestTest.php b/tests/unit/Utopia/RequestTest.php index ab067b81d7..9ae754555e 100644 --- a/tests/unit/Utopia/RequestTest.php +++ b/tests/unit/Utopia/RequestTest.php @@ -7,7 +7,7 @@ use PHPUnit\Framework\TestCase; use Swoole\Http\Request as SwooleRequest; use Tests\Unit\Utopia\Request\Filters\First; use Tests\Unit\Utopia\Request\Filters\Second; -use Utopia\Route; +use Utopia\Http\Route; class RequestTest extends TestCase { From 5cfe4db83953d1e40be8ab792017cc6bc52d0ef6 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 8 May 2024 18:24:15 +0100 Subject: [PATCH 059/195] Updated console --- app/console | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/console b/app/console index f483d9631d..053a975eb7 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit f483d9631d6f21e94aedb20b5c37c56fea06c23e +Subproject commit 053a975eb7f9b8c28847e7a52852f3b6189b986b From 0b7c795e2b82da1cef0f074e75b2892422c926b4 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 8 May 2024 19:33:51 +0100 Subject: [PATCH 060/195] Updated lock file --- composer.lock | 208 +++++++++++++++++++++++++++----------------------- 1 file changed, 114 insertions(+), 94 deletions(-) diff --git a/composer.lock b/composer.lock index 0283621297..d2ad73cfa6 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": "3f703f77225d4022cd5823814ab7c3e7", + "content-hash": "bb3213cfb05b043e7d2ea2002069bb98", "packages": [ { "name": "adhocore/jwt", @@ -822,16 +822,16 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.6.3", + "version": "v2.x-dev", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "58c3f47f650c94ec05a151692652a868995d2938" + "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", - "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/52a0d99e69f56b9ec27ace92ba56897fe6993105", + "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105", "shasum": "" }, "require": { @@ -885,7 +885,7 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2022-06-14T06:56:20+00:00" + "time": "2024-05-08T12:18:48+00:00" }, { "name": "phpmailer/phpmailer", @@ -1737,16 +1737,16 @@ }, { "name": "utopia-php/dsn", - "version": "0.2.0", + "version": "0.2.1", "source": { "type": "git", "url": "https://github.com/utopia-php/dsn.git", - "reference": "c11f37a12c3f6aaf9fea97ca7cb363dcc93668d7" + "reference": "42ee37a3d1785100b2f69091c9d4affadb6846eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dsn/zipball/c11f37a12c3f6aaf9fea97ca7cb363dcc93668d7", - "reference": "c11f37a12c3f6aaf9fea97ca7cb363dcc93668d7", + "url": "https://api.github.com/repos/utopia-php/dsn/zipball/42ee37a3d1785100b2f69091c9d4affadb6846eb", + "reference": "42ee37a3d1785100b2f69091c9d4affadb6846eb", "shasum": "" }, "require": { @@ -1778,9 +1778,9 @@ ], "support": { "issues": "https://github.com/utopia-php/dsn/issues", - "source": "https://github.com/utopia-php/dsn/tree/0.2.0" + "source": "https://github.com/utopia-php/dsn/tree/0.2.1" }, - "time": "2023-11-02T12:01:43+00:00" + "time": "2024-05-07T02:01:25+00:00" }, { "name": "utopia-php/fetch", @@ -2075,16 +2075,16 @@ }, { "name": "utopia-php/migration", - "version": "0.4.0", + "version": "0.4.1", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "a72f27bd3dde68752fb185d306c4820e1b8d9657" + "reference": "ae3cfe93f6d313105d226aeb68806660c806a925" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/a72f27bd3dde68752fb185d306c4820e1b8d9657", - "reference": "a72f27bd3dde68752fb185d306c4820e1b8d9657", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/ae3cfe93f6d313105d226aeb68806660c806a925", + "reference": "ae3cfe93f6d313105d226aeb68806660c806a925", "shasum": "" }, "require": { @@ -2117,9 +2117,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.4.0" + "source": "https://github.com/utopia-php/migration/tree/0.4.1" }, - "time": "2024-02-25T12:35:21+00:00" + "time": "2024-05-01T13:19:18+00:00" }, { "name": "utopia-php/mongo", @@ -2397,17 +2397,88 @@ "time": "2021-03-10T10:45:22+00:00" }, { - "name": "utopia-php/storage", - "version": "0.18.4", + "name": "utopia-php/servers", + "version": "dev-dev", "source": { "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "94ab8758fabcefee5c5fa723616e45719833f922" + "url": "https://github.com/utopia-php/servers.git", + "reference": "e7eee7f82399c89adc28cf79912a9a41d7bb3233" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/94ab8758fabcefee5c5fa723616e45719833f922", - "reference": "94ab8758fabcefee5c5fa723616e45719833f922", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/e7eee7f82399c89adc28cf79912a9a41d7bb3233", + "reference": "e7eee7f82399c89adc28cf79912a9a41d7bb3233", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/di": "dev-main" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Servers\\": "src/Servers" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\E2E\\": "tests/Servers/Unit" + } + }, + "scripts": { + "test": [ + "phpunit" + ], + "analyse": [ + "vendor/bin/phpstan analyse" + ], + "format": [ + "vendor/bin/pint" + ], + "lint": [ + "vendor/bin/pint --test" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A base library for building Utopia style servers.", + "keywords": [ + "framework", + "php", + "servers", + "upf", + "utopia" + ], + "support": { + "source": "https://github.com/utopia-php/servers/tree/dev", + "issues": "https://github.com/utopia-php/servers/issues" + }, + "time": "2024-04-22T21:23:28+00:00" + }, + { + "name": "utopia-php/storage", + "version": "dev-feat-framework-v2-v2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "80eafa63cb86b33ce35d48c0189bae6ebdc02734" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/80eafa63cb86b33ce35d48c0189bae6ebdc02734", + "reference": "80eafa63cb86b33ce35d48c0189bae6ebdc02734", "shasum": "" }, "require": { @@ -2447,60 +2518,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.4" + "source": "https://github.com/utopia-php/storage/tree/feat-framework-v2-v2" }, - "time": "2024-04-02T08:24:09+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.8.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.33.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "5.0.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.8.2" - }, - "time": "2024-02-01T14:54:12+00:00" + "time": "2024-03-08T09:39:46+00:00" }, { "name": "utopia-php/system", @@ -3004,16 +3024,16 @@ }, { "name": "laravel/pint", - "version": "v1.15.2", + "version": "v1.15.3", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "2c9f8004899815f3f0ee3cb28ef7281e2b589134" + "reference": "3600b5d17aff52f6100ea4921849deacbbeb8656" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/2c9f8004899815f3f0ee3cb28ef7281e2b589134", - "reference": "2c9f8004899815f3f0ee3cb28ef7281e2b589134", + "url": "https://api.github.com/repos/laravel/pint/zipball/3600b5d17aff52f6100ea4921849deacbbeb8656", + "reference": "3600b5d17aff52f6100ea4921849deacbbeb8656", "shasum": "" }, "require": { @@ -3066,7 +3086,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-04-23T15:42:34+00:00" + "time": "2024-04-30T15:02:26+00:00" }, { "name": "matthiasmullie/minify", @@ -3486,16 +3506,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.4.0", + "version": "5.x-dev", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "298d2febfe79d03fe714eb871d5538da55205b1a" + "reference": "2f7b34c2ee49829a36cec9f545605d385bf3e291" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/298d2febfe79d03fe714eb871d5538da55205b1a", - "reference": "298d2febfe79d03fe714eb871d5538da55205b1a", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2f7b34c2ee49829a36cec9f545605d385bf3e291", + "reference": "2f7b34c2ee49829a36cec9f545605d385bf3e291", "shasum": "" }, "require": { @@ -3545,9 +3565,9 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.0" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.x" }, - "time": "2024-04-09T21:13:58+00:00" + "time": "2024-05-07T05:11:38+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -3680,16 +3700,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.28.0", + "version": "1.29.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb" + "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", - "reference": "cd06d6b1a1b3c75b0b83f97577869fd85a3cd4fb", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/536889f2b340489d328f5ffb7b02bb6b183ddedc", + "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc", "shasum": "" }, "require": { @@ -3721,9 +3741,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.28.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.0" }, - "time": "2024-04-03T18:51:33+00:00" + "time": "2024-05-06T12:04:23+00:00" }, { "name": "phpstan/phpstan", From 525383c725431916f3b321d193dd6d14ba6291a9 Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Wed, 8 May 2024 21:01:07 +0100 Subject: [PATCH 061/195] Updates to realtime --- app/realtime.php | 58 ++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index e7d8faf2c0..2e2024216a 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -32,19 +32,21 @@ use Utopia\System\System; use Utopia\WebSocket\Adapter; use Utopia\WebSocket\Server; +global $global; + /** - * @var \Utopia\Registry\Registry $register + * @var \Utopia\Registry\Registry $global */ -require_once __DIR__ . '/init.php'; +require_once __DIR__ . '/init2.php'; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); function getConsoleDB(Authorization $auth): Database { - global $register; + global $global; /** @var \Utopia\Pools\Group $pools */ - $pools = $register->get('pools'); + $pools = $global->get('pools'); $dbAdapter = $pools ->get('console') @@ -65,10 +67,10 @@ function getConsoleDB(Authorization $auth): Database function getProjectDB(Document $project, Authorization $auth): Database { - global $register; + global $global; /** @var \Utopia\Pools\Group $pools */ - $pools = $register->get('pools'); + $pools = $global->get('pools'); if ($project->isEmpty() || $project->getId() === 'console') { return getConsoleDB($auth); @@ -93,9 +95,9 @@ function getProjectDB(Document $project, Authorization $auth): Database function getCache(): Cache { - global $register; + global $global; - $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ + $pools = $global->get('pools'); /** @var \Utopia\Pools\Group $pools */ $list = Config::getParam('pools-cache', []); $adapters = []; @@ -135,8 +137,8 @@ $adapter $server = new Server($adapter); -$logError = function (Throwable $error, string $action) use ($register) { - $logger = $register->get('logger'); +$logError = function (Throwable $error, string $action) use ($global) { + $logger = $global->get('logger'); if ($logger && !$error instanceof Exception) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); @@ -173,7 +175,7 @@ $logError = function (Throwable $error, string $action) use ($register) { $server->error($logError); -$server->onStart(function () use ($stats, $register, $containerId, &$statsDocument, $logError) { +$server->onStart(function () use ($stats, $global, $containerId, &$statsDocument, $logError) { $auth = new Authorization(); sleep(5); // wait for the initial database schema to be ready @@ -182,7 +184,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume /** * Create document for this worker to share stats across Containers. */ - go(function () use ($register, $containerId, &$statsDocument, $auth) { + go(function () use ($global, $containerId, &$statsDocument, $auth) { $attempts = 0; $database = getConsoleDB($auth); @@ -206,13 +208,13 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume sleep(DATABASE_RECONNECT_SLEEP); } } while (true); - $register->get('pools')->reclaim(); + $global->get('pools')->reclaim(); }); /** * Save current connections to the Database every 5 seconds. */ - Timer::tick(5000, function () use ($register, $stats, &$statsDocument, $logError, $auth) { + Timer::tick(5000, function () use ($global, $stats, &$statsDocument, $logError, $auth) { $payload = []; foreach ($stats as $projectId => $value) { $payload[$projectId] = $stats->get($projectId, 'connectionsTotal'); @@ -233,12 +235,12 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume } catch (Throwable $th) { call_user_func($logError, $th, "updateWorkerDocument"); } finally { - $register->get('pools')->reclaim(); + $global->get('pools')->reclaim(); } }); }); -$server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime, $logError) { +$server->onWorkerStart(function (int $workerId) use ($server, $global, $stats, $realtime, $logError) { Console::success('Worker ' . $workerId . ' started successfully'); $attempts = 0; @@ -246,7 +248,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $auth = new Authorization(); - Timer::tick(5000, function () use ($server, $register, $realtime, $stats, $logError, $auth) { + Timer::tick(5000, function () use ($server, $global, $realtime, $stats, $logError, $auth) { /** * Sending current connections to project channels on the console project every 5 seconds. */ @@ -296,7 +298,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, ])); } - $register->get('pools')->reclaim(); + $global->get('pools')->reclaim(); } /** * Sending test message for SDK E2E tests every 5 seconds. @@ -329,9 +331,10 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, Attempting restart in 5 seconds (attempt #' . $attempts . ')'); sleep(5); // 5 sec delay between connection attempts } + $start = time(); - $redis = $register->get('pools')->get('pubsub')->pop()->getResource(); /** @var Redis $redis */ + $redis = $global->get('pools')->get('pubsub')->pop()->getResource(); /** @var Redis $redis */ $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); if ($redis->ping(true)) { @@ -341,7 +344,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, Console::error('Pub/sub failed (worker: ' . $workerId . ')'); } - $redis->subscribe(['realtime'], function (Redis $redis, string $channel, string $payload) use ($server, $workerId, $stats, $register, $realtime, $auth) { + $redis->subscribe(['realtime'], function (Redis $redis, string $channel, string $payload) use ($server, $workerId, $stats, $global, $realtime, $auth) { $event = json_decode($payload, true); if ($event['permissionsChanged'] && isset($event['userId'])) { @@ -361,7 +364,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime->subscribe($projectId, $connection, $roles, $realtime->connections[$connection]['channels']); - $register->get('pools')->reclaim(); + $global->get('pools')->reclaim(); } } @@ -393,14 +396,15 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, sleep(DATABASE_RECONNECT_SLEEP); continue; } finally { - $register->get('pools')->reclaim(); + //$global->get('pools')->reclaim(); + // TODO eldad add connections reclaim } } Console::error('Failed to restart pub/sub...'); }); -$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime, $logError) { +$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $global, $stats, &$realtime, $logError) { $auth = new Authorization(); $http = new Http(new FPMServer(), 'UTC'); @@ -409,7 +413,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::info("Connection open (user: {$connection})"); - Http::setResource('pools', fn () => $register->get('pools')); + Http::setResource('pools', fn () => $global->get('pools')); Http::setResource('request', fn () => $request); Http::setResource('response', fn () => $response); @@ -515,11 +519,11 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::error('[Error] Message: ' . $response['data']['message']); } } finally { - $register->get('pools')->reclaim(); + $global->get('pools')->reclaim(); } }); -$server->onMessage(function (int $connection, string $message) use ($server, $register, $realtime, $containerId) { +$server->onMessage(function (int $connection, string $message) use ($server, $global, $realtime, $containerId) { $auth = new Authorization(); try { @@ -615,7 +619,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re $server->close($connection, $th->getCode()); } } finally { - $register->get('pools')->reclaim(); + $global->get('pools')->reclaim(); } }); From 90fd0ba3922020a7fbabcac6e2bc8b0b917165ae Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Thu, 9 May 2024 21:50:34 +0100 Subject: [PATCH 062/195] Updated db lib --- composer.lock | 97 +++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/composer.lock b/composer.lock index d2ad73cfa6..5efc100fdf 100644 --- a/composer.lock +++ b/composer.lock @@ -1562,12 +1562,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "0d7b914c7770a5c488f104a36147d91db3b71368" + "reference": "f53d63bc5903ea6d6ff8f61293a20235e912b242" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/0d7b914c7770a5c488f104a36147d91db3b71368", - "reference": "0d7b914c7770a5c488f104a36147d91db3b71368", + "url": "https://api.github.com/repos/utopia-php/database/zipball/f53d63bc5903ea6d6ff8f61293a20235e912b242", + "reference": "f53d63bc5903ea6d6ff8f61293a20235e912b242", "shasum": "" }, "require": { @@ -1575,7 +1575,6 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.9.*", - "utopia-php/fetch": "0.1.*", "utopia-php/framework": "0.34.*", "utopia-php/mongo": "0.3.*" }, @@ -1609,9 +1608,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/feat-framework-v2" + "source": "https://github.com/utopia-php/database/tree/feat-framework-v2-1" }, - "time": "2024-04-19T14:37:34+00:00" + "time": "2024-05-09T18:33:47+00:00" }, { "name": "utopia-php/di", @@ -1782,45 +1781,6 @@ }, "time": "2024-05-07T02:01:25+00:00" }, - { - "name": "utopia-php/fetch", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/fetch.git", - "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/fetch/zipball/2fa214b9262acd1a3583515a364da4f35929d5c5", - "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "^1.5.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Fetch\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "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.1.0" - }, - "time": "2023-10-10T11:58:32+00:00" - }, { "name": "utopia-php/framework", "version": "dev-feat-di-upgrade", @@ -3510,12 +3470,12 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "2f7b34c2ee49829a36cec9f545605d385bf3e291" + "reference": "88a07d262854c827db22f2eac8b072138e492f65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2f7b34c2ee49829a36cec9f545605d385bf3e291", - "reference": "2f7b34c2ee49829a36cec9f545605d385bf3e291", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/88a07d262854c827db22f2eac8b072138e492f65", + "reference": "88a07d262854c827db22f2eac8b072138e492f65", "shasum": "" }, "require": { @@ -3567,7 +3527,7 @@ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.x" }, - "time": "2024-05-07T05:11:38+00:00" + "time": "2024-05-08T18:52:15+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -5604,6 +5564,45 @@ } ], "time": "2023-11-21T18:54:41+00:00" + }, + { + "name": "utopia-php/fetch", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/fetch.git", + "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/fetch/zipball/2fa214b9262acd1a3583515a364da4f35929d5c5", + "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.5.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Fetch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "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.1.0" + }, + "time": "2023-10-10T11:58:32+00:00" } ], "aliases": [ From 6083d8b7a8c524bdcb0c2ec5db04bf8d309895dc Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:09:58 -0400 Subject: [PATCH 063/195] feat(tasks): Coroutine --- app/cli.php | 344 ++++++++++-------- app/http.php | 4 +- app/worker.php | 2 +- composer.json | 2 +- composer.lock | 158 ++++---- docker-compose.yml | 121 +++--- src/Appwrite/Platform/Tasks/ScheduleBase.php | 112 +++--- .../Platform/Tasks/ScheduleFunctions.php | 16 +- .../Platform/Tasks/ScheduleMessages.php | 17 +- 9 files changed, 415 insertions(+), 361 deletions(-) diff --git a/app/cli.php b/app/cli.php index 423dff72b5..b5e639ef11 100644 --- a/app/cli.php +++ b/app/cli.php @@ -9,192 +9,236 @@ use Appwrite\Event\Func; use Appwrite\Event\Hamster; use Appwrite\Platform\Appwrite; use Appwrite\Utopia\Queue\Connections; +use Utopia\Cache\Adapter\None; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; -use Utopia\CLI\CLI; use Utopia\CLI\Console; -use Utopia\Config\Config; +use Utopia\Queue\Connection\Redis; +use Utopia\Database\Adapter\MariaDB; +use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; +use Utopia\DI\Dependency; use Utopia\Logger\Log; use Utopia\Platform\Service; use Utopia\Pools\Group; use Utopia\Queue\Connection; use Utopia\Registry\Registry; use Utopia\System\System; +use Swoole\Runtime; +use Utopia\CLI\Adapters\Swoole as SwooleCLI; -global $register; +global $global, $container; -CLI::setResource('register', fn () => $register); +Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -CLI::setResource('connections', function () { - return new Connections(); -}); +$registry = new Dependency(); +$registry + ->setName('register') + ->setCallback(fn() => $global); -CLI::setResource('cache', function ($pools, Connections $connections) { - $list = Config::getParam('pools-cache', []); - $adapters = []; +$connections = new Dependency(); +$connections + ->setName('connections') + ->setCallback(fn() => new Connections()); - foreach ($list as $value) { - $connection = $pools->get($value)->pop(); - $connections->add($connection); - $adapters[] = $connection->getResource(); - } +$cache = new Dependency(); +$cache + ->setName('cache') + ->setCallback(function () { + return new Cache(new None()); + }); +$container->set($cache); - return new Cache(new Sharding($adapters)); -}, ['pools', 'connections']); +$pools = new Dependency(); +$pools + ->setName('pools') + ->inject('register') + ->setCallback(function (Registry $register) { + return $register->get('pools'); + }); -CLI::setResource('pools', function (Registry $register) { - return $register->get('pools'); -}, ['register']); +$dbForConsole = new Dependency(); +$dbForConsole + ->setName('dbForConsole') + ->inject('pools') + ->inject('cache') + ->inject('auth') + ->inject('connections') + ->setCallback(function ($pools, $cache, $auth, Connections $connections) { + $pool = $pools['pools-console-main']['pool']; + $dsn = $pools['pools-console-main']['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); -CLI::setResource('dbForConsole', function ($pools, $cache, $auth, Connections $connections) { - $sleep = 3; - $maxAttempts = 5; - $attempts = 0; - $ready = false; + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; - $connection = null; + $adapter->setDatabase($dsn->getPath()); - do { - $attempts++; - try { - // Prepare database connection - $connection = $pools->get('console')->pop(); - $dbAdapter = $connection->getResource(); - - $dbForConsole = new Database($dbAdapter, $cache); - $dbForConsole->setAuthorization($auth); - - $dbForConsole - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console'); - - // Ensure tables exist - $collections = Config::getParam('collections', [])['console']; - $last = \array_key_last($collections); - - if (!($dbForConsole->exists($dbForConsole->getDatabase(), $last))) { /** TODO cache ready variable using registry */ - throw new Exception('Tables not ready yet.'); - } - - $ready = true; - } catch (\Throwable $err) { - if($connection !== null) { - $connection->reclaim(); - $connection = null; - } - - Console::warning($err->getMessage()); - sleep($sleep); - } - } while ($attempts < $maxAttempts && !$ready); - - if($connection !== null) { - $connections->add($connection); - } - - if (!$ready) { - throw new Exception("Console is not ready yet. Please try again later."); - } - - return $dbForConsole; -}, ['pools', 'cache', 'auth', 'connections']); - -CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, $auth, Connections $connections) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - $databaseName = $project->getAttribute('database'); - - if (isset($databases[$databaseName])) { - $database = $databases[$databaseName]; - $database->setNamespace('_' . $project->getInternalId()); - return $database; - } - - $connection = $pools->get($databaseName)->pop(); - $connections->add($connection); - $dbAdapter = $connection->getResource(); - - $database = new Database($dbAdapter, $cache); + $database = new Database($adapter, $cache); $database->setAuthorization($auth); - - $databases[$databaseName] = $database; - - $database - ->setNamespace('_' . $project->getInternalId()) - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()); + $database->setNamespace('_console'); return $database; - }; -}, ['pools', 'dbForConsole', 'cache', 'auth', 'connections']); + }); -CLI::setResource('queue', function (Group $pools, Connections $connections) { - $connection = $pools->get('queue')->pop(); - $connections->add($connection); - return $connection->getResource(); -}, ['pools', 'connections']); -CLI::setResource('queueForFunctions', function (Connection $queue) { - return new Func($queue); -}, ['queue']); -CLI::setResource('queueForHamster', function (Connection $queue) { - return new Hamster($queue); -}, ['queue']); -CLI::setResource('queueForDeletes', function (Connection $queue) { - return new Delete($queue); -}, ['queue']); -CLI::setResource('queueForCertificates', function (Connection $queue) { - return new Certificate($queue); -}, ['queue']); -CLI::setResource('logError', function (Registry $register) { - return function (Throwable $error, string $namespace, string $action) use ($register) { - $logger = $register->get('logger'); +$getProjectDB = new Dependency(); +$getProjectDB + ->setName('getProjectDB') + ->inject('pools') + ->inject('dbForConsole') + ->inject('cache') + ->inject('auth') + ->inject('connections') + ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $auth, Connections $connections) { + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections): Database { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } - if ($logger) { - $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); + $databaseName = $project->getAttribute('database'); - $log = new Log(); - $log->setNamespace($namespace); - $log->setServer(\gethostname()); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($error->getMessage()); + $pool = $pools['pools-database-' . $databaseName]['pool']; + $dsn = $pools['pools-database-' . $databaseName]['dsn']; - $log->addTag('code', $error->getCode()); - $log->addTag('verboseType', get_class($error)); + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + $adapter->setDatabase($dsn->getPath()); - $log->addExtra('file', $error->getFile()); - $log->addExtra('line', $error->getLine()); - $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('detailedTrace', $error->getTrace()); + $database = new Database($adapter, $cache); + $database->setAuthorization($auth); + $database->setNamespace('_' . $project->getInternalId()); - $log->setAction($action); + return $database; + }; + }); - $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; +$queue = new Dependency(); +$queue + ->setName('queue') + ->inject('pools') + ->inject('connections') + ->setCallback(function (array $pools, Connections $connections) { + $pool = $pools['pools-queue-main']['pool']; + $dsn = $pools['pools-queue-main']['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); + return new Redis($dsn->getHost(), $dsn->getPort()); + }); - $responseCode = $logger->addLog($log); - Console::info('Usage stats log pushed with status code: ' . $responseCode); - } +$queueForFunctions = new Dependency(); +$queueForFunctions + ->setName('queueForFunctions') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Func($queue); + }); - Console::warning("Failed: {$error->getMessage()}"); - Console::warning($error->getTraceAsString()); - }; -}, ['register']); +$queueForHamster = new Dependency(); +$queueForHamster + ->setName('queueForHamster') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Hamster($queue); + }); -CLI::setResource('auth', fn () => new Authorization()); +$queueForDeletes = new Dependency(); +$queueForDeletes + ->setName('queueForDeletes') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Delete($queue); + }); + +$queueForCertificates = new Dependency(); +$queueForCertificates + ->setName('queueForCertificates') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Certificate($queue); + }); + +$queueForCertificates = new Dependency(); +$queueForCertificates + ->setName('queueForCertificates') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Certificate($queue); + }); + +$logError = new Dependency(); +$logError + ->setName('logError') + ->inject('register') + ->setCallback(function (Registry $register) { + return function (Throwable $error, string $namespace, string $action) use ($register) { + $logger = $register->get('logger'); + + if ($logger) { + $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); + + $log = new Log(); + $log->setNamespace($namespace); + $log->setServer(\gethostname()); + $log->setVersion($version); + $log->setType(Log::TYPE_ERROR); + $log->setMessage($error->getMessage()); + + $log->addTag('code', $error->getCode()); + $log->addTag('verboseType', get_class($error)); + + $log->addExtra('file', $error->getFile()); + $log->addExtra('line', $error->getLine()); + $log->addExtra('trace', $error->getTraceAsString()); + $log->addExtra('detailedTrace', $error->getTrace()); + + $log->setAction($action); + + $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; + + $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); + + $responseCode = $logger->addLog($log); + Console::info('Usage stats log pushed with status code: ' . $responseCode); + } + + Console::warning("Failed: {$error->getMessage()}"); + Console::warning($error->getTraceAsString()); + }; + }); + +$auth = new Dependency(); +$auth + ->setName('auth') + ->setCallback(fn() => new Authorization()); + +$container->set($registry); +$container->set($connections); +$container->set($cache); +$container->set($pools); +$container->set($dbForConsole); +$container->set($getProjectDB); +$container->set($queue); +$container->set($queueForFunctions); +$container->set($queueForHamster); +$container->set($queueForDeletes); +$container->set($queueForCertificates); +$container->set($logError); +$container->set($auth); $platform = new Appwrite(); -$platform->init(Service::TYPE_CLI); +$platform->init(Service::TYPE_CLI, ['adapter' => new SwooleCLI(1)]); $cli = $platform->getCli(); @@ -212,4 +256,6 @@ $cli Console::error($error->getMessage()); }); -$cli->run(); +$cli + ->setContainer($container) + ->run(); diff --git a/app/http.php b/app/http.php index 307b9252fe..d2bf60769a 100644 --- a/app/http.php +++ b/app/http.php @@ -23,6 +23,8 @@ use Utopia\Http\Adapter\Swoole\Server; use Utopia\Http\Http; use Utopia\System\System; +global $global, $container; + $workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); $payloadSize = 6 * (1024 * 1024); // 6MB @@ -48,7 +50,7 @@ $server = new Server('0.0.0.0', '80', [ $http = new Http($server, $container, 'UTC'); -// $http->loadFiles(__DIR__ . '/../console'); +$http->loadFiles(__DIR__ . '/../console'); $http->setRequestClass(Request::class); $http->setResponseClass(Response::class); diff --git a/app/worker.php b/app/worker.php index 33db5fa6a8..0f64d78d29 100644 --- a/app/worker.php +++ b/app/worker.php @@ -39,7 +39,7 @@ use Utopia\Registry\Registry; use Utopia\Storage\Device\Local; use Utopia\System\System; -global $gloabl, $container; +global $global, $container; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); diff --git a/composer.json b/composer.json index b2ef46a22a..1c60df3890 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,7 @@ "utopia-php/analytics": "dev-feat-framework-v2 as 0.10.99", "utopia-php/audit": "dev-feat-framework-v2 as 0.39.99", "utopia-php/cache": "0.9.*", - "utopia-php/cli": "0.17.*", + "utopia-php/cli": "dev-dev-coroutines as 0.17.99", "utopia-php/config": "0.2.*", "utopia-php/database": "dev-feat-framework-v2 as 0.49.99", "utopia-php/domains": "dev-feat-framework-v2 as 0.5.99", diff --git a/composer.lock b/composer.lock index 5efc100fdf..e069e098cc 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": "bb3213cfb05b043e7d2ea2002069bb98", + "content-hash": "7778579de897a3e077914bd37686a62b", "packages": [ { "name": "adhocore/jwt", @@ -1050,12 +1050,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7d191eb4022901cd3d91a816ec5464ca3a08a8aa" + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7d191eb4022901cd3d91a816ec5464ca3a08a8aa", - "reference": "7d191eb4022901cd3d91a816ec5464ca3a08a8aa", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", "shasum": "" }, "require": { @@ -1123,7 +1123,7 @@ "type": "tidelift" } ], - "time": "2024-04-19T06:31:17+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "thecodingmachine/safe", @@ -1458,27 +1458,29 @@ }, { "name": "utopia-php/cli", - "version": "0.17.0", + "version": "dev-dev-coroutines", "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "0829fd5215afe88f53f3091cedc808da801fd1bb" + "reference": "c9b049160aff2eecea7dd1ff041165ae15f41f76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/0829fd5215afe88f53f3091cedc808da801fd1bb", - "reference": "0829fd5215afe88f53f3091cedc808da801fd1bb", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/c9b049160aff2eecea7dd1ff041165ae15f41f76", + "reference": "c9b049160aff2eecea7dd1ff041165ae15f41f76", "shasum": "" }, "require": { "php": ">=7.4", - "utopia-php/framework": "0.34.*" + "utopia-php/di": "dev-main", + "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99" }, "require-dev": { "laravel/pint": "1.2.*", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.3", "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" + "swoole/ide-helper": "4.8.8" }, "type": "library", "autoload": { @@ -1501,9 +1503,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.17.0" + "source": "https://github.com/utopia-php/cli/tree/dev-coroutines" }, - "time": "2024-01-24T11:37:29+00:00" + "time": "2024-06-03T17:55:34+00:00" }, { "name": "utopia-php/config", @@ -2035,22 +2037,21 @@ }, { "name": "utopia-php/migration", - "version": "0.4.1", + "version": "0.4.4", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "ae3cfe93f6d313105d226aeb68806660c806a925" + "reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/ae3cfe93f6d313105d226aeb68806660c806a925", - "reference": "ae3cfe93f6d313105d226aeb68806660c806a925", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/a8a5d392bebf082faf289f4dfe09d9fd76844c33", + "reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33", "shasum": "" }, "require": { "appwrite/appwrite": "10.1.0", - "php": "8.*", - "utopia-php/cli": "0.*" + "php": "8.*" }, "require-dev": { "laravel/pint": "1.*", @@ -2077,9 +2078,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.4.1" + "source": "https://github.com/utopia-php/migration/tree/0.4.4" }, - "time": "2024-05-01T13:19:18+00:00" + "time": "2024-05-17T05:25:31+00:00" }, { "name": "utopia-php/mongo", @@ -2197,20 +2198,20 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "e6f2f281b2ad962211b1c6f88650d0230f615a3a" + "reference": "55b1d8675a35d8fb269614a1e916a1bff616983c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/e6f2f281b2ad962211b1c6f88650d0230f615a3a", - "reference": "e6f2f281b2ad962211b1c6f88650d0230f615a3a", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/55b1d8675a35d8fb269614a1e916a1bff616983c", + "reference": "55b1d8675a35d8fb269614a1e916a1bff616983c", "shasum": "" }, "require": { "ext-json": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/cli": "0.17.*", - "utopia-php/framework": "0.34.*", + "utopia-php/cli": "dev-dev-coroutines as 0.17.99", + "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99", "utopia-php/queue": "dev-feat-coroutine-and-di as 0.7.99" }, "require-dev": { @@ -2239,7 +2240,7 @@ "issues": "https://github.com/utopia-php/platform/issues", "source": "https://github.com/utopia-php/platform/tree/feat-framework-v2" }, - "time": "2024-04-21T18:38:38+00:00" + "time": "2024-06-03T18:01:18+00:00" }, { "name": "utopia-php/queue", @@ -2540,16 +2541,16 @@ }, { "name": "utopia-php/vcs", - "version": "0.6.5", + "version": "0.6.6", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "104e47ea8e38c156ec0e0bd415caa3dcd5046fe2" + "reference": "e538264cfee5e3efdfe1771efba04750cf20b2c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/104e47ea8e38c156ec0e0bd415caa3dcd5046fe2", - "reference": "104e47ea8e38c156ec0e0bd415caa3dcd5046fe2", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/e538264cfee5e3efdfe1771efba04750cf20b2c4", + "reference": "e538264cfee5e3efdfe1771efba04750cf20b2c4", "shasum": "" }, "require": { @@ -2583,9 +2584,9 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.6.5" + "source": "https://github.com/utopia-php/vcs/tree/0.6.6" }, - "time": "2024-01-08T17:11:12+00:00" + "time": "2024-05-17T09:36:30+00:00" }, { "name": "utopia-php/view", @@ -2815,16 +2816,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.38.2", + "version": "0.38.6", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "51284668529e2b10ed933412a42b603c76cded23" + "reference": "d7016d6d72545e84709892faca972eb4bf5bd699" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/51284668529e2b10ed933412a42b603c76cded23", - "reference": "51284668529e2b10ed933412a42b603c76cded23", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d7016d6d72545e84709892faca972eb4bf5bd699", + "reference": "d7016d6d72545e84709892faca972eb4bf5bd699", "shasum": "" }, "require": { @@ -2860,9 +2861,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.38.2" + "source": "https://github.com/appwrite/sdk-generator/tree/0.38.6" }, - "time": "2024-04-25T07:49:29+00:00" + "time": "2024-05-20T18:00:16+00:00" }, { "name": "doctrine/deprecations", @@ -2984,16 +2985,16 @@ }, { "name": "laravel/pint", - "version": "v1.15.3", + "version": "v1.16.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "3600b5d17aff52f6100ea4921849deacbbeb8656" + "reference": "1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/3600b5d17aff52f6100ea4921849deacbbeb8656", - "reference": "3600b5d17aff52f6100ea4921849deacbbeb8656", + "url": "https://api.github.com/repos/laravel/pint/zipball/1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98", + "reference": "1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98", "shasum": "" }, "require": { @@ -3004,11 +3005,11 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.54.0", - "illuminate/view": "^10.48.8", - "larastan/larastan": "^2.9.5", - "laravel-zero/framework": "^10.3.0", - "mockery/mockery": "^1.6.11", + "friendsofphp/php-cs-fixer": "^3.57.1", + "illuminate/view": "^10.48.10", + "larastan/larastan": "^2.9.6", + "laravel-zero/framework": "^10.4.0", + "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^1.15.1", "pestphp/pest": "^2.34.7" }, @@ -3046,7 +3047,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-04-30T15:02:26+00:00" + "time": "2024-05-21T18:08:25+00:00" }, { "name": "matthiasmullie/minify", @@ -3239,12 +3240,12 @@ "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "c5ee33df86c06b3278c670f64273b1ba768a0744" + "reference": "daaadc3bae458908aa477b90a8932e7da9253f22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c5ee33df86c06b3278c670f64273b1ba768a0744", - "reference": "c5ee33df86c06b3278c670f64273b1ba768a0744", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/daaadc3bae458908aa477b90a8932e7da9253f22", + "reference": "daaadc3bae458908aa477b90a8932e7da9253f22", "shasum": "" }, "require": { @@ -3290,7 +3291,7 @@ "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/master" }, - "time": "2024-04-19T12:04:10+00:00" + "time": "2024-06-03T06:24:19+00:00" }, { "name": "phar-io/manifest", @@ -3470,12 +3471,12 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "88a07d262854c827db22f2eac8b072138e492f65" + "reference": "aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/88a07d262854c827db22f2eac8b072138e492f65", - "reference": "88a07d262854c827db22f2eac8b072138e492f65", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39", + "reference": "aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39", "shasum": "" }, "require": { @@ -3527,7 +3528,7 @@ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.x" }, - "time": "2024-05-08T18:52:15+00:00" + "time": "2024-05-21T06:14:15+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -3535,12 +3536,12 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "483fb7fe262607b0a5ec32f99bdc42e2212b22fe" + "reference": "eee054a3d40f09920f5b27c9b09a6483f88d9d24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/483fb7fe262607b0a5ec32f99bdc42e2212b22fe", - "reference": "483fb7fe262607b0a5ec32f99bdc42e2212b22fe", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/eee054a3d40f09920f5b27c9b09a6483f88d9d24", + "reference": "eee054a3d40f09920f5b27c9b09a6483f88d9d24", "shasum": "" }, "require": { @@ -3586,7 +3587,7 @@ "issues": "https://github.com/phpDocumentor/TypeResolver/issues", "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.x" }, - "time": "2024-03-29T20:21:22+00:00" + "time": "2024-05-24T14:24:30+00:00" }, { "name": "phpspec/prophecy", @@ -3660,16 +3661,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.29.0", + "version": "1.29.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc" + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/536889f2b340489d328f5ffb7b02bb6b183ddedc", - "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", "shasum": "" }, "require": { @@ -3701,9 +3702,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" }, - "time": "2024-05-06T12:04:23+00:00" + "time": "2024-05-31T08:52:43+00:00" }, { "name": "phpstan/phpstan", @@ -5239,12 +5240,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "c9e59dec962d38cf2e0e4c61c4a1a1312f4dd7fe" + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c9e59dec962d38cf2e0e4c61c4a1a1312f4dd7fe", - "reference": "c9e59dec962d38cf2e0e4c61c4a1a1312f4dd7fe", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", "shasum": "" }, "require": { @@ -5311,7 +5312,7 @@ "type": "tidelift" } ], - "time": "2024-04-19T06:31:17+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -5319,12 +5320,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "e642fbe7a7b73cdb05460555289a9057bfd6ead6" + "reference": "098e36a5b73de12beeb5ac17e80abf3696f7ad5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e642fbe7a7b73cdb05460555289a9057bfd6ead6", - "reference": "e642fbe7a7b73cdb05460555289a9057bfd6ead6", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/098e36a5b73de12beeb5ac17e80abf3696f7ad5f", + "reference": "098e36a5b73de12beeb5ac17e80abf3696f7ad5f", "shasum": "" }, "require": { @@ -5392,7 +5393,7 @@ "type": "tidelift" } ], - "time": "2024-04-19T06:31:17+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "textalk/websocket", @@ -5624,6 +5625,12 @@ "alias": "0.39.99", "alias_normalized": "0.39.99.0" }, + { + "package": "utopia-php/cli", + "version": "dev-dev-coroutines", + "alias": "0.17.99", + "alias_normalized": "0.17.99.0" + }, { "package": "utopia-php/database", "version": "dev-feat-framework-v2", @@ -5672,6 +5679,7 @@ "utopia-php/abuse": 20, "utopia-php/analytics": 20, "utopia-php/audit": 20, + "utopia-php/cli": 20, "utopia-php/database": 20, "utopia-php/domains": 20, "utopia-php/framework": 20, diff --git a/docker-compose.yml b/docker-compose.yml index f12df5742e..e116569c50 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,8 @@ x-logging: &x-logging max-file: "5" max-size: "10m" +version: "3" + services: traefik: image: traefik:2.11 @@ -40,6 +42,7 @@ services: networks: - gateway - appwrite + - runtimes appwrite: container_name: appwrite @@ -48,7 +51,7 @@ services: build: context: . args: - DEBUG: false + DEBUG: true TESTING: true VERSION: dev ports: @@ -75,14 +78,15 @@ services: - appwrite-config:/storage/config:rw - appwrite-certificates:/storage/certificates:rw - appwrite-functions:/storage/functions:rw + - appwrite-builds:/storage/builds:rw - ./phpunit.xml:/usr/src/code/phpunit.xml - ./tests:/usr/src/code/tests - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./docs:/usr/src/code/docs - ./public:/usr/src/code/public - ./src:/usr/src/code/src - ./dev:/usr/src/code/dev - - ./temp-debug:/tmp/xdebug depends_on: - mariadb - redis @@ -92,6 +96,7 @@ services: - -e - app/http.php environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_LOCALE @@ -187,7 +192,6 @@ services: - _APP_MESSAGE_EMAIL_TEST_DSN - _APP_MESSAGE_PUSH_TEST_DSN - _APP_CONSOLE_COUNTRIES_DENYLIST - appwrite-realtime: entrypoint: realtime <<: *x-logging @@ -214,11 +218,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - mariadb - redis environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPTIONS_ABUSE @@ -246,11 +252,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -275,12 +283,14 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb - request-catcher environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -315,8 +325,10 @@ services: - appwrite-builds:/storage/builds:rw - appwrite-certificates:/storage/certificates:rw - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -364,11 +376,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -397,11 +411,13 @@ services: - appwrite-functions:/storage/functions:rw - appwrite-builds:/storage/builds:rw - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -465,8 +481,10 @@ services: - appwrite-config:/storage/config:rw - appwrite-certificates:/storage/certificates:rw - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -495,15 +513,19 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb - openruntimes-executor environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_OPTIONS_FORCE_HTTPS - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -534,12 +556,14 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - maildev # - smtp environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -569,10 +593,12 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -601,11 +627,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src - ./tests:/usr/src/code/tests depends_on: - mariadb environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -626,19 +654,21 @@ services: - _APP_MIGRATIONS_FIREBASE_CLIENT_ID - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET - appwrite-maintenance: + appwrite-task-maintenance: entrypoint: maintenance <<: *x-logging - container_name: appwrite-maintenance + container_name: appwrite-task-maintenance image: appwrite-dev networks: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_DOMAIN @@ -672,11 +702,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -703,11 +735,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -725,20 +759,22 @@ services: - _APP_LOGGING_CONFIG - _APP_USAGE_AGGREGATION_INTERVAL - appwrite-scheduler-functions: + appwrite-task-scheduler-functions: entrypoint: schedule-functions <<: *x-logging - container_name: appwrite-scheduler-functions + container_name: appwrite-task-scheduler-functions image: appwrite-dev networks: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - mariadb - redis environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -752,20 +788,22 @@ services: - _APP_DB_USER - _APP_DB_PASS - appwrite-scheduler-messages: + appwrite-task-scheduler-messages: entrypoint: schedule-messages <<: *x-logging - container_name: appwrite-scheduler-messages + container_name: appwrite-task-scheduler-messages image: appwrite-dev networks: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - mariadb - redis environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -785,65 +823,9 @@ services: networks: - appwrite environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ASSISTANT_OPENAI_API_KEY - appwrite-worker-hamster: - entrypoint: worker-hamster - <<: *x-logging - container_name: appwrite-worker-hamster - image: appwrite-dev - networks: - - appwrite - volumes: - - ./app:/usr/src/code/app - - ./src:/usr/src/code/src - depends_on: - - redis - - mariadb - environment: - - _APP_ENV - - _APP_WORKER_PER_CORE - - _APP_OPENSSL_KEY_V1 - - _APP_DB_HOST - - _APP_DB_PORT - - _APP_DB_SCHEMA - - _APP_DB_USER - - _APP_DB_PASS - - _APP_REDIS_HOST - - _APP_REDIS_PORT - - _APP_REDIS_USER - - _APP_REDIS_PASS - - _APP_MIXPANEL_TOKEN - - appwrite-hamster-scheduler: - entrypoint: hamster - <<: *x-logging - container_name: appwrite-hamster-scheduler - image: appwrite-dev - networks: - - appwrite - volumes: - - ./app:/usr/src/code/app - - ./src:/usr/src/code/src - depends_on: - - redis - - mariadb - environment: - - _APP_ENV - - _APP_WORKER_PER_CORE - - _APP_OPENSSL_KEY_V1 - - _APP_REDIS_HOST - - _APP_REDIS_PORT - - _APP_REDIS_USER - - _APP_REDIS_PASS - - _APP_DB_HOST - - _APP_DB_PORT - - _APP_DB_SCHEMA - - _APP_DB_USER - - _APP_DB_PASS - - _APP_HAMSTER_TIME - - _APP_HAMSTER_INTERVAL - openruntimes-executor: container_name: openruntimes-executor hostname: appwrite-executor @@ -862,6 +844,7 @@ services: # It's not possible to share mount file between 2 containers without host mount (copying is too slow) - /tmp:/tmp:rw environment: + - PHP_IDE_CONFIG=serverName=Appwrite - OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD - OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL - OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK @@ -905,6 +888,7 @@ services: - appwrite - runtimes environment: + - PHP_IDE_CONFIG=serverName=Appwrite - OPR_PROXY_WORKER_PER_CORE=$_APP_WORKER_PER_CORE - OPR_PROXY_ENV=$_APP_ENV - OPR_PROXY_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET @@ -925,10 +909,10 @@ services: - appwrite volumes: - appwrite-mariadb:/var/lib/mysql:rw - - ./mariadb-config.cnf:/etc/mysql/conf.d/mariadb-config.cnf ports: - "3306:3306" environment: + - PHP_IDE_CONFIG=serverName=Appwrite - MYSQL_ROOT_PASSWORD=${_APP_DB_ROOT_PASS} - MYSQL_DATABASE=${_APP_DB_SCHEMA} - MYSQL_USER=${_APP_DB_USER} @@ -944,6 +928,7 @@ services: # networks: # - appwrite # environment: + # PHP_IDE_CONFIG=serverName=Appwrite # - LOCAL_DOMAINS=@ # - RELAY_FROM_HOSTS=192.168.0.0/16 ; *.yourdomain.com # - SMARTHOST_HOST=smtp @@ -1019,6 +1004,7 @@ services: networks: - appwrite environment: + - PHP_IDE_CONFIG=serverName=Appwrite - REDIS_HOSTS=redis ports: - "8081:5540" @@ -1032,6 +1018,7 @@ services: ports: - "9509:3000" environment: + - PHP_IDE_CONFIG=serverName=Appwrite - SERVER_URL=http://localhost/v1/graphql # Dev Tools End ------------------------------------------------------------------------------------------ diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index a50fbb2403..907d7d1a87 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Tasks; +use Appwrite\Utopia\Queue\Connections; use Swoole\Timer; use Utopia\CLI\Console; use Utopia\Database\Database; @@ -21,17 +22,19 @@ abstract class ScheduleBase extends Action protected const ENQUEUE_TIMER = 60; //seconds protected array $schedules = []; + protected Connections $connections; abstract public static function getName(): string; abstract public static function getSupportedResource(): string; abstract protected function enqueueResources( - Group $pools, + array $pools, Database $dbForConsole ); public function __construct() { + $this->connections = new Connections(); $type = static::getSupportedResource(); $this @@ -39,7 +42,7 @@ abstract class ScheduleBase extends Action ->inject('pools') ->inject('dbForConsole') ->inject('getProjectDB') - ->callback(fn (Group $pools, Database $dbForConsole, callable $getProjectDB) => $this->action($pools, $dbForConsole, $getProjectDB)); + ->callback(fn (array $pools, Database $dbForConsole, callable $getProjectDB) => $this->action($pools, $dbForConsole, $getProjectDB)); } /** @@ -47,7 +50,7 @@ abstract class ScheduleBase extends Action * 2. Create timer that sync all changes from 'schedules' collection to local copy. Only reading changes thanks to 'resourceUpdatedAt' attribute * 3. Create timer that prepares coroutines for soon-to-execute schedules. When it's ready, coroutine sleeps until exact time before sending request to worker. */ - public function action(Group $pools, Database $dbForConsole, callable $getProjectDB): void + public function action(array $pools, Database $dbForConsole, callable $getProjectDB): void { Console::title(\ucfirst(static::getSupportedResource()) . ' scheduler V1'); Console::success(APP_NAME . ' ' . \ucfirst(static::getSupportedResource()) . ' scheduler v1 has started'); @@ -124,76 +127,71 @@ abstract class ScheduleBase extends Action $latestDocument = \end($results); } - $pools->reclaim(); Console::success("{$total} resources were loaded in " . (\microtime(true) - $loadStart) . " seconds"); Console::success("Starting timers at " . DateTime::now()); - run(function () use ($dbForConsole, &$lastSyncUpdate, $getSchedule, $pools) { - /** - * The timer synchronize $schedules copy with database collection. - */ - Timer::tick(static::UPDATE_TIMER * 1000, function () use ($dbForConsole, &$lastSyncUpdate, $getSchedule, $pools) { - $time = DateTime::now(); - $timerStart = \microtime(true); - $limit = 1000; - $sum = $limit; - $total = 0; - $latestDocument = null; + Timer::tick(static::UPDATE_TIMER * 1000, function () use ($dbForConsole, &$lastSyncUpdate, $getSchedule, $pools) { + $time = DateTime::now(); + $timerStart = \microtime(true); - Console::log("Sync tick: Running at $time"); + $limit = 1000; + $sum = $limit; + $total = 0; + $latestDocument = null; - while ($sum === $limit) { - $paginationQueries = [Query::limit($limit)]; + Console::log("Sync tick: Running at $time"); - if ($latestDocument) { - $paginationQueries[] = Query::cursorAfter($latestDocument); - } + while ($sum === $limit) { + $paginationQueries = [Query::limit($limit)]; - $results = $dbForConsole->find('schedules', \array_merge($paginationQueries, [ - Query::equal('region', [System::getEnv('_APP_REGION', 'default')]), - Query::equal('resourceType', [static::getSupportedResource()]), - Query::greaterThanEqual('resourceUpdatedAt', $lastSyncUpdate), - ])); - - $sum = count($results); - $total = $total + $sum; - - foreach ($results as $document) { - $localDocument = $schedules[$document['resourceId']] ?? null; - - // Check if resource has been updated since last sync - $org = $localDocument !== null ? \strtotime($localDocument['resourceUpdatedAt']) : null; - $new = \strtotime($document['resourceUpdatedAt']); - - if (!$document['active']) { - Console::info("Removing: {$document['resourceId']}"); - unset($this->schedules[$document['resourceId']]); - } elseif ($new !== $org) { - Console::info("Updating: {$document['resourceId']}"); - $this->schedules[$document['resourceId']] = $getSchedule($document); - } - } - - $latestDocument = \end($results); + if ($latestDocument) { + $paginationQueries[] = Query::cursorAfter($latestDocument); } - $lastSyncUpdate = $time; - $timerEnd = \microtime(true); + $results = $dbForConsole->find('schedules', \array_merge($paginationQueries, [ + Query::equal('region', [System::getEnv('_APP_REGION', 'default')]), + Query::equal('resourceType', [static::getSupportedResource()]), + Query::greaterThanEqual('resourceUpdatedAt', $lastSyncUpdate), + ])); - $pools->reclaim(); + $sum = count($results); + $total = $total + $sum; - Console::log("Sync tick: {$total} schedules were updated in " . ($timerEnd - $timerStart) . " seconds"); - }); + foreach ($results as $document) { + $localDocument = $schedules[$document['resourceId']] ?? null; - Timer::tick( - static::ENQUEUE_TIMER * 1000, - fn () => $this->enqueueResources($pools, $dbForConsole) - ); + // Check if resource has been updated since last sync + $org = $localDocument !== null ? \strtotime($localDocument['resourceUpdatedAt']) : null; + $new = \strtotime($document['resourceUpdatedAt']); - $this->enqueueResources($pools, $dbForConsole); + if (!$document['active']) { + Console::info("Removing: {$document['resourceId']}"); + unset($this->schedules[$document['resourceId']]); + } elseif ($new !== $org) { + Console::info("Updating: {$document['resourceId']}"); + $this->schedules[$document['resourceId']] = $getSchedule($document); + } + } + + $latestDocument = \end($results); + } + + $lastSyncUpdate = $time; + $timerEnd = \microtime(true); + + + Console::log("Sync tick: {$total} schedules were updated in " . ($timerEnd - $timerStart) . " seconds"); }); + + Timer::tick( + static::ENQUEUE_TIMER * 1000, + fn() => $this->enqueueResources($pools, $dbForConsole) + ); + + $this->enqueueResources($pools, $dbForConsole); + } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index a5d4918b7a..1e2f4a5614 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -8,6 +8,7 @@ use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Pools\Group; +use Utopia\Queue\Connection\Redis; class ScheduleFunctions extends ScheduleBase { @@ -26,7 +27,7 @@ class ScheduleFunctions extends ScheduleBase return 'function'; } - protected function enqueueResources(Group $pools, Database $dbForConsole): void + protected function enqueueResources(array $pools, Database $dbForConsole): void { $timerStart = \microtime(true); $time = DateTime::now(); @@ -68,8 +69,12 @@ class ScheduleFunctions extends ScheduleBase \go(function () use ($delay, $scheduleKeys, $pools) { \sleep($delay); // in seconds - $queue = $pools->get('queue')->pop(); - $connection = $queue->getResource(); + $pool = $pools['pools-queue-main']['pool']; + $dsn = $pools['pools-queue-main']['dsn']; + $connection = $pool->get(); + $this->connections->add($connection, $pool); + + $queueConnection = new Redis($dsn->getHost(), $dsn->getPort()); foreach ($scheduleKeys as $scheduleKey) { // Ensure schedule was not deleted @@ -79,7 +84,7 @@ class ScheduleFunctions extends ScheduleBase $schedule = $this->schedules[$scheduleKey]; - $queueForFunctions = new Func($connection); + $queueForFunctions = new Func($queueConnection); $queueForFunctions ->setType('schedule') @@ -90,7 +95,8 @@ class ScheduleFunctions extends ScheduleBase ->trigger(); } - $queue->reclaim(); // TODO: Do in try/catch/finally, or add to connectons resource + $this->connections->reclaim(); + // $queue->reclaim(); // TODO: Do in try/catch/finally, or add to connectons resource }); } diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 8538a24234..3aad7d54cd 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -5,6 +5,7 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Messaging; use Utopia\Database\Database; use Utopia\Pools\Group; +use Utopia\Queue\Connection\Redis; class ScheduleMessages extends ScheduleBase { @@ -21,7 +22,7 @@ class ScheduleMessages extends ScheduleBase return 'message'; } - protected function enqueueResources(Group $pools, Database $dbForConsole): void + protected function enqueueResources(array $pools, Database $dbForConsole): void { foreach ($this->schedules as $schedule) { if (!$schedule['active']) { @@ -36,9 +37,14 @@ class ScheduleMessages extends ScheduleBase } \go(function () use ($now, $schedule, $pools, $dbForConsole) { - $queue = $pools->get('queue')->pop(); - $connection = $queue->getResource(); - $queueForMessaging = new Messaging($connection); + $pool = $pools['pools-queue-main']['pool']; + $dsn = $pools['pools-queue-main']['dsn']; + $connection = $pool->get(); + $this->connections->add($connection, $pool); + + $queueConnection = new Redis($dsn->getHost(), $dsn->getPort()); + + $queueForMessaging = new Messaging($queueConnection); $queueForMessaging ->setType(MESSAGE_SEND_TYPE_EXTERNAL) @@ -51,7 +57,8 @@ class ScheduleMessages extends ScheduleBase $schedule['$id'], ); - $queue->reclaim(); // TODO: Do in try/catch/finally, or add to connectons resource + $this->connections->reclaim(); + // $queue->reclaim(); // TODO: Do in try/catch/finally, or add to connectons resource unset($this->schedules[$schedule['resourceId']]); }); From 0664c39533d9d050e9168a3136aeadc7a1640085 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:11:31 -0400 Subject: [PATCH 064/195] fix: Typo --- app/controllers/api/avatars.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index bd9b588f40..b3f09d7630 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -595,7 +595,7 @@ Http::get('/v1/cards/cloud') ->inject('contributors') ->inject('employees') ->inject('logger') - ->inject('authp') + ->inject('auth') ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); From 7dfc6541218b91f7430785075134028f47efd609 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 3 Jun 2024 15:24:39 -0400 Subject: [PATCH 065/195] wip: Starting realtime --- app/realtime.php | 89 ++++++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index 2e2024216a..ae34e768b4 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -44,17 +44,21 @@ Runtime::enableCoroutine(SWOOLE_HOOK_ALL); function getConsoleDB(Authorization $auth): Database { global $global; - - /** @var \Utopia\Pools\Group $pools */ $pools = $global->get('pools'); - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource() - ; + $pool = $pools['pools-console-main']['pool']; + $dsn = $pools['pools-console-main']['dsn']; + $connection = $pool->get(); - $database = new Database($dbAdapter, getCache()); + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); + + $database = new Database($adapter, getCache()); $database->setAuthorization($auth); $database @@ -67,22 +71,29 @@ function getConsoleDB(Authorization $auth): Database function getProjectDB(Document $project, Authorization $auth): Database { - global $global; - - /** @var \Utopia\Pools\Group $pools */ - $pools = $global->get('pools'); - if ($project->isEmpty() || $project->getId() === 'console') { return getConsoleDB($auth); } - $dbAdapter = $pools - ->get($project->getAttribute('database')) - ->pop() - ->getResource() - ; + global $global; + $pools = $global->get('pools'); - $database = new Database($dbAdapter, getCache()); + $databaseName = $project->getAttribute('database'); + + $pool = $pools['pools-database-' . $databaseName]['pool']; + $dsn = $pools['pools-database-' . $databaseName]['dsn']; + + $connection = $pool->get(); + + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + $adapter->setDatabase($dsn->getPath()); + + + $database = new Database($adapter, getCache()); $database->setAuthorization($auth); $database @@ -95,22 +106,23 @@ function getProjectDB(Document $project, Authorization $auth): Database function getCache(): Cache { - global $global; - - $pools = $global->get('pools'); /** @var \Utopia\Pools\Group $pools */ - - $list = Config::getParam('pools-cache', []); - $adapters = []; - - foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource() - ; - } - - return new Cache(new Sharding($adapters)); + return new Cache(new \Utopia\Cache\Adapter\None()); +// global $global; +// +// $pools = $global->get('pools'); /** @var \Utopia\Pools\Group $pools */ +// +// $list = Config::getParam('pools-cache', []); +// $adapters = []; +// +// foreach ($list as $value) { +// $adapters[] = $pools +// ->get($value) +// ->pop() +// ->getResource() +// ; +// } +// +// return new Cache(new Sharding($adapters)); } $realtime = new Realtime(); @@ -334,7 +346,12 @@ $server->onWorkerStart(function (int $workerId) use ($server, $global, $stats, $ $start = time(); - $redis = $global->get('pools')->get('pubsub')->pop()->getResource(); /** @var Redis $redis */ + $pools = $global->get('pools'); + $pool = $pools['pools-pubsub-main']['pool']; + $dsn = $pools['pools-pubsub-main']['dsn']; + $redis = new \Redis(); + $redis->connect($dsn->getHost(), $dsn->getPort()); + $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); if ($redis->ping(true)) { From 0093ad7799fe2ddc341f4fe68fbce0c52cd75f4d Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 4 Jun 2024 11:57:16 -0400 Subject: [PATCH 066/195] Revert "wip: Starting realtime" This reverts commit 7dfc6541218b91f7430785075134028f47efd609. --- app/realtime.php | 89 ++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 53 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index ae34e768b4..2e2024216a 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -44,21 +44,17 @@ Runtime::enableCoroutine(SWOOLE_HOOK_ALL); function getConsoleDB(Authorization $auth): Database { global $global; + + /** @var \Utopia\Pools\Group $pools */ $pools = $global->get('pools'); - $pool = $pools['pools-console-main']['pool']; - $dsn = $pools['pools-console-main']['dsn']; - $connection = $pool->get(); + $dbAdapter = $pools + ->get('console') + ->pop() + ->getResource() + ; - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - - $database = new Database($adapter, getCache()); + $database = new Database($dbAdapter, getCache()); $database->setAuthorization($auth); $database @@ -71,29 +67,22 @@ function getConsoleDB(Authorization $auth): Database function getProjectDB(Document $project, Authorization $auth): Database { + global $global; + + /** @var \Utopia\Pools\Group $pools */ + $pools = $global->get('pools'); + if ($project->isEmpty() || $project->getId() === 'console') { return getConsoleDB($auth); } - global $global; - $pools = $global->get('pools'); + $dbAdapter = $pools + ->get($project->getAttribute('database')) + ->pop() + ->getResource() + ; - $databaseName = $project->getAttribute('database'); - - $pool = $pools['pools-database-' . $databaseName]['pool']; - $dsn = $pools['pools-database-' . $databaseName]['dsn']; - - $connection = $pool->get(); - - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - $adapter->setDatabase($dsn->getPath()); - - - $database = new Database($adapter, getCache()); + $database = new Database($dbAdapter, getCache()); $database->setAuthorization($auth); $database @@ -106,23 +95,22 @@ function getProjectDB(Document $project, Authorization $auth): Database function getCache(): Cache { - return new Cache(new \Utopia\Cache\Adapter\None()); -// global $global; -// -// $pools = $global->get('pools'); /** @var \Utopia\Pools\Group $pools */ -// -// $list = Config::getParam('pools-cache', []); -// $adapters = []; -// -// foreach ($list as $value) { -// $adapters[] = $pools -// ->get($value) -// ->pop() -// ->getResource() -// ; -// } -// -// return new Cache(new Sharding($adapters)); + global $global; + + $pools = $global->get('pools'); /** @var \Utopia\Pools\Group $pools */ + + $list = Config::getParam('pools-cache', []); + $adapters = []; + + foreach ($list as $value) { + $adapters[] = $pools + ->get($value) + ->pop() + ->getResource() + ; + } + + return new Cache(new Sharding($adapters)); } $realtime = new Realtime(); @@ -346,12 +334,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $global, $stats, $ $start = time(); - $pools = $global->get('pools'); - $pool = $pools['pools-pubsub-main']['pool']; - $dsn = $pools['pools-pubsub-main']['dsn']; - $redis = new \Redis(); - $redis->connect($dsn->getHost(), $dsn->getPort()); - + $redis = $global->get('pools')->get('pubsub')->pop()->getResource(); /** @var Redis $redis */ $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); if ($redis->ping(true)) { From f7d9efc18e4ebe3a76b7fb67ee87d643d2a7d286 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:12:10 -0400 Subject: [PATCH 067/195] feat: Adding DI to realtime --- app/realtime.php | 221 +++++++++++++++++++++++------------------------ 1 file changed, 109 insertions(+), 112 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index 2e2024216a..eb2eb4d7e4 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -5,6 +5,7 @@ use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Network\Validator\Origin; +use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Swoole\Http\Request as SwooleRequest; @@ -14,10 +15,11 @@ use Swoole\Table; use Swoole\Timer; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; -use Utopia\Cache\Adapter\Sharding; +use Utopia\Cache\Adapter\None; 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\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -25,93 +27,80 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\Http\Adapter\FPM\Server as FPMServer; +use Utopia\DI\Container; +use Utopia\DI\Dependency; +use Utopia\Http\Adapter\Swoole\Request as UtopiaRequest; +use Utopia\Http\Adapter\Swoole\Response as UtopiaResponse; use Utopia\Http\Http; use Utopia\Logger\Log; +use Utopia\Registry\Registry; use Utopia\System\System; use Utopia\WebSocket\Adapter; use Utopia\WebSocket\Server; -global $global; - /** - * @var \Utopia\Registry\Registry $global + * @var Registry $global + * @var Container $container */ +global $global, $container; + + require_once __DIR__ . '/init2.php'; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -function getConsoleDB(Authorization $auth): Database -{ - global $global; +$auth = new Dependency(); +$cache = new Dependency(); +$getProjectDB = new Dependency(); - /** @var \Utopia\Pools\Group $pools */ - $pools = $global->get('pools'); +$auth + ->setName('auth') + ->setCallback(fn () => new Authorization()); - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource() - ; +$getProjectDB + ->setName('getProjectDB') + ->inject('pools') + ->inject('dbForConsole') + ->inject('cache') + ->inject('auth') + ->inject('connections') + ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $auth, Connections $connections) { + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections): Database { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } - $database = new Database($dbAdapter, getCache()); - $database->setAuthorization($auth); + $databaseName = $project->getAttribute('database'); - $database - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', '_console'); + $pool = $pools['pools-database-' . $databaseName]['pool']; + $dsn = $pools['pools-database-' . $databaseName]['dsn']; - return $database; -} + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + $adapter->setDatabase($dsn->getPath()); -function getProjectDB(Document $project, Authorization $auth): Database -{ - global $global; + $database = new Database($adapter, $cache); + $database->setAuthorization($auth); + $database->setNamespace('_' . $project->getInternalId()); - /** @var \Utopia\Pools\Group $pools */ - $pools = $global->get('pools'); + return $database; + }; + }); - if ($project->isEmpty() || $project->getId() === 'console') { - return getConsoleDB($auth); - } +$cache + ->setName('cache') + ->setCallback(function () { + return new Cache(new None()); + }); - $dbAdapter = $pools - ->get($project->getAttribute('database')) - ->pop() - ->getResource() - ; - - $database = new Database($dbAdapter, getCache()); - $database->setAuthorization($auth); - - $database - ->setNamespace('_' . $project->getInternalId()) - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()); - - return $database; -} - -function getCache(): Cache -{ - global $global; - - $pools = $global->get('pools'); /** @var \Utopia\Pools\Group $pools */ - - $list = Config::getParam('pools-cache', []); - $adapters = []; - - foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource() - ; - } - - return new Cache(new Sharding($adapters)); -} +$container->set($auth); +$container->set($cache); +$container->set($getProjectDB); $realtime = new Realtime(); @@ -175,18 +164,16 @@ $logError = function (Throwable $error, string $action) use ($global) { $server->error($logError); -$server->onStart(function () use ($stats, $global, $containerId, &$statsDocument, $logError) { - $auth = new Authorization(); - +$server->onStart(function () use ($stats, $container, $containerId, &$statsDocument, $logError) { sleep(5); // wait for the initial database schema to be ready Console::success('Server started successfully'); /** * Create document for this worker to share stats across Containers. */ - go(function () use ($global, $containerId, &$statsDocument, $auth) { + go(function () use ($container, $containerId, &$statsDocument) { $attempts = 0; - $database = getConsoleDB($auth); + $database = $container->get('dbForConsole'); do { try { @@ -208,13 +195,13 @@ $server->onStart(function () use ($stats, $global, $containerId, &$statsDocument sleep(DATABASE_RECONNECT_SLEEP); } } while (true); - $global->get('pools')->reclaim(); + // TODO NOW $global->get('pools')->reclaim(); }); /** * Save current connections to the Database every 5 seconds. */ - Timer::tick(5000, function () use ($global, $stats, &$statsDocument, $logError, $auth) { + Timer::tick(5000, function () use ($container, $stats, &$statsDocument, $logError) { $payload = []; foreach ($stats as $projectId => $value) { $payload[$projectId] = $stats->get($projectId, 'connectionsTotal'); @@ -224,7 +211,7 @@ $server->onStart(function () use ($stats, $global, $containerId, &$statsDocument } try { - $database = getConsoleDB($auth); + $database = $container->get('dbForConsole'); $statsDocument ->setAttribute('timestamp', DateTime::now()) @@ -235,12 +222,12 @@ $server->onStart(function () use ($stats, $global, $containerId, &$statsDocument } catch (Throwable $th) { call_user_func($logError, $th, "updateWorkerDocument"); } finally { - $global->get('pools')->reclaim(); + // TODO NOW $global->get('pools')->reclaim(); } }); }); -$server->onWorkerStart(function (int $workerId) use ($server, $global, $stats, $realtime, $logError) { +$server->onWorkerStart(function (int $workerId) use ($server, $container, $stats, $realtime, $logError) { Console::success('Worker ' . $workerId . ' started successfully'); $attempts = 0; @@ -248,12 +235,12 @@ $server->onWorkerStart(function (int $workerId) use ($server, $global, $stats, $ $auth = new Authorization(); - Timer::tick(5000, function () use ($server, $global, $realtime, $stats, $logError, $auth) { + Timer::tick(5000, function () use ($server, $container, $realtime, $stats, $logError, $auth) { /** * Sending current connections to project channels on the console project every 5 seconds. */ if ($realtime->hasSubscriber('console', Role::users()->toString(), 'project')) { - $database = getConsoleDB($auth); + $database = $container->get('dbForConsole'); $payload = []; @@ -267,9 +254,9 @@ $server->onWorkerStart(function (int $workerId) use ($server, $global, $stats, $ foreach ($list as $document) { foreach (json_decode($document->getAttribute('value')) as $projectId => $value) { if (array_key_exists($projectId, $payload)) { - $payload[$projectId] += $value; + $payload[$projectId] += $value; } else { - $payload[$projectId] = $value; + $payload[$projectId] = $value; } } } @@ -297,8 +284,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $global, $stats, $ 'data' => $event['data'] ])); } - - $global->get('pools')->reclaim(); + // TODO NOW $global->get('pools')->reclaim(); } /** * Sending test message for SDK E2E tests every 5 seconds. @@ -327,14 +313,22 @@ $server->onWorkerStart(function (int $workerId) use ($server, $global, $stats, $ while ($attempts < 300) { try { if ($attempts > 0) { - Console::error('Pub/sub connection lost (lasted ' . (time() - $start) . ' seconds, worker: ' . $workerId . '). - Attempting restart in 5 seconds (attempt #' . $attempts . ')'); + Console::error( + 'Pub/sub connection lost (lasted ' . (time() - $start) . ' seconds, worker: ' . $workerId . '). + Attempting restart in 5 seconds (attempt #' . $attempts . ')' + ); sleep(5); // 5 sec delay between connection attempts } $start = time(); - $redis = $global->get('pools')->get('pubsub')->pop()->getResource(); /** @var Redis $redis */ + $pools = $container->get('pools'); + $pool = $pools['pools-pubsub-main']['pool']; + $dsn = $pools['pools-pubsub-main']['dsn']; + $redis = new \Redis(); + $redis->connect($dsn->getHost(), $dsn->getPort()); + + /** @var Redis $redis */ $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); if ($redis->ping(true)) { @@ -344,7 +338,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $global, $stats, $ Console::error('Pub/sub failed (worker: ' . $workerId . ')'); } - $redis->subscribe(['realtime'], function (Redis $redis, string $channel, string $payload) use ($server, $workerId, $stats, $global, $realtime, $auth) { + $redis->subscribe(['realtime'], function (Redis $redis, string $channel, string $payload) use ($server, $workerId, $stats, $realtime, $auth, $container) { $event = json_decode($payload, true); if ($event['permissionsChanged'] && isset($event['userId'])) { @@ -353,18 +347,17 @@ $server->onWorkerStart(function (int $workerId) use ($server, $global, $stats, $ if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) { $connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId])); - $consoleDatabase = getConsoleDB($auth); + $consoleDatabase = $container->get('dbForConsole'); $auth = new Authorization(); $project = $auth->skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); - $database = getProjectDB($project, $auth); + $database = $container->get('getProjectDB')($project); $user = $database->getDocument('users', $userId); $roles = Auth::getRoles($user, $auth); $realtime->subscribe($projectId, $connection, $roles, $realtime->connections[$connection]['channels']); - - $global->get('pools')->reclaim(); + //TODO NOW $global->get('pools')->reclaim(); } } @@ -404,25 +397,29 @@ $server->onWorkerStart(function (int $workerId) use ($server, $global, $stats, $ Console::error('Failed to restart pub/sub...'); }); -$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $global, $stats, &$realtime, $logError) { +$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $container, $stats, &$realtime, $logError) { $auth = new Authorization(); - $http = new Http(new FPMServer(), 'UTC'); - $request = new Request($request); - $response = new Response(new SwooleResponse()); + $request = new Request(new UtopiaRequest($request)); + $response = new Response(new UtopiaResponse(new SwooleResponse())); + + $requestInjection = new Dependency(); + $responseInjection = new Dependency(); + + $requestInjection->setName('request')->setCallback(fn () => $request); + $responseInjection->setName('response')->setCallback(fn () => $response); + + $container->set($requestInjection); + $container->set($responseInjection); Console::info("Connection open (user: {$connection})"); - Http::setResource('pools', fn () => $global->get('pools')); - Http::setResource('request', fn () => $request); - Http::setResource('response', fn () => $response); - try { /** @var Document $project */ - $project = $http->getResource('project'); + $project = $container->get('project'); /* - * Project Check + * Project Check */ if (empty($project->getId())) { throw new Exception(Exception::REALTIME_POLICY_VIOLATION, 'Missing or unknown project ID'); @@ -437,9 +434,11 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } - $dbForProject = getProjectDB($project, $auth); - $console = $http->getResource('console'); /** @var Document $console */ - $user = $http->getResource('user'); /** @var Document $user */ + $dbForProject = $container->get('getProjectDB')($project); + $console = $container->get('console'); + /** @var Document $console */ + $user = $container->get('user'); + /** @var Document $user */ /* * Abuse Check @@ -519,22 +518,20 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::error('[Error] Message: ' . $response['data']['message']); } } finally { - $global->get('pools')->reclaim(); + // TODO NOW $global->get('pools')->reclaim(); } }); -$server->onMessage(function (int $connection, string $message) use ($server, $global, $realtime, $containerId) { - $auth = new Authorization(); - +$server->onMessage(function (int $connection, string $message) use ($server, $container, $realtime, $containerId) { try { $response = new Response(new SwooleResponse()); $projectId = $realtime->connections[$connection]['projectId']; - $database = getConsoleDB($auth); + $database = $container->get('dbForConsole'); if ($projectId !== 'console') { $auth = new Authorization(); $project = $auth->skip(fn () => $database->getDocument('projects', $projectId)); - $database = getProjectDB($project, $auth); + $database = $container->get('getProjectDB')($project); } else { $project = null; } @@ -619,7 +616,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $gl $server->close($connection, $th->getCode()); } } finally { - $global->get('pools')->reclaim(); + // TODO NOW $global->get('pools')->reclaim(); } }); From d1c3e47392b5da80835333ff3bd79fbd0aeb22ca Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:22:27 -0400 Subject: [PATCH 068/195] refactor: CLI injections --- app/cli.php | 156 ++++------------------------------------------------ 1 file changed, 12 insertions(+), 144 deletions(-) diff --git a/app/cli.php b/app/cli.php index b5e639ef11..ff096c2906 100644 --- a/app/cli.php +++ b/app/cli.php @@ -5,19 +5,9 @@ require_once __DIR__ . '/controllers/general.php'; use Appwrite\Event\Certificate; use Appwrite\Event\Delete; -use Appwrite\Event\Func; use Appwrite\Event\Hamster; use Appwrite\Platform\Appwrite; -use Appwrite\Utopia\Queue\Connections; -use Utopia\Cache\Adapter\None; -use Utopia\Cache\Adapter\Sharding; -use Utopia\Cache\Cache; use Utopia\CLI\Console; -use Utopia\Queue\Connection\Redis; -use Utopia\Database\Adapter\MariaDB; -use Utopia\Database\Adapter\MySQL; -use Utopia\Database\Database; -use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; use Utopia\DI\Dependency; use Utopia\Logger\Log; @@ -33,119 +23,16 @@ global $global, $container; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -$registry = new Dependency(); -$registry - ->setName('register') - ->setCallback(fn() => $global); - -$connections = new Dependency(); -$connections - ->setName('connections') - ->setCallback(fn() => new Connections()); - -$cache = new Dependency(); -$cache - ->setName('cache') - ->setCallback(function () { - return new Cache(new None()); - }); -$container->set($cache); - -$pools = new Dependency(); -$pools - ->setName('pools') - ->inject('register') - ->setCallback(function (Registry $register) { - return $register->get('pools'); - }); - -$dbForConsole = new Dependency(); -$dbForConsole - ->setName('dbForConsole') - ->inject('pools') - ->inject('cache') - ->inject('auth') - ->inject('connections') - ->setCallback(function ($pools, $cache, $auth, Connections $connections) { - $pool = $pools['pools-console-main']['pool']; - $dsn = $pools['pools-console-main']['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - - $database = new Database($adapter, $cache); - $database->setAuthorization($auth); - $database->setNamespace('_console'); - - return $database; - }); - -$getProjectDB = new Dependency(); -$getProjectDB - ->setName('getProjectDB') - ->inject('pools') - ->inject('dbForConsole') - ->inject('cache') - ->inject('auth') - ->inject('connections') - ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $auth, Connections $connections) { - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections): Database { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - $databaseName = $project->getAttribute('database'); - - $pool = $pools['pools-database-' . $databaseName]['pool']; - $dsn = $pools['pools-database-' . $databaseName]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - $adapter->setDatabase($dsn->getPath()); - - $database = new Database($adapter, $cache); - $database->setAuthorization($auth); - $database->setNamespace('_' . $project->getInternalId()); - - return $database; - }; - }); - -$queue = new Dependency(); -$queue - ->setName('queue') - ->inject('pools') - ->inject('connections') - ->setCallback(function (array $pools, Connections $connections) { - $pool = $pools['pools-queue-main']['pool']; - $dsn = $pools['pools-queue-main']['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - return new Redis($dsn->getHost(), $dsn->getPort()); - }); - -$queueForFunctions = new Dependency(); -$queueForFunctions - ->setName('queueForFunctions') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Func($queue); - }); - +/** + * @var Registry $global + * @var Container $container + */ +$auth = new Dependency(); +$logError = new Dependency(); +$queueForDeletes = new Dependency(); $queueForHamster = new Dependency(); +$queueForCertificates = new Dependency(); + $queueForHamster ->setName('queueForHamster') ->inject('queue') @@ -153,7 +40,6 @@ $queueForHamster return new Hamster($queue); }); -$queueForDeletes = new Dependency(); $queueForDeletes ->setName('queueForDeletes') ->inject('queue') @@ -161,7 +47,6 @@ $queueForDeletes return new Delete($queue); }); -$queueForCertificates = new Dependency(); $queueForCertificates ->setName('queueForCertificates') ->inject('queue') @@ -169,15 +54,6 @@ $queueForCertificates return new Certificate($queue); }); -$queueForCertificates = new Dependency(); -$queueForCertificates - ->setName('queueForCertificates') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Certificate($queue); - }); - -$logError = new Dependency(); $logError ->setName('logError') ->inject('register') @@ -218,24 +94,16 @@ $logError }; }); -$auth = new Dependency(); + $auth ->setName('auth') ->setCallback(fn() => new Authorization()); -$container->set($registry); -$container->set($connections); -$container->set($cache); -$container->set($pools); -$container->set($dbForConsole); -$container->set($getProjectDB); -$container->set($queue); -$container->set($queueForFunctions); +$container->set($auth); +$container->set($logError); $container->set($queueForHamster); $container->set($queueForDeletes); $container->set($queueForCertificates); -$container->set($logError); -$container->set($auth); $platform = new Appwrite(); $platform->init(Service::TYPE_CLI, ['adapter' => new SwooleCLI(1)]); From c411aa234218eaebffd5f605fc5ceec0148902a0 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:23:08 -0400 Subject: [PATCH 069/195] fix: Realtime auth --- app/realtime.php | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index eb2eb4d7e4..4a4dfaecb8 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -55,7 +55,7 @@ $getProjectDB = new Dependency(); $auth ->setName('auth') - ->setCallback(fn () => new Authorization()); + ->setCallback(fn() => new Authorization()); $getProjectDB ->setName('getProjectDB') @@ -167,7 +167,7 @@ $server->error($logError); $server->onStart(function () use ($stats, $container, $containerId, &$statsDocument, $logError) { sleep(5); // wait for the initial database schema to be ready Console::success('Server started successfully'); - + $auth = $container->get('auth'); /** * Create document for this worker to share stats across Containers. */ @@ -187,8 +187,8 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum 'value' => '{}' ]); - $auth = new Authorization(); - $statsDocument = $auth->skip(fn () => $database->createDocument('realtime', $document)); + $auth = $container->get('auth'); + $statsDocument = $auth->skip(fn() => $database->createDocument('realtime', $document)); break; } catch (Throwable) { Console::warning("Collection not ready. Retrying connection ({$attempts})..."); @@ -201,7 +201,7 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum /** * Save current connections to the Database every 5 seconds. */ - Timer::tick(5000, function () use ($container, $stats, &$statsDocument, $logError) { + Timer::tick(5000, function () use ($container, $stats, &$statsDocument, $logError, $auth) { $payload = []; foreach ($stats as $projectId => $value) { $payload[$projectId] = $stats->get($projectId, 'connectionsTotal'); @@ -217,8 +217,7 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum ->setAttribute('timestamp', DateTime::now()) ->setAttribute('value', json_encode($payload)); - $auth = new Authorization(); - $auth->skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); + $auth->skip(fn() => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); } catch (Throwable $th) { call_user_func($logError, $th, "updateWorkerDocument"); } finally { @@ -233,7 +232,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats $attempts = 0; $start = time(); - $auth = new Authorization(); + $auth = $container->get('auth'); Timer::tick(5000, function () use ($server, $container, $realtime, $stats, $logError, $auth) { /** @@ -244,7 +243,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats $payload = []; - $list = $auth->skip(fn () => $database->find('realtime', [ + $list = $auth->skip(fn() => $database->find('realtime', [ Query::greaterThan('timestamp', DateTime::addSeconds(new \DateTime(), -15)), ])); @@ -348,8 +347,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) { $connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId])); $consoleDatabase = $container->get('dbForConsole'); - $auth = new Authorization(); - $project = $auth->skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); + + $project = $auth->skip(fn() => $consoleDatabase->getDocument('projects', $projectId)); $database = $container->get('getProjectDB')($project); $user = $database->getDocument('users', $userId); @@ -398,7 +397,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats }); $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $container, $stats, &$realtime, $logError) { - $auth = new Authorization(); + $auth = $container->get('auth'); $request = new Request(new UtopiaRequest($request)); $response = new Response(new UtopiaResponse(new SwooleResponse())); @@ -406,8 +405,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $requestInjection = new Dependency(); $responseInjection = new Dependency(); - $requestInjection->setName('request')->setCallback(fn () => $request); - $responseInjection->setName('response')->setCallback(fn () => $response); + $requestInjection->setName('request')->setCallback(fn() => $request); + $responseInjection->setName('response')->setCallback(fn() => $response); $container->set($requestInjection); $container->set($responseInjection); @@ -468,7 +467,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription()); } - $auth = new Authorization(); + $auth = $container->get('auth'); $roles = Auth::getRoles($user, $auth); $channels = Realtime::convertChannels($request->getQuery('channels', []), $user->getId()); @@ -529,8 +528,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $co $database = $container->get('dbForConsole'); if ($projectId !== 'console') { - $auth = new Authorization(); - $project = $auth->skip(fn () => $database->getDocument('projects', $projectId)); + $auth = $container->get('auth'); + $project = $auth->skip(fn() => $database->getDocument('projects', $projectId)); $database = $container->get('getProjectDB')($project); } else { $project = null; From 3682da417a2c4c5c970612356051d256d076777c Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:23:29 -0400 Subject: [PATCH 070/195] fix: reverting to run swoole 5.1.1 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index d7343dd71d..2b99ce6589 100755 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT RUN npm ci RUN npm run build -FROM appwrite/base:0.9.0 as final +FROM appwrite/base:0.7.2 as final LABEL maintainer="team@appwrite.io" @@ -138,4 +138,4 @@ RUN if [ "$DEBUG" = "false" ]; then rm -f /usr/local/lib/php/extensions/no-debug EXPOSE 80 -CMD [ "php", "app/http.php" ] \ No newline at end of file +CMD [ "php", "app/http.php" ] From 8ac7d0a160c9b140ada47b152ee90b6ad6dc9f1d Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:31:20 -0400 Subject: [PATCH 071/195] fix: Removing local compose values --- docker-compose.yml | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index e116569c50..ddec7d41a6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -51,7 +51,7 @@ services: build: context: . args: - DEBUG: true + DEBUG: false TESTING: true VERSION: dev ports: @@ -96,7 +96,6 @@ services: - -e - app/http.php environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_LOCALE @@ -224,7 +223,6 @@ services: - mariadb - redis environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPTIONS_ABUSE @@ -258,7 +256,6 @@ services: - redis - mariadb environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -290,7 +287,6 @@ services: - mariadb - request-catcher environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -328,7 +324,6 @@ services: - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -382,7 +377,6 @@ services: - redis - mariadb environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -417,7 +411,6 @@ services: - redis - mariadb environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -484,7 +477,6 @@ services: - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -520,7 +512,6 @@ services: - mariadb - openruntimes-executor environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -563,7 +554,6 @@ services: - maildev # - smtp environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -598,7 +588,6 @@ services: depends_on: - redis environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -633,7 +622,6 @@ services: depends_on: - mariadb environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -668,7 +656,6 @@ services: depends_on: - redis environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_DOMAIN @@ -708,7 +695,6 @@ services: - redis - mariadb environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -741,7 +727,6 @@ services: - redis - mariadb environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -774,7 +759,6 @@ services: - mariadb - redis environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -803,7 +787,6 @@ services: - mariadb - redis environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -823,7 +806,6 @@ services: networks: - appwrite environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ASSISTANT_OPENAI_API_KEY openruntimes-executor: @@ -844,7 +826,6 @@ services: # It's not possible to share mount file between 2 containers without host mount (copying is too slow) - /tmp:/tmp:rw environment: - - PHP_IDE_CONFIG=serverName=Appwrite - OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD - OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL - OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK @@ -888,7 +869,6 @@ services: - appwrite - runtimes environment: - - PHP_IDE_CONFIG=serverName=Appwrite - OPR_PROXY_WORKER_PER_CORE=$_APP_WORKER_PER_CORE - OPR_PROXY_ENV=$_APP_ENV - OPR_PROXY_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET @@ -912,7 +892,6 @@ services: ports: - "3306:3306" environment: - - PHP_IDE_CONFIG=serverName=Appwrite - MYSQL_ROOT_PASSWORD=${_APP_DB_ROOT_PASS} - MYSQL_DATABASE=${_APP_DB_SCHEMA} - MYSQL_USER=${_APP_DB_USER} @@ -1004,7 +983,6 @@ services: networks: - appwrite environment: - - PHP_IDE_CONFIG=serverName=Appwrite - REDIS_HOSTS=redis ports: - "8081:5540" @@ -1018,7 +996,6 @@ services: ports: - "9509:3000" environment: - - PHP_IDE_CONFIG=serverName=Appwrite - SERVER_URL=http://localhost/v1/graphql # Dev Tools End ------------------------------------------------------------------------------------------ From 187ccc64decef76b360788785b1ea83c68d20441 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:31:34 -0400 Subject: [PATCH 072/195] cicd: checking compose logs --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2cc4c700f7..5b44239de9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -65,6 +65,7 @@ jobs: docker load --input /tmp/${{ env.IMAGE }}.tar docker compose up -d sleep 10 + docker compose logs - name: Doctor run: docker compose exec -T appwrite doctor @@ -95,6 +96,7 @@ jobs: docker load --input /tmp/${{ env.IMAGE }}.tar docker compose up -d sleep 10 + docker compose logs - name: Run General Tests run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/General --debug From 85ccfd883ef1ba199f0f20afa6b7368e595ef25f Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:36:49 -0400 Subject: [PATCH 073/195] fix: Removing local compose values --- docker-compose.yml | 56 ++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ddec7d41a6..7a461f4ecc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,8 +10,6 @@ x-logging: &x-logging max-file: "5" max-size: "10m" -version: "3" - services: traefik: image: traefik:2.11 @@ -42,7 +40,6 @@ services: networks: - gateway - appwrite - - runtimes appwrite: container_name: appwrite @@ -78,11 +75,9 @@ services: - appwrite-config:/storage/config:rw - appwrite-certificates:/storage/certificates:rw - appwrite-functions:/storage/functions:rw - - appwrite-builds:/storage/builds:rw - ./phpunit.xml:/usr/src/code/phpunit.xml - ./tests:/usr/src/code/tests - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./docs:/usr/src/code/docs - ./public:/usr/src/code/public - ./src:/usr/src/code/src @@ -97,6 +92,7 @@ services: - app/http.php environment: - _APP_ENV + - _APP_EDITION - _APP_WORKER_PER_CORE - _APP_LOCALE - _APP_CONSOLE_WHITELIST_ROOT @@ -191,6 +187,9 @@ services: - _APP_MESSAGE_EMAIL_TEST_DSN - _APP_MESSAGE_PUSH_TEST_DSN - _APP_CONSOLE_COUNTRIES_DENYLIST + - _APP_EXPERIMENT_LOGGING_PROVIDER + - _APP_EXPERIMENT_LOGGING_CONFIG + appwrite-realtime: entrypoint: realtime <<: *x-logging @@ -217,7 +216,6 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - mariadb @@ -250,7 +248,6 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis @@ -280,7 +277,6 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis @@ -321,7 +317,6 @@ services: - appwrite-builds:/storage/builds:rw - appwrite-certificates:/storage/certificates:rw - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src environment: - _APP_ENV @@ -371,7 +366,6 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis @@ -405,7 +399,6 @@ services: - appwrite-functions:/storage/functions:rw - appwrite-builds:/storage/builds:rw - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis @@ -474,7 +467,6 @@ services: - appwrite-config:/storage/config:rw - appwrite-certificates:/storage/certificates:rw - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src environment: - _APP_ENV @@ -505,7 +497,6 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis @@ -515,8 +506,6 @@ services: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 - - _APP_DOMAIN - - _APP_OPTIONS_FORCE_HTTPS - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER @@ -547,7 +536,6 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis @@ -582,8 +570,8 @@ services: networks: - appwrite volumes: + - appwrite-uploads:/storage/uploads:rw - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis @@ -605,6 +593,27 @@ services: - _APP_SMS_FROM - _APP_SMS_PROVIDER - _APP_SMS_PROJECTS_DENY_LIST + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET appwrite-worker-migrations: entrypoint: worker-migrations @@ -616,7 +625,6 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src - ./tests:/usr/src/code/tests depends_on: @@ -651,7 +659,6 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis @@ -689,7 +696,6 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis @@ -721,7 +727,6 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis @@ -753,7 +758,6 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - mariadb @@ -781,7 +785,6 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - mariadb @@ -810,10 +813,10 @@ services: openruntimes-executor: container_name: openruntimes-executor - hostname: appwrite-executor + hostname: exc1 <<: *x-logging stop_signal: SIGINT - image: openruntimes/executor:0.4.12 + image: openruntimes/executor:0.5.5 restart: unless-stopped networks: - appwrite @@ -876,7 +879,7 @@ services: - OPR_PROXY_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER - OPR_PROXY_LOGGING_CONFIG=$_APP_LOGGING_CONFIG - OPR_PROXY_ALGORITHM=random - - OPR_PROXY_EXECUTORS=appwrite-executor + - OPR_PROXY_EXECUTORS=exc1 - OPR_PROXY_HEALTHCHECK_INTERVAL=10000 - OPR_PROXY_MAX_TIMEOUT=600 - OPR_PROXY_HEALTHCHECK=enabled @@ -907,7 +910,6 @@ services: # networks: # - appwrite # environment: - # PHP_IDE_CONFIG=serverName=Appwrite # - LOCAL_DOMAINS=@ # - RELAY_FROM_HOSTS=192.168.0.0/16 ; *.yourdomain.com # - SMARTHOST_HOST=smtp From 1c9457b1ed8871af889618570faf8f05bccdaca6 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:47:55 -0400 Subject: [PATCH 074/195] cicd: removing logs --- .github/workflows/tests.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5b44239de9..2cc4c700f7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -65,7 +65,6 @@ jobs: docker load --input /tmp/${{ env.IMAGE }}.tar docker compose up -d sleep 10 - docker compose logs - name: Doctor run: docker compose exec -T appwrite doctor @@ -96,7 +95,6 @@ jobs: docker load --input /tmp/${{ env.IMAGE }}.tar docker compose up -d sleep 10 - docker compose logs - name: Run General Tests run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/General --debug From 72d5e94c973c814a916aa1fe93d5cebc577d118b Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:56:28 -0400 Subject: [PATCH 075/195] wip: coroutines --- app/cli.php | 8 ++++++++ src/Appwrite/Platform/Tasks/Doctor.php | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/cli.php b/app/cli.php index ff096c2906..5f146e59bd 100644 --- a/app/cli.php +++ b/app/cli.php @@ -28,11 +28,18 @@ Runtime::enableCoroutine(SWOOLE_HOOK_ALL); * @var Container $container */ $auth = new Dependency(); +$register = new Dependency(); $logError = new Dependency(); $queueForDeletes = new Dependency(); $queueForHamster = new Dependency(); $queueForCertificates = new Dependency(); +$register + ->setName('register') + ->setCallback(function () use (&$global): Registry { + return $global; + }); + $queueForHamster ->setName('queueForHamster') ->inject('queue') @@ -101,6 +108,7 @@ $auth $container->set($auth); $container->set($logError); +$container->set($register); $container->set($queueForHamster); $container->set($queueForDeletes); $container->set($queueForCertificates); diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index b639f420ef..e227364e17 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -111,7 +111,7 @@ class Doctor extends Action Console::log('🟢 Logging adapter is enabled (' . $providerName . ')'); } - \sleep(0.2); + \sleep(1); try { Console::log("\n" . '[Connectivity]'); @@ -200,7 +200,7 @@ class Doctor extends Action Console::error('🔴 ' . str_pad("SMTP", 47, '.') . 'disconnected'); } - \sleep(0.2); + \sleep(1); Console::log(''); Console::log('[Volumes]'); @@ -228,7 +228,7 @@ class Doctor extends Action } } - \sleep(0.2); + \sleep(1); Console::log(''); Console::log('[Disk Space]'); From 6a7ee3df8a0394344a31f3e99c3652eb3706f9ca Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:08:27 -0400 Subject: [PATCH 076/195] fix: realtime auth --- app/realtime.php | 53 ++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index 4a4dfaecb8..69009c9186 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -49,23 +49,19 @@ require_once __DIR__ . '/init2.php'; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -$auth = new Dependency(); $cache = new Dependency(); $getProjectDB = new Dependency(); -$auth - ->setName('auth') - ->setCallback(fn() => new Authorization()); $getProjectDB ->setName('getProjectDB') ->inject('pools') ->inject('dbForConsole') ->inject('cache') - ->inject('auth') + ->inject('authorization') ->inject('connections') - ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $auth, Connections $connections) { - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections): Database { + ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $authorization, Connections $connections) { + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $authorization, $connections): Database { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } @@ -85,7 +81,7 @@ $getProjectDB $adapter->setDatabase($dsn->getPath()); $database = new Database($adapter, $cache); - $database->setAuthorization($auth); + $database->setAuthorization($authorization); $database->setNamespace('_' . $project->getInternalId()); return $database; @@ -98,7 +94,6 @@ $cache return new Cache(new None()); }); -$container->set($auth); $container->set($cache); $container->set($getProjectDB); @@ -167,7 +162,7 @@ $server->error($logError); $server->onStart(function () use ($stats, $container, $containerId, &$statsDocument, $logError) { sleep(5); // wait for the initial database schema to be ready Console::success('Server started successfully'); - $auth = $container->get('auth'); + $authorization = $container->get('authorization'); /** * Create document for this worker to share stats across Containers. */ @@ -187,8 +182,8 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum 'value' => '{}' ]); - $auth = $container->get('auth'); - $statsDocument = $auth->skip(fn() => $database->createDocument('realtime', $document)); + $authorization = $container->get('authorization'); + $statsDocument = $authorization->skip(fn () => $database->createDocument('realtime', $document)); break; } catch (Throwable) { Console::warning("Collection not ready. Retrying connection ({$attempts})..."); @@ -201,7 +196,7 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum /** * Save current connections to the Database every 5 seconds. */ - Timer::tick(5000, function () use ($container, $stats, &$statsDocument, $logError, $auth) { + Timer::tick(5000, function () use ($container, $stats, &$statsDocument, $logError, $authorization) { $payload = []; foreach ($stats as $projectId => $value) { $payload[$projectId] = $stats->get($projectId, 'connectionsTotal'); @@ -217,7 +212,7 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum ->setAttribute('timestamp', DateTime::now()) ->setAttribute('value', json_encode($payload)); - $auth->skip(fn() => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); + $authorization->skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); } catch (Throwable $th) { call_user_func($logError, $th, "updateWorkerDocument"); } finally { @@ -232,9 +227,9 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats $attempts = 0; $start = time(); - $auth = $container->get('auth'); + $authorization = $container->get('authorization'); - Timer::tick(5000, function () use ($server, $container, $realtime, $stats, $logError, $auth) { + Timer::tick(5000, function () use ($server, $container, $realtime, $stats, $logError, $authorization) { /** * Sending current connections to project channels on the console project every 5 seconds. */ @@ -243,7 +238,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats $payload = []; - $list = $auth->skip(fn() => $database->find('realtime', [ + $list = $authorization->skip(fn () => $database->find('realtime', [ Query::greaterThan('timestamp', DateTime::addSeconds(new \DateTime(), -15)), ])); @@ -337,7 +332,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats Console::error('Pub/sub failed (worker: ' . $workerId . ')'); } - $redis->subscribe(['realtime'], function (Redis $redis, string $channel, string $payload) use ($server, $workerId, $stats, $realtime, $auth, $container) { + $redis->subscribe(['realtime'], function (Redis $redis, string $channel, string $payload) use ($server, $workerId, $stats, $realtime, $authorization, $container) { $event = json_decode($payload, true); if ($event['permissionsChanged'] && isset($event['userId'])) { @@ -348,12 +343,12 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats $connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId])); $consoleDatabase = $container->get('dbForConsole'); - $project = $auth->skip(fn() => $consoleDatabase->getDocument('projects', $projectId)); + $project = $authorization->skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); $database = $container->get('getProjectDB')($project); $user = $database->getDocument('users', $userId); - $roles = Auth::getRoles($user, $auth); + $roles = Auth::getRoles($user, $authorization); $realtime->subscribe($projectId, $connection, $roles, $realtime->connections[$connection]['channels']); //TODO NOW $global->get('pools')->reclaim(); @@ -397,7 +392,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats }); $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $container, $stats, &$realtime, $logError) { - $auth = $container->get('auth'); + $authorization = $container->get('authorization'); $request = new Request(new UtopiaRequest($request)); $response = new Response(new UtopiaResponse(new SwooleResponse())); @@ -405,8 +400,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $requestInjection = new Dependency(); $responseInjection = new Dependency(); - $requestInjection->setName('request')->setCallback(fn() => $request); - $responseInjection->setName('response')->setCallback(fn() => $response); + $requestInjection->setName('request')->setCallback(fn () => $request); + $responseInjection->setName('response')->setCallback(fn () => $response); $container->set($requestInjection); $container->set($responseInjection); @@ -428,7 +423,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, if ( array_key_exists('realtime', $project->getAttribute('apis', [])) && !$project->getAttribute('apis', [])['realtime'] - && !(Auth::isPrivilegedUser($auth->getRoles()) || Auth::isAppUser($auth->getRoles())) + && !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles())) ) { throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } @@ -467,8 +462,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription()); } - $auth = $container->get('auth'); - $roles = Auth::getRoles($user, $auth); + $authorization = $container->get('authorization'); + $roles = Auth::getRoles($user, $authorization); $channels = Realtime::convertChannels($request->getQuery('channels', []), $user->getId()); @@ -528,8 +523,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $co $database = $container->get('dbForConsole'); if ($projectId !== 'console') { - $auth = $container->get('auth'); - $project = $auth->skip(fn() => $database->getDocument('projects', $projectId)); + $authorization = $container->get('authorization'); + $project = $authorization->skip(fn () => $database->getDocument('projects', $projectId)); $database = $container->get('getProjectDB')($project); } else { $project = null; @@ -581,7 +576,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $co throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.'); } - $roles = Auth::getRoles($user, $auth); + $roles = Auth::getRoles($user, $authorization); $channels = Realtime::convertChannels(array_flip($realtime->connections[$connection]['channels']), $user->getId()); $realtime->subscribe($realtime->connections[$connection]['projectId'], $connection, $roles, $channels); From 07582ecc5f4bbf5c89913c20b624d0f269471c16 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:38:02 -0400 Subject: [PATCH 077/195] fix: Changing auth function to instance --- app/controllers/shared/api.php | 2 +- composer.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 7eeeb2c5a6..20286666e8 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -713,7 +713,7 @@ Http::shutdown() } if ($project->getId() !== 'console') { - if (!Auth::isPrivilegedUser(Authorization::getRoles())) { + if (!Auth::isPrivilegedUser($authorization->getRoles())) { $fileSize = 0; $file = $request->getFiles('file'); if (!empty($file)) { diff --git a/composer.lock b/composer.lock index e069e098cc..249eb1fbf2 100644 --- a/composer.lock +++ b/composer.lock @@ -1462,12 +1462,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "c9b049160aff2eecea7dd1ff041165ae15f41f76" + "reference": "caea27c18a7d701a63e985a7aad1bfa7827ab469" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/c9b049160aff2eecea7dd1ff041165ae15f41f76", - "reference": "c9b049160aff2eecea7dd1ff041165ae15f41f76", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/caea27c18a7d701a63e985a7aad1bfa7827ab469", + "reference": "caea27c18a7d701a63e985a7aad1bfa7827ab469", "shasum": "" }, "require": { @@ -1505,7 +1505,7 @@ "issues": "https://github.com/utopia-php/cli/issues", "source": "https://github.com/utopia-php/cli/tree/dev-coroutines" }, - "time": "2024-06-03T17:55:34+00:00" + "time": "2024-06-05T12:46:38+00:00" }, { "name": "utopia-php/config", From 6b0892a4950af1457d99514d37a6dff31dfd0f3c Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 5 Jun 2024 20:41:23 -0400 Subject: [PATCH 078/195] refactor: Adjusting to merge and some code cleanups --- app/controllers/api/projects.php | 161 +++--- app/controllers/api/vcs.php | 2 +- app/controllers/general.php | 55 +- app/init/constants.php | 2 +- app/init2.php | 561 ++++++++++++--------- app/worker.php | 405 +-------------- composer.json | 2 +- composer.lock | 505 +++++++++++++------ docker-compose.yml | 47 +- src/Appwrite/Platform/Tasks/QueueRetry.php | 1 + src/Appwrite/Platform/Workers/Audits.php | 4 +- src/Appwrite/Platform/Workers/Builds.php | 4 +- src/Appwrite/Platform/Workers/Deletes.php | 4 +- 13 files changed, 844 insertions(+), 909 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 8b89393f09..6de5a87ea2 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -69,7 +69,7 @@ Http::post('/v1/projects') ->param('projectId', '', new ProjectId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, and hyphen. Can\'t start with a special char. Max length is 36 chars.') ->param('name', null, new Text(128), 'Project name. Max length: 128 chars.') ->param('teamId', '', new UID(), 'Team unique ID.') - ->param('region', System::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn ($config) => !$config['disabled']))), 'Project Region.', true) + ->param('region', System::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn($config) => !$config['disabled']))), 'Project Region.', true) ->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true) ->param('logo', '', new Text(1024), 'Project logo.', true) ->param('url', '', new URL(), 'Project URL.', true) @@ -88,7 +88,6 @@ Http::post('/v1/projects') ->inject('authorization') ->inject('connections') ->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Request $request, Response $response, Database $dbForConsole, Cache $cache, array $pools, Hooks $hooks, Authorization $authorization, Connections $connections) { - $team = $dbForConsole->getDocument('teams', $teamId); if ($team->isEmpty()) { @@ -164,9 +163,7 @@ Http::post('/v1/projects') && System::getEnv('_APP_DATABASE_SHARED_TABLES', 'enabled') === 'enabled' && System::getEnv('_APP_EDITION', 'self-hosted') !== 'self-hosted' ) || - ( - $dsn === DATABASE_SHARED_TABLES - ) + ($dsn === DATABASE_SHARED_TABLES) ) { $schema = 'appwrite'; $database = 'appwrite'; @@ -180,7 +177,7 @@ Http::post('/v1/projects') // TODO: Allow overriding in development mode. Temporary until all projects are using shared tables. if ( - App::isDevelopment() + Http::isDevelopment() && System::getEnv('_APP_EDITION', 'self-hosted') !== 'self-hosted' && $request->getHeader('x-appwrited-share-tables', false) ) { @@ -231,7 +228,6 @@ Http::post('/v1/projects') throw new Exception(Exception::PROJECT_ALREADY_EXISTS); } - $dbForProject = new Database($adapter, $cache); try { $dsn = new DSN($dsn); } catch (\InvalidArgumentException) { @@ -239,8 +235,8 @@ Http::post('/v1/projects') $dsn = new DSN('mysql://' . $dsn); } - $pool = $pools['pools-database-'.$dsn->getHost()['pool']; - $connectionDsn = $pools['pools-database-'.$dsn->getHost()['dsn']; + $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; + $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; $connection = $pool->get(); $connections->add($connection, $pool); @@ -298,7 +294,7 @@ Http::post('/v1/projects') // Hook allowing instant project mirroring during migration // Outside of migration, hook is not registered and has no effect - $hooks->trigger('afterProjectCreation', [ $project, $pools, $cache ]); + $hooks->trigger('afterProjectCreation', [$project, $pools, $cache]); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -320,7 +316,6 @@ Http::get('/v1/projects') ->inject('response') ->inject('dbForConsole') ->action(function (array $queries, string $search, Response $response, Database $dbForConsole) { - try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -372,7 +367,6 @@ Http::get('/v1/projects/:projectId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -406,25 +400,28 @@ Http::patch('/v1/projects/:projectId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $name, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } - $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('name', $name) - ->setAttribute('description', $description) - ->setAttribute('logo', $logo) - ->setAttribute('url', $url) - ->setAttribute('legalName', $legalName) - ->setAttribute('legalCountry', $legalCountry) - ->setAttribute('legalState', $legalState) - ->setAttribute('legalCity', $legalCity) - ->setAttribute('legalAddress', $legalAddress) - ->setAttribute('legalTaxId', $legalTaxId) - ->setAttribute('search', implode(' ', [$projectId, $name]))); + $project = $dbForConsole->updateDocument( + 'projects', + $project->getId(), + $project + ->setAttribute('name', $name) + ->setAttribute('description', $description) + ->setAttribute('logo', $logo) + ->setAttribute('url', $url) + ->setAttribute('legalName', $legalName) + ->setAttribute('legalCountry', $legalCountry) + ->setAttribute('legalState', $legalState) + ->setAttribute('legalCity', $legalCity) + ->setAttribute('legalAddress', $legalAddress) + ->setAttribute('legalTaxId', $legalTaxId) + ->setAttribute('search', implode(' ', [$projectId, $name])) + ); $response->dynamic($project, Response::MODEL_PROJECT); }); @@ -444,7 +441,6 @@ Http::patch('/v1/projects/:projectId/team') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $teamId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); $team = $dbForConsole->getDocument('teams', $teamId); @@ -508,12 +504,11 @@ Http::patch('/v1/projects/:projectId/service') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PROJECT) ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), fn ($element) => $element['optional'])), true), 'Service name.') + ->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), fn($element) => $element['optional'])), true), 'Service name.') ->param('status', null, new Boolean(), 'Service status.') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $service, bool $status, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -543,14 +538,13 @@ Http::patch('/v1/projects/:projectId/service/all') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, bool $status, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } - $allServices = array_keys(array_filter(Config::getParam('services'), fn ($element) => $element['optional'])); + $allServices = array_keys(array_filter(Config::getParam('services'), fn($element) => $element['optional'])); $services = []; foreach ($allServices as $service) { @@ -578,7 +572,6 @@ Http::patch('/v1/projects/:projectId/api') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $api, bool $status, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -608,7 +601,6 @@ Http::patch('/v1/projects/:projectId/api/all') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, bool $status, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -645,7 +637,6 @@ Http::patch('/v1/projects/:projectId/oauth2') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $provider, ?string $appId, ?string $secret, ?bool $enabled, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -686,7 +677,6 @@ Http::patch('/v1/projects/:projectId/auth/limit') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -696,8 +686,12 @@ Http::patch('/v1/projects/:projectId/auth/limit') $auths = $project->getAttribute('auths', []); $auths['limit'] = $limit; - $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('auths', $auths)); + $dbForConsole->updateDocument( + 'projects', + $project->getId(), + $project + ->setAttribute('auths', $auths) + ); $response->dynamic($project, Response::MODEL_PROJECT); }); @@ -717,7 +711,6 @@ Http::patch('/v1/projects/:projectId/auth/duration') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, int $duration, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -727,8 +720,12 @@ Http::patch('/v1/projects/:projectId/auth/duration') $auths = $project->getAttribute('auths', []); $auths['duration'] = $duration; - $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('auths', $auths)); + $dbForConsole->updateDocument( + 'projects', + $project->getId(), + $project + ->setAttribute('auths', $auths) + ); $response->dynamic($project, Response::MODEL_PROJECT); }); @@ -749,7 +746,6 @@ Http::patch('/v1/projects/:projectId/auth/:method') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $method, bool $status, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); $authConfig = Config::getParam('auth')[$method] ?? []; $authKey = $authConfig['key'] ?? ''; @@ -782,7 +778,6 @@ Http::patch('/v1/projects/:projectId/auth/password-history') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -792,8 +787,12 @@ Http::patch('/v1/projects/:projectId/auth/password-history') $auths = $project->getAttribute('auths', []); $auths['passwordHistory'] = $limit; - $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('auths', $auths)); + $dbForConsole->updateDocument( + 'projects', + $project->getId(), + $project + ->setAttribute('auths', $auths) + ); $response->dynamic($project, Response::MODEL_PROJECT); }); @@ -813,7 +812,6 @@ Http::patch('/v1/projects/:projectId/auth/password-dictionary') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -823,8 +821,12 @@ Http::patch('/v1/projects/:projectId/auth/password-dictionary') $auths = $project->getAttribute('auths', []); $auths['passwordDictionary'] = $enabled; - $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('auths', $auths)); + $dbForConsole->updateDocument( + 'projects', + $project->getId(), + $project + ->setAttribute('auths', $auths) + ); $response->dynamic($project, Response::MODEL_PROJECT); }); @@ -844,7 +846,6 @@ Http::patch('/v1/projects/:projectId/auth/personal-data') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -854,8 +855,12 @@ Http::patch('/v1/projects/:projectId/auth/personal-data') $auths = $project->getAttribute('auths', []); $auths['personalDataCheck'] = $enabled; - $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('auths', $auths)); + $dbForConsole->updateDocument( + 'projects', + $project->getId(), + $project + ->setAttribute('auths', $auths) + ); $response->dynamic($project, Response::MODEL_PROJECT); }); @@ -875,7 +880,6 @@ Http::patch('/v1/projects/:projectId/auth/max-sessions') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -885,8 +889,12 @@ Http::patch('/v1/projects/:projectId/auth/max-sessions') $auths = $project->getAttribute('auths', []); $auths['maxSessions'] = $limit; - $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('auths', $auths)); + $dbForConsole->updateDocument( + 'projects', + $project->getId(), + $project + ->setAttribute('auths', $auths) + ); $response->dynamic($project, Response::MODEL_PROJECT); }); @@ -939,21 +947,20 @@ Http::post('/v1/projects/:projectId/webhooks') ->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.') ->param('enabled', true, new Boolean(true), 'Enable or disable a webhook.', true) ->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') - ->param('url', '', fn ($request) => new Multiple([new URL(['http', 'https']), new PublicDomain()], Multiple::TYPE_STRING), 'Webhook URL.', false, ['request']) + ->param('url', '', fn($request) => new Multiple([new URL(['http', 'https']), new PublicDomain()], Multiple::TYPE_STRING), 'Webhook URL.', false, ['request']) ->param('security', false, new Boolean(true), 'Certificate verification, false for disabled or true for enabled.') ->param('httpUser', '', new Text(256), 'Webhook HTTP user. Max length: 256 chars.', true) ->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true) ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $name, bool $enabled, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } - $security = (bool) filter_var($security, FILTER_VALIDATE_BOOLEAN); + $security = (bool)filter_var($security, FILTER_VALIDATE_BOOLEAN); $webhook = new Document([ '$id' => ID::unique(), @@ -997,7 +1004,6 @@ Http::get('/v1/projects/:projectId/webhooks') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1030,7 +1036,6 @@ Http::get('/v1/projects/:projectId/webhooks/:webhookId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1064,14 +1069,13 @@ Http::put('/v1/projects/:projectId/webhooks/:webhookId') ->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.') ->param('enabled', true, new Boolean(true), 'Enable or disable a webhook.', true) ->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') - ->param('url', '', fn ($request) => new Multiple([new URL(['http', 'https']), new PublicDomain()], Multiple::TYPE_STRING), 'Webhook URL.', false, ['request']) + ->param('url', '', fn($request) => new Multiple([new URL(['http', 'https']), new PublicDomain()], Multiple::TYPE_STRING), 'Webhook URL.', false, ['request']) ->param('security', false, new Boolean(true), 'Certificate verification, false for disabled or true for enabled.') ->param('httpUser', '', new Text(256), 'Webhook HTTP user. Max length: 256 chars.', true) ->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true) ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $webhookId, string $name, bool $enabled, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1123,7 +1127,6 @@ Http::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1161,7 +1164,6 @@ Http::delete('/v1/projects/:projectId/webhooks/:webhookId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1203,7 +1205,6 @@ Http::post('/v1/projects/:projectId/keys') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1250,7 +1251,6 @@ Http::get('/v1/projects/:projectId/keys') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1283,7 +1283,6 @@ Http::get('/v1/projects/:projectId/keys/:keyId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1320,7 +1319,6 @@ Http::put('/v1/projects/:projectId/keys/:keyId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1362,7 +1360,6 @@ Http::delete('/v1/projects/:projectId/keys/:keyId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1398,7 +1395,7 @@ Http::post('/v1/projects/:projectId/platforms') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PLATFORM) ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('type', null, new WhiteList([Origin::CLIENT_TYPE_WEB, Origin::CLIENT_TYPE_FLUTTER_WEB, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_LINUX, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_APPLE_IOS, Origin::CLIENT_TYPE_APPLE_MACOS, Origin::CLIENT_TYPE_APPLE_WATCHOS, Origin::CLIENT_TYPE_APPLE_TVOS, Origin::CLIENT_TYPE_ANDROID, Origin::CLIENT_TYPE_UNITY], true), 'Platform type.') + ->param('type', null, new WhiteList([Origin::CLIENT_TYPE_WEB, Origin::CLIENT_TYPE_FLUTTER_WEB, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_LINUX, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_APPLE_IOS, Origin::CLIENT_TYPE_APPLE_MACOS, Origin::CLIENT_TYPE_APPLE_WATCHOS, Origin::CLIENT_TYPE_APPLE_TVOS, Origin::CLIENT_TYPE_ANDROID, Origin::CLIENT_TYPE_UNITY], true), 'Platform type.') ->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.') ->param('key', '', new Text(256), 'Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.', true) ->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true) @@ -1451,7 +1448,6 @@ Http::get('/v1/projects/:projectId/platforms') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1484,7 +1480,6 @@ Http::get('/v1/projects/:projectId/platforms/:platformId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1564,7 +1559,6 @@ Http::delete('/v1/projects/:projectId/platforms/:platformId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1612,7 +1606,6 @@ Http::patch('/v1/projects/:projectId/smtp') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, bool $enabled, string $senderName, string $senderEmail, string $replyTo, string $host, int $port, string $username, string $password, string $secure, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1750,11 +1743,10 @@ Http::get('/v1/projects/:projectId/templates/sms/:type/:locale') ->label('sdk.response.model', Response::MODEL_SMS_TEMPLATE) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? []), 'Template type') - ->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) + ->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) { - throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED); $project = $dbForConsole->getDocument('projects', $projectId); @@ -1764,7 +1756,7 @@ Http::get('/v1/projects/:projectId/templates/sms/:type/:locale') } $templates = $project->getAttribute('templates', []); - $template = $templates['sms.' . $type . '-' . $locale] ?? null; + $template = $templates['sms.' . $type . '-' . $locale] ?? null; if (is_null($template)) { $template = [ @@ -1791,11 +1783,10 @@ Http::get('/v1/projects/:projectId/templates/email/:type/:locale') ->label('sdk.response.model', Response::MODEL_EMAIL_TEMPLATE) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? []), 'Template type') - ->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) + ->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1803,7 +1794,7 @@ Http::get('/v1/projects/:projectId/templates/email/:type/:locale') } $templates = $project->getAttribute('templates', []); - $template = $templates['email.' . $type . '-' . $locale] ?? null; + $template = $templates['email.' . $type . '-' . $locale] ?? null; $localeObj = new Locale($locale); if (is_null($template)) { @@ -1843,12 +1834,11 @@ Http::patch('/v1/projects/:projectId/templates/sms/:type/:locale') ->label('sdk.response.model', Response::MODEL_SMS_TEMPLATE) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? []), 'Template type') - ->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) + ->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) ->param('message', '', new Text(0), 'Template message') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, string $message, Response $response, Database $dbForConsole) { - throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED); $project = $dbForConsole->getDocument('projects', $projectId); @@ -1883,7 +1873,7 @@ Http::patch('/v1/projects/:projectId/templates/email/:type/:locale') ->label('sdk.response.model', Response::MODEL_PROJECT) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? []), 'Template type') - ->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) + ->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) ->param('subject', '', new Text(255), 'Email Subject') ->param('message', '', new Text(0), 'Template message') ->param('senderName', '', new Text(255, 0), 'Name of the email sender', true) @@ -1892,7 +1882,6 @@ Http::patch('/v1/projects/:projectId/templates/email/:type/:locale') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, string $subject, string $message, string $senderName, string $senderEmail, string $replyTo, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1933,11 +1922,10 @@ Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale') ->label('sdk.response.model', Response::MODEL_SMS_TEMPLATE) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? []), 'Template type') - ->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) + ->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) { - throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED); $project = $dbForConsole->getDocument('projects', $projectId); @@ -1947,7 +1935,7 @@ Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale') } $templates = $project->getAttribute('templates', []); - $template = $templates['sms.' . $type . '-' . $locale] ?? null; + $template = $templates['sms.' . $type . '-' . $locale] ?? null; if (is_null($template)) { throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION); @@ -1976,11 +1964,10 @@ Http::delete('/v1/projects/:projectId/templates/email/:type/:locale') ->label('sdk.response.model', Response::MODEL_EMAIL_TEMPLATE) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? []), 'Template type') - ->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) + ->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) { - $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1988,7 +1975,7 @@ Http::delete('/v1/projects/:projectId/templates/email/:type/:locale') } $templates = $project->getAttribute('templates', []); - $template = $templates['email.' . $type . '-' . $locale] ?? null; + $template = $templates['email.' . $type . '-' . $locale] ?? null; if (is_null($template)) { throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION); diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 4a1278dcf5..b502204bd2 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -101,7 +101,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $latestCommentId = ''; - if (!empty($providerPullRequestId) && $function->getAttribute('providerSilentMode', false) { + if (!empty($providerPullRequestId) && $function->getAttribute('providerSilentMode', false)) { $latestComment = $auth->skip(fn () => $dbForConsole->findOne('vcsComments', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::equal('providerPullRequestId', [$providerPullRequestId]), diff --git a/app/controllers/general.php b/app/controllers/general.php index 77d09a6d80..0e35f8d599 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -45,7 +45,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request $host = $request->getHostname() ?? ''; $rule = $auth->skip( - fn () => $dbForConsole->find('rules', [ + fn() => $dbForConsole->find('rules', [ Query::equal('domain', [$host]), Query::limit(1) ]) @@ -73,7 +73,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request $projectId = $rule->getAttribute('projectId'); $project = $auth->skip( - fn () => $dbForConsole->getDocument('projects', $projectId) + fn() => $dbForConsole->getDocument('projects', $projectId) ); if (array_key_exists('proxy', $project->getAttribute('services', []))) { $status = $project->getAttribute('services', [])['proxy']; @@ -115,11 +115,11 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request $requestHeaders = $request->getHeaders(); - $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $project = $auth->skip(fn() => $dbForConsole->getDocument('projects', $projectId)); $dbForProject = $getProjectDB($project); - $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = $auth->skip(fn() => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty() || !$function->getAttribute('enabled')) { throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND); @@ -134,7 +134,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } - $deployment = $auth->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); + $deployment = $auth->skip(fn() => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); if ($deployment->getAttribute('resourceId') !== $function->getId()) { throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); @@ -145,7 +145,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request } /** Check if build has completed */ - $build = $auth->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); + $build = $auth->skip(fn() => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); if ($build->isEmpty()) { throw new AppwriteException(AppwriteException::BUILD_NOT_FOUND); } @@ -198,7 +198,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request 'deploymentInternalId' => $deployment->getInternalId(), 'deploymentId' => $deployment->getId(), 'trigger' => 'http', // http / schedule / event - 'status' => 'processing', // waiting / processing / completed / failed + 'status' => 'processing', // waiting / processing / completed / failed 'responseStatusCode' => 0, 'responseHeaders' => [], 'requestPath' => $path, @@ -287,7 +287,6 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request $execution->setAttribute('logs', $executionResponse['logs']); $execution->setAttribute('errors', $executionResponse['errors']); $execution->setAttribute('duration', $executionResponse['duration']); - } catch (\Throwable $th) { $durationEnd = \microtime(true); @@ -311,7 +310,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request if ($function->getAttribute('logging')) { /** @var Document $execution */ - $execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = $auth->skip(fn() => $dbForProject->createDocument('executions', $execution)); } } @@ -509,7 +508,7 @@ Http::init() Config::setParam('domains', $domains); } - $localeParam = (string) $request->getParam('locale', $request->getHeader('x-appwrite-locale', '')); + $localeParam = (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', '')); if (\in_array($localeParam, $localeCodes)) { $locale->setDefault($localeParam); } @@ -537,7 +536,7 @@ Http::init() Config::setParam( 'domainVerification', ($selfDomain->getRegisterable() === $endDomain->getRegisterable()) && - $endDomain->getRegisterable() !== '' + $endDomain->getRegisterable() !== '' ); $isLocalHost = $request->getHostname() === 'localhost' || $request->getHostname() === 'localhost:' . $request->getPort(); @@ -551,10 +550,10 @@ Http::init() $isLocalHost || $isIpAddress ? null : ( - $isConsoleProject && $isConsoleRootSession - ? '.' . $selfDomain->getRegisterable() - : '.' . $request->getHostname() - ) + $isConsoleProject && $isConsoleRootSession + ? '.' . $selfDomain->getRegisterable() + : '.' . $request->getHostname() + ) ); /* @@ -569,7 +568,7 @@ Http::init() $response->addFilter(new ResponseV17()); } if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) { - $response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is ". APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks"); + $response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is " . APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks"); } } @@ -671,7 +670,7 @@ Http::error() ->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization, Connections $connections) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - if(is_null($route)) { + if (is_null($route)) { $route = new Route($request->getMethod(), $request->getURI()); } @@ -891,8 +890,6 @@ Http::get('/robots.txt') ->desc('Robots.txt File') ->label('scope', 'public') ->label('docs', false) - ->inject('utopia') - ->inject('swooleRequest') ->inject('request') ->inject('response') ->inject('dbForConsole') @@ -900,7 +897,9 @@ Http::get('/robots.txt') ->inject('queueForEvents') ->inject('queueForUsage') ->inject('geodb') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) { + ->inject('route') + ->inject('authorization') + ->action(function (Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, ?Route $route, Authorization $authorization) { $host = $request->getHostname() ?? ''; $mainDomain = System::getEnv('_APP_DOMAIN', ''); @@ -908,7 +907,10 @@ Http::get('/robots.txt') $template = new View(__DIR__ . '/../views/general/robots.phtml'); $response->text($template->render(false)); } else { - router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb); + if (is_null($route)) { + $route = new Route($request->getMethod(), $request->getURI()); + } + router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization); } }); @@ -916,8 +918,6 @@ Http::get('/humans.txt') ->desc('Humans.txt File') ->label('scope', 'public') ->label('docs', false) - ->inject('utopia') - ->inject('swooleRequest') ->inject('request') ->inject('response') ->inject('dbForConsole') @@ -925,7 +925,9 @@ Http::get('/humans.txt') ->inject('queueForEvents') ->inject('queueForUsage') ->inject('geodb') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) { + ->inject('route') + ->inject('authorization') + ->action(function (Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, ?Route $route, Authorization $authorization) { $host = $request->getHostname() ?? ''; $mainDomain = System::getEnv('_APP_DOMAIN', ''); @@ -933,7 +935,10 @@ Http::get('/humans.txt') $template = new View(__DIR__ . '/../views/general/humans.phtml'); $response->text($template->render(false)); } else { - router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb); + if (is_null($route)) { + $route = new Route($request->getMethod(), $request->getURI()); + } + router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization); } }); diff --git a/app/init/constants.php b/app/init/constants.php index 39eb4a7ad2..ca1790f71f 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -57,7 +57,7 @@ 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 DATABASE_SHARED_TABLES = 'database_db_fra1_self_hosted_16_0'; // Database Reconnect const DATABASE_RECONNECT_SLEEP = 2; const DATABASE_RECONNECT_MAX_ATTEMPTS = 10; diff --git a/app/init2.php b/app/init2.php index f0432f5cee..2b7358d60b 100644 --- a/app/init2.php +++ b/app/init2.php @@ -202,144 +202,149 @@ $global->set('hooks', function () { return new Hooks(); }); -$global->set('pools', (function () { - $fallbackForDB = 'db_main=' . URL::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=' . URL::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', ''), - ]); +$global->set( + 'pools', + (function () { + $fallbackForDB = 'db_main=' . URL::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=' . URL::unparse([ + 'scheme' => 'redis', + 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), + 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), + 'user' => System::getEnv('_APP_REDIS_USER', ''), + 'pass' => System::getEnv('_APP_REDIS_PASS', ''), + ]); - $connections = [ - 'console' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), - 'multiple' => false, - 'schemes' => ['mariadb', 'mysql'], - ], - 'database' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), - 'multiple' => true, - 'schemes' => ['mariadb', 'mysql'], - ], - 'queue' => [ - 'type' => 'queue', - 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'pubsub' => [ - 'type' => 'pubsub', - 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'cache' => [ - 'type' => 'cache', - 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), - 'multiple' => true, - 'schemes' => ['redis'], - ], - ]; + $connections = [ + 'console' => [ + 'type' => 'database', + 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), + 'multiple' => false, + 'schemes' => ['mariadb', 'mysql'], + ], + 'database' => [ + 'type' => 'database', + 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), + 'multiple' => true, + 'schemes' => ['mariadb', 'mysql'], + ], + 'queue' => [ + 'type' => 'queue', + 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'pubsub' => [ + 'type' => 'pubsub', + 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'cache' => [ + 'type' => 'cache', + 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), + 'multiple' => true, + 'schemes' => ['redis'], + ], + ]; - $pools = []; - $poolSize = (int)System::getEnv('_APP_POOL_CLIENTS', 9000); - $poolSize = 9000; + $pools = []; + $poolSize = (int)System::getEnv('_APP_POOL_CLIENTS', 9000); + $poolSize = 9000; - foreach ($connections as $key => $connection) { - $dsns = $connection['dsns'] ?? ''; - $multipe = $connection['multiple'] ?? false; - $schemes = $connection['schemes'] ?? []; - $dsns = explode(',', $connection['dsns'] ?? ''); - $config = []; + foreach ($connections as $key => $connection) { + $dsns = $connection['dsns'] ?? ''; + $multipe = $connection['multiple'] ?? false; + $schemes = $connection['schemes'] ?? []; + $dsns = explode(',', $connection['dsns'] ?? ''); + $config = []; - foreach ($dsns as &$dsn) { - $dsn = explode('=', $dsn); - $name = ($multipe) ? $dsn[0] : 'main'; - $config[] = $name; - $dsn = $dsn[1] ?? ''; + foreach ($dsns as &$dsn) { + $dsn = explode('=', $dsn); + $name = ($multipe) ? $dsn[0] : 'main'; + $config[] = $name; + $dsn = $dsn[1] ?? ''; - if (empty($dsn)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); + if (empty($dsn)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); + } + + $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 accross connection types like database, cache, queue, etc. + * + * Resource assignment to an adapter will happen below. + */ + switch ($dsnScheme) { + case 'mysql': + case 'mariadb': + $pool = new PDOPool( + (new PDOConfig()) + ->withHost($dsnHost) + ->withPort($dsnPort) + ->withDbName($dsnDatabase) + ->withCharset('utf8mb4') + ->withUsername($dsnUser) + ->withPassword($dsnPass) + ->withOptions([ + // No need to set PDO::ATTR_ERRMODE it is overwitten in PDOProxy + // 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, + PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, + + ]), + $poolSize + ); + break; + case 'redis': + $pool = new RedisPool( + (new RedisConfig()) + ->withHost($dsnHost) + ->withPort((int)$dsnPort) + ->withAuth($dsnPass), $poolSize + ); + break; + + default: + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid scheme"); + } + + $pools['pools-' . $key . '-' . $name] = [ + 'pool' => $pool, + 'dsn' => $dsn, + ]; } - $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 accross connection types like database, cache, queue, etc. - * - * Resource assignment to an adapter will happen below. - */ - switch ($dsnScheme) { - case 'mysql': - case 'mariadb': - $pool = new PDOPool( - (new PDOConfig()) - ->withHost($dsnHost) - ->withPort($dsnPort) - ->withDbName($dsnDatabase) - ->withCharset('utf8mb4') - ->withUsername($dsnUser) - ->withPassword($dsnPass) - ->withOptions([ - // No need to set PDO::ATTR_ERRMODE it is overwitten in PDOProxy - // 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, - PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, - - ]), - $poolSize - ); - break; - case 'redis': - $pool = new RedisPool((new RedisConfig()) - ->withHost($dsnHost) - ->withPort((int)$dsnPort) - ->withAuth($dsnPass), $poolSize); - break; - - default: - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid scheme"); - } - - $pools['pools-' . $key . '-' . $name] = [ - 'pool' => $pool, - 'dsn' => $dsn, - ]; + Config::setParam('pools-' . $key, $config); } - Config::setParam('pools-' . $key, $config); - } - - return function () use ($pools): array { - return $pools; - }; -})()); + return function () use ($pools): array { + return $pools; + }; + })() +); $global->set('smtp', function () { $mail = new PHPMailer(true); @@ -374,7 +379,52 @@ $global->set('promiseAdapter', function () { return new Swoole(); }); + +$log = new Dependency(); $mode = new Dependency(); +$user = new Dependency(); +$pools = new Dependency(); +$geodb = new Dependency(); +$cache = new Dependency(); +$pools = new Dependency(); +$queue = new Dependency(); +$hooks = new Dependency(); +$logger = new Dependency(); +$locale = new Dependency(); +$schema = new Dependency(); +$github = new Dependency(); +$session = new Dependency(); +$console = new Dependency(); +$project = new Dependency(); +$clients = new Dependency(); +$servers = new Dependency(); +$registry = new Dependency(); +$getProjectDB = new Dependency(); +$localeCodes = new Dependency(); +$connections = new Dependency(); +$dbForProject = new Dependency(); +$dbForConsole = new Dependency(); +$queueForUsage = new Dependency(); +$authorization = new Dependency(); +$queueForMails = new Dependency(); +$queueForBuilds = new Dependency(); +$deviceForLocal = new Dependency(); +$deviceForFiles = new Dependency(); +$queueForEvents = new Dependency(); +$queueForAudits = new Dependency(); +$promiseAdapter = new Dependency(); +$requestTimestamp = new Dependency(); +$deviceForBuilds = new Dependency(); +$queueForDeletes = new Dependency(); +$queueForDatabase = new Dependency(); +$queueForMessaging = new Dependency(); +$queueForFunctions = new Dependency(); +$queueForMigrations = new Dependency(); +$deviceForFunctions = new Dependency(); +$passwordsDictionary = new Dependency(); +$queueForCertificates = new Dependency(); + + $mode ->setName('mode') ->inject('request') @@ -386,9 +436,7 @@ $mode */ return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); }); -$container->set($mode); -$user = new Dependency(); $user ->setName('user') ->inject('mode') @@ -500,9 +548,8 @@ $user return $user; }); -$container->set($user); -$session = new Dependency(); + $session ->setName('session') ->inject('user') @@ -528,9 +575,7 @@ $session return; }); -$container->set($session); -$console = new Dependency(); $console ->setName('console') ->setCallback(function () { @@ -572,9 +617,8 @@ $console ], ]); }); -$container->set($console); -$project = new Dependency(); + $project ->setName('project') ->inject('dbForConsole') @@ -588,62 +632,76 @@ $project return $console; } - $project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $project = $authorization->skip(fn() => $dbForConsole->getDocument('projects', $projectId)); return $project; }); -$container->set($project); -$pools = new Dependency(); $pools ->setName('pools') ->inject('registry') ->setCallback(function (Registry $registry) { return $registry->get('pools'); }); -$container->set($pools); -$dbForProject = new Dependency(); $dbForProject ->setName('dbForProject') + ->inject('cache') ->inject('pools') ->inject('project') - ->inject('cache') ->inject('dbForConsole') - ->inject('connections') ->inject('authorization') - ->setCallback(function (array $pools, Document $project, Cache $cache, Database $dbForConsole, Connections $connections, Authorization $authorization) { + ->inject('connections') + ->setCallback(function (Cache $cache, array $pools, Document $project, Database $dbForConsole, Authorization $authorization, Connections $connections) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } - $pool = $pools['pools-database-'.$project->getAttribute('database')]['pool']; - $dsn = $pools['pools-database-'.$project->getAttribute('database')]['dsn']; + 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')); + } + + $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; + $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; $connection = $pool->get(); $connections->add($connection, $pool); - $adapter = match ($dsn->getScheme()) { + $adapter = match ($connectionDsn->getScheme()) { 'mariadb' => new MariaDB($connection), 'mysql' => new MySQL($connection), default => null }; - $adapter->setDatabase($dsn->getPath()); + $adapter->setDatabase($connectionDsn->getPath()); $database = new Database($adapter, $cache); + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + if ($dsn->getHost() === DATABASE_SHARED_TABLES) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + $database->setAuthorization($authorization); - - $database - ->setNamespace('_' . $project->getInternalId()) - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - return $database; }); -$container->set($dbForProject); -$dbForConsole = new Dependency(); $dbForConsole ->setName('dbForConsole') ->inject('pools') @@ -675,79 +733,59 @@ $dbForConsole return $database; }); -$container->set($dbForConsole); -$cache = new Dependency(); $cache ->setName('cache') ->setCallback(function (): Cache { return new Cache(new None()); }); -$container->set($cache); -$authorization = new Dependency(); $authorization ->setName('authorization') ->setCallback(function (): Authorization { return new Authorization(); }); -$container->set($authorization); -$registry = new Dependency(); $registry ->setName('registry') ->setCallback(function () use (&$global): Registry { return $global; }); -$container->set($registry); -$pools = new Dependency(); $pools ->setName('pools') ->inject('registry') ->setCallback(function (Registry $registry) { return $registry->get('pools'); }); -$container->set($pools); -$logger = new Dependency(); $logger ->setName('logger') ->inject('registry') ->setCallback(function (Registry $registry) { return $registry->get('logger'); }); -$container->set($logger); -$log = new Dependency(); $log ->setName('log') ->setCallback(function () { return new Log(); }); -$container->set($log); -$connections = new Dependency(); $connections ->setName('connections') ->setCallback(function () { return new Connections(); }); -$container->set($connections); -$locale = new Dependency(); $locale ->setName('locale') - ->setCallback(fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); -$container->set($locale); + ->setCallback(fn() => new Locale(System::getEnv('_APP_LOCALE', 'en'))); -$localeCodes = new Dependency(); $localeCodes ->setName('localeCodes') - ->setCallback(fn () => array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []))); -$container->set($localeCodes); + ->setCallback(fn() => array_map(fn($locale) => $locale['code'], Config::getParam('locale-codes', []))); -$queue = new Dependency(); $queue ->setName('queue') ->inject('pools') @@ -760,143 +798,111 @@ $queue return new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()); }); -$container->set($queue); -$queueForMessaging = new Dependency(); $queueForMessaging ->setName('queueForMessaging') ->inject('queue') ->setCallback(function (Connection $queue) { return new Messaging($queue); }); -$container->set($queueForMessaging); -$queueForMails = new Dependency(); $queueForMails ->setName('queueForMails') ->inject('queue') ->setCallback(function (Connection $queue) { return new Mail($queue); }); -$container->set($queueForMails); -$queueForBuilds = new Dependency(); $queueForBuilds ->setName('queueForBuilds') ->inject('queue') ->setCallback(function (Connection $queue) { return new Build($queue); }); -$container->set($queueForBuilds); -$queueForDatabase = new Dependency(); $queueForDatabase ->setName('queueForDatabase') ->inject('queue') ->setCallback(function (Connection $queue) { return new EventDatabase($queue); }); -$container->set($queueForDatabase); -$queueForDeletes = new Dependency(); $queueForDeletes ->setName('queueForDeletes') ->inject('queue') ->setCallback(function (Connection $queue) { return new Delete($queue); }); -$container->set($queueForDeletes); -$queueForEvents = new Dependency(); $queueForEvents ->setName('queueForEvents') ->inject('queue') ->setCallback(function (Connection $queue) { return new Event($queue); }); -$container->set($queueForEvents); -$queueForAudits = new Dependency(); $queueForAudits ->setName('queueForAudits') ->inject('queue') ->setCallback(function (Connection $queue) { return new Audit($queue); }); -$container->set($queueForAudits); -$queueForFunctions = new Dependency(); $queueForFunctions ->setName('queueForFunctions') ->inject('queue') ->setCallback(function (Connection $queue) { return new Func($queue); }); -$container->set($queueForFunctions); -$queueForUsage = new Dependency(); $queueForUsage ->setName('queueForUsage') ->inject('queue') ->setCallback(function (Connection $queue) { return new Usage($queue); }); -$container->set($queueForUsage); -$queueForCertificates = new Dependency(); $queueForCertificates ->setName('queueForCertificates') ->inject('queue') ->setCallback(function (Connection $queue) { return new Certificate($queue); }); -$container->set($queueForCertificates); -$queueForMigrations = new Dependency(); $queueForMigrations ->setName('queueForMigrations') ->inject('queue') ->setCallback(function (Connection $queue) { return new Migration($queue); }); -$container->set($queueForMigrations); -$deviceForLocal = new Dependency(); $deviceForLocal ->setName('deviceForLocal') ->setCallback(function () { return new Local(); }); -$container->set($deviceForLocal); -$deviceForFiles = new Dependency(); $deviceForFiles ->setName('deviceForFiles') ->inject('project') ->setCallback(function ($project) { return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); }); -$container->set($deviceForFiles); -$deviceForFunctions = new Dependency(); $deviceForFunctions ->setName('deviceForFunctions') ->inject('project') ->setCallback(function ($project) { return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); }); -$container->set($deviceForFunctions); -$deviceForBuilds = new Dependency(); $deviceForBuilds ->setName('deviceForBuilds') ->inject('project') ->setCallback(function ($project) { return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); }); -$container->set($deviceForBuilds); -$clients = new Dependency(); $clients ->setName('clients') ->inject('request') @@ -930,10 +936,10 @@ $clients * + Filter for duplicated entries */ $clientsConsole = \array_map( - fn ($node) => $node['hostname'], + fn($node) => $node['hostname'], \array_filter( $console->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && !empty($node['hostname'])) + fn($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ); @@ -941,10 +947,10 @@ $clients \array_merge( $clientsConsole, \array_map( - fn ($node) => $node['hostname'], + fn($node) => $node['hostname'], \array_filter( $project->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) + fn($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ) ) @@ -952,9 +958,7 @@ $clients return $clients; }); -$container->set($clients); -$servers = new Dependency(); $servers ->setName('servers') ->setCallback(function () { @@ -967,18 +971,14 @@ $servers return $languages; }); -$container->set($servers); -$geodb = new Dependency(); $geodb ->setName('geodb') ->inject('registry') ->setCallback(function (Registry $register) { return $register->get('geodb'); }); -$container->set($geodb); -$passwordsDictionary = new Dependency(); $passwordsDictionary ->setName('passwordsDictionary') ->setCallback(function () { @@ -988,27 +988,20 @@ $passwordsDictionary return $content; }); -$container->set($passwordsDictionary); - -$hooks = new Dependency(); $hooks ->setName('hooks') ->inject('registry') ->setCallback(function (Registry $registry) { return $registry->get('hooks'); }); -$container->set($hooks); -$github = new Dependency(); $github ->setName('gitHub') ->inject('cache') ->setCallback(function (Cache $cache) { return new GitHub($cache); }); -$container->set($github); -$requestTimestamp = new Dependency(); $requestTimestamp ->setName('requestTimestamp') ->inject('request') @@ -1024,9 +1017,7 @@ $requestTimestamp } return $requestTimestamp; }); -$container->set($requestTimestamp); -$getProjectDB = new Dependency(); $getProjectDB ->setName('getProjectDB') ->inject('pools') @@ -1034,51 +1025,85 @@ $getProjectDB ->inject('cache') ->inject('authorization') ->inject('connections') - ->setCallback(function (array $pools, Database $dbForConsole, $cache, Authorization $authorization, Connections $connections) { + ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $authorization, Connections $connections) { + $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $authorization, $connections): Database { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; } - $databaseName = $project->getAttribute('database'); + 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')); + } - $pool = $pools['pools-database-'.$databaseName]['pool']; - $dsn = $pools['pools-database-'.$databaseName]['dsn']; + if (isset($databases[$dsn->getHost()])) { + $database = $databases[$dsn->getHost()]; + + if ($dsn->getHost() === DATABASE_SHARED_TABLES) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + return $database; + } + + $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; + $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; $connection = $pool->get(); $connections->add($connection, $pool); - $adapter = match ($dsn->getScheme()) { + $adapter = match ($connectionDsn->getScheme()) { 'mariadb' => new MariaDB($connection), 'mysql' => new MySQL($connection), default => null }; - $adapter->setDatabase($dsn->getPath()); + $adapter->setDatabase($connectionDsn->getPath()); $database = new Database($adapter, $cache); $database->setAuthorization($authorization); - $database->setNamespace('_' . $project->getInternalId()); + + $databases[$dsn->getHost()] = $database; + + if ($dsn->getHost() === DATABASE_SHARED_TABLES) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } return $database; }; }); -$container->set($getProjectDB); -$promiseAdapter = new Dependency(); $promiseAdapter ->setName('promiseAdapter') ->inject('register') ->setCallback(function ($register) { return $register->get('promiseAdapter'); }); -$container->set($promiseAdapter); -$schema = new Dependency(); $schema ->setName('schema') ->inject('utopia') ->inject('dbForProject') - ->inject('auth') - ->setCallback(function (Http $utopia, Database $dbForProject, Authorization $auth) { + ->inject('authorization') + ->setCallback(function (Http $utopia, Database $dbForProject, Authorization $authorization) { $complexity = function (int $complexity, array $args) { $queries = Query::parseQueries($args['queries'] ?? []); $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; @@ -1087,8 +1112,8 @@ $schema return $complexity * $limit; }; - $attributes = function (int $limit, int $offset) use ($dbForProject, $auth) { - $attrs = $auth->skip(fn () => $dbForProject->find('attributes', [ + $attributes = function (int $limit, int $offset) use ($dbForProject, $authorization) { + $attrs = $authorization->skip(fn() => $dbForProject->find('attributes', [ Query::limit($limit), Query::offset($offset), ])); @@ -1118,7 +1143,7 @@ $schema $params = [ 'list' => function (string $databaseId, string $collectionId, array $args) { - return [ 'queries' => $args['queries']]; + return ['queries' => $args['queries']]; }, 'create' => function (string $databaseId, string $collectionId, array $args) { $id = $args['id'] ?? 'unique()'; @@ -1160,4 +1185,46 @@ $schema $params, ); }); +$container->set($log); +$container->set($mode); +$container->set($user); +$container->set($pools); +$container->set($cache); +$container->set($pools); +$container->set($queue); +$container->set($geodb); +$container->set($hooks); +$container->set($locale); $container->set($schema); +$container->set($github); +$container->set($logger); +$container->set($session); +$container->set($console); +$container->set($project); +$container->set($clients); +$container->set($servers); +$container->set($registry); +$container->set($connections); +$container->set($localeCodes); +$container->set($dbForProject); +$container->set($dbForConsole); +$container->set($getProjectDB); +$container->set($authorization); +$container->set($queueForUsage); +$container->set($queueForMails); +$container->set($queueForBuilds); +$container->set($queueForEvents); +$container->set($queueForAudits); +$container->set($deviceForLocal); +$container->set($deviceForFiles); +$container->set($promiseAdapter); +$container->set($queueForDeletes); +$container->set($deviceForBuilds); +$container->set($queueForDatabase); +$container->set($requestTimestamp); +$container->set($queueForMessaging); +$container->set($queueForFunctions); +$container->set($queueForMigrations); +$container->set($deviceForFunctions); +$container->set($passwordsDictionary); +$container->set($queueForCertificates); diff --git a/app/worker.php b/app/worker.php index d3a59cff84..7a62931a49 100644 --- a/app/worker.php +++ b/app/worker.php @@ -44,120 +44,20 @@ global $global, $container; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); +$project = new Dependency(); $register = new Dependency(); +$dbForProject = new Dependency(); +$abuseRetention = new Dependency(); +$deviceForCache = new Dependency(); +$auditRetention = new Dependency(); +$queueForUsageDump = new Dependency(); +$executionRetention = new Dependency(); +$deviceForLocalFiles = new Dependency(); + $register ->setName('register') - ->setCallback(fn () => $global); -$container->set($register); + ->setCallback(fn() => $global); -$connections = new Dependency(); -$connections - ->setName('connections') - ->setCallback(function () { - return new Connections(); - }); -$container->set($connections); - -$pools = new Dependency(); -$pools - ->setName('pools') - ->inject('register') - ->setCallback(function ($register) { - return $register->get('pools'); - }); -$container->set($pools); - -$dbForConsole = new Dependency(); -$dbForConsole - ->setName('dbForConsole') - ->inject('cache') - ->inject('pools') - ->inject('auth') - ->inject('connections') - ->setCallback(function (Cache $cache, array $pools, Authorization $auth, Connections $connections) { - $pool = $pools['pools-console-main']['pool']; - $dsn = $pools['pools-console-main']['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - - $database = new Database($adapter, $cache); - $database->setAuthorization($auth); - $database->setNamespace('_console'); - - return $database; - }); -$container->set($dbForConsole); - -$dbForProject = new Dependency(); -$dbForProject - ->setName('dbForProject') - ->inject('cache') - ->inject('pools') - ->inject('message') - ->inject('project') - ->inject('dbForConsole') - ->inject('auth') - ->inject('connections') - ->setCallback(function (Cache $cache, array $pools, Message $message, Document $project, Database $dbForConsole, Authorization $auth, Connections $connections) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; - $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($connectionDsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($connectionDsn->getPath()); - - $database = new Database($adapter, $cache); - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - $database->setAuthorization($auth); - return $database; - }); -$container->set($dbForProject); - -$project = new Dependency(); $project ->setName('project') ->inject('message') @@ -172,218 +72,25 @@ $project return $dbForConsole->getDocument('projects', $project->getId()); }); -$container->set($project); -$getProjectDB = new Dependency(); -$getProjectDB - ->setName('getProjectDB') - ->inject('pools') - ->inject('dbForConsole') - ->inject('cache') - ->inject('auth') - ->inject('connections') - ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $auth, Connections $connections) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections): Database { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if (isset($databases[$dsn->getHost()])) { - $database = $databases[$dsn->getHost()]; - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; - } - - $pool = $pools['pools-database-'.$dsn->getHost()]['pool']; - $connectionDsn = $pools['pools-database-'.$dsn->getHost()]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($connectionDsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - $adapter->setDatabase($connectionDsn->getPath()); - - $database = new Database($adapter, $cache); - $database->setAuthorization($auth); - - $databases[$dsn->getHost()] = $database; - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; - }; - }); -$container->set($getProjectDB); - -$abuseRetention = new Dependency(); $abuseRetention ->setName('abuseRetention') ->setCallback(function () { return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400)); }); -$container->set($abuseRetention); -$auditRetention = new Dependency(); $auditRetention ->setName('auditRetention') ->setCallback(function () { return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600)); }); -$container->set($auditRetention); -$executionRetention = new Dependency(); $executionRetention ->setName('executionRetention') ->setCallback(function () { return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); }); -$container->set($executionRetention); -$cache = new Dependency(); -$cache - ->setName('cache') - ->setCallback(function () { - return new Cache(new None()); - }); -$container->set($cache); - -$log = new Dependency(); -$log - ->setName('log') - ->setCallback(fn () => new Log()); -$container->set($log); - -$queue = new Dependency(); -$queue - ->setName('queue') - ->inject('pools') - ->inject('connections') - ->setCallback(function (array $pools, Connections $connections) { - $pool = $pools['pools-queue-main']['pool']; - $dsn = $pools['pools-queue-main']['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - return new Redis($dsn->getHost(), $dsn->getPort()); - }); -$container->set($queue); - -$queueForMessaging = new Dependency(); -$queueForMessaging - ->setName('queueForMessaging') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Messaging($queue); - }); -$container->set($queueForMessaging); - -$queueForMails = new Dependency(); -$queueForMails - ->setName('queueForMails') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Mail($queue); - }); -$container->set($queueForMails); - -$queueForBuilds = new Dependency(); -$queueForBuilds - ->setName('queueForBuilds') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Build($queue); - }); -$container->set($queueForBuilds); - -$queueForDatabase = new Dependency(); -$queueForDatabase - ->setName('queueForDatabase') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new EventDatabase($queue); - }); -$container->set($queueForDatabase); - -$queueForDeletes = new Dependency(); -$queueForDeletes - ->setName('queueForDeletes') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Delete($queue); - }); -$container->set($queueForDeletes); - -$queueForEvents = new Dependency(); -$queueForEvents - ->setName('queueForEvents') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Event($queue); - }); -$container->set($queueForEvents); - -$queueForAudits = new Dependency(); -$queueForAudits - ->setName('queueForAudits') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Audit($queue); - }); -$container->set($queueForAudits); - -$queueForFunctions = new Dependency(); -$queueForFunctions - ->setName('queueForFunctions') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Func($queue); - }); -$container->set($queueForFunctions); - -$queueForUsage = new Dependency(); -$queueForUsage - ->setName('queueForUsage') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Usage($queue); - }); -$container->set($queueForUsage); - -$queueForUsageDump = new Dependency(); $queueForUsageDump ->setName('queueForUsageDump') ->inject('queue') @@ -391,74 +98,13 @@ $queueForUsageDump return new UsageDump($queue); }); -$container->set($queueForUsageDump); - -$queueForCertificates = new Dependency(); -$queueForCertificates - ->setName('queueForCertificates') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Certificate($queue); - }); -$container->set($queueForCertificates); - -$queueForMigrations = new Dependency(); -$queueForMigrations - ->setName('queueForMigrations') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Migration($queue); - }); -$container->set($queueForMigrations); - - - -$logger = new Dependency(); -$logger - ->setName('logger') - ->inject('register') - ->setCallback(function (Registry $register) { - return $register->get('logger'); - }); -$container->set($logger); - -$deviceForFunctions = new Dependency(); -$deviceForFunctions - ->setName('deviceForFunctions') - ->inject('project') - ->setCallback(function (Document $project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); - }); -$container->set($deviceForFunctions); - -$deviceForFiles = new Dependency(); -$deviceForFiles - ->setName('deviceForFiles') - ->inject('project') - ->setCallback(function (Document $project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); - }); -$container->set($deviceForFiles); - -$deviceForBuilds = new Dependency(); -$deviceForBuilds - ->setName('deviceForBuilds') - ->inject('project') - ->setCallback(function (Document $project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); - }); -$container->set($deviceForBuilds); - -$deviceForCache = new Dependency(); $deviceForCache ->setName('deviceForCache') ->inject('project') ->setCallback(function (Document $project) { return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId()); }); -$container->set($deviceForCache); -$deviceForLocalFiles = new Dependency(); $deviceForLocalFiles ->setName('deviceForLocalFiles') ->inject('project') @@ -466,16 +112,18 @@ $deviceForLocalFiles return new Local(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); }); +$container->set($project); +$container->set($register); +$container->set($dbForProject); +$container->set($abuseRetention); +$container->set($auditRetention); +$container->set($deviceForCache); +$container->set($queueForUsageDump); +$container->set($executionRetention); $container->set($deviceForLocalFiles); -$auth = new Dependency(); -$auth - ->setName('auth') - ->setCallback(fn () => new Authorization()); -$container->set($auth); - $platform = new Appwrite(); -$args = $platform->getEnv('argv'); +$args = $_SERVER['argv']; if (!isset($args[1])) { Console::error('Missing worker name'); @@ -497,7 +145,6 @@ if (\str_starts_with($workerName, 'databases')) { } try { - $connection = new Connection\Redis( System::getEnv('_APP_REDIS_HOST', 'redis'), System::getEnv('_APP_REDIS_PORT', '6379'), @@ -518,13 +165,13 @@ try { 'queueName' => $queueName ]); } catch (\Throwable $e) { - Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); + Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); } Worker::init() - ->inject('auth') - ->action(function (Authorization $auth) { - $auth->disable(); + ->inject('authorization') + ->action(function (Authorization $authorization) { + $authorization->disable(); }); Worker::shutdown() @@ -539,8 +186,8 @@ Worker::error() ->inject('log') ->inject('connections') ->inject('project') - ->inject('auth') - ->action(function (Throwable $error, ?Logger $logger, Log $log, Connections $connections, Document $project, Authorization $auth) use ($queueName) { + ->inject('authorization') + ->action(function (Throwable $error, ?Logger $logger, Log $log, Connections $connections, Document $project, Authorization $authorization) use ($queueName) { $connections->reclaim(); $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); @@ -558,7 +205,7 @@ Worker::error() $log->addExtra('line', $error->getLine()); $log->addExtra('trace', $error->getTraceAsString()); $log->addExtra('detailedTrace', $error->getTrace()); - $log->addExtra('roles', $auth->getRoles()); + $log->addExtra('roles', $authorization->getRoles()); $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); diff --git a/composer.json b/composer.json index c51e90d46b..2ac000424f 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ "utopia-php/abuse": "dev-feat-framework-v2 as 0.37.99", "utopia-php/analytics": "dev-feat-framework-v2 as 0.10.99", "utopia-php/audit": "dev-feat-framework-v2 as 0.39.99", - "utopia-php/cache": "0.9.*", + "utopia-php/cache": "0.10.*", "utopia-php/cli": "dev-dev-coroutines as 0.17.99", "utopia-php/config": "0.2.*", "utopia-php/database": "dev-feat-framework-v2 as 0.49.99", diff --git a/composer.lock b/composer.lock index 249eb1fbf2..e65142d1f1 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": "7778579de897a3e077914bd37686a62b", + "content-hash": "cea93b7a5d3b401c01b535df70df0b35", "packages": [ { "name": "adhocore/jwt", @@ -479,6 +479,89 @@ ], "time": "2022-09-10T18:51:20+00:00" }, + { + "name": "giggsey/libphonenumber-for-php-lite", + "version": "8.13.36", + "source": { + "type": "git", + "url": "https://github.com/giggsey/libphonenumber-for-php-lite.git", + "reference": "144bbe70d67664b5245910a475c7190ff140ab4b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php-lite/zipball/144bbe70d67664b5245910a475c7190ff140ab4b", + "reference": "144bbe70d67664b5245910a475c7190ff140ab4b", + "shasum": "" + }, + "require": { + "php": "^8.1", + "symfony/polyfill-mbstring": "^1.17" + }, + "conflict": { + "giggsey/libphonenumber-for-php": "*" + }, + "require-dev": { + "ext-dom": "*", + "friendsofphp/php-cs-fixer": "^3.12", + "infection/infection": "^0.28", + "pear/pear-core-minimal": "^1.10.11", + "pear/pear_exception": "^1.0.2", + "pear/versioncontrol_git": "^0.7", + "phing/phing": "^2.17.4", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.2", + "phpunit/phpunit": "^10.5", + "symfony/console": "^6.0", + "symfony/var-exporter": "^6.0" + }, + "suggest": { + "giggsey/libphonenumber-for-php": "Use libphonenumber-for-php for geocoding, carriers, timezones and matching" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "libphonenumber\\": "src/" + }, + "exclude-from-classmap": [ + "/src/data/", + "/src/carrier/data/", + "/src/geocoding/data/", + "/src/timezone/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "A lite version of giggsey/libphonenumber-for-php, which is a PHP Port of Google's libphonenumber", + "homepage": "https://github.com/giggsey/libphonenumber-for-php-lite", + "keywords": [ + "geocoding", + "geolocation", + "libphonenumber", + "mobile", + "phonenumber", + "validation" + ], + "support": { + "issues": "https://github.com/giggsey/libphonenumber-for-php-lite/issues", + "source": "https://github.com/giggsey/libphonenumber-for-php-lite" + }, + "time": "2024-05-03T06:31:11+00:00" + }, { "name": "jean85/pretty-package-versions", "version": "2.x-dev", @@ -1044,6 +1127,87 @@ }, "time": "2022-03-17T08:00:35+00:00" }, + { + "name": "symfony/polyfill-mbstring", + "version": "1.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "098e36a5b73de12beeb5ac17e80abf3696f7ad5f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/098e36a5b73de12beeb5ac17e80abf3696f7ad5f", + "reference": "098e36a5b73de12beeb5ac17e80abf3696f7ad5f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "default-branch": true, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/1.x" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T15:07:36+00:00" + }, { "name": "symfony/polyfill-php80", "version": "1.x-dev", @@ -1408,16 +1572,16 @@ }, { "name": "utopia-php/cache", - "version": "0.9.1", + "version": "0.10.0", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "552b4c554bb14d0c529631ce304cdf4a2b9d06a6" + "reference": "313bcdfbb166f75c2c205a59d1467cead63a9626" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/552b4c554bb14d0c529631ce304cdf4a2b9d06a6", - "reference": "552b4c554bb14d0c529631ce304cdf4a2b9d06a6", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/313bcdfbb166f75c2c205a59d1467cead63a9626", + "reference": "313bcdfbb166f75c2c205a59d1467cead63a9626", "shasum": "" }, "require": { @@ -1452,9 +1616,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.9.1" + "source": "https://github.com/utopia-php/cache/tree/0.10.0" }, - "time": "2024-03-19T17:07:20+00:00" + "time": "2024-06-05T16:40:43+00:00" }, { "name": "utopia-php/cli", @@ -1564,19 +1728,19 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "f53d63bc5903ea6d6ff8f61293a20235e912b242" + "reference": "5b0d6ba40141dd9d4983d3aac018782f8ecdfc8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/f53d63bc5903ea6d6ff8f61293a20235e912b242", - "reference": "f53d63bc5903ea6d6ff8f61293a20235e912b242", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5b0d6ba40141dd9d4983d3aac018782f8ecdfc8a", + "reference": "5b0d6ba40141dd9d4983d3aac018782f8ecdfc8a", "shasum": "" }, "require": { "ext-mbstring": "*", "ext-pdo": "*", "php": ">=8.0", - "utopia-php/cache": "0.9.*", + "utopia-php/cache": "0.10.*", "utopia-php/framework": "0.34.*", "utopia-php/mongo": "0.3.*" }, @@ -1610,9 +1774,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/feat-framework-v2-1" + "source": "https://github.com/utopia-php/database/tree/feat-framework-v2" }, - "time": "2024-05-09T18:33:47+00:00" + "time": "2024-06-06T00:07:47+00:00" }, { "name": "utopia-php/di", @@ -1783,6 +1947,45 @@ }, "time": "2024-05-07T02:01:25+00:00" }, + { + "name": "utopia-php/fetch", + "version": "0.2.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/fetch.git", + "reference": "1423c0ee3eef944d816ca6e31706895b585aea82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/fetch/zipball/1423c0ee3eef944d816ca6e31706895b585aea82", + "reference": "1423c0ee3eef944d816ca6e31706895b585aea82", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.5.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Fetch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "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.2.1" + }, + "time": "2024-03-18T11:50:59+00:00" + }, { "name": "utopia-php/framework", "version": "dev-feat-di-upgrade", @@ -1934,22 +2137,23 @@ }, { "name": "utopia-php/logger", - "version": "0.3.x-dev", + "version": "0.5.2", "source": { "type": "git", "url": "https://github.com/utopia-php/logger.git", - "reference": "ba763c10688fe2ed715ad2bed3f13d18dfec6253" + "reference": "c6dfdb672e41364c309b0c30dc03bc6d45446dba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/ba763c10688fe2ed715ad2bed3f13d18dfec6253", - "reference": "ba763c10688fe2ed715ad2bed3f13d18dfec6253", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/c6dfdb672e41364c309b0c30dc03bc6d45446dba", + "reference": "c6dfdb672e41364c309b0c30dc03bc6d45446dba", "shasum": "" }, "require": { "php": ">=8.0" }, "require-dev": { + "laravel/pint": "1.2.*", "phpstan/phpstan": "1.9.x-dev", "phpunit/phpunit": "^9.3", "vimeo/psalm": "4.0.1" @@ -1981,27 +2185,28 @@ ], "support": { "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.3.x" + "source": "https://github.com/utopia-php/logger/tree/0.5.2" }, - "time": "2023-11-22T14:45:43+00:00" + "time": "2024-05-17T09:32:59+00:00" }, { "name": "utopia-php/messaging", - "version": "0.10.0", + "version": "0.11.0", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "71dce00ad43eb278a877cb2c329f7b8d677adfeb" + "reference": "b499c3ad11af711c28252c62d83f24e6106a2154" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/71dce00ad43eb278a877cb2c329f7b8d677adfeb", - "reference": "71dce00ad43eb278a877cb2c329f7b8d677adfeb", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/b499c3ad11af711c28252c62d83f24e6106a2154", + "reference": "b499c3ad11af711c28252c62d83f24e6106a2154", "shasum": "" }, "require": { "ext-curl": "*", "ext-openssl": "*", + "giggsey/libphonenumber-for-php-lite": "8.13.36", "php": ">=8.0.0", "phpmailer/phpmailer": "6.9.1" }, @@ -2031,9 +2236,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.10.0" + "source": "https://github.com/utopia-php/messaging/tree/0.11.0" }, - "time": "2024-02-20T07:30:15+00:00" + "time": "2024-05-08T17:10:02+00:00" }, { "name": "utopia-php/migration", @@ -2242,6 +2447,110 @@ }, "time": "2024-06-03T18:01:18+00:00" }, + { + "name": "utopia-php/pools", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/pools.git", + "reference": "6f716a213a08db95eda1b5dddfa90983c1834817" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/6f716a213a08db95eda1b5dddfa90983c1834817", + "reference": "6f716a213a08db95eda1b5dddfa90983c1834817", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.8.*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Pools\\": "src/Pools" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A simple library to manage connection pools", + "keywords": [ + "framework", + "php", + "pools", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/pools/issues", + "source": "https://github.com/utopia-php/pools/tree/0.5.0" + }, + "time": "2024-04-19T11:11:54+00:00" + }, + { + "name": "utopia-php/preloader", + "version": "0.2.4", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/preloader.git", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/preloader/zipball/65ef48392e72172f584b0baa2e224f9a1cebcce0", + "reference": "65ef48392e72172f584b0baa2e224f9a1cebcce0", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Preloader\\": "src/Preloader" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eldad Fux", + "email": "team@appwrite.io" + } + ], + "description": "Utopia Preloader library is simple and lite library for managing PHP preloading configuration", + "keywords": [ + "framework", + "php", + "preload", + "preloader", + "preloading", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/preloader/issues", + "source": "https://github.com/utopia-php/preloader/tree/0.2.4" + }, + "time": "2020-10-24T07:04:59+00:00" + }, { "name": "utopia-php/queue", "version": "dev-feat-coroutine-and-di", @@ -2541,22 +2850,22 @@ }, { "name": "utopia-php/vcs", - "version": "0.6.6", + "version": "0.6.7", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "e538264cfee5e3efdfe1771efba04750cf20b2c4" + "reference": "8d8ff1ac68e991b95adb6f91fcde8f9bb8f24974" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/e538264cfee5e3efdfe1771efba04750cf20b2c4", - "reference": "e538264cfee5e3efdfe1771efba04750cf20b2c4", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/8d8ff1ac68e991b95adb6f91fcde8f9bb8f24974", + "reference": "8d8ff1ac68e991b95adb6f91fcde8f9bb8f24974", "shasum": "" }, "require": { "adhocore/jwt": "^1.1", "php": ">=8.0", - "utopia-php/cache": "^0.9.0", + "utopia-php/cache": "^0.10.0", "utopia-php/framework": "0.*.*" }, "require-dev": { @@ -2584,9 +2893,9 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.6.6" + "source": "https://github.com/utopia-php/vcs/tree/0.6.7" }, - "time": "2024-05-17T09:36:30+00:00" + "time": "2024-06-05T17:38:29+00:00" }, { "name": "utopia-php/view", @@ -5204,16 +5513,16 @@ }, { "name": "swoole/ide-helper", - "version": "5.0.2", + "version": "5.1.2", "source": { "type": "git", "url": "https://github.com/swoole/ide-helper.git", - "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea" + "reference": "33ec7af9111b76d06a70dd31191cc74793551112" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swoole/ide-helper/zipball/16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", - "reference": "16cfee44a6ec92254228c39bcab2fb8ae74cc2ea", + "url": "https://api.github.com/repos/swoole/ide-helper/zipball/33ec7af9111b76d06a70dd31191cc74793551112", + "reference": "33ec7af9111b76d06a70dd31191cc74793551112", "shasum": "" }, "type": "library", @@ -5230,9 +5539,9 @@ "description": "IDE help files for Swoole.", "support": { "issues": "https://github.com/swoole/ide-helper/issues", - "source": "https://github.com/swoole/ide-helper/tree/5.0.2" + "source": "https://github.com/swoole/ide-helper/tree/5.1.2" }, - "time": "2023-03-20T06:05:55+00:00" + "time": "2024-02-01T22:28:11+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5314,87 +5623,6 @@ ], "time": "2024-05-31T15:07:36+00:00" }, - { - "name": "symfony/polyfill-mbstring", - "version": "1.x-dev", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "098e36a5b73de12beeb5ac17e80abf3696f7ad5f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/098e36a5b73de12beeb5ac17e80abf3696f7ad5f", - "reference": "098e36a5b73de12beeb5ac17e80abf3696f7ad5f", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "default-branch": true, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/1.x" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-05-31T15:07:36+00:00" - }, { "name": "textalk/websocket", "version": "1.5.7", @@ -5565,45 +5793,6 @@ } ], "time": "2023-11-21T18:54:41+00:00" - }, - { - "name": "utopia-php/fetch", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/fetch.git", - "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/fetch/zipball/2fa214b9262acd1a3583515a364da4f35929d5c5", - "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "^1.5.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Fetch\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "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.1.0" - }, - "time": "2023-10-10T11:58:32+00:00" } ], "aliases": [ diff --git a/docker-compose.yml b/docker-compose.yml index b1e30a1e07..454df05b67 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,6 +40,7 @@ services: networks: - gateway - appwrite + - runtimes appwrite: container_name: appwrite @@ -48,10 +49,10 @@ services: build: context: . args: - DEBUG: false + DEBUG: true TESTING: true VERSION: dev - ports: + ports: - 9501:80 networks: - appwrite @@ -75,9 +76,11 @@ services: - appwrite-config:/storage/config:rw - appwrite-certificates:/storage/certificates:rw - appwrite-functions:/storage/functions:rw + - appwrite-builds:/storage/builds:rw - ./phpunit.xml:/usr/src/code/phpunit.xml - ./tests:/usr/src/code/tests - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./docs:/usr/src/code/docs - ./public:/usr/src/code/public - ./src:/usr/src/code/src @@ -91,8 +94,8 @@ services: - -e - app/http.php environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - - _APP_EDITION - _APP_WORKER_PER_CORE - _APP_LOCALE - _APP_CONSOLE_WHITELIST_ROOT @@ -216,11 +219,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - mariadb - redis environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPTIONS_ABUSE @@ -248,11 +253,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -277,12 +284,14 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb - request-catcher environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -317,8 +326,10 @@ services: - appwrite-builds:/storage/builds:rw - appwrite-certificates:/storage/certificates:rw - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -366,11 +377,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -399,11 +412,13 @@ services: - appwrite-functions:/storage/functions:rw - appwrite-builds:/storage/builds:rw - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -467,8 +482,10 @@ services: - appwrite-config:/storage/config:rw - appwrite-certificates:/storage/certificates:rw - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -497,12 +514,14 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb - openruntimes-executor environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -536,12 +555,14 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - maildev # - smtp environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -570,12 +591,13 @@ services: networks: - appwrite volumes: - - appwrite-uploads:/storage/uploads:rw - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -625,11 +647,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src - ./tests:/usr/src/code/tests depends_on: - mariadb environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -659,10 +683,12 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_DOMAIN @@ -696,11 +722,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -727,11 +755,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -758,11 +788,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - mariadb - redis environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -785,11 +817,13 @@ services: - appwrite volumes: - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - mariadb - redis environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -809,6 +843,7 @@ services: networks: - appwrite environment: + - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ASSISTANT_OPENAI_API_KEY openruntimes-executor: @@ -829,6 +864,7 @@ services: # It's not possible to share mount file between 2 containers without host mount (copying is too slow) - /tmp:/tmp:rw environment: + - PHP_IDE_CONFIG=serverName=Appwrite - OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD - OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL - OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK @@ -872,6 +908,7 @@ services: - appwrite - runtimes environment: + - PHP_IDE_CONFIG=serverName=Appwrite - OPR_PROXY_WORKER_PER_CORE=$_APP_WORKER_PER_CORE - OPR_PROXY_ENV=$_APP_ENV - OPR_PROXY_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET @@ -985,6 +1022,7 @@ services: networks: - appwrite environment: + - PHP_IDE_CONFIG=serverName=Appwrite - REDIS_HOSTS=redis ports: - "8081:5540" @@ -998,6 +1036,7 @@ services: ports: - "9509:3000" environment: + - PHP_IDE_CONFIG=serverName=Appwrite - SERVER_URL=http://localhost/v1/graphql # Dev Tools End ------------------------------------------------------------------------------------------ diff --git a/src/Appwrite/Platform/Tasks/QueueRetry.php b/src/Appwrite/Platform/Tasks/QueueRetry.php index aeed4305ee..634147ec9c 100644 --- a/src/Appwrite/Platform/Tasks/QueueRetry.php +++ b/src/Appwrite/Platform/Tasks/QueueRetry.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Tasks; use Utopia\CLI\Console; +use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; use Utopia\Http\Validator\Wildcard; use Utopia\Platform\Action; diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index 4712e58cbe..d21f1c267d 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -29,8 +29,8 @@ class Audits extends Action ->desc('Audits worker') ->inject('message') ->inject('dbForProject') - ->inject('auth') - ->callback(fn ($message, $dbForProject, ValidatorAuthorization $auth) => $this->action($message, $dbForProject, $auth)); + ->inject('authorization') + ->callback(fn ($message, $dbForProject, ValidatorAuthorization $authorization) => $this->action($message, $dbForProject, $authorization)); } diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 3e7992ed19..4199fe0268 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -52,8 +52,8 @@ class Builds extends Action ->inject('dbForProject') ->inject('deviceForFunctions') ->inject('log') - ->inject('auth') - ->callback(fn ($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log, Authorization $auth) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $deviceForFunctions, $log, $auth)); + ->inject('authorization') + ->callback(fn ($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log, Authorization $authorization) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $deviceForFunctions, $log, $authorization)); } /** diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 9e0ac558c6..45df3e924f 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -55,8 +55,8 @@ class Deletes extends Action ->inject('executionRetention') ->inject('auditRetention') ->inject('log') - ->inject('auth') - ->callback(fn ($message, $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log, ValidatorAuthorization $auth) => $this->action($message, $dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $abuseRetention, $executionRetention, $auditRetention, $log, $auth)); + ->inject('authorization') + ->callback(fn ($message, $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log, ValidatorAuthorization $authorization) => $this->action($message, $dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $abuseRetention, $executionRetention, $auditRetention, $log, $authorization)); } /** From cf30941514bba751a4c7edb106e3af32d9f65d33 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 5 Jun 2024 20:46:49 -0400 Subject: [PATCH 079/195] fix: compose file --- docker-compose.yml | 45 +++------------------------------------------ 1 file changed, 3 insertions(+), 42 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 454df05b67..7a461f4ecc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,7 +40,6 @@ services: networks: - gateway - appwrite - - runtimes appwrite: container_name: appwrite @@ -49,7 +48,7 @@ services: build: context: . args: - DEBUG: true + DEBUG: false TESTING: true VERSION: dev ports: @@ -76,11 +75,9 @@ services: - appwrite-config:/storage/config:rw - appwrite-certificates:/storage/certificates:rw - appwrite-functions:/storage/functions:rw - - appwrite-builds:/storage/builds:rw - ./phpunit.xml:/usr/src/code/phpunit.xml - ./tests:/usr/src/code/tests - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./docs:/usr/src/code/docs - ./public:/usr/src/code/public - ./src:/usr/src/code/src @@ -94,8 +91,8 @@ services: - -e - app/http.php environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV + - _APP_EDITION - _APP_WORKER_PER_CORE - _APP_LOCALE - _APP_CONSOLE_WHITELIST_ROOT @@ -219,13 +216,11 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - mariadb - redis environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPTIONS_ABUSE @@ -253,13 +248,11 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -284,14 +277,12 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb - request-catcher environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -326,10 +317,8 @@ services: - appwrite-builds:/storage/builds:rw - appwrite-certificates:/storage/certificates:rw - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -377,13 +366,11 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -412,13 +399,11 @@ services: - appwrite-functions:/storage/functions:rw - appwrite-builds:/storage/builds:rw - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -482,10 +467,8 @@ services: - appwrite-config:/storage/config:rw - appwrite-certificates:/storage/certificates:rw - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -514,14 +497,12 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb - openruntimes-executor environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -555,14 +536,12 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - maildev # - smtp environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -591,13 +570,12 @@ services: networks: - appwrite volumes: + - appwrite-uploads:/storage/uploads:rw - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -647,13 +625,11 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src - ./tests:/usr/src/code/tests depends_on: - mariadb environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -683,12 +659,10 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_DOMAIN @@ -722,13 +696,11 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -755,13 +727,11 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - redis - mariadb environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -788,13 +758,11 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - mariadb - redis environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -817,13 +785,11 @@ services: - appwrite volumes: - ./app:/usr/src/code/app - - ./vendor:/usr/src/code/vendor - ./src:/usr/src/code/src depends_on: - mariadb - redis environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPENSSL_KEY_V1 @@ -843,7 +809,6 @@ services: networks: - appwrite environment: - - PHP_IDE_CONFIG=serverName=Appwrite - _APP_ASSISTANT_OPENAI_API_KEY openruntimes-executor: @@ -864,7 +829,6 @@ services: # It's not possible to share mount file between 2 containers without host mount (copying is too slow) - /tmp:/tmp:rw environment: - - PHP_IDE_CONFIG=serverName=Appwrite - OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD - OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL - OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK @@ -908,7 +872,6 @@ services: - appwrite - runtimes environment: - - PHP_IDE_CONFIG=serverName=Appwrite - OPR_PROXY_WORKER_PER_CORE=$_APP_WORKER_PER_CORE - OPR_PROXY_ENV=$_APP_ENV - OPR_PROXY_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET @@ -1022,7 +985,6 @@ services: networks: - appwrite environment: - - PHP_IDE_CONFIG=serverName=Appwrite - REDIS_HOSTS=redis ports: - "8081:5540" @@ -1036,7 +998,6 @@ services: ports: - "9509:3000" environment: - - PHP_IDE_CONFIG=serverName=Appwrite - SERVER_URL=http://localhost/v1/graphql # Dev Tools End ------------------------------------------------------------------------------------------ From 1b80ec289b968dc5ca41c02783a309490c2fbca2 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:12:05 -0400 Subject: [PATCH 080/195] test: Adjusting unit testing to new structure --- tests/unit/Event/EventTest.php | 12 ++++++++---- tests/unit/GraphQL/BuilderTest.php | 3 ++- tests/unit/Utopia/RequestTest.php | 3 ++- tests/unit/Utopia/ResponseTest.php | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index 51e7d06801..e0436b06b2 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -11,7 +11,7 @@ use Utopia\Queue; use Utopia\Queue\Client; use Utopia\System\System; -//require_once __DIR__ . '/../../../app/init.php'; +require_once __DIR__ . '/../../../app/init2.php'; class EventTest extends TestCase { @@ -66,9 +66,13 @@ class EventTest extends TestCase $this->assertEquals('eventValue1', $this->object->getParam('eventKey1')); $this->assertEquals('eventValue2', $this->object->getParam('eventKey2')); $this->assertEquals(null, $this->object->getParam('eventKey3')); - global $register; - $pools = $register->get('pools'); - $client = new Client($this->object->getQueue(), $pools->get('queue')->pop()->getResource()); + + global $global; + $pools = $global->get('pools'); + $dsn = $pools['pools-queue-main']['dsn']; + $queue = new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()); + + $client = new Client($this->object->getQueue(), $queue); $this->assertEquals($client->getQueueSize(), 1); } diff --git a/tests/unit/GraphQL/BuilderTest.php b/tests/unit/GraphQL/BuilderTest.php index d79a104c90..22350c150b 100644 --- a/tests/unit/GraphQL/BuilderTest.php +++ b/tests/unit/GraphQL/BuilderTest.php @@ -6,6 +6,7 @@ use Appwrite\GraphQL\Types\Mapper; use Appwrite\Utopia\Response; use PHPUnit\Framework\TestCase; use Swoole\Http\Response as SwooleResponse; +use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse; class BuilderTest extends TestCase { @@ -13,7 +14,7 @@ class BuilderTest extends TestCase public function setUp(): void { - $this->response = new Response(new SwooleResponse()); + $this->response = new Response(new UtopiaSwooleResponse(new SwooleResponse())); Mapper::init($this->response->getModels()); } diff --git a/tests/unit/Utopia/RequestTest.php b/tests/unit/Utopia/RequestTest.php index 9ae754555e..0889c5ef6c 100644 --- a/tests/unit/Utopia/RequestTest.php +++ b/tests/unit/Utopia/RequestTest.php @@ -5,6 +5,7 @@ namespace Tests\Unit\Utopia; use Appwrite\Utopia\Request; use PHPUnit\Framework\TestCase; use Swoole\Http\Request as SwooleRequest; +use Utopia\Http\Adapter\Swoole\Request as UtopiaSwooleRequest; use Tests\Unit\Utopia\Request\Filters\First; use Tests\Unit\Utopia\Request\Filters\Second; use Utopia\Http\Route; @@ -15,7 +16,7 @@ class RequestTest extends TestCase public function setUp(): void { - $this->request = new Request(new SwooleRequest()); + $this->request = new Request(new UtopiaSwooleRequest(new SwooleRequest())); } public function testFilters(): void diff --git a/tests/unit/Utopia/ResponseTest.php b/tests/unit/Utopia/ResponseTest.php index cd111ec22c..b1d9f97752 100644 --- a/tests/unit/Utopia/ResponseTest.php +++ b/tests/unit/Utopia/ResponseTest.php @@ -9,6 +9,7 @@ use Swoole\Http\Response as SwooleResponse; use Tests\Unit\Utopia\Response\Filters\First; use Tests\Unit\Utopia\Response\Filters\Second; use Utopia\Database\Document; +use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse; class ResponseTest extends TestCase { @@ -16,7 +17,7 @@ class ResponseTest extends TestCase public function setUp(): void { - $this->response = new Response(new SwooleResponse()); + $this->response = new Response(new UtopiaSwooleResponse(new SwooleResponse())); $this->response->setModel(new Single()); $this->response->setModel(new Lists()); $this->response->setModel(new Nested()); From 0bd9426aa773c7e9300613624b850f9d947aa17c Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 6 Jun 2024 13:13:39 -0400 Subject: [PATCH 081/195] feat: Adding `plans`, updating server and exception class --- app/controllers/general.php | 2 +- app/init2.php | 12 +++++++++--- composer.lock | 8 ++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 0e35f8d599..d45d24e696 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -696,7 +696,7 @@ Http::error() } switch ($class) { - case 'Utopia\Exception': + case 'Utopia\Servers\Exception': $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); switch ($code) { case 400: diff --git a/app/init2.php b/app/init2.php index 2b7358d60b..bccee1b107 100644 --- a/app/init2.php +++ b/app/init2.php @@ -383,6 +383,7 @@ $global->set('promiseAdapter', function () { $log = new Dependency(); $mode = new Dependency(); $user = new Dependency(); +$plan = new Dependency(); $pools = new Dependency(); $geodb = new Dependency(); $cache = new Dependency(); @@ -399,9 +400,9 @@ $project = new Dependency(); $clients = new Dependency(); $servers = new Dependency(); $registry = new Dependency(); -$getProjectDB = new Dependency(); -$localeCodes = new Dependency(); $connections = new Dependency(); +$localeCodes = new Dependency(); +$getProjectDB = new Dependency(); $dbForProject = new Dependency(); $dbForConsole = new Dependency(); $queueForUsage = new Dependency(); @@ -413,9 +414,9 @@ $deviceForFiles = new Dependency(); $queueForEvents = new Dependency(); $queueForAudits = new Dependency(); $promiseAdapter = new Dependency(); -$requestTimestamp = new Dependency(); $deviceForBuilds = new Dependency(); $queueForDeletes = new Dependency(); +$requestTimestamp = new Dependency(); $queueForDatabase = new Dependency(); $queueForMessaging = new Dependency(); $queueForFunctions = new Dependency(); @@ -425,6 +426,10 @@ $passwordsDictionary = new Dependency(); $queueForCertificates = new Dependency(); +$plan + ->setName('plan') + ->setCallback(fn() => []); + $mode ->setName('mode') ->inject('request') @@ -1188,6 +1193,7 @@ $schema $container->set($log); $container->set($mode); $container->set($user); +$container->set($plan); $container->set($pools); $container->set($cache); $container->set($pools); diff --git a/composer.lock b/composer.lock index e65142d1f1..83c2b40509 100644 --- a/composer.lock +++ b/composer.lock @@ -2672,12 +2672,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/servers.git", - "reference": "e7eee7f82399c89adc28cf79912a9a41d7bb3233" + "reference": "4ce97c42158c20c7dcf0cdcdcf79080b879a1ee7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/servers/zipball/e7eee7f82399c89adc28cf79912a9a41d7bb3233", - "reference": "e7eee7f82399c89adc28cf79912a9a41d7bb3233", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/4ce97c42158c20c7dcf0cdcdcf79080b879a1ee7", + "reference": "4ce97c42158c20c7dcf0cdcdcf79080b879a1ee7", "shasum": "" }, "require": { @@ -2735,7 +2735,7 @@ "source": "https://github.com/utopia-php/servers/tree/dev", "issues": "https://github.com/utopia-php/servers/issues" }, - "time": "2024-04-22T21:23:28+00:00" + "time": "2024-06-06T14:41:32+00:00" }, { "name": "utopia-php/storage", From ba629e94efb73b65771734e3a19cd7cfb47f325f Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:52:30 -0400 Subject: [PATCH 082/195] feat: Adjusting Doctor to new pools --- src/Appwrite/Platform/Tasks/Doctor.php | 74 ++++++++++++++++++-------- 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index 4923c1fdff..8f3d763f48 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -6,10 +6,13 @@ use Appwrite\ClamAV\Network; use Appwrite\Utopia\Queue\Connections; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Adapter\MariaDB; +use Utopia\Database\Adapter\MySQL; use Utopia\Domains\Domain; use Utopia\Http\Http; use Utopia\Logger\Logger; use Utopia\Platform\Action; +use Utopia\Queue\Connection\Redis; use Utopia\Registry\Registry; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; @@ -119,24 +122,41 @@ class Doctor extends Action //throw $th; } - $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ + /** @var array $pools */ + $pools = $register->get('pools'); $configs = [ - 'Console.DB' => Config::getParam('pools-console'), - 'Projects.DB' => Config::getParam('pools-database'), + 'Console.DB' => [ + 'prefix' => 'console', + 'databases' => Config::getParam('pools-console') + ], + 'Database.DB' => [ + 'prefix' => 'database', + 'databases' => Config::getParam('pools-database') + ], ]; + foreach ($configs as $key => $config) { - foreach ($config as $database) { + foreach ($config['databases'] as $database) { try { - $connection = $pools->get($database)->pop(); - $connections->add($connection); - $adapter = $connection->getResource(); + $pool = $pools['pools-' . $config['prefix'] . '-' . $database]['pool']; + $dsn = $pools['pools-' . $config['prefix'] . '-' . $database]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + $adapter->setDatabase($dsn->getPath()); + if ($adapter->ping()) { - Console::success('🟢 ' . str_pad("{$key}({$database})", 50, '.') . 'connected'); + Console::success('🟢 ' . str_pad("$key({$database})", 50, '.') . 'connected'); } else { - Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected'); + Console::error('🔴 ' . str_pad("$key({$database})", 47, '.') . 'disconnected'); } } catch (\Throwable $th) { Console::error('🔴 ' . str_pad("{$key}.({$database})", 47, '.') . 'disconnected'); @@ -144,27 +164,39 @@ class Doctor extends Action } } - $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ + /** @var array $pools */ + $pools = $register->get('pools'); $configs = [ - 'Cache' => Config::getParam('pools-cache'), - 'Queue' => Config::getParam('pools-queue'), - 'PubSub' => Config::getParam('pools-pubsub'), + 'Cache' => [ + 'prefix' => 'cache', + 'databases' => Config::getParam('pools-cache') + ], + 'Queue' => [ + 'prefix' => 'queue', + 'databases' => Config::getParam('pools-queue') + ], + 'PubSub' => [ + 'prefix' => 'pubsub', + 'databases' => Config::getParam('pools-pubsub') + ], ]; - foreach ($configs as $key => $config) { - foreach ($config as $pool) { + foreach ($config['databases'] as $database) { try { - $connection = $pools->get($pool)->pop(); - $connections->add($connection); - $adapter = $connection->getResource(); + $pool = $pools['pools-' . $config['prefix'] . '-' . $database]['pool']; + $dsn = $pools['pools-' . $config['prefix'] . '-' . $database]['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + $adapter = new Redis($dsn->getHost(), $dsn->getPort()); if ($adapter->ping()) { - Console::success('🟢 ' . str_pad("{$key}({$pool})", 50, '.') . 'connected'); + Console::success('🟢 ' . str_pad("{$key}({$database})", 50, '.') . 'connected'); } else { - Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); + Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected'); } } catch (\Throwable $th) { - Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); + Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected'); } } } From 757cf24d4dd3ffe2c71175399c3635ce688232ff Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:52:51 -0400 Subject: [PATCH 083/195] feat: Adjusting health API to new pools --- app/controllers/api/health.php | 42 ++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 99b0077aa2..79c83aa13a 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -16,7 +16,6 @@ use Utopia\Http\Validator\Integer; use Utopia\Http\Validator\Multiple; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; -use Utopia\Pools\Group; use Utopia\Queue\Client; use Utopia\Queue\Connection; use Utopia\Registry\Registry; @@ -85,6 +84,8 @@ Http::get('/v1/health/db') foreach ($configs as $key => $config) { foreach ($config as $database) { + $checkStart = \microtime(true); + try { $pool = $pools['pools-'.$key.'-'.$database]['pool']; @@ -99,7 +100,6 @@ Http::get('/v1/health/db') }; $adapter->setDatabase($dsn->getPath()); - $checkStart = \microtime(true); if ($adapter->ping()) { $output[] = new Document([ @@ -140,7 +140,7 @@ Http::get('/v1/health/cache') ->inject('response') ->inject('pools') ->inject('connections') - ->action(function (Response $response, Group $pools, Connections $connections) { + ->action(function (Response $response, array $pools, Connections $connections) { $output = []; @@ -150,12 +150,15 @@ Http::get('/v1/health/cache') foreach ($configs as $key => $config) { foreach ($config as $database) { + $checkStart = \microtime(true); try { - $connection = $pools->get($database)->pop(); - $connections->add($connection); - $adapter = $connection->getResource(); + $pool = $pools['pools-cache-' . $database]['pool']; + $dsn = $pools['pools-cache-' . $database]['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + $adapter = new Connection\Redis($dsn->getHost(), $dsn->getPort()); - $checkStart = \microtime(true); if ($adapter->ping()) { $output[] = new Document([ @@ -200,7 +203,7 @@ Http::get('/v1/health/queue') ->inject('response') ->inject('pools') ->inject('connections') - ->action(function (Response $response, Group $pools, Connections $connections) { + ->action(function (Response $response, array $pools, Connections $connections) { $output = []; @@ -209,14 +212,16 @@ Http::get('/v1/health/queue') ]; foreach ($configs as $key => $config) { + $checkStart = \microtime(true); + foreach ($config as $database) { try { - $connection = $pools->get($database)->pop(); - $connections->add($connection); - $adapter = $connection->getResource(); - - $checkStart = \microtime(true); + $pool = $pools['pools-queue-' . $database]['pool']; + $dsn = $pools['pools-queue-' . $database]['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = new Connection\Redis($dsn->getHost(), $dsn->getPort()); if ($adapter->ping()) { $output[] = new Document([ 'name' => $key . " ($database)", @@ -260,7 +265,7 @@ Http::get('/v1/health/pubsub') ->inject('response') ->inject('pools') ->inject('connections') - ->action(function (Response $response, Group $pools, Connections $connections) { + ->action(function (Response $response, array $pools, Connections $connections) { $output = []; @@ -271,9 +276,12 @@ Http::get('/v1/health/pubsub') foreach ($configs as $key => $config) { foreach ($config as $database) { try { - $connection = $pools->get($database)->pop(); - $connections->add($connection); - $adapter = $connection->getResource(); + $pool = $pools['pools-pubsub-' . $database]['pool']; + $dsn = $pools['pools-pubsub-' . $database]['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + $adapter = new Connection\Redis($dsn->getHost(), $dsn->getPort()); $checkStart = \microtime(true); From 269b9b27c26ca63da5835f82bb4fee619701cf50 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:54:08 -0400 Subject: [PATCH 084/195] feat: Adding cache --- app/init2.php | 102 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/app/init2.php b/app/init2.php index bccee1b107..74b5bd2c83 100644 --- a/app/init2.php +++ b/app/init2.php @@ -3,22 +3,10 @@ require_once __DIR__ . '/../vendor/autoload.php'; use PHPMailer\PHPMailer\PHPMailer; +use Redis; +use Utopia\Cache\Adapter\Redis as CacheRedis; +use Utopia\Cache\Adapter\Sharding; use Utopia\VCS\Adapter\Git\GitHub; - -require_once __DIR__ . '/init/constants.php'; -require_once __DIR__ . '/init/config.php'; -require_once __DIR__ . '/init/locale.php'; -require_once __DIR__ . '/init/database/filters.php'; -require_once __DIR__ . '/init/database/formats.php'; - -ini_set('memory_limit', '-1'); -ini_set('display_errors', 1); -ini_set('display_startup_errors', 1); -ini_set('default_socket_timeout', -1); -error_reporting(E_ALL); - -global $http, $container; - use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; use Appwrite\Auth\Auth; @@ -45,7 +33,6 @@ use Swoole\Database\PDOConfig; use Swoole\Database\PDOPool; use Swoole\Database\RedisConfig; use Swoole\Database\RedisPool; -use Utopia\Cache\Adapter\None; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -80,6 +67,20 @@ use Utopia\Storage\Device\Wasabi; use Utopia\Storage\Storage; use Utopia\System\System; +require_once __DIR__ . '/init/constants.php'; +require_once __DIR__ . '/init/config.php'; +require_once __DIR__ . '/init/locale.php'; +require_once __DIR__ . '/init/database/filters.php'; +require_once __DIR__ . '/init/database/formats.php'; + +ini_set('memory_limit', '-1'); +ini_set('display_errors', 1); +ini_set('display_startup_errors', 1); +ini_set('default_socket_timeout', -1); +error_reporting(E_ALL); + +global $http, $container; + Http::setMode(System::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION)); if (!Http::isProduction()) { @@ -206,20 +207,20 @@ $global->set( 'pools', (function () { $fallbackForDB = 'db_main=' . URL::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', ''), - ]); + '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=' . URL::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', ''), - ]); + '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' => [ @@ -323,7 +324,8 @@ $global->set( (new RedisConfig()) ->withHost($dsnHost) ->withPort((int)$dsnPort) - ->withAuth($dsnPass), $poolSize + ->withAuth($dsnPass), + $poolSize ); break; @@ -428,7 +430,7 @@ $queueForCertificates = new Dependency(); $plan ->setName('plan') - ->setCallback(fn() => []); + ->setCallback(fn () => []); $mode ->setName('mode') @@ -637,7 +639,7 @@ $project return $console; } - $project = $authorization->skip(fn() => $dbForConsole->getDocument('projects', $projectId)); + $project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); return $project; }); @@ -741,8 +743,26 @@ $dbForConsole $cache ->setName('cache') - ->setCallback(function (): Cache { - return new Cache(new None()); + ->inject('pools') + ->inject('connections') + ->setCallback(function (array $pools, Connections $connections) { + $adapters = []; + $databases = Config::getParam('pools-cache'); + + foreach ($databases as $database) { + $pool = $pools['pools-cache-' . $database]['pool']; + $dsn = $pools['pools-cache-' . $database]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + + $redis = new Redis(); + $redis->connect($dsn->getHost(), $dsn->getPort()); + + $adapters[] = new CacheRedis($redis); + } + + return new Cache(new Sharding($adapters)); }); $authorization @@ -785,11 +805,11 @@ $connections $locale ->setName('locale') - ->setCallback(fn() => new Locale(System::getEnv('_APP_LOCALE', 'en'))); + ->setCallback(fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); $localeCodes ->setName('localeCodes') - ->setCallback(fn() => array_map(fn($locale) => $locale['code'], Config::getParam('locale-codes', []))); + ->setCallback(fn () => array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []))); $queue ->setName('queue') @@ -941,10 +961,10 @@ $clients * + Filter for duplicated entries */ $clientsConsole = \array_map( - fn($node) => $node['hostname'], + fn ($node) => $node['hostname'], \array_filter( $console->getAttribute('platforms', []), - fn($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && !empty($node['hostname'])) + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ); @@ -952,10 +972,10 @@ $clients \array_merge( $clientsConsole, \array_map( - fn($node) => $node['hostname'], + fn ($node) => $node['hostname'], \array_filter( $project->getAttribute('platforms', []), - fn($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ) ) @@ -1118,7 +1138,7 @@ $schema }; $attributes = function (int $limit, int $offset) use ($dbForProject, $authorization) { - $attrs = $authorization->skip(fn() => $dbForProject->find('attributes', [ + $attrs = $authorization->skip(fn () => $dbForProject->find('attributes', [ Query::limit($limit), Query::offset($offset), ])); From ea5d0fbaf2a1301d98b20a90ad2cf8f083e82768 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:54:39 -0400 Subject: [PATCH 085/195] refactor: Removing redundant injections --- app/realtime.php | 89 ++++-------------------------------------------- 1 file changed, 7 insertions(+), 82 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index 2f6eebbf46..53764f9c8c 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -15,24 +15,18 @@ use Swoole\Table; use Swoole\Timer; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; -use Utopia\Cache\Adapter\None; -use Utopia\Cache\Cache; use Utopia\CLI\Console; -use Utopia\Database\Adapter\MariaDB; -use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization; use Utopia\DI\Container; use Utopia\DI\Dependency; use Utopia\Http\Adapter\Swoole\Request as UtopiaRequest; use Utopia\Http\Adapter\Swoole\Response as UtopiaResponse; use Utopia\Http\Http; -use Utopia\DSN\DSN; use Utopia\Logger\Log; use Utopia\Registry\Registry; use Utopia\System\System; @@ -50,75 +44,6 @@ require_once __DIR__ . '/init2.php'; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -$cache = new Dependency(); -$getProjectDB = new Dependency(); - - -$getProjectDB - ->setName('getProjectDB') - ->inject('pools') - ->inject('dbForConsole') - ->inject('cache') - ->inject('authorization') - ->inject('connections') - ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $authorization, Connections $connections) { - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $authorization, $connections): Database { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - - $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; - $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($connectionDsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - $adapter->setDatabase($connectionDsn->getPath()); - - $database = new Database($adapter, $cache); - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - $database - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()) - ->setAuthorization($authorization); - - return $database; - }; - }); - -$cache - ->setName('cache') - ->setCallback(function () { - return new Cache(new None()); - }); - -$container->set($cache); -$container->set($getProjectDB); - $realtime = new Realtime(); /** @@ -220,7 +145,7 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum */ // TODO: Remove this if check once it doesn't cause issues for cloud if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted') { - Timer::tick(5000, function () use ($container, $stats, &$statsDocument, $logError, $authorization) { + Timer::tick(5000, function () use ($container, $stats, &$statsDocument, $logError, $authorization) { $payload = []; foreach ($stats as $projectId => $value) { $payload[$projectId] = $stats->get($projectId, 'connectionsTotal'); @@ -230,17 +155,17 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum } try { - $database = $container->get('dbForConsole'); + $database = $container->get('dbForConsole'); $statsDocument ->setAttribute('timestamp', DateTime::now()) ->setAttribute('value', json_encode($payload)); - $authorization->skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); + $authorization->skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); } catch (Throwable $th) { call_user_func($logError, $th, "updateWorkerDocument"); } finally { - // TODO NOW $global->get('pools')->reclaim(); + // TODO NOW $global->get('pools')->reclaim(); } }); } @@ -261,11 +186,11 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats // TODO: Remove this if check once it doesn't cause issues for cloud if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted') { if ($realtime->hasSubscriber('console', Role::users()->toString(), 'project')) { - $database = $container->get('dbForConsole'); + $database = $container->get('dbForConsole'); $payload = []; - $list = $authorization->skip(fn () => $database->find('realtime', [ + $list = $authorization->skip(fn () => $database->find('realtime', [ Query::greaterThan('timestamp', DateTime::addSeconds(new \DateTime(), -15)), ])); @@ -305,7 +230,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats 'data' => $event['data'] ])); } - // TODO NOW $global->get('pools')->reclaim(); + // TODO NOW $global->get('pools')->reclaim(); } } /** From d0a55e266c3b9c3ab3da602fa73712716dffd4ee Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:54:51 -0400 Subject: [PATCH 086/195] lint: Linting --- app/cli.php | 8 +++---- app/controllers/api/avatars.php | 2 +- app/controllers/api/functions.php | 2 +- app/controllers/api/project.php | 3 ++- app/controllers/api/projects.php | 24 +++++++++---------- app/controllers/api/storage.php | 8 +++---- app/controllers/general.php | 18 +++++++------- app/http.php | 2 +- app/worker.php | 21 +--------------- src/Appwrite/Platform/Tasks/QueueCount.php | 2 +- src/Appwrite/Platform/Tasks/QueueRetry.php | 1 - src/Appwrite/Platform/Tasks/ScheduleBase.php | 5 +--- .../Platform/Tasks/ScheduleMessages.php | 1 - src/Appwrite/Platform/Workers/Deletes.php | 2 +- tests/unit/Utopia/RequestTest.php | 2 +- 15 files changed, 38 insertions(+), 63 deletions(-) diff --git a/app/cli.php b/app/cli.php index 8b4a7c8616..7dd16f5c2f 100644 --- a/app/cli.php +++ b/app/cli.php @@ -7,18 +7,16 @@ use Appwrite\Event\Certificate; use Appwrite\Event\Delete; use Appwrite\Event\Hamster; use Appwrite\Platform\Appwrite; +use Swoole\Runtime; +use Utopia\CLI\Adapters\Swoole as SwooleCLI; use Utopia\CLI\Console; use Utopia\Database\Validator\Authorization; use Utopia\DI\Dependency; -use Utopia\DSN\DSN; use Utopia\Logger\Log; use Utopia\Platform\Service; -use Utopia\Pools\Group; use Utopia\Queue\Connection; use Utopia\Registry\Registry; use Utopia\System\System; -use Swoole\Runtime; -use Utopia\CLI\Adapters\Swoole as SwooleCLI; global $global, $container; @@ -105,7 +103,7 @@ $logError $auth ->setName('auth') - ->setCallback(fn() => new Authorization()); + ->setCallback(fn () => new Authorization()); $container->set($auth); $container->set($logError); diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 6030e4b28a..b54413f457 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -13,6 +13,7 @@ use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; +use Utopia\Fetch\Client; use Utopia\Http\Http; use Utopia\Http\Validator\Boolean; use Utopia\Http\Validator\HexColor; @@ -20,7 +21,6 @@ use Utopia\Http\Validator\Range; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\URL; use Utopia\Http\Validator\WhiteList; -use Utopia\Fetch\Client; use Utopia\Image\Image; use Utopia\Logger\Log; use Utopia\Logger\Logger; diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index ef9a77298f..b5e02b9dad 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1772,7 +1772,7 @@ Http::post('/v1/functions/:functionId/executions') if ($function->getAttribute('logging')) { /** @var Document $execution */ - $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); } } diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index aaa25df1b5..d24a33c6c1 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -71,7 +71,8 @@ Http::get('/v1/project/usage') '1d' => 'Y-m-d\T00:00:00.000P', }; - $authorization->skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) { foreach ($metrics['total'] as $metric) { + $authorization->skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) { + foreach ($metrics['total'] as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), Query::equal('period', ['inf']) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 6de5a87ea2..f23e40e581 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -11,8 +11,8 @@ use Appwrite\Network\Validator\Origin; use Appwrite\Template\Template; use Appwrite\Utopia\Database\Validator\ProjectId; use Appwrite\Utopia\Database\Validator\Queries\Projects; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Queue\Connections; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use PHPMailer\PHPMailer\PHPMailer; use Utopia\Abuse\Adapters\TimeLimit; @@ -69,7 +69,7 @@ Http::post('/v1/projects') ->param('projectId', '', new ProjectId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, and hyphen. Can\'t start with a special char. Max length is 36 chars.') ->param('name', null, new Text(128), 'Project name. Max length: 128 chars.') ->param('teamId', '', new UID(), 'Team unique ID.') - ->param('region', System::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn($config) => !$config['disabled']))), 'Project Region.', true) + ->param('region', System::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn ($config) => !$config['disabled']))), 'Project Region.', true) ->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true) ->param('logo', '', new Text(1024), 'Project logo.', true) ->param('url', '', new URL(), 'Project URL.', true) @@ -504,7 +504,7 @@ Http::patch('/v1/projects/:projectId/service') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PROJECT) ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), fn($element) => $element['optional'])), true), 'Service name.') + ->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), fn ($element) => $element['optional'])), true), 'Service name.') ->param('status', null, new Boolean(), 'Service status.') ->inject('response') ->inject('dbForConsole') @@ -544,7 +544,7 @@ Http::patch('/v1/projects/:projectId/service/all') throw new Exception(Exception::PROJECT_NOT_FOUND); } - $allServices = array_keys(array_filter(Config::getParam('services'), fn($element) => $element['optional'])); + $allServices = array_keys(array_filter(Config::getParam('services'), fn ($element) => $element['optional'])); $services = []; foreach ($allServices as $service) { @@ -947,7 +947,7 @@ Http::post('/v1/projects/:projectId/webhooks') ->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.') ->param('enabled', true, new Boolean(true), 'Enable or disable a webhook.', true) ->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') - ->param('url', '', fn($request) => new Multiple([new URL(['http', 'https']), new PublicDomain()], Multiple::TYPE_STRING), 'Webhook URL.', false, ['request']) + ->param('url', '', fn ($request) => new Multiple([new URL(['http', 'https']), new PublicDomain()], Multiple::TYPE_STRING), 'Webhook URL.', false, ['request']) ->param('security', false, new Boolean(true), 'Certificate verification, false for disabled or true for enabled.') ->param('httpUser', '', new Text(256), 'Webhook HTTP user. Max length: 256 chars.', true) ->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true) @@ -1069,7 +1069,7 @@ Http::put('/v1/projects/:projectId/webhooks/:webhookId') ->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.') ->param('enabled', true, new Boolean(true), 'Enable or disable a webhook.', true) ->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') - ->param('url', '', fn($request) => new Multiple([new URL(['http', 'https']), new PublicDomain()], Multiple::TYPE_STRING), 'Webhook URL.', false, ['request']) + ->param('url', '', fn ($request) => new Multiple([new URL(['http', 'https']), new PublicDomain()], Multiple::TYPE_STRING), 'Webhook URL.', false, ['request']) ->param('security', false, new Boolean(true), 'Certificate verification, false for disabled or true for enabled.') ->param('httpUser', '', new Text(256), 'Webhook HTTP user. Max length: 256 chars.', true) ->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true) @@ -1743,7 +1743,7 @@ Http::get('/v1/projects/:projectId/templates/sms/:type/:locale') ->label('sdk.response.model', Response::MODEL_SMS_TEMPLATE) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? []), 'Template type') - ->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) + ->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) { @@ -1783,7 +1783,7 @@ Http::get('/v1/projects/:projectId/templates/email/:type/:locale') ->label('sdk.response.model', Response::MODEL_EMAIL_TEMPLATE) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? []), 'Template type') - ->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) + ->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) { @@ -1834,7 +1834,7 @@ Http::patch('/v1/projects/:projectId/templates/sms/:type/:locale') ->label('sdk.response.model', Response::MODEL_SMS_TEMPLATE) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? []), 'Template type') - ->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) + ->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) ->param('message', '', new Text(0), 'Template message') ->inject('response') ->inject('dbForConsole') @@ -1873,7 +1873,7 @@ Http::patch('/v1/projects/:projectId/templates/email/:type/:locale') ->label('sdk.response.model', Response::MODEL_PROJECT) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? []), 'Template type') - ->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) + ->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) ->param('subject', '', new Text(255), 'Email Subject') ->param('message', '', new Text(0), 'Template message') ->param('senderName', '', new Text(255, 0), 'Name of the email sender', true) @@ -1922,7 +1922,7 @@ Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale') ->label('sdk.response.model', Response::MODEL_SMS_TEMPLATE) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? []), 'Template type') - ->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) + ->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) { @@ -1964,7 +1964,7 @@ Http::delete('/v1/projects/:projectId/templates/email/:type/:locale') ->label('sdk.response.model', Response::MODEL_EMAIL_TEMPLATE) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? []), 'Template type') - ->param('locale', '', fn($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) + ->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes']) ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) { diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index aa2001712f..bfd98dd636 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -631,10 +631,10 @@ Http::post('/v1/storage/buckets/:bucketId/files') * However as with chunk upload even if we are updating, we are essentially creating a file * adding it's new chunk so we validate create permission instead of update */ - if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { + if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { throw new Exception(Exception::USER_UNAUTHORIZED); } - $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); } } else { if ($file->isEmpty()) { @@ -669,10 +669,10 @@ Http::post('/v1/storage/buckets/:bucketId/files') * However as with chunk upload even if we are updating, we are essentially creating a file * adding it's new chunk so we validate create permission instead of update */ - if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { + if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { throw new Exception(Exception::USER_UNAUTHORIZED); } - $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); } } diff --git a/app/controllers/general.php b/app/controllers/general.php index d45d24e696..c31ee413f6 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -45,7 +45,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request $host = $request->getHostname() ?? ''; $rule = $auth->skip( - fn() => $dbForConsole->find('rules', [ + fn () => $dbForConsole->find('rules', [ Query::equal('domain', [$host]), Query::limit(1) ]) @@ -73,7 +73,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request $projectId = $rule->getAttribute('projectId'); $project = $auth->skip( - fn() => $dbForConsole->getDocument('projects', $projectId) + fn () => $dbForConsole->getDocument('projects', $projectId) ); if (array_key_exists('proxy', $project->getAttribute('services', []))) { $status = $project->getAttribute('services', [])['proxy']; @@ -115,11 +115,11 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request $requestHeaders = $request->getHeaders(); - $project = $auth->skip(fn() => $dbForConsole->getDocument('projects', $projectId)); + $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); $dbForProject = $getProjectDB($project); - $function = $auth->skip(fn() => $dbForProject->getDocument('functions', $functionId)); + $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty() || !$function->getAttribute('enabled')) { throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND); @@ -134,7 +134,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } - $deployment = $auth->skip(fn() => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); + $deployment = $auth->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); if ($deployment->getAttribute('resourceId') !== $function->getId()) { throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); @@ -145,7 +145,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request } /** Check if build has completed */ - $build = $auth->skip(fn() => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); + $build = $auth->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); if ($build->isEmpty()) { throw new AppwriteException(AppwriteException::BUILD_NOT_FOUND); } @@ -310,7 +310,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request if ($function->getAttribute('logging')) { /** @var Document $execution */ - $execution = $auth->skip(fn() => $dbForProject->createDocument('executions', $execution)); + $execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution)); } } @@ -550,10 +550,10 @@ Http::init() $isLocalHost || $isIpAddress ? null : ( - $isConsoleProject && $isConsoleRootSession + $isConsoleProject && $isConsoleRootSession ? '.' . $selfDomain->getRegisterable() : '.' . $request->getHostname() - ) + ) ); /* diff --git a/app/http.php b/app/http.php index 98996691e8..fdee2e4d4e 100644 --- a/app/http.php +++ b/app/http.php @@ -34,7 +34,7 @@ $server = new Server('0.0.0.0', '80', [ 'http_compression_level' => 6, 'package_max_length' => $payloadSize, 'buffer_output_size' => $payloadSize, - 'buffer_output_size' => $payloadSize, + 'buffer_output_size' => $payloadSize, // Server // 'log_level' => 0, diff --git a/app/worker.php b/app/worker.php index 7a62931a49..39cb69fd6f 100644 --- a/app/worker.php +++ b/app/worker.php @@ -2,41 +2,22 @@ require_once __DIR__ . '/init2.php'; -use Appwrite\Event\Audit; -use Appwrite\Event\Build; -use Appwrite\Event\Certificate; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Delete; -use Appwrite\Event\Event; -use Appwrite\Event\Func; -use Appwrite\Event\Mail; -use Appwrite\Event\Messaging; -use Appwrite\Event\Migration; -use Appwrite\Event\Usage; use Appwrite\Event\UsageDump; use Appwrite\Platform\Appwrite; use Appwrite\Utopia\Queue\Connections; use Swoole\Runtime; -use Utopia\Cache\Adapter\None; -use Utopia\Cache\Adapter\Sharding; -use Utopia\Cache\Cache; use Utopia\CLI\Console; -use Utopia\Database\Adapter\MariaDB; -use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\DSN\DSN; use Utopia\DI\Dependency; use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Platform\Service; use Utopia\Queue\Connection; -use Utopia\Queue\Connection\Redis; use Utopia\Queue\Message; use Utopia\Queue\Worker; -use Utopia\Registry\Registry; use Utopia\Storage\Device\Local; use Utopia\System\System; @@ -56,7 +37,7 @@ $deviceForLocalFiles = new Dependency(); $register ->setName('register') - ->setCallback(fn() => $global); + ->setCallback(fn () => $global); $project ->setName('project') diff --git a/src/Appwrite/Platform/Tasks/QueueCount.php b/src/Appwrite/Platform/Tasks/QueueCount.php index 01fc7160c1..63f829073a 100644 --- a/src/Appwrite/Platform/Tasks/QueueCount.php +++ b/src/Appwrite/Platform/Tasks/QueueCount.php @@ -3,8 +3,8 @@ namespace Appwrite\Platform\Tasks; use Utopia\CLI\Console; -use Utopia\Http\Validator\WhiteList; use Utopia\Http\Validator\Text; +use Utopia\Http\Validator\WhiteList; use Utopia\Platform\Action; use Utopia\Queue\Client; use Utopia\Queue\Connection; diff --git a/src/Appwrite/Platform/Tasks/QueueRetry.php b/src/Appwrite/Platform/Tasks/QueueRetry.php index 634147ec9c..63f6c8e11e 100644 --- a/src/Appwrite/Platform/Tasks/QueueRetry.php +++ b/src/Appwrite/Platform/Tasks/QueueRetry.php @@ -4,7 +4,6 @@ namespace Appwrite\Platform\Tasks; use Utopia\CLI\Console; use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; use Utopia\Http\Validator\Wildcard; use Utopia\Platform\Action; use Utopia\Queue\Client; diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index 907d7d1a87..1877f878da 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -11,11 +11,8 @@ use Utopia\Database\Document; use Utopia\Database\Exception; use Utopia\Database\Query; use Utopia\Platform\Action; -use Utopia\Pools\Group; use Utopia\System\System; -use function Swoole\Coroutine\run; - abstract class ScheduleBase extends Action { protected const UPDATE_TIMER = 10; //seconds @@ -188,7 +185,7 @@ abstract class ScheduleBase extends Action Timer::tick( static::ENQUEUE_TIMER * 1000, - fn() => $this->enqueueResources($pools, $dbForConsole) + fn () => $this->enqueueResources($pools, $dbForConsole) ); $this->enqueueResources($pools, $dbForConsole); diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 3aad7d54cd..58e4218799 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -4,7 +4,6 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Messaging; use Utopia\Database\Database; -use Utopia\Pools\Group; use Utopia\Queue\Connection\Redis; class ScheduleMessages extends ScheduleBase diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 45df3e924f..2cb1a276b4 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -22,8 +22,8 @@ use Utopia\Database\Exception\Conflict; use Utopia\Database\Exception\Restricted; use Utopia\Database\Exception\Structure; use Utopia\Database\Query; -use Utopia\DSN\DSN; use Utopia\Database\Validator\Authorization as ValidatorAuthorization; +use Utopia\DSN\DSN; use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; diff --git a/tests/unit/Utopia/RequestTest.php b/tests/unit/Utopia/RequestTest.php index 0889c5ef6c..6124a7a0c1 100644 --- a/tests/unit/Utopia/RequestTest.php +++ b/tests/unit/Utopia/RequestTest.php @@ -5,9 +5,9 @@ namespace Tests\Unit\Utopia; use Appwrite\Utopia\Request; use PHPUnit\Framework\TestCase; use Swoole\Http\Request as SwooleRequest; -use Utopia\Http\Adapter\Swoole\Request as UtopiaSwooleRequest; use Tests\Unit\Utopia\Request\Filters\First; use Tests\Unit\Utopia\Request\Filters\Second; +use Utopia\Http\Adapter\Swoole\Request as UtopiaSwooleRequest; use Utopia\Http\Route; class RequestTest extends TestCase From 5ac5baca38bfd8f8742ed348a4f2cfc4e13f5662 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 6 Jun 2024 21:06:52 -0400 Subject: [PATCH 087/195] fixes: General refactors --- app/cli.php | 9 ------ app/controllers/api/databases.php | 1 + app/controllers/general.php | 7 ----- app/http.php | 1 - app/init.php | 47 ++++++++++++++++++++++++++++++- 5 files changed, 47 insertions(+), 18 deletions(-) diff --git a/app/cli.php b/app/cli.php index 7dd16f5c2f..035565aaeb 100644 --- a/app/cli.php +++ b/app/cli.php @@ -5,7 +5,6 @@ require_once __DIR__ . '/controllers/general.php'; use Appwrite\Event\Certificate; use Appwrite\Event\Delete; -use Appwrite\Event\Hamster; use Appwrite\Platform\Appwrite; use Swoole\Runtime; use Utopia\CLI\Adapters\Swoole as SwooleCLI; @@ -30,7 +29,6 @@ $auth = new Dependency(); $register = new Dependency(); $logError = new Dependency(); $queueForDeletes = new Dependency(); -$queueForHamster = new Dependency(); $queueForCertificates = new Dependency(); $register @@ -39,12 +37,6 @@ $register return $global; }); -$queueForHamster - ->setName('queueForHamster') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Hamster($queue); - }); $queueForDeletes ->setName('queueForDeletes') @@ -108,7 +100,6 @@ $auth $container->set($auth); $container->set($logError); $container->set($register); -$container->set($queueForHamster); $container->set($queueForDeletes); $container->set($queueForCertificates); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index a2b4ad6823..194066994d 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -25,6 +25,7 @@ use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Exception\Structure as StructureException; +use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; diff --git a/app/controllers/general.php b/app/controllers/general.php index c31ee413f6..1839777a2e 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -761,13 +761,6 @@ Http::error() } if ($logger && ($publish || $error->getCode() === 0)) { - try { - /** @var Utopia\Database\Document $user */ - $user = $utopia->getResource('user'); - } catch (\Throwable) { - // All good, user is optional information for logger - } - if (isset($user) && !$user->isEmpty()) { $log->setUser(new User($user->getId())); } diff --git a/app/http.php b/app/http.php index fdee2e4d4e..546338555c 100644 --- a/app/http.php +++ b/app/http.php @@ -34,7 +34,6 @@ $server = new Server('0.0.0.0', '80', [ 'http_compression_level' => 6, 'package_max_length' => $payloadSize, 'buffer_output_size' => $payloadSize, - 'buffer_output_size' => $payloadSize, // Server // 'log_level' => 0, diff --git a/app/init.php b/app/init.php index 8d1290d832..5e11f96e5d 100644 --- a/app/init.php +++ b/app/init.php @@ -1,5 +1,4 @@ new Log()); +// @phpstan-ignore Http::setResource('logger', function ($register) { return $register->get('logger'); }, ['register']); +// @phpstan-ignore Http::setResource('hooks', function ($register) { return $register->get('hooks'); }, ['register']); +// @phpstan-ignore Http::setResource('register', fn () => $register); +// @phpstan-ignore Http::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); +// @phpstan-ignore Http::setResource('localeCodes', function () { return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); }); +// @phpstan-ignore Http::setResource('connections', function () { return new Connections(); }); // Queues +// @phpstan-ignore Http::setResource('queue', function (Group $pools, Connections $connections) { $connection = $pools->get('queue')->pop(); $connections->add($connection); return $connection->getResource(); }, ['pools', 'connections']); +// @phpstan-ignore Http::setResource('queueForMessaging', function (Connection $queue) { return new Messaging($queue); }, ['queue']); +// @phpstan-ignore Http::setResource('queueForMails', function (Connection $queue) { return new Mail($queue); }, ['queue']); +// @phpstan-ignore Http::setResource('queueForBuilds', function (Connection $queue) { return new Build($queue); }, ['queue']); +// @phpstan-ignore Http::setResource('queueForDatabase', function (Connection $queue) { return new EventDatabase($queue); }, ['queue']); +// @phpstan-ignore Http::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); +// @phpstan-ignore Http::setResource('queueForEvents', function (Connection $queue) { return new Event($queue); }, ['queue']); +// @phpstan-ignore Http::setResource('queueForAudits', function (Connection $queue) { return new Audit($queue); }, ['queue']); +// @phpstan-ignore Http::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); +// @phpstan-ignore Http::setResource('queueForUsage', function (Connection $queue) { return new Usage($queue); }, ['queue']); +// @phpstan-ignore Http::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); +// @phpstan-ignore Http::setResource('queueForMigrations', function (Connection $queue) { return new Migration($queue); }, ['queue']); +// @phpstan-ignore Http::setResource('clients', function ($request, $console, $project) { $console->setAttribute('platforms', [ // Always allow current host '$collection' => ID::custom('platforms'), @@ -1132,6 +1151,7 @@ Http::setResource('clients', function ($request, $console, $project) { return \array_unique($clients); }, ['request', 'console', 'project']); +// @phpstan-ignore Http::setResource('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $auth) { $auth->setDefaultStatus(true); @@ -1233,6 +1253,7 @@ Http::setResource('user', function (string $mode, Document $project, Document $c return $user; }, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole', 'auth']); +// @phpstan-ignore Http::setResource('project', function (Database $dbForConsole, Request $request, Document $console, Authorization $auth) { $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); @@ -1246,6 +1267,7 @@ Http::setResource('project', function (Database $dbForConsole, Request $request, return $project; }, ['dbForConsole', 'request', 'console', 'auth']); +// @phpstan-ignore Http::setResource('session', function (Document $user) { if ($user->isEmpty()) { return; @@ -1267,6 +1289,7 @@ Http::setResource('session', function (Document $user) { return; }, ['user']); +// @phpstan-ignore Http::setResource('console', function () { return new Document([ '$id' => ID::custom('console'), @@ -1307,6 +1330,7 @@ Http::setResource('console', function () { ]); }, []); +// @phpstan-ignore Http::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project, Authorization $auth, Connections $connections) { if ($project->isEmpty() || $project->getId() === 'console') { return $dbForConsole; @@ -1353,6 +1377,7 @@ Http::setResource('dbForProject', function (Group $pools, Database $dbForConsole return $database; }, ['pools', 'dbForConsole', 'cache', 'project', 'auth', 'connections']); +// @phpstan-ignore Http::setResource('dbForConsole', function (Group $pools, Cache $cache, Authorization $auth, Connections $connections) { $connection = $pools->get('console')->pop(); $connections->add($connection); @@ -1370,6 +1395,7 @@ Http::setResource('dbForConsole', function (Group $pools, Cache $cache, Authoriz return $database; }, ['pools', 'cache', 'auth', 'connections']); +// @phpstan-ignore Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth, Connections $connections) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools @@ -1425,6 +1451,7 @@ Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole return $getProjectDB; }, ['pools', 'dbForConsole', 'cache', 'auth', 'connections']); +// @phpstan-ignore Http::setResource('cache', function (Group $pools, Connections $connections) { $list = Config::getParam('pools-cache', []); $adapters = []; @@ -1438,18 +1465,22 @@ Http::setResource('cache', function (Group $pools, Connections $connections) { return new Cache(new Sharding($adapters)); }, ['pools', 'connections']); +// @phpstan-ignore Http::setResource('deviceForLocal', function () { return new Local(); }); +// @phpstan-ignore Http::setResource('deviceForFiles', function ($project) { return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); }, ['project']); +// @phpstan-ignore Http::setResource('deviceForFunctions', function ($project) { return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); }, ['project']); +// @phpstan-ignore Http::setResource('deviceForBuilds', function ($project) { return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); }, ['project']); @@ -1540,6 +1571,7 @@ function getDevice($root): Device } } +// @phpstan-ignore Http::setResource('mode', function ($request) { /** @var Appwrite\Utopia\Request $request */ @@ -1551,17 +1583,20 @@ Http::setResource('mode', function ($request) { return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); }, ['request']); +// @phpstan-ignore Http::setResource('geodb', function ($register) { /** @var Utopia\Registry\Registry $register */ return $register->get('geodb'); }, ['register']); +// @phpstan-ignore Http::setResource('passwordsDictionary', function ($register) { /** @var Utopia\Registry\Registry $register */ return $register->get('passwordsDictionary'); }, ['register']); +// @phpstan-ignore Http::setResource('servers', function () { $platforms = Config::getParam('platforms'); $server = $platforms[APP_PLATFORM_SERVER]; @@ -1573,10 +1608,12 @@ Http::setResource('servers', function () { return $languages; }); +// @phpstan-ignore Http::setResource('promiseAdapter', function ($register) { return $register->get('promiseAdapter'); }, ['register']); +// @phpstan-ignore Http::setResource('schema', function (Http $utopia, Database $dbForProject, Authorization $auth) { $complexity = function (int $complexity, array $args) { @@ -1663,28 +1700,33 @@ Http::setResource('schema', function (Http $utopia, Database $dbForProject, Auth ); }, ['utopia', 'dbForProject', 'auth']); +// @phpstan-ignore Http::setResource('contributors', function () { $path = 'app/config/contributors.json'; $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; return $list; }); +// @phpstan-ignore Http::setResource('employees', function () { $path = 'app/config/employees.json'; $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; return $list; }); +// @phpstan-ignore Http::setResource('heroes', function () { $path = 'app/config/heroes.json'; $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; return $list; }); +// @phpstan-ignore Http::setResource('gitHub', function (Cache $cache) { return new VcsGitHub($cache); }, ['cache']); +// @phpstan-ignore Http::setResource('requestTimestamp', function ($request) { //TODO: Move this to the Request class itself $timestampHeader = $request->getHeader('x-appwrite-timestamp'); @@ -1699,12 +1741,15 @@ Http::setResource('requestTimestamp', function ($request) { return $requestTimestamp; }, ['request']); +// @phpstan-ignore Http::setResource('plan', function (array $plan = []) { return []; }); +// @phpstan-ignore Http::setResource('auth', fn () => new Authorization()); +// @phpstan-ignore Http::setResource('pools', function ($register) { return $register->get('pools'); }, ['pools']); From 321f62113251cc6efab3f58a16b155d953bb100d Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 7 Jun 2024 12:13:12 -0400 Subject: [PATCH 088/195] test: Functions tests --- app/cli.php | 12 +++--------- app/init2.php | 1 - src/Appwrite/Platform/Tasks/Migrate.php | 4 ++-- .../Services/Functions/FunctionsCustomClientTest.php | 3 ++- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/app/cli.php b/app/cli.php index 035565aaeb..60bf81b262 100644 --- a/app/cli.php +++ b/app/cli.php @@ -25,7 +25,6 @@ Runtime::enableCoroutine(SWOOLE_HOOK_ALL); * @var Registry $global * @var Container $container */ -$auth = new Dependency(); $register = new Dependency(); $logError = new Dependency(); $queueForDeletes = new Dependency(); @@ -93,11 +92,6 @@ $logError }); -$auth - ->setName('auth') - ->setCallback(fn () => new Authorization()); - -$container->set($auth); $container->set($logError); $container->set($register); $container->set($queueForDeletes); @@ -110,9 +104,9 @@ $cli = $platform->getCli(); $cli ->init() - ->inject('auth') - ->action(function (Authorization $auth) { - $auth->disable(); + ->inject('authorization') + ->action(function (Authorization $authorization) { + $authorization->disable(); }); $cli diff --git a/app/init2.php b/app/init2.php index 74b5bd2c83..c41ab7e203 100644 --- a/app/init2.php +++ b/app/init2.php @@ -3,7 +3,6 @@ require_once __DIR__ . '/../vendor/autoload.php'; use PHPMailer\PHPMailer\PHPMailer; -use Redis; use Utopia\Cache\Adapter\Redis as CacheRedis; use Utopia\Cache\Adapter\Sharding; use Utopia\VCS\Adapter\Git\GitHub; diff --git a/src/Appwrite/Platform/Tasks/Migrate.php b/src/Appwrite/Platform/Tasks/Migrate.php index f5a4d66994..e08985e1ae 100644 --- a/src/Appwrite/Platform/Tasks/Migrate.php +++ b/src/Appwrite/Platform/Tasks/Migrate.php @@ -32,8 +32,8 @@ class Migrate extends Action ->inject('dbForConsole') ->inject('getProjectDB') ->inject('register') - ->inject('auth') - ->callback(fn ($version, $cache, $dbForConsole, $getProjectDB, Registry $register, Authorization $auth) => $this->action($version, $cache, $dbForConsole, $getProjectDB, $register, $auth)); + ->inject('authorization') + ->callback(fn ($version, $cache, $dbForConsole, $getProjectDB, Registry $register, Authorization $authorization) => $this->action($version, $cache, $dbForConsole, $getProjectDB, $register, $authorization)); } private function clearProjectsCache(Cache $cache, Document $project) diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 119c1a2223..1008c83ed5 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -164,7 +164,8 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(202, $execution['headers']['status-code']); // Wait for the first scheduled execution to be created - sleep(65); + // Longer waiting time to cover all use-cases + sleep(119); $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions', [ 'content-type' => 'application/json', From 38cabc8afcd007a37c8e8bb0b68350db2dc6c2ac Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 7 Jun 2024 12:54:07 -0400 Subject: [PATCH 089/195] test: Databases tests --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 83c2b40509..15ecceac0c 100644 --- a/composer.lock +++ b/composer.lock @@ -2672,12 +2672,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/servers.git", - "reference": "4ce97c42158c20c7dcf0cdcdcf79080b879a1ee7" + "reference": "02bb5cfff6d7a39b1da2271d25eab51b34420bd0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/servers/zipball/4ce97c42158c20c7dcf0cdcdcf79080b879a1ee7", - "reference": "4ce97c42158c20c7dcf0cdcdcf79080b879a1ee7", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/02bb5cfff6d7a39b1da2271d25eab51b34420bd0", + "reference": "02bb5cfff6d7a39b1da2271d25eab51b34420bd0", "shasum": "" }, "require": { @@ -2735,7 +2735,7 @@ "source": "https://github.com/utopia-php/servers/tree/dev", "issues": "https://github.com/utopia-php/servers/issues" }, - "time": "2024-06-06T14:41:32+00:00" + "time": "2024-06-07T16:48:33+00:00" }, { "name": "utopia-php/storage", From dc47007566a8d659d2bb15090413b1515d4d2b74 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 7 Jun 2024 15:36:45 -0400 Subject: [PATCH 090/195] test: Realtime tests --- app/realtime.php | 21 ++++++++++++--------- composer.lock | 43 +++++++++++++++++++++---------------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/app/realtime.php b/app/realtime.php index 53764f9c8c..a30a094115 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -5,10 +5,10 @@ use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Network\Validator\Origin; -use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Swoole\Http\Request as SwooleRequest; +use Swoole\Http\Response as SwooleHttpResponse; use Swoole\Http\Response as SwooleResponse; use Swoole\Runtime; use Swoole\Table; @@ -16,7 +16,6 @@ use Swoole\Timer; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; use Utopia\CLI\Console; -use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; @@ -25,6 +24,7 @@ use Utopia\Database\Query; use Utopia\DI\Container; use Utopia\DI\Dependency; use Utopia\Http\Adapter\Swoole\Request as UtopiaRequest; +use Utopia\Http\Adapter\Swoole\Response as HttpResponse; use Utopia\Http\Adapter\Swoole\Response as UtopiaResponse; use Utopia\Http\Http; use Utopia\Logger\Log; @@ -362,8 +362,12 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::info("Connection open (user: {$connection})"); try { + $dbForConsole = $container->get('dbForConsole'); + /** @var Document $project */ - $project = $container->get('project'); + $project = $container->refresh('project')->get('project'); + + $container->refresh('dbForProject'); /* * Project Check @@ -372,7 +376,6 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, throw new Exception(Exception::REALTIME_POLICY_VIOLATION, 'Missing or unknown project ID'); } - if ( array_key_exists('realtime', $project->getAttribute('apis', [])) && !$project->getAttribute('apis', [])['realtime'] @@ -382,11 +385,10 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, } $dbForProject = $container->get('getProjectDB')($project); - $console = $container->get('console'); /** @var Document $console */ - $user = $container->get('user'); + $console = $container->get('console'); /** @var Document $user */ - + $user = $container->refresh('user')->get('user'); /* * Abuse Check * @@ -477,12 +479,13 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $server->onMessage(function (int $connection, string $message) use ($server, $container, $realtime, $containerId) { try { - $response = new Response(new SwooleResponse()); + $response = new Response(new HttpResponse(new SwooleHttpResponse())); $projectId = $realtime->connections[$connection]['projectId']; $database = $container->get('dbForConsole'); + $authorization = $container->get('authorization'); if ($projectId !== 'console') { - $authorization = $container->get('authorization'); + $project = $authorization->skip(fn () => $database->getDocument('projects', $projectId)); $database = $container->get('getProjectDB')($project); } else { diff --git a/composer.lock b/composer.lock index 15ecceac0c..939de6426a 100644 --- a/composer.lock +++ b/composer.lock @@ -1626,17 +1626,17 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "caea27c18a7d701a63e985a7aad1bfa7827ab469" + "reference": "bedbca08f451dc96f0321014e805a1f46f76f6b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/caea27c18a7d701a63e985a7aad1bfa7827ab469", - "reference": "caea27c18a7d701a63e985a7aad1bfa7827ab469", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/bedbca08f451dc96f0321014e805a1f46f76f6b9", + "reference": "bedbca08f451dc96f0321014e805a1f46f76f6b9", "shasum": "" }, "require": { "php": ">=7.4", - "utopia-php/di": "dev-main", + "utopia-php/di": "dev-feat-framework-v2", "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99" }, "require-dev": { @@ -1669,7 +1669,7 @@ "issues": "https://github.com/utopia-php/cli/issues", "source": "https://github.com/utopia-php/cli/tree/dev-coroutines" }, - "time": "2024-06-05T12:46:38+00:00" + "time": "2024-06-07T18:51:16+00:00" }, { "name": "utopia-php/config", @@ -1780,16 +1780,16 @@ }, { "name": "utopia-php/di", - "version": "dev-main", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/di.git", - "reference": "a0c8be65c19570c80e904d58f54bdd901d1d5d9c" + "reference": "8edd2c86df5db8383b197b6c5b8e35774ff8e4a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/di/zipball/a0c8be65c19570c80e904d58f54bdd901d1d5d9c", - "reference": "a0c8be65c19570c80e904d58f54bdd901d1d5d9c", + "url": "https://api.github.com/repos/utopia-php/di/zipball/8edd2c86df5db8383b197b6c5b8e35774ff8e4a8", + "reference": "8edd2c86df5db8383b197b6c5b8e35774ff8e4a8", "shasum": "" }, "require": { @@ -1802,7 +1802,6 @@ "phpunit/phpunit": "^9.5.25", "swoole/ide-helper": "4.8.3" }, - "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -1835,10 +1834,10 @@ "upf" ], "support": { - "source": "https://github.com/utopia-php/di/tree/main", + "source": "https://github.com/utopia-php/di/tree/feat-framework-v2", "issues": "https://github.com/utopia-php/di/issues" }, - "time": "2024-04-22T21:22:44+00:00" + "time": "2024-06-07T18:49:13+00:00" }, { "name": "utopia-php/domains", @@ -2557,18 +2556,18 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "2133eb6da85156ff4abf0d0940715fa3975424f2" + "reference": "cad5651b38f0f69e20e805424d0c29818c15c174" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/2133eb6da85156ff4abf0d0940715fa3975424f2", - "reference": "2133eb6da85156ff4abf0d0940715fa3975424f2", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/cad5651b38f0f69e20e805424d0c29818c15c174", + "reference": "cad5651b38f0f69e20e805424d0c29818c15c174", "shasum": "" }, "require": { "php": ">=8.0", "utopia-php/cli": "0.17.*", - "utopia-php/di": "dev-main", + "utopia-php/di": "dev-feat-framework-v2", "utopia-php/servers": "dev-dev" }, "require-dev": { @@ -2612,7 +2611,7 @@ "issues": "https://github.com/utopia-php/queue/issues", "source": "https://github.com/utopia-php/queue/tree/feat-coroutine-and-di" }, - "time": "2024-04-22T21:24:21+00:00" + "time": "2024-06-07T18:50:32+00:00" }, { "name": "utopia-php/registry", @@ -2672,17 +2671,17 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/servers.git", - "reference": "02bb5cfff6d7a39b1da2271d25eab51b34420bd0" + "reference": "4565c1c111f6da6b18bc0f00f350377a1e691e48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/servers/zipball/02bb5cfff6d7a39b1da2271d25eab51b34420bd0", - "reference": "02bb5cfff6d7a39b1da2271d25eab51b34420bd0", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/4565c1c111f6da6b18bc0f00f350377a1e691e48", + "reference": "4565c1c111f6da6b18bc0f00f350377a1e691e48", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/di": "dev-main" + "utopia-php/di": "dev-feat-framework-v2" }, "require-dev": { "laravel/pint": "^0.2.3", @@ -2735,7 +2734,7 @@ "source": "https://github.com/utopia-php/servers/tree/dev", "issues": "https://github.com/utopia-php/servers/issues" }, - "time": "2024-06-07T16:48:33+00:00" + "time": "2024-06-07T18:49:59+00:00" }, { "name": "utopia-php/storage", From c1ded34c12f3ea606a33bf1f099954292c4aeeb9 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 10 Jun 2024 07:16:07 -0400 Subject: [PATCH 091/195] test: Functions. reverting time and adding restart policy --- docker-compose.yml | 3 +++ tests/e2e/Services/Functions/FunctionsCustomClientTest.php | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7a461f4ecc..69c2832b46 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -654,6 +654,7 @@ services: entrypoint: maintenance <<: *x-logging container_name: appwrite-task-maintenance + restart: unless-stopped image: appwrite-dev networks: - appwrite @@ -753,6 +754,7 @@ services: entrypoint: schedule-functions <<: *x-logging container_name: appwrite-task-scheduler-functions + restart: unless-stopped image: appwrite-dev networks: - appwrite @@ -780,6 +782,7 @@ services: entrypoint: schedule-messages <<: *x-logging container_name: appwrite-task-scheduler-messages + restart: unless-stopped image: appwrite-dev networks: - appwrite diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 1008c83ed5..119c1a2223 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -164,8 +164,7 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(202, $execution['headers']['status-code']); // Wait for the first scheduled execution to be created - // Longer waiting time to cover all use-cases - sleep(119); + sleep(65); $executions = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions', [ 'content-type' => 'application/json', From 300c0f0ee28b934a4ee4358dadc0488a87dbbed4 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 10 Jun 2024 07:49:12 -0400 Subject: [PATCH 092/195] test: Messaging, refactor to use own auth --- app/controllers/api/messaging.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index f1fd157f79..e506b7ad20 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -2246,10 +2246,8 @@ Http::post('/v1/messaging/topics/:topicId/subscribers') throw new Exception(Exception::TOPIC_NOT_FOUND); } - $validator = new Authorization(); - - if (!$validator->isValid(new Input('subscribe', $topic->getAttribute('subscribe')))) { - throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription()); + if (!$authorization->isValid(new Input('subscribe', $topic->getAttribute('subscribe')))) { + throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); } $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); From e75645334ac2040ca6c8ce72c13c442b07533381 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 10 Jun 2024 12:56:29 -0400 Subject: [PATCH 093/195] test: GraphQL wip --- app/controllers/api/graphql.php | 5 +- app/init2.php | 71 ++++++++------ composer.lock | 20 ++-- src/Appwrite/GraphQL/Resolvers.php | 136 ++++++++++++-------------- src/Appwrite/GraphQL/Schema.php | 101 ++++++++++--------- src/Appwrite/GraphQL/Types/Mapper.php | 24 +++-- 6 files changed, 191 insertions(+), 166 deletions(-) diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index 6be785f5a0..a9bd5dd1eb 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -310,6 +310,7 @@ function processResult($result, $debugFlags): array Http::shutdown() ->groups(['schema']) ->inject('project') - ->action(function (Document $project) { - Schema::setDirty($project->getId()); + ->inject('schemaVariable') + ->action(function (Document $project, Schema $schemaVariable) { + $schemaVariable->setDirty($project->getId()); }); diff --git a/app/init2.php b/app/init2.php index c41ab7e203..40060cf5f2 100644 --- a/app/init2.php +++ b/app/init2.php @@ -206,20 +206,20 @@ $global->set( 'pools', (function () { $fallbackForDB = 'db_main=' . URL::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', ''), - ]); + '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=' . URL::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', ''), - ]); + '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' => [ @@ -415,6 +415,7 @@ $deviceForFiles = new Dependency(); $queueForEvents = new Dependency(); $queueForAudits = new Dependency(); $promiseAdapter = new Dependency(); +$schemaVariable = new Dependency(); $deviceForBuilds = new Dependency(); $queueForDeletes = new Dependency(); $requestTimestamp = new Dependency(); @@ -429,7 +430,7 @@ $queueForCertificates = new Dependency(); $plan ->setName('plan') - ->setCallback(fn () => []); + ->setCallback(fn() => []); $mode ->setName('mode') @@ -638,7 +639,7 @@ $project return $console; } - $project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $project = $authorization->skip(fn() => $dbForConsole->getDocument('projects', $projectId)); return $project; }); @@ -804,11 +805,11 @@ $connections $locale ->setName('locale') - ->setCallback(fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); + ->setCallback(fn() => new Locale(System::getEnv('_APP_LOCALE', 'en'))); $localeCodes ->setName('localeCodes') - ->setCallback(fn () => array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []))); + ->setCallback(fn() => array_map(fn($locale) => $locale['code'], Config::getParam('locale-codes', []))); $queue ->setName('queue') @@ -960,10 +961,10 @@ $clients * + Filter for duplicated entries */ $clientsConsole = \array_map( - fn ($node) => $node['hostname'], + fn($node) => $node['hostname'], \array_filter( $console->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && !empty($node['hostname'])) + fn($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ); @@ -971,10 +972,10 @@ $clients \array_merge( $clientsConsole, \array_map( - fn ($node) => $node['hostname'], + fn($node) => $node['hostname'], \array_filter( $project->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) + fn($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ) ) @@ -1117,17 +1118,24 @@ $getProjectDB $promiseAdapter ->setName('promiseAdapter') - ->inject('register') - ->setCallback(function ($register) { - return $register->get('promiseAdapter'); + ->setCallback(function () use ($global) { + return $global->get('promiseAdapter'); }); +$schemaVariable + ->setName('schemaVariable') + ->setCallback(fn()=> new Schema()); + $schema ->setName('schema') - ->inject('utopia') + ->inject('http') + ->inject('context') + ->inject('request') + ->inject('response') ->inject('dbForProject') ->inject('authorization') - ->setCallback(function (Http $utopia, Database $dbForProject, Authorization $authorization) { + ->inject('schemaVariable') + ->setCallback(function (Http $http, Container $context, Request $request, Response $response, Database $dbForProject, Authorization $authorization, $schemaVariable) { $complexity = function (int $complexity, array $args) { $queries = Query::parseQueries($args['queries'] ?? []); $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; @@ -1137,7 +1145,7 @@ $schema }; $attributes = function (int $limit, int $offset) use ($dbForProject, $authorization) { - $attrs = $authorization->skip(fn () => $dbForProject->find('attributes', [ + $attrs = $authorization->skip(fn() => $dbForProject->find('attributes', [ Query::limit($limit), Query::offset($offset), ])); @@ -1201,14 +1209,18 @@ $schema }, ]; - return Schema::build( - $utopia, + return $schemaVariable->build( + $http, + $request, + $response, + $context, $complexity, $attributes, $urls, $params, ); }); + $container->set($log); $container->set($mode); $container->set($user); @@ -1237,6 +1249,7 @@ $container->set($getProjectDB); $container->set($authorization); $container->set($queueForUsage); $container->set($queueForMails); +$container->set($schemaVariable); $container->set($queueForBuilds); $container->set($queueForEvents); $container->set($queueForAudits); diff --git a/composer.lock b/composer.lock index 939de6426a..6b575bd178 100644 --- a/composer.lock +++ b/composer.lock @@ -1991,12 +1991,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "cc3341f13d6b90f3104f197acb9136abe2fc708a" + "reference": "d64cfc3868057d539c7fa27c672ac938eedc3656" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/cc3341f13d6b90f3104f197acb9136abe2fc708a", - "reference": "cc3341f13d6b90f3104f197acb9136abe2fc708a", + "url": "https://api.github.com/repos/utopia-php/http/zipball/d64cfc3868057d539c7fa27c672ac938eedc3656", + "reference": "d64cfc3868057d539c7fa27c672ac938eedc3656", "shasum": "" }, "require": { @@ -2033,7 +2033,7 @@ "issues": "https://github.com/utopia-php/http/issues", "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-04-22T21:25:06+00:00" + "time": "2024-06-10T12:37:51+00:00" }, { "name": "utopia-php/image", @@ -3124,16 +3124,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.38.6", + "version": "0.38.7", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "d7016d6d72545e84709892faca972eb4bf5bd699" + "reference": "0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d7016d6d72545e84709892faca972eb4bf5bd699", - "reference": "d7016d6d72545e84709892faca972eb4bf5bd699", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a", + "reference": "0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a", "shasum": "" }, "require": { @@ -3169,9 +3169,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.38.6" + "source": "https://github.com/appwrite/sdk-generator/tree/0.38.7" }, - "time": "2024-05-20T18:00:16+00:00" + "time": "2024-06-10T00:23:02+00:00" }, { "name": "doctrine/deprecations", diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index cd62cc3776..ab8f8c23b6 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -6,7 +6,10 @@ use Appwrite\GraphQL\Exception as GQLException; use Appwrite\Promises\Swoole; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; +use Utopia\DI\Container; use Utopia\Exception; +use Utopia\Http\Response as UtopiaHttpResponse; +use Utopia\Http\Request as UtopiaHttpRequest; use Utopia\Http\Http; use Utopia\Http\Route; use Utopia\System\System; @@ -16,24 +19,19 @@ class Resolvers /** * Create a resolver for a given API {@see Route}. * - * @param Http $utopia + * @param Http $http * @param ?Route $route * @return callable */ public static function api( - Http $utopia, + Http $http, ?Route $route, + UtopiaHttpRequest $request, + UtopiaHttpResponse $response, + Container $container, ): callable { - return static fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($utopia, $route, $args, $context, $info) { - /** @var Http $utopia */ - /** @var Response $response */ - /** @var Request $request */ - - $utopia = $utopia->getResource('utopia:graphql', true); - $request = $utopia->getResource('request', true); - $response = $utopia->getResource('response', true); - + return fn($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($http, $route, $args, $context, $container, $info, $request, $response) { $path = $route->getPath(); foreach ($args as $key => $value) { if (\str_contains($path, '/:' . $key)) { @@ -53,7 +51,7 @@ class Resolvers break; } - self::resolve($utopia, $request, $response, $resolve, $reject); + self::resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -61,20 +59,20 @@ class Resolvers /** * Create a resolver for a document in a specified database and collection with a specific method type. * - * @param Http $utopia + * @param Http $http * @param string $databaseId * @param string $collectionId * @param string $methodType * @return callable */ public static function document( - Http $utopia, + Http $http, string $databaseId, string $collectionId, string $methodType, ): callable { return [self::class, 'document' . \ucfirst($methodType)]( - $utopia, + $http, $databaseId, $collectionId ); @@ -83,28 +81,27 @@ class Resolvers /** * Create a resolver for getting a document in a specified database and collection. * - * @param Http $utopia + * @param Http $http * @param string $databaseId * @param string $collectionId * @param callable $url * @return callable */ public static function documentGet( - Http $utopia, + Http $http, string $databaseId, string $collectionId, callable $url, + UtopiaHttpRequest $request, + UtopiaHttpResponse $response, + Container $container, ): callable { - return static fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $type, $args) { - $utopia = $utopia->getResource('utopia:graphql', true); - $request = $utopia->getResource('request', true); - $response = $utopia->getResource('response', true); - + return static fn($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response) { $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); - self::resolve($utopia, $request, $response, $resolve, $reject); + self::resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -112,7 +109,7 @@ class Resolvers /** * Create a resolver for listing documents in a specified database and collection. * - * @param Http $utopia + * @param Http $http * @param string $databaseId * @param string $collectionId * @param callable $url @@ -120,18 +117,17 @@ class Resolvers * @return callable */ public static function documentList( - Http $utopia, + Http $http, string $databaseId, string $collectionId, callable $url, callable $params, + UtopiaHttpRequest $request, + UtopiaHttpResponse $response, + Container $container, ): callable { - return static fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $type, $args) { - $utopia = $utopia->getResource('utopia:graphql', true); - $request = $utopia->getResource('request', true); - $response = $utopia->getResource('response', true); - + return static fn($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response) { $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); $request->setQuery($params($databaseId, $collectionId, $args)); @@ -140,7 +136,7 @@ class Resolvers return $payload['documents']; }; - self::resolve($utopia, $request, $response, $resolve, $reject, $beforeResolve); + self::resolve($http, $request, $response, $container,$resolve, $reject, $beforeResolve); } ); } @@ -148,7 +144,7 @@ class Resolvers /** * Create a resolver for creating a document in a specified database and collection. * - * @param Http $utopia + * @param Http $http * @param string $databaseId * @param string $collectionId * @param callable $url @@ -156,23 +152,22 @@ class Resolvers * @return callable */ public static function documentCreate( - Http $utopia, + Http $http, string $databaseId, string $collectionId, callable $url, callable $params, + UtopiaHttpRequest $request, + UtopiaHttpResponse $response, + Container $container, ): callable { - return static fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $type, $args) { - $utopia = $utopia->getResource('utopia:graphql', true); - $request = $utopia->getResource('request', true); - $response = $utopia->getResource('response', true); - + return static fn($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container,$request, $response) { $request->setMethod('POST'); $request->setURI($url($databaseId, $collectionId, $args)); $request->setPayload($params($databaseId, $collectionId, $args)); - self::resolve($utopia, $request, $response, $resolve, $reject); + self::resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -180,7 +175,7 @@ class Resolvers /** * Create a resolver for updating a document in a specified database and collection. * - * @param Http $utopia + * @param Http $http * @param string $databaseId * @param string $collectionId * @param callable $url @@ -188,23 +183,22 @@ class Resolvers * @return callable */ public static function documentUpdate( - Http $utopia, + Http $http, string $databaseId, string $collectionId, callable $url, callable $params, + UtopiaHttpRequest $request, + UtopiaHttpResponse $response, + Container $container, ): callable { - return static fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $type, $args) { - $utopia = $utopia->getResource('utopia:graphql', true); - $request = $utopia->getResource('request', true); - $response = $utopia->getResource('response', true); - + return static fn($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container,$request, $response) { $request->setMethod('PATCH'); $request->setURI($url($databaseId, $collectionId, $args)); $request->setPayload($params($databaseId, $collectionId, $args)); - self::resolve($utopia, $request, $response, $resolve, $reject); + self::resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -212,34 +206,33 @@ class Resolvers /** * Create a resolver for deleting a document in a specified database and collection. * - * @param Http $utopia + * @param Http $http * @param string $databaseId * @param string $collectionId * @param callable $url * @return callable */ public static function documentDelete( - Http $utopia, + Http $http, string $databaseId, string $collectionId, callable $url, + UtopiaHttpRequest $request, + UtopiaHttpResponse $response, + Container $container, ): callable { - return static fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $type, $args) { - $utopia = $utopia->getResource('utopia:graphql', true); - $request = $utopia->getResource('request', true); - $response = $utopia->getResource('response', true); - + return static fn($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container,$request, $response) { $request->setMethod('DELETE'); $request->setURI($url($databaseId, $collectionId, $args)); - self::resolve($utopia, $request, $response, $resolve, $reject); + self::resolve($http, $request, $response, $container, $resolve, $reject); } ); } /** - * @param Http $utopia + * @param Http $http * @param Request $request * @param Response $response * @param callable $resolve @@ -250,9 +243,10 @@ class Resolvers * @throws Exception */ private static function resolve( - Http $utopia, + Http $http, Request $request, Response $response, + Container $context, callable $resolve, callable $reject, ?callable $beforeResolve = null, @@ -263,14 +257,12 @@ class Resolvers $request->removeHeader('content-type'); } - $request = clone $request; - $utopia->setResource('request', static fn () => $request); $response->setContentType(Response::CONTENT_TYPE_NULL); try { - $route = $utopia->match($request, fresh: true); + $route = $http->match($request); - $utopia->execute($route, $request, 'xx'); + $http->execute($route, $request, $context); } catch (\Throwable $e) { if ($beforeReject) { $e = $beforeReject($e); @@ -285,10 +277,12 @@ class Resolvers if ($beforeReject) { $payload = $beforeReject($payload); } - $reject(new GQLException( - message: $payload['message'], - code: $response->getStatusCode() - )); + $reject( + new GQLException( + message: $payload['message'], + code: $response->getStatusCode() + ) + ); return; } diff --git a/src/Appwrite/GraphQL/Schema.php b/src/Appwrite/GraphQL/Schema.php index 887ea621dd..bf5354f891 100644 --- a/src/Appwrite/GraphQL/Schema.php +++ b/src/Appwrite/GraphQL/Schema.php @@ -6,65 +6,74 @@ use Appwrite\GraphQL\Types\Mapper; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; use GraphQL\Type\Schema as GQLSchema; +use Appwrite\Utopia\Response; +use Utopia\DI\Container; +use Utopia\Http\Response as UtopiaHttpResponse; +use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse; use Utopia\Exception; use Utopia\Http\Http; use Utopia\Http\Route; +use Utopia\Http\Request; class Schema { - protected static ?GQLSchema $schema = null; - protected static array $dirty = []; + protected ?GQLSchema $schema = null; + protected array $dirty = []; /** * - * @param Http $utopia - * @param callable $complexity Function to calculate complexity - * @param callable $attributes Function to get attributes - * @param array $urls Array of functions to get urls for specific method types - * @param array $params Array of functions to build parameters for specific method types + * @param Http $http + * @param callable $complexity Function to calculate complexity + * @param callable $attributes Function to get attributes + * @param array $urls Array of functions to get urls for specific method types + * @param array $params Array of functions to build parameters for specific method types * @return GQLSchema * @throws Exception */ - public static function build( - Http $utopia, + public function build( + Http $http, + Request $request, + UtopiaHttpResponse $response, + Container $container, callable $complexity, callable $attributes, array $urls, array $params, ): GQLSchema { - Http::setResource('utopia:graphql', static function () use ($utopia) { - return $utopia; - }); - - if (!empty(self::$schema)) { - return self::$schema; + if (!empty($this->schema)) { + return $this->schema; } - $api = static::api( - $utopia, + $api = $this->api( + $http, + $request, + $response, + $container, $complexity ); - //$collections = static::collections( - // $utopia, - // $complexity, - // $attributes, - // $urls, - // $params, - //); +// $collections = $this->collections( +// $http, +// $complexity, +// $request, +// $response, +// $attributes, +// $urls, +// $params, +// ); $queries = \array_merge_recursive( $api['query'], - //$collections['query'] + //$collections['query'] ); $mutations = \array_merge_recursive( $api['mutation'], - //$collections['mutation'] + //$collections['mutation'] ); \ksort($queries); \ksort($mutations); - return static::$schema = new GQLSchema([ + return $this->schema = new GQLSchema([ 'query' => new ObjectType([ 'name' => 'Query', 'fields' => $queries @@ -80,21 +89,23 @@ class Schema * This function iterates all API routes and builds a GraphQL * schema defining types and resolvers for all response models. * - * @param Http $utopia + * @param Http $http + * @param Request $request + * @param UtopiaSwooleResponse $response * @param callable $complexity * @return array - * @throws Exception + * @throws \Exception */ - protected static function api(Http $utopia, callable $complexity): array + protected function api(Http $http, Request $request, UtopiaHttpResponse $response, Container $container, callable $complexity): array { - Mapper::init($utopia - ->getResource('response') - ->getModels()); + Mapper::init((new Response($response))->getModels()); + + $mapper = new Mapper(); $queries = []; $mutations = []; - foreach ($utopia->getRoutes() as $routes) { + foreach ($http->getRoutes() as $routes) { foreach ($routes as $route) { /** @var Route $route */ @@ -106,7 +117,7 @@ class Schema continue; } - foreach (Mapper::route($utopia, $route, $complexity) as $field) { + foreach ($mapper->route($http, $route, $request, $response, $container, $complexity) as $field) { switch ($route->getMethod()) { case 'GET': $queries[$name] = $field; @@ -134,7 +145,7 @@ class Schema * Iterates all of a projects attributes and builds GraphQL * queries and mutations for the collections they make up. * - * @param Http $utopia + * @param Http $http * @param callable $complexity * @param callable $attributes * @param array $urls @@ -143,7 +154,7 @@ class Schema * @throws \Exception */ protected static function collections( - Http $utopia, + Http $http, callable $complexity, callable $attributes, array $urls, @@ -195,7 +206,7 @@ class Schema 'type' => $objectType, 'args' => Mapper::args('id'), 'resolve' => Resolvers::documentGet( - $utopia, + $http, $databaseId, $collectionId, $urls['get'], @@ -205,7 +216,7 @@ class Schema 'type' => Type::listOf($objectType), 'args' => Mapper::args('list'), 'resolve' => Resolvers::documentList( - $utopia, + $http, $databaseId, $collectionId, $urls['list'], @@ -218,7 +229,7 @@ class Schema 'type' => $objectType, 'args' => $attributes, 'resolve' => Resolvers::documentCreate( - $utopia, + $http, $databaseId, $collectionId, $urls['create'], @@ -230,12 +241,12 @@ class Schema 'args' => \array_merge( Mapper::args('id'), \array_map( - fn ($attr) => $attr['type'] = Type::getNullableType($attr['type']), + fn($attr) => $attr['type'] = Type::getNullableType($attr['type']), $attributes ) ), 'resolve' => Resolvers::documentUpdate( - $utopia, + $http, $databaseId, $collectionId, $urls['update'], @@ -246,7 +257,7 @@ class Schema 'type' => Mapper::model('none'), 'args' => Mapper::args('id'), 'resolve' => Resolvers::documentDelete( - $utopia, + $http, $databaseId, $collectionId, $urls['delete'], @@ -262,8 +273,8 @@ class Schema ]; } - public static function setDirty(string $projectId): void + public function setDirty(string $projectId): void { - self::$dirty[$projectId] = true; + $this->dirty[$projectId] = true; } } diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index 41c7de6e56..e1e9856e16 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -8,7 +8,10 @@ use Exception; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\UnionType; +use Utopia\DI\Container; +use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse; use Utopia\Http\Http; +use Utopia\Http\Request; use Utopia\Http\Route; use Utopia\Http\Validator; use Utopia\Http\Validator\Nullable; @@ -74,12 +77,15 @@ class Mapper return self::$args[$key] ?? []; } - public static function route( - Http $utopia, + public function route( + Http $http, Route $route, + Request $request, + UtopiaSwooleResponse $response, + Container $container, callable $complexity ): iterable { - foreach (self::$blacklist as $blacklist) { + foreach (static::$blacklist as $blacklist) { if (\str_starts_with($route->getPath(), $blacklist)) { return; } @@ -101,7 +107,7 @@ class Mapper $list = true; } $parameterType = Mapper::param( - $utopia, + $container, $parameter['validator'], !$parameter['optional'], $parameter['injections'] @@ -116,7 +122,7 @@ class Mapper 'type' => $type, 'description' => $description, 'args' => $params, - 'resolve' => Resolvers::api($utopia, $route) + 'resolve' => Resolvers::api($http, $route, $request, $response, $container) ]; if ($list) { @@ -205,7 +211,7 @@ class Mapper /** * Map a {@see Route} parameter to a GraphQL Type * - * @param Http $utopia + * @param Container $container * @param Validator|callable $validator * @param bool $required * @param array $injections @@ -213,13 +219,13 @@ class Mapper * @throws Exception */ public static function param( - Http $utopia, + Container $container, Validator|callable $validator, bool $required, array $injections ): Type { $validator = \is_callable($validator) - ? \call_user_func_array($validator, $utopia->getResources($injections)) + ? \call_user_func_array($validator, array_map(fn($injection) => $container->get($injection), $injections)) : $validator; $isNullable = $validator instanceof Nullable; @@ -278,7 +284,7 @@ class Mapper break; case 'Utopia\Http\Validator\ArrayList': $type = Type::listOf(self::param( - $utopia, + $container, $validator->getValidator(), $required, $injections From c14fc2a72109bcbcb44402203a62d5959f62afc1 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 10 Jun 2024 15:06:42 -0400 Subject: [PATCH 094/195] test: GraphQL wip --- app/init2.php | 5 +-- src/Appwrite/GraphQL/Resolvers.php | 57 +++++++++++++++------------ src/Appwrite/GraphQL/Types/Mapper.php | 2 +- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/app/init2.php b/app/init2.php index 40060cf5f2..feeef6a423 100644 --- a/app/init2.php +++ b/app/init2.php @@ -756,10 +756,7 @@ $cache $connection = $pool->get(); $connections->add($connection, $pool); - $redis = new Redis(); - $redis->connect($dsn->getHost(), $dsn->getPort()); - - $adapters[] = new CacheRedis($redis); + $adapters[] = new CacheRedis($connection); } return new Cache(new Sharding($adapters)); diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index ab8f8c23b6..79f36eb3ea 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -23,15 +23,16 @@ class Resolvers * @param ?Route $route * @return callable */ - public static function api( + public function api( Http $http, ?Route $route, UtopiaHttpRequest $request, UtopiaHttpResponse $response, Container $container, ): callable { + $resolver = $this; return fn($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $route, $args, $context, $container, $info, $request, $response) { + function (callable $resolve, callable $reject) use ($http, $route, $args, $context, $container, $info, $request, $response, $resolver) { $path = $route->getPath(); foreach ($args as $key => $value) { if (\str_contains($path, '/:' . $key)) { @@ -51,7 +52,7 @@ class Resolvers break; } - self::resolve($http, $request, $response, $container, $resolve, $reject); + $resolver->resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -65,7 +66,7 @@ class Resolvers * @param string $methodType * @return callable */ - public static function document( + public function document( Http $http, string $databaseId, string $collectionId, @@ -87,7 +88,7 @@ class Resolvers * @param callable $url * @return callable */ - public static function documentGet( + public function documentGet( Http $http, string $databaseId, string $collectionId, @@ -96,12 +97,13 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { - return static fn($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response) { + $resolver = $this; + return fn($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); - self::resolve($http, $request, $response, $container, $resolve, $reject); + $resolver->resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -116,7 +118,7 @@ class Resolvers * @param callable $params * @return callable */ - public static function documentList( + public function documentList( Http $http, string $databaseId, string $collectionId, @@ -126,8 +128,9 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { - return static fn($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response) { + $resolver = $this; + return fn($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); $request->setQuery($params($databaseId, $collectionId, $args)); @@ -136,7 +139,7 @@ class Resolvers return $payload['documents']; }; - self::resolve($http, $request, $response, $container,$resolve, $reject, $beforeResolve); + $resolver->resolve($http, $request, $response, $container, $resolve, $reject, $beforeResolve); } ); } @@ -151,7 +154,7 @@ class Resolvers * @param callable $params * @return callable */ - public static function documentCreate( + public function documentCreate( Http $http, string $databaseId, string $collectionId, @@ -161,13 +164,14 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { - return static fn($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container,$request, $response) { + $resolver = $this; + return fn($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('POST'); $request->setURI($url($databaseId, $collectionId, $args)); $request->setPayload($params($databaseId, $collectionId, $args)); - self::resolve($http, $request, $response, $container, $resolve, $reject); + $resolver->resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -182,7 +186,7 @@ class Resolvers * @param callable $params * @return callable */ - public static function documentUpdate( + public function documentUpdate( Http $http, string $databaseId, string $collectionId, @@ -192,13 +196,14 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { - return static fn($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container,$request, $response) { + $resolver = $this; + return fn($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('PATCH'); $request->setURI($url($databaseId, $collectionId, $args)); $request->setPayload($params($databaseId, $collectionId, $args)); - self::resolve($http, $request, $response, $container, $resolve, $reject); + $resolver->resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -212,7 +217,7 @@ class Resolvers * @param callable $url * @return callable */ - public static function documentDelete( + public function documentDelete( Http $http, string $databaseId, string $collectionId, @@ -221,12 +226,13 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { - return static fn($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container,$request, $response) { + $resolver = $this; + return fn($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('DELETE'); $request->setURI($url($databaseId, $collectionId, $args)); - self::resolve($http, $request, $response, $container, $resolve, $reject); + $resolver->resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -242,7 +248,7 @@ class Resolvers * @return void * @throws Exception */ - private static function resolve( + private function resolve( Http $http, Request $request, Response $response, @@ -252,6 +258,7 @@ class Resolvers ?callable $beforeResolve = null, ?callable $beforeReject = null, ): void { + var_dump('HEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE'); // Drop json content type so post args are used directly if (\str_starts_with($request->getHeader('content-type'), 'application/json')) { $request->removeHeader('content-type'); diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index e1e9856e16..cf4ce79340 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -122,7 +122,7 @@ class Mapper 'type' => $type, 'description' => $description, 'args' => $params, - 'resolve' => Resolvers::api($http, $route, $request, $response, $container) + 'resolve' => (new Resolvers())->api($http, $route, $request, $response, $container) ]; if ($list) { From 8795d721460ac7ac2c20c89e249f678f442d7a6b Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 10 Jun 2024 15:09:01 -0400 Subject: [PATCH 095/195] test: Temp database workaround for old PHP --- tests/e2e/Services/Databases/DatabasesBase.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 80b158de4f..781855646a 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -2868,7 +2868,14 @@ trait DatabasesBase $this->assertEquals('Invalid document structure: Attribute "floatRange" has invalid format. Value must be a valid range between 1 and 1', $badFloatRange['body']['message']); $this->assertEquals('Invalid document structure: Attribute "probability" has invalid format. Value must be a valid range between 0 and 1', $badProbability['body']['message']); $this->assertEquals('Invalid document structure: Attribute "upperBound" has invalid format. Value must be a valid range between -9,223,372,036,854,775,808 and 10', $tooHigh['body']['message']); - $this->assertEquals('Invalid document structure: Attribute "lowerBound" has invalid format. Value must be a valid range between 5 and 9,223,372,036,854,775,807', $tooLow['body']['message']); + + $max = '9,223,372,036,854,775,807'; + + if (PHP_VERSION_ID < 80300) { + $max = '9,223,372,036,854,775,808'; + } + + $this->assertEquals('Invalid document structure: Attribute "lowerBound" has invalid format. Value must be a valid range between 5 and '.$max, $tooLow['body']['message']); } /** From 7dccb21ffcbe8a3c23ad33e65b9c3956028afb84 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 10 Jun 2024 15:14:06 -0400 Subject: [PATCH 096/195] lint --- app/controllers/api/databases.php | 2 +- app/init2.php | 54 +++++++++++++-------------- src/Appwrite/GraphQL/Resolvers.php | 26 ++++++------- src/Appwrite/GraphQL/Schema.php | 32 ++++++++-------- src/Appwrite/GraphQL/Types/Mapper.php | 2 +- 5 files changed, 58 insertions(+), 58 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 194066994d..f308d30e2d 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -23,9 +23,9 @@ use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; use Utopia\Database\Exception\Limit as LimitException; +use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Exception\Restricted as RestrictedException; use Utopia\Database\Exception\Structure as StructureException; -use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; diff --git a/app/init2.php b/app/init2.php index feeef6a423..9e179427ec 100644 --- a/app/init2.php +++ b/app/init2.php @@ -2,10 +2,6 @@ require_once __DIR__ . '/../vendor/autoload.php'; -use PHPMailer\PHPMailer\PHPMailer; -use Utopia\Cache\Adapter\Redis as CacheRedis; -use Utopia\Cache\Adapter\Sharding; -use Utopia\VCS\Adapter\Git\GitHub; use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; use Appwrite\Auth\Auth; @@ -28,10 +24,13 @@ use Appwrite\Network\Validator\Origin; use Appwrite\URL\URL; use Appwrite\Utopia\Queue\Connections; use MaxMind\Db\Reader; +use PHPMailer\PHPMailer\PHPMailer; use Swoole\Database\PDOConfig; use Swoole\Database\PDOPool; use Swoole\Database\RedisConfig; use Swoole\Database\RedisPool; +use Utopia\Cache\Adapter\Redis as CacheRedis; +use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -65,6 +64,7 @@ use Utopia\Storage\Device\S3; use Utopia\Storage\Device\Wasabi; use Utopia\Storage\Storage; use Utopia\System\System; +use Utopia\VCS\Adapter\Git\GitHub; require_once __DIR__ . '/init/constants.php'; require_once __DIR__ . '/init/config.php'; @@ -206,20 +206,20 @@ $global->set( 'pools', (function () { $fallbackForDB = 'db_main=' . URL::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', ''), - ]); + '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=' . URL::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', ''), - ]); + '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' => [ @@ -430,7 +430,7 @@ $queueForCertificates = new Dependency(); $plan ->setName('plan') - ->setCallback(fn() => []); + ->setCallback(fn () => []); $mode ->setName('mode') @@ -639,7 +639,7 @@ $project return $console; } - $project = $authorization->skip(fn() => $dbForConsole->getDocument('projects', $projectId)); + $project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); return $project; }); @@ -802,11 +802,11 @@ $connections $locale ->setName('locale') - ->setCallback(fn() => new Locale(System::getEnv('_APP_LOCALE', 'en'))); + ->setCallback(fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); $localeCodes ->setName('localeCodes') - ->setCallback(fn() => array_map(fn($locale) => $locale['code'], Config::getParam('locale-codes', []))); + ->setCallback(fn () => array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []))); $queue ->setName('queue') @@ -958,10 +958,10 @@ $clients * + Filter for duplicated entries */ $clientsConsole = \array_map( - fn($node) => $node['hostname'], + fn ($node) => $node['hostname'], \array_filter( $console->getAttribute('platforms', []), - fn($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && !empty($node['hostname'])) + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ); @@ -969,10 +969,10 @@ $clients \array_merge( $clientsConsole, \array_map( - fn($node) => $node['hostname'], + fn ($node) => $node['hostname'], \array_filter( $project->getAttribute('platforms', []), - fn($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) ) ) ) @@ -1121,7 +1121,7 @@ $promiseAdapter $schemaVariable ->setName('schemaVariable') - ->setCallback(fn()=> new Schema()); + ->setCallback(fn () => new Schema()); $schema ->setName('schema') @@ -1142,7 +1142,7 @@ $schema }; $attributes = function (int $limit, int $offset) use ($dbForProject, $authorization) { - $attrs = $authorization->skip(fn() => $dbForProject->find('attributes', [ + $attrs = $authorization->skip(fn () => $dbForProject->find('attributes', [ Query::limit($limit), Query::offset($offset), ])); diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index 79f36eb3ea..871b3f7f4d 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -8,9 +8,9 @@ use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\DI\Container; use Utopia\Exception; -use Utopia\Http\Response as UtopiaHttpResponse; -use Utopia\Http\Request as UtopiaHttpRequest; use Utopia\Http\Http; +use Utopia\Http\Request as UtopiaHttpRequest; +use Utopia\Http\Response as UtopiaHttpResponse; use Utopia\Http\Route; use Utopia\System\System; @@ -31,7 +31,7 @@ class Resolvers Container $container, ): callable { $resolver = $this; - return fn($type, $args, $context, $info) => new Swoole( + return fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $route, $args, $context, $container, $info, $request, $response, $resolver) { $path = $route->getPath(); foreach ($args as $key => $value) { @@ -97,8 +97,8 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { - $resolver = $this; - return fn($type, $args, $context, $info) => new Swoole( + $resolver = $this; + return fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); @@ -128,8 +128,8 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { - $resolver = $this; - return fn($type, $args, $context, $info) => new Swoole( + $resolver = $this; + return fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); @@ -164,8 +164,8 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { - $resolver = $this; - return fn($type, $args, $context, $info) => new Swoole( + $resolver = $this; + return fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('POST'); $request->setURI($url($databaseId, $collectionId, $args)); @@ -196,8 +196,8 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { - $resolver = $this; - return fn($type, $args, $context, $info) => new Swoole( + $resolver = $this; + return fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('PATCH'); $request->setURI($url($databaseId, $collectionId, $args)); @@ -226,8 +226,8 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { - $resolver = $this; - return fn($type, $args, $context, $info) => new Swoole( + $resolver = $this; + return fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('DELETE'); $request->setURI($url($databaseId, $collectionId, $args)); diff --git a/src/Appwrite/GraphQL/Schema.php b/src/Appwrite/GraphQL/Schema.php index bf5354f891..073213e4ae 100644 --- a/src/Appwrite/GraphQL/Schema.php +++ b/src/Appwrite/GraphQL/Schema.php @@ -3,17 +3,17 @@ namespace Appwrite\GraphQL; use Appwrite\GraphQL\Types\Mapper; +use Appwrite\Utopia\Response; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; use GraphQL\Type\Schema as GQLSchema; -use Appwrite\Utopia\Response; use Utopia\DI\Container; -use Utopia\Http\Response as UtopiaHttpResponse; -use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse; use Utopia\Exception; +use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse; use Utopia\Http\Http; -use Utopia\Http\Route; use Utopia\Http\Request; +use Utopia\Http\Response as UtopiaHttpResponse; +use Utopia\Http\Route; class Schema { @@ -51,23 +51,23 @@ class Schema $container, $complexity ); -// $collections = $this->collections( -// $http, -// $complexity, -// $request, -// $response, -// $attributes, -// $urls, -// $params, -// ); + // $collections = $this->collections( + // $http, + // $complexity, + // $request, + // $response, + // $attributes, + // $urls, + // $params, + // ); $queries = \array_merge_recursive( $api['query'], - //$collections['query'] + //$collections['query'] ); $mutations = \array_merge_recursive( $api['mutation'], - //$collections['mutation'] + //$collections['mutation'] ); \ksort($queries); @@ -241,7 +241,7 @@ class Schema 'args' => \array_merge( Mapper::args('id'), \array_map( - fn($attr) => $attr['type'] = Type::getNullableType($attr['type']), + fn ($attr) => $attr['type'] = Type::getNullableType($attr['type']), $attributes ) ), diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index cf4ce79340..a15c6aa475 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -225,7 +225,7 @@ class Mapper array $injections ): Type { $validator = \is_callable($validator) - ? \call_user_func_array($validator, array_map(fn($injection) => $container->get($injection), $injections)) + ? \call_user_func_array($validator, array_map(fn ($injection) => $container->get($injection), $injections)) : $validator; $isNullable = $validator instanceof Nullable; From 8e9dd17dfd622501b35d364fa46ff3661500daaf Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:29:19 -0400 Subject: [PATCH 097/195] feat: Refreshing GraphQL coroutine cache related resources --- src/Appwrite/GraphQL/Resolvers.php | 38 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index 871b3f7f4d..65365eb269 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -30,9 +30,8 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { - $resolver = $this; - return fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $route, $args, $context, $container, $info, $request, $response, $resolver) { + return fn($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($http, $route, $args, $context, $container, $info, $request, $response) { $path = $route->getPath(); foreach ($args as $key => $value) { if (\str_contains($path, '/:' . $key)) { @@ -51,8 +50,7 @@ class Resolvers $request->setPayload($args); break; } - - $resolver->resolve($http, $request, $response, $container, $resolve, $reject); + Resolvers::resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -98,12 +96,12 @@ class Resolvers Container $container, ): callable { $resolver = $this; - return fn ($type, $args, $context, $info) => new Swoole( + return fn($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); - $resolver->resolve($http, $request, $response, $container, $resolve, $reject); + Resolvers::resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -129,7 +127,7 @@ class Resolvers Container $container, ): callable { $resolver = $this; - return fn ($type, $args, $context, $info) => new Swoole( + return fn($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); @@ -139,7 +137,7 @@ class Resolvers return $payload['documents']; }; - $resolver->resolve($http, $request, $response, $container, $resolve, $reject, $beforeResolve); + Resolvers::resolve($http, $request, $response, $container, $resolve, $reject, $beforeResolve); } ); } @@ -165,13 +163,13 @@ class Resolvers Container $container, ): callable { $resolver = $this; - return fn ($type, $args, $context, $info) => new Swoole( + return fn($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('POST'); $request->setURI($url($databaseId, $collectionId, $args)); $request->setPayload($params($databaseId, $collectionId, $args)); - $resolver->resolve($http, $request, $response, $container, $resolve, $reject); + Resolvers::resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -197,13 +195,13 @@ class Resolvers Container $container, ): callable { $resolver = $this; - return fn ($type, $args, $context, $info) => new Swoole( + return fn($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('PATCH'); $request->setURI($url($databaseId, $collectionId, $args)); $request->setPayload($params($databaseId, $collectionId, $args)); - $resolver->resolve($http, $request, $response, $container, $resolve, $reject); + Resolvers::resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -227,12 +225,12 @@ class Resolvers Container $container, ): callable { $resolver = $this; - return fn ($type, $args, $context, $info) => new Swoole( + return fn($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('DELETE'); $request->setURI($url($databaseId, $collectionId, $args)); - $resolver->resolve($http, $request, $response, $container, $resolve, $reject); + Resolvers::resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -248,7 +246,7 @@ class Resolvers * @return void * @throws Exception */ - private function resolve( + private static function resolve( Http $http, Request $request, Response $response, @@ -258,7 +256,6 @@ class Resolvers ?callable $beforeResolve = null, ?callable $beforeReject = null, ): void { - var_dump('HEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE'); // Drop json content type so post args are used directly if (\str_starts_with($request->getHeader('content-type'), 'application/json')) { $request->removeHeader('content-type'); @@ -268,8 +265,13 @@ class Resolvers try { $route = $http->match($request); + $context + ->refresh('cache') + ->refresh('dbForProject') + ->refresh('dbForConsole') + ->refresh('getProjectDb'); - $http->execute($route, $request, $context); + $http->execute($route, $request, clone $context); } catch (\Throwable $e) { if ($beforeReject) { $e = $beforeReject($e); From a2b8f74ae1224e90f94cf073d2d1a725a2387d0d Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:30:14 -0400 Subject: [PATCH 098/195] chore: Updating dependencies --- composer.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index 6b575bd178..1adfe7ef0c 100644 --- a/composer.lock +++ b/composer.lock @@ -1784,12 +1784,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/di.git", - "reference": "8edd2c86df5db8383b197b6c5b8e35774ff8e4a8" + "reference": "1fb7da5ead16de5d795ef10c1e22e39726eb6943" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/di/zipball/8edd2c86df5db8383b197b6c5b8e35774ff8e4a8", - "reference": "8edd2c86df5db8383b197b6c5b8e35774ff8e4a8", + "url": "https://api.github.com/repos/utopia-php/di/zipball/1fb7da5ead16de5d795ef10c1e22e39726eb6943", + "reference": "1fb7da5ead16de5d795ef10c1e22e39726eb6943", "shasum": "" }, "require": { @@ -1837,7 +1837,7 @@ "source": "https://github.com/utopia-php/di/tree/feat-framework-v2", "issues": "https://github.com/utopia-php/di/issues" }, - "time": "2024-06-07T18:49:13+00:00" + "time": "2024-06-11T16:03:00+00:00" }, { "name": "utopia-php/domains", @@ -1991,12 +1991,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "d64cfc3868057d539c7fa27c672ac938eedc3656" + "reference": "f54000a0f47b6eea34a373212711729b336a56bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/d64cfc3868057d539c7fa27c672ac938eedc3656", - "reference": "d64cfc3868057d539c7fa27c672ac938eedc3656", + "url": "https://api.github.com/repos/utopia-php/http/zipball/f54000a0f47b6eea34a373212711729b336a56bc", + "reference": "f54000a0f47b6eea34a373212711729b336a56bc", "shasum": "" }, "require": { @@ -2033,7 +2033,7 @@ "issues": "https://github.com/utopia-php/http/issues", "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-06-10T12:37:51+00:00" + "time": "2024-06-11T16:03:46+00:00" }, { "name": "utopia-php/image", From 7d248f29f026d12c1be14e1de5090adba41c799d Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 11 Jun 2024 18:08:40 -0400 Subject: [PATCH 099/195] feat: Coroutine graphql wip --- app/controllers/api/graphql.php | 1 - app/controllers/shared/api/auth.php | 4 ++++ composer.lock | 8 ++++---- src/Appwrite/GraphQL/Resolvers.php | 12 ++++++------ 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index a9bd5dd1eb..0e7ddc783d 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -157,7 +157,6 @@ Http::post('/v1/graphql') if (\str_starts_with($type, 'multipart/form-data')) { $query = parseMultipart($query, $request); } - $output = execute($schema, $promiseAdapter, $query); $response diff --git a/app/controllers/shared/api/auth.php b/app/controllers/shared/api/auth.php index 2d676e16b5..b5ccc36d28 100644 --- a/app/controllers/shared/api/auth.php +++ b/app/controllers/shared/api/auth.php @@ -55,6 +55,10 @@ Http::init() return; } + if(str_contains($route->getPath(), '/v1/graphql')) { // Skip for graphQL recursive call + return; + } + $auths = $project->getAttribute('auths', []); switch ($route->getLabel('auth.type', '')) { case 'emailPassword': diff --git a/composer.lock b/composer.lock index 1adfe7ef0c..72b2b0405d 100644 --- a/composer.lock +++ b/composer.lock @@ -1991,12 +1991,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "f54000a0f47b6eea34a373212711729b336a56bc" + "reference": "bf2474554f78d870c74aaaa1dfb4f54795ae9497" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/f54000a0f47b6eea34a373212711729b336a56bc", - "reference": "f54000a0f47b6eea34a373212711729b336a56bc", + "url": "https://api.github.com/repos/utopia-php/http/zipball/bf2474554f78d870c74aaaa1dfb4f54795ae9497", + "reference": "bf2474554f78d870c74aaaa1dfb4f54795ae9497", "shasum": "" }, "require": { @@ -2033,7 +2033,7 @@ "issues": "https://github.com/utopia-php/http/issues", "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-06-11T16:03:46+00:00" + "time": "2024-06-11T16:38:45+00:00" }, { "name": "utopia-php/image", diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index 65365eb269..9ce0de4735 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -30,7 +30,7 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { - return fn($type, $args, $context, $info) => new Swoole( + return fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $route, $args, $context, $container, $info, $request, $response) { $path = $route->getPath(); foreach ($args as $key => $value) { @@ -96,7 +96,7 @@ class Resolvers Container $container, ): callable { $resolver = $this; - return fn($type, $args, $context, $info) => new Swoole( + return fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); @@ -127,7 +127,7 @@ class Resolvers Container $container, ): callable { $resolver = $this; - return fn($type, $args, $context, $info) => new Swoole( + return fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); @@ -163,7 +163,7 @@ class Resolvers Container $container, ): callable { $resolver = $this; - return fn($type, $args, $context, $info) => new Swoole( + return fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('POST'); $request->setURI($url($databaseId, $collectionId, $args)); @@ -195,7 +195,7 @@ class Resolvers Container $container, ): callable { $resolver = $this; - return fn($type, $args, $context, $info) => new Swoole( + return fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('PATCH'); $request->setURI($url($databaseId, $collectionId, $args)); @@ -225,7 +225,7 @@ class Resolvers Container $container, ): callable { $resolver = $this; - return fn($type, $args, $context, $info) => new Swoole( + return fn ($type, $args, $context, $info) => new Swoole( function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) { $request->setMethod('DELETE'); $request->setURI($url($databaseId, $collectionId, $args)); From 74ef1a4257437644b1f86e4ade008e29ac55c2d3 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:51:30 -0400 Subject: [PATCH 100/195] chore: GraphQL arrangement --- composer.lock | 60 +++++++++++++++--------------- src/Appwrite/GraphQL/Resolvers.php | 3 +- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/composer.lock b/composer.lock index 72b2b0405d..39d72e911f 100644 --- a/composer.lock +++ b/composer.lock @@ -1133,12 +1133,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "098e36a5b73de12beeb5ac17e80abf3696f7ad5f" + "reference": "8740a072b86292957feb42703edde77fcfca84fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/098e36a5b73de12beeb5ac17e80abf3696f7ad5f", - "reference": "098e36a5b73de12beeb5ac17e80abf3696f7ad5f", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8740a072b86292957feb42703edde77fcfca84fb", + "reference": "8740a072b86292957feb42703edde77fcfca84fb", "shasum": "" }, "require": { @@ -1206,7 +1206,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-06-20T08:18:00+00:00" }, { "name": "symfony/polyfill-php80", @@ -1271,7 +1271,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/1.x" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" }, "funding": [ { @@ -1572,16 +1572,16 @@ }, { "name": "utopia-php/cache", - "version": "0.10.0", + "version": "0.10.1", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "313bcdfbb166f75c2c205a59d1467cead63a9626" + "reference": "87ee4fc91e50d4ddfef650aa999ea12be3a99583" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/313bcdfbb166f75c2c205a59d1467cead63a9626", - "reference": "313bcdfbb166f75c2c205a59d1467cead63a9626", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/87ee4fc91e50d4ddfef650aa999ea12be3a99583", + "reference": "87ee4fc91e50d4ddfef650aa999ea12be3a99583", "shasum": "" }, "require": { @@ -1616,9 +1616,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.10.0" + "source": "https://github.com/utopia-php/cache/tree/0.10.1" }, - "time": "2024-06-05T16:40:43+00:00" + "time": "2024-06-18T13:20:25+00:00" }, { "name": "utopia-php/cli", @@ -3293,16 +3293,16 @@ }, { "name": "laravel/pint", - "version": "v1.16.0", + "version": "v1.16.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98" + "reference": "9266a47f1b9231b83e0cfd849009547329d871b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98", - "reference": "1b3a3dc5bc6a81ff52828ba7277621f1d49d6d98", + "url": "https://api.github.com/repos/laravel/pint/zipball/9266a47f1b9231b83e0cfd849009547329d871b1", + "reference": "9266a47f1b9231b83e0cfd849009547329d871b1", "shasum": "" }, "require": { @@ -3313,13 +3313,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.57.1", - "illuminate/view": "^10.48.10", - "larastan/larastan": "^2.9.6", + "friendsofphp/php-cs-fixer": "^3.59.3", + "illuminate/view": "^10.48.12", + "larastan/larastan": "^2.9.7", "laravel-zero/framework": "^10.4.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.34.7" + "pestphp/pest": "^2.34.8" }, "bin": [ "builds/pint" @@ -3355,7 +3355,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-05-21T18:08:25+00:00" + "time": "2024-06-18T16:50:05+00:00" }, { "name": "matthiasmullie/minify", @@ -3487,12 +3487,12 @@ "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "2f5294676c802a62b0549f6bc8983f14294ce369" + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/2f5294676c802a62b0549f6bc8983f14294ce369", - "reference": "2f5294676c802a62b0549f6bc8983f14294ce369", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { @@ -3532,7 +3532,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.x" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" }, "funding": [ { @@ -3540,7 +3540,7 @@ "type": "tidelift" } ], - "time": "2024-02-10T11:10:03+00:00" + "time": "2024-06-12T14:39:25+00:00" }, { "name": "nikic/php-parser", @@ -3548,12 +3548,12 @@ "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "daaadc3bae458908aa477b90a8932e7da9253f22" + "reference": "3ef0811e45ba7e91fb0f066af5af7d52c3b24469" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/daaadc3bae458908aa477b90a8932e7da9253f22", - "reference": "daaadc3bae458908aa477b90a8932e7da9253f22", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3ef0811e45ba7e91fb0f066af5af7d52c3b24469", + "reference": "3ef0811e45ba7e91fb0f066af5af7d52c3b24469", "shasum": "" }, "require": { @@ -3599,7 +3599,7 @@ "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/master" }, - "time": "2024-06-03T06:24:19+00:00" + "time": "2024-06-12T18:31:58+00:00" }, { "name": "phar-io/manifest", @@ -5604,7 +5604,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/1.x" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" }, "funding": [ { diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index 9ce0de4735..2946f594d6 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -264,14 +264,13 @@ class Resolvers $response->setContentType(Response::CONTENT_TYPE_NULL); try { - $route = $http->match($request); $context ->refresh('cache') ->refresh('dbForProject') ->refresh('dbForConsole') ->refresh('getProjectDb'); - $http->execute($route, $request, clone $context); + $http->run(clone $context); } catch (\Throwable $e) { if ($beforeReject) { $e = $beforeReject($e); From bc46ce30abccad17567dfec4e807755921a70b97 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 20 Jun 2024 17:26:40 -0400 Subject: [PATCH 101/195] chore: merge --- app/cli.php | 81 --------------------------------------------------- app/console | 2 +- composer.json | 1 - 3 files changed, 1 insertion(+), 83 deletions(-) diff --git a/app/cli.php b/app/cli.php index df4c82203f..60bf81b262 100644 --- a/app/cli.php +++ b/app/cli.php @@ -119,84 +119,3 @@ $cli $cli ->setContainer($container) ->run(); -require_once __DIR__ . '/init2.php'; -use Swoole\Runtime; -use Utopia\CLI\Adapters\Swoole as SwooleCLI; -use Utopia\DI\Dependency; -global $global, $container; -Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -/** - * @var Registry $global - * @var Container $container - */ -$register = new Dependency(); -$logError = new Dependency(); -$queueForDeletes = new Dependency(); -$queueForCertificates = new Dependency(); -$register - ->setName('register') - ->setCallback(function () use (&$global): Registry { - return $global; - }); -$queueForDeletes - ->setName('queueForDeletes') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Delete($queue); - }); - -$queueForCertificates - ->setName('queueForCertificates') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Certificate($queue); - }); - -$logError - ->setName('logError') - ->inject('register') - ->setCallback(function (Registry $register) { - return function (Throwable $error, string $namespace, string $action) use ($register) { - $logger = $register->get('logger'); - if ($logger) { - $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - $log = new Log(); - $log->setNamespace($namespace); - $log->setServer(\gethostname()); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($error->getMessage()); - $log->addTag('code', $error->getCode()); - $log->addTag('verboseType', get_class($error)); - $log->addExtra('file', $error->getFile()); - $log->addExtra('line', $error->getLine()); - $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('detailedTrace', $error->getTrace()); - $log->setAction($action); - $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; - - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - $responseCode = $logger->addLog($log); - Console::info('Usage stats log pushed with status code: ' . $responseCode); - } - Console::warning("Failed: {$error->getMessage()}"); - Console::warning($error->getTraceAsString()); - }; - }); - - -$container->set($logError); -$container->set($register); -$container->set($queueForDeletes); -$container->set($queueForCertificates); -$platform->init(Service::TYPE_CLI, ['adapter' => new SwooleCLI(1)]); -$cli - ->init() - ->inject('authorization') - ->action(function (Authorization $authorization) { - $authorization->disable(); - }); - -$cli - ->setContainer($container) - ->run(); diff --git a/app/console b/app/console index 053a975eb7..5169fe16d6 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 053a975eb7f9b8c28847e7a52852f3b6189b986b +Subproject commit 5169fe16d63066f64ab5013c78953aea04e24b53 diff --git a/composer.json b/composer.json index b06b27d75c..2ac000424f 100644 --- a/composer.json +++ b/composer.json @@ -70,7 +70,6 @@ "utopia-php/queue": "dev-feat-coroutine-and-di as 0.7.99", "utopia-php/registry": "0.5.*", "utopia-php/storage": "dev-feat-framework-v2-v2 as 0.18.99", - "utopia-php/swoole": "0.8.*", "utopia-php/system": "0.8.*", "utopia-php/vcs": "0.6.*", "utopia-php/websocket": "0.1.*", From c7ba512db53a00e99d6238ac4cacfb87ccd392a7 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:23:07 -0400 Subject: [PATCH 102/195] chore: merge --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 39d72e911f..b0196829db 100644 --- a/composer.lock +++ b/composer.lock @@ -2402,12 +2402,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "55b1d8675a35d8fb269614a1e916a1bff616983c" + "reference": "3b54dad46e7cbd767ea5bf84af0e03c92ff0fada" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/55b1d8675a35d8fb269614a1e916a1bff616983c", - "reference": "55b1d8675a35d8fb269614a1e916a1bff616983c", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/3b54dad46e7cbd767ea5bf84af0e03c92ff0fada", + "reference": "3b54dad46e7cbd767ea5bf84af0e03c92ff0fada", "shasum": "" }, "require": { @@ -2444,7 +2444,7 @@ "issues": "https://github.com/utopia-php/platform/issues", "source": "https://github.com/utopia-php/platform/tree/feat-framework-v2" }, - "time": "2024-06-03T18:01:18+00:00" + "time": "2024-06-21T19:21:45+00:00" }, { "name": "utopia-php/pools", From ca0e44620ad0e88f8773e87bb2f7216e881fd6f2 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:26:58 -0400 Subject: [PATCH 103/195] chore: merge --- app/cli.php | 2 +- composer.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/cli.php b/app/cli.php index 60bf81b262..2b6434cc80 100644 --- a/app/cli.php +++ b/app/cli.php @@ -98,7 +98,7 @@ $container->set($queueForDeletes); $container->set($queueForCertificates); $platform = new Appwrite(); -$platform->init(Service::TYPE_CLI, ['adapter' => new SwooleCLI(1)]); +$platform->init(Service::TYPE_TASK, ['adapter' => new SwooleCLI(1)]); $cli = $platform->getCli(); diff --git a/composer.lock b/composer.lock index b0196829db..ed40b64d09 100644 --- a/composer.lock +++ b/composer.lock @@ -2402,12 +2402,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "3b54dad46e7cbd767ea5bf84af0e03c92ff0fada" + "reference": "f40b23bf84f8fd95bc954ef9297663c01fed5c4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/3b54dad46e7cbd767ea5bf84af0e03c92ff0fada", - "reference": "3b54dad46e7cbd767ea5bf84af0e03c92ff0fada", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/f40b23bf84f8fd95bc954ef9297663c01fed5c4c", + "reference": "f40b23bf84f8fd95bc954ef9297663c01fed5c4c", "shasum": "" }, "require": { @@ -2444,7 +2444,7 @@ "issues": "https://github.com/utopia-php/platform/issues", "source": "https://github.com/utopia-php/platform/tree/feat-framework-v2" }, - "time": "2024-06-21T19:21:45+00:00" + "time": "2024-06-21T19:25:15+00:00" }, { "name": "utopia-php/pools", From 4ef09b680ff4e6c2f828b4f73cd645f3914cc405 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 21 Jun 2024 17:22:58 -0400 Subject: [PATCH 104/195] feat: GraphQL DI --- src/Appwrite/GraphQL/Resolvers.php | 18 ++++++++++-------- src/Appwrite/Promises/Swoole.php | 12 +++++------- tests/e2e/Services/GraphQL/Base.php | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index 2946f594d6..49d7c421f7 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -30,8 +30,10 @@ class Resolvers UtopiaHttpResponse $response, Container $container, ): callable { + $resolver = $this; + return fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $route, $args, $context, $container, $info, $request, $response) { + function (callable $resolve, callable $reject) use ($http, $route, $args, $context, $container, $info, $request, $response, $resolver) { $path = $route->getPath(); foreach ($args as $key => $value) { if (\str_contains($path, '/:' . $key)) { @@ -50,7 +52,7 @@ class Resolvers $request->setPayload($args); break; } - Resolvers::resolve($http, $request, $response, $container, $resolve, $reject); + $resolver->resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -101,7 +103,7 @@ class Resolvers $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); - Resolvers::resolve($http, $request, $response, $container, $resolve, $reject); + $resolver->resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -137,7 +139,7 @@ class Resolvers return $payload['documents']; }; - Resolvers::resolve($http, $request, $response, $container, $resolve, $reject, $beforeResolve); + $resolver->resolve($http, $request, $response, $container, $resolve, $reject, $beforeResolve); } ); } @@ -169,7 +171,7 @@ class Resolvers $request->setURI($url($databaseId, $collectionId, $args)); $request->setPayload($params($databaseId, $collectionId, $args)); - Resolvers::resolve($http, $request, $response, $container, $resolve, $reject); + $resolver->resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -201,7 +203,7 @@ class Resolvers $request->setURI($url($databaseId, $collectionId, $args)); $request->setPayload($params($databaseId, $collectionId, $args)); - Resolvers::resolve($http, $request, $response, $container, $resolve, $reject); + $resolver->resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -230,7 +232,7 @@ class Resolvers $request->setMethod('DELETE'); $request->setURI($url($databaseId, $collectionId, $args)); - Resolvers::resolve($http, $request, $response, $container, $resolve, $reject); + $resolver->resolve($http, $request, $response, $container, $resolve, $reject); } ); } @@ -246,7 +248,7 @@ class Resolvers * @return void * @throws Exception */ - private static function resolve( + private function resolve( Http $http, Request $request, Response $response, diff --git a/src/Appwrite/Promises/Swoole.php b/src/Appwrite/Promises/Swoole.php index d0b3eb8855..8cddd567f3 100644 --- a/src/Appwrite/Promises/Swoole.php +++ b/src/Appwrite/Promises/Swoole.php @@ -11,13 +11,11 @@ class Swoole extends Promise callable $resolve, callable $reject ): void { - \go(function () use ($executor, $resolve, $reject) { - try { - $executor($resolve, $reject); - } catch (\Throwable $exception) { - $reject($exception); - } - }); + try { + $executor($resolve, $reject); + } catch (\Throwable $exception) { + $reject($exception); + } } /** diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index 735e4eced5..b77f006bf8 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -2498,6 +2498,6 @@ trait Base protected function packageCode($folder): void { - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout); } } From 4ea291705ad55abab8c0e6a45fd383179f396fda Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 24 Jun 2024 13:30:51 -0400 Subject: [PATCH 105/195] feat: Adapting specs to DI --- src/Appwrite/Platform/Tasks/Specs.php | 28 ++++++++++++++----- .../Specification/Format/OpenAPI3.php | 12 +++++--- .../Specification/Format/Swagger2.php | 8 +++++- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 44d2b9904c..7c4c91fc60 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -15,6 +15,8 @@ use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; +use Utopia\DI\Container; +use Utopia\DI\Dependency; use Utopia\Http\Adapter\FPM\Server; use Utopia\Http\Adapter\Swoole\Request; use Utopia\Http\Adapter\Swoole\Response as HttpResponse; @@ -39,20 +41,32 @@ class Specs extends Action ->param('version', 'latest', new Text(16), 'Spec version', true) ->param('mode', 'normal', new WhiteList(['normal', 'mocks']), 'Spec Mode', true) ->inject('register') - ->callback(fn (string $version, string $mode, Registry $register) => $this->action($version, $mode, $register)); + ->inject('context') + ->callback(fn (string $version, string $mode, Registry $register, Container $context) => $this->action($version, $mode, $register, $context)); } - public function action(string $version, string $mode, Registry $register): void + public function action(string $version, string $mode, Registry $register, Container $container): void { $appRoutes = Http::getRoutes(); $response = new Response(new HttpResponse(new SwooleHttpResponse())); $mocks = ($mode === 'mocks'); + $requestDependency = new Dependency(); + $responseDependency = new Dependency(); + $dbForConsole = new Dependency(); + $dbForProject = new Dependency(); + // Mock dependencies - Http::setResource('request', fn () => new Request(new SwooleHttpRequest())); - Http::setResource('response', fn () => $response); - Http::setResource('dbForConsole', fn () => new Database(new MySQL(''), new Cache(new None()))); - Http::setResource('dbForProject', fn () => new Database(new MySQL(''), new Cache(new None()))); + $requestDependency->setName('request')->setCallback(fn () => new Request(new SwooleHttpRequest())); + $responseDependency->setName('response')->setCallback(fn () => $response); + $dbForConsole->setName('dbForConsole')->setCallback(fn () => new Database(new MySQL(''), new Cache(new None()))); + $dbForProject->setName('dbForProject')->setCallback(fn () => new Database(new MySQL(''), new Cache(new None()))); + + $container + ->set($requestDependency) + ->set($responseDependency) + ->set($dbForProject) + ->set($dbForConsole); $platforms = [ 'client' => APP_PLATFORM_CLIENT, @@ -252,7 +266,7 @@ class Specs extends Action } } - $arguments = [new Http(new Server(), 'UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0]; + $arguments = [new Http(new Server(), $container, 'UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0]; foreach (['swagger2', 'open-api3'] as $format) { $formatInstance = match ($format) { 'swagger2' => new Swagger2(...$arguments), diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index af11667ff5..a2cbd0a139 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -272,10 +272,14 @@ class OpenAPI3 extends Format $bodyRequired = []; foreach ($route->getParams() as $name => $param) { // Set params - /** - * @var \Utopia\Http\Validator $validator - */ - $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->http->getResources($param['injections'])) : $param['validator']; + $injections = []; + + if(isset($param['injections'])) { + $injections = array_map(fn ($injection) => $this->http->getContainer()->get($injection), $param['injections']); + } + + /** @var Validator $validator */ + $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $injections) : $param['validator']; $node = [ 'name' => $name, diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 55d3956943..428a8ce605 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -270,8 +270,14 @@ class Swagger2 extends Format ); foreach ($parameters as $name => $param) { // Set params + $injections = []; + + if(isset($param['injections'])) { + $injections = array_map(fn ($injection) => $this->http->getContainer()->get($injection), $param['injections']); + } + /** @var Validator $validator */ - $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->http->getResources($param['injections'])) : $param['validator']; + $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $injections) : $param['validator']; $node = [ 'name' => $name, From aa4bc1c7769f632a1d0fec8887ba187517c253b3 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 24 Jun 2024 13:31:01 -0400 Subject: [PATCH 106/195] feat: Adapting Migrate to DI --- src/Appwrite/Migration/Migration.php | 56 ++++++++++++------------- src/Appwrite/Platform/Tasks/Migrate.php | 12 ++---- 2 files changed, 30 insertions(+), 38 deletions(-) diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 86fffb4b3e..695c42f05f 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -78,14 +78,14 @@ abstract class Migration '1.4.11' => 'V19', '1.4.12' => 'V19', '1.4.13' => 'V19', - '1.5.0' => 'V20', - '1.5.1' => 'V20', - '1.5.2' => 'V20', - '1.5.3' => 'V20', - '1.5.4' => 'V20', - '1.5.5' => 'V20', - '1.5.6' => 'V20', - '1.5.7' => 'V20', + '1.5.0' => 'V20', + '1.5.1' => 'V20', + '1.5.2' => 'V20', + '1.5.3' => 'V20', + '1.5.4' => 'V20', + '1.5.5' => 'V20', + '1.5.6' => 'V20', + '1.5.7' => 'V20', ]; /** @@ -170,29 +170,25 @@ abstract class Migration Console::log('Migrating Collection ' . $collection['$id'] . ':'); - \Co\run(function (array $collection, callable $callback) { - foreach ($this->documentsIterator($collection['$id']) as $document) { - go(function (Document $document, callable $callback) { - if (empty($document->getId()) || empty($document->getCollection())) { - return; - } - - $old = $document->getArrayCopy(); - $new = call_user_func($callback, $document); - - if (is_null($new) || $new->getArrayCopy() == $old) { - return; - } - - try { - $this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document); - } catch (\Throwable $th) { - Console::error('Failed to update document: ' . $th->getMessage()); - return; - } - }, $document, $callback); + foreach ($this->documentsIterator($collection['$id']) as $document) { + if (empty($document->getId()) || empty($document->getCollection())) { + return; } - }, $collection, $callback); + + $old = $document->getArrayCopy(); + $new = call_user_func($callback, $document); + + if (is_null($new) || $new->getArrayCopy() == $old) { + return; + } + + try { + $this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document); + } catch (\Throwable $th) { + Console::error('Failed to update document: ' . $th->getMessage()); + return; + } + } } } diff --git a/src/Appwrite/Platform/Tasks/Migrate.php b/src/Appwrite/Platform/Tasks/Migrate.php index e08985e1ae..5d000acdc5 100644 --- a/src/Appwrite/Platform/Tasks/Migrate.php +++ b/src/Appwrite/Platform/Tasks/Migrate.php @@ -9,8 +9,6 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\Http\Adapter\FPM\Server; -use Utopia\Http\Http; use Utopia\Http\Validator\Text; use Utopia\Platform\Action; use Utopia\Registry\Registry; @@ -33,7 +31,8 @@ class Migrate extends Action ->inject('getProjectDB') ->inject('register') ->inject('authorization') - ->callback(fn ($version, $cache, $dbForConsole, $getProjectDB, Registry $register, Authorization $authorization) => $this->action($version, $cache, $dbForConsole, $getProjectDB, $register, $authorization)); + ->inject('console') + ->callback(fn ($version, $cache, $dbForConsole, $getProjectDB, Registry $register, Authorization $authorization, Document $console) => $this->action($version, $cache, $dbForConsole, $getProjectDB, $register, $authorization, $console)); } private function clearProjectsCache(Cache $cache, Document $project) @@ -45,7 +44,7 @@ class Migrate extends Action } } - public function action(string $version, Cache $cache, Database $dbForConsole, callable $getProjectDB, Registry $register, Authorization $auth) + public function action(string $version, Cache $cache, Database $dbForConsole, callable $getProjectDB, Registry $register, Authorization $auth, Document $console) { $auth->disable(); if (!array_key_exists($version, Migration::$versions)) { @@ -54,12 +53,9 @@ class Migrate extends Action return; } - $http = new Http(new Server(), 'UTC'); Console::success('Starting Data Migration to version ' . $version); - $console = $http->getResource('console'); - $limit = 30; $sum = 30; $offset = 0; @@ -78,7 +74,7 @@ class Migrate extends Action $class = 'Appwrite\\Migration\\Version\\' . Migration::$versions[$version]; /** @var Migration $migration */ - $migration = new $class(); + $migration = new $class($auth, ); while (!empty($projects)) { foreach ($projects as $project) { From 3ecd8ce189fa7a88ae76d7434aea2ef1fe1faaa2 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 24 Jun 2024 13:31:24 -0400 Subject: [PATCH 107/195] chore: Commenting unused code --- src/Appwrite/GraphQL/Schema.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Appwrite/GraphQL/Schema.php b/src/Appwrite/GraphQL/Schema.php index 073213e4ae..01b9711dec 100644 --- a/src/Appwrite/GraphQL/Schema.php +++ b/src/Appwrite/GraphQL/Schema.php @@ -205,36 +205,36 @@ class Schema $queryFields[$collectionId . 'Get'] = [ 'type' => $objectType, 'args' => Mapper::args('id'), - 'resolve' => Resolvers::documentGet( + /*'resolve' => Resolvers::documentGet( $http, $databaseId, $collectionId, $urls['get'], - ) + )*/ ]; $queryFields[$collectionId . 'List'] = [ 'type' => Type::listOf($objectType), 'args' => Mapper::args('list'), - 'resolve' => Resolvers::documentList( + /*'resolve' => Resolvers::documentList( $http, $databaseId, $collectionId, $urls['list'], $params['list'], - ), + ),*/ 'complexity' => $complexity, ]; $mutationFields[$collectionId . 'Create'] = [ 'type' => $objectType, 'args' => $attributes, - 'resolve' => Resolvers::documentCreate( + /*'resolve' => Resolvers::documentCreate( $http, $databaseId, $collectionId, $urls['create'], $params['create'], - ) + )*/ ]; $mutationFields[$collectionId . 'Update'] = [ 'type' => $objectType, @@ -245,23 +245,23 @@ class Schema $attributes ) ), - 'resolve' => Resolvers::documentUpdate( + /*'resolve' => Resolvers::documentUpdate( $http, $databaseId, $collectionId, $urls['update'], $params['update'], - ) + )*/ ]; $mutationFields[$collectionId . 'Delete'] = [ 'type' => Mapper::model('none'), 'args' => Mapper::args('id'), - 'resolve' => Resolvers::documentDelete( + /*'resolve' => Resolvers::documentDelete( $http, $databaseId, $collectionId, $urls['delete'], - ) + )*/ ]; } $offset += $limit; From 97cd5e4934477ac5ba0d2df03c6a44153e9d4671 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 24 Jun 2024 13:31:40 -0400 Subject: [PATCH 108/195] chore: DI+CO improvements --- app/cli.php | 7 +- app/init.php | 3461 ++++++++++++++++++++++++------------------------- app/init2.php | 16 + composer.lock | 8 +- 4 files changed, 1734 insertions(+), 1758 deletions(-) diff --git a/app/cli.php b/app/cli.php index 2b6434cc80..1b6756174f 100644 --- a/app/cli.php +++ b/app/cli.php @@ -25,11 +25,16 @@ Runtime::enableCoroutine(SWOOLE_HOOK_ALL); * @var Registry $global * @var Container $container */ +$context = new Dependency(); $register = new Dependency(); $logError = new Dependency(); $queueForDeletes = new Dependency(); $queueForCertificates = new Dependency(); +$context + ->setName('context') + ->setCallback(fn() => $container); + $register ->setName('register') ->setCallback(function () use (&$global): Registry { @@ -91,7 +96,7 @@ $logError }; }); - +$container->set($context); $container->set($logError); $container->set($register); $container->set($queueForDeletes); diff --git a/app/init.php b/app/init.php index c31c83864a..dfa80a600f 100644 --- a/app/init.php +++ b/app/init.php @@ -1,1755 +1,1710 @@ $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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 = $database->getAuthorization()->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->find('targets', [ - Query::equal('$internalId', $targetIds) - ]); - } - return []; - } -); - -Database::addFilter( - 'providerSearch', - function (mixed $value, Document $provider) { - $searchValues = [ - $provider->getId(), - $provider->getAttribute('name', ''), - $provider->getAttribute('provider', ''), - $provider->getAttribute('type', '') - ]; - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'topicSearch', - function (mixed $value, Document $topic) { - $searchValues = [ - $topic->getId(), - $topic->getAttribute('name', ''), - $topic->getAttribute('description', ''), - ]; - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'messageSearch', - function (mixed $value, Document $message) { - $searchValues = [ - $message->getId(), - $message->getAttribute('description', ''), - $message->getAttribute('status', ''), - ]; - - $data = \json_decode($message->getAttribute('data', []), true); - $providerType = $message->getAttribute('providerType', ''); - - if ($providerType === MESSAGE_TYPE_EMAIL) { - $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); - } elseif ($providerType === MESSAGE_TYPE_SMS) { - $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); - } else { - $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); - } - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -/** - * DB Formats - */ -Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () { - return new Email(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () { - return new DatetimeValidator(); -}, Database::VAR_DATETIME); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) { - $elements = $attribute['formatOptions']['elements']; - return new WhiteList($elements, true); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () { - return new IP(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () { - return new URL(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) { - $min = $attribute['formatOptions']['min'] ?? -INF; - $max = $attribute['formatOptions']['max'] ?? INF; - return new Range($min, $max, Range::TYPE_INTEGER); -}, Database::VAR_INTEGER); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) { - $min = $attribute['formatOptions']['min'] ?? -INF; - $max = $attribute['formatOptions']['max'] ?? INF; - return new Range($min, $max, Range::TYPE_FLOAT); -}, Database::VAR_FLOAT); - -/* - * Registry - */ -$register->set('logger', function () { - // Register error logger - $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); - $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); - - if (empty($providerName) || empty($providerConfig)) { - return; - } - - if (!Logger::hasProvider($providerName)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); - } - - // Old Sentry Format conversion. Fallback until the old syntax is completely deprecated. - if (str_contains($providerConfig, ';') && strtolower($providerName) == 'sentry') { - $configChunks = \explode(";", $providerConfig); - - $sentryKey = $configChunks[0]; - $projectId = $configChunks[1]; - - $providerConfig = 'https://' . $sentryKey . '@sentry.io/' . $projectId; - } - - $classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName); - $adapter = new $classname($providerConfig); - return new Logger($adapter); -}); -$register->set('pools', function () { - $group = new Group(); - - $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ - 'scheme' => 'mariadb', - 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), - 'port' => System::getEnv('_APP_DB_PORT', '3306'), - 'user' => System::getEnv('_APP_DB_USER', ''), - 'pass' => System::getEnv('_APP_DB_PASS', ''), - 'path' => System::getEnv('_APP_DB_SCHEMA', ''), - ]); - $fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([ - 'scheme' => 'redis', - 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => System::getEnv('_APP_REDIS_USER', ''), - 'pass' => System::getEnv('_APP_REDIS_PASS', ''), - ]); - - $connections = [ - 'console' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), - 'multiple' => false, - 'schemes' => ['mariadb', 'mysql'], - ], - 'database' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), - 'multiple' => true, - 'schemes' => ['mariadb', 'mysql'], - ], - 'queue' => [ - 'type' => 'queue', - 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'pubsub' => [ - 'type' => 'pubsub', - 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'cache' => [ - 'type' => 'cache', - 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), - 'multiple' => true, - 'schemes' => ['redis'], - ], - ]; - - $maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151); - $instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14); - - $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; - - if ($multiprocessing) { - $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); - } else { - $workerCount = 1; - } - - if ($workerCount > $instanceConnections) { - throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); - } - - $poolSize = (int)($instanceConnections / $workerCount); - - foreach ($connections as $key => $connection) { - $type = $connection['type'] ?? ''; - $multiple = $connection['multiple'] ?? false; - $schemes = $connection['schemes'] ?? []; - $config = []; - $dsns = explode(',', $connection['dsns'] ?? ''); - foreach ($dsns as &$dsn) { - $dsn = explode('=', $dsn); - $name = ($multiple) ? $key . '_' . $dsn[0] : $key; - $dsn = $dsn[1] ?? ''; - $config[] = $name; - if (empty($dsn)) { - //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); - continue; - } - - $dsn = new DSN($dsn); - $dsnHost = $dsn->getHost(); - $dsnPort = $dsn->getPort(); - $dsnUser = $dsn->getUser(); - $dsnPass = $dsn->getPassword(); - $dsnScheme = $dsn->getScheme(); - $dsnDatabase = $dsn->getPath(); - - if (!in_array($dsnScheme, $schemes)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); - } - - /** - * Get Resource - * - * Creation could be reused across connection types like database, cache, queue, etc. - * - * Resource assignment to an adapter will happen below. - */ - $resource = match ($dsnScheme) { - 'mysql', - 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( - PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_EMULATE_PREPARES => true, - PDO::ATTR_STRINGIFY_FETCHES => true - )); - }); - }, - 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { - $redis = new Redis(); - @$redis->pconnect($dsnHost, (int)$dsnPort); - if ($dsnPass) { - $redis->auth($dsnPass); - } - $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); - - return $redis; - }, - default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'), - }; - - $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { - // Get Adapter - switch ($type) { - case 'database': - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($resource()), - 'mysql' => new MySQL($resource()), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - break; - case 'pubsub': - $adapter = $resource(); - break; - case 'queue': - $adapter = match ($dsn->getScheme()) { - 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), - default => null - }; - break; - case 'cache': - $adapter = match ($dsn->getScheme()) { - 'redis' => new RedisCache($resource()), - default => null - }; - break; - - default: - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); - } - - return $adapter; - }); - - $group->add($pool); - } - - Config::setParam('pools-' . $key, $config); - } - - return $group; -}); - -$register->set('db', function () { - // This is usually for our workers or CLI commands scope - $dbHost = System::getEnv('_APP_DB_HOST', ''); - $dbPort = System::getEnv('_APP_DB_PORT', ''); - $dbUser = System::getEnv('_APP_DB_USER', ''); - $dbPass = System::getEnv('_APP_DB_PASS', ''); - $dbScheme = System::getEnv('_APP_DB_SCHEMA', ''); - - return new PDO( - "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", - $dbUser, - $dbPass, - SQL::getPDOAttributes() - ); -}); - -$register->set('smtp', function () { - $mail = new PHPMailer(true); - - $mail->isSMTP(); - - $username = System::getEnv('_APP_SMTP_USERNAME'); - $password = System::getEnv('_APP_SMTP_PASSWORD'); - - $mail->XMailer = 'Appwrite Mailer'; - $mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp'); - $mail->Port = System::getEnv('_APP_SMTP_PORT', 25); - $mail->SMTPAuth = !empty($username) && !empty($password); - $mail->Username = $username; - $mail->Password = $password; - $mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', ''); - $mail->SMTPAutoTLS = false; - $mail->CharSet = 'UTF-8'; - - $from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); - $email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - - $mail->setFrom($email, $from); - $mail->addReplyTo($email, $from); - - $mail->isHTML(true); - - return $mail; -}); -$register->set('geodb', function () { - return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.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); -} - -\stream_context_set_default([ // Set global user agent and http settings - 'http' => [ - 'method' => 'GET', - 'user_agent' => \sprintf( - APP_USERAGENT, - System::getEnv('_APP_VERSION', 'UNKNOWN'), - System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)) - ), - 'timeout' => 2, - ], -]); - -// Runtime Execution -// @phpstan-ignore -Http::setResource('log', fn () => new Log()); -// @phpstan-ignore -Http::setResource('logger', function ($register) { - return $register->get('logger'); -}, ['register']); - -// @phpstan-ignore -Http::setResource('hooks', function ($register) { - return $register->get('hooks'); -}, ['register']); - -// @phpstan-ignore -Http::setResource('register', fn () => $register); -// @phpstan-ignore -Http::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); - -// @phpstan-ignore -Http::setResource('localeCodes', function () { - return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); -}); - -// @phpstan-ignore -Http::setResource('connections', function () { - return new Connections(); -}); - -// Queues -// @phpstan-ignore -Http::setResource('queue', function (Group $pools, Connections $connections) { - $connection = $pools->get('queue')->pop(); - $connections->add($connection); - return $connection->getResource(); -}, ['pools', 'connections']); -// @phpstan-ignore -Http::setResource('queueForMessaging', function (Connection $queue) { - return new Messaging($queue); -}, ['queue']); -// @phpstan-ignore -Http::setResource('queueForMails', function (Connection $queue) { - return new Mail($queue); -}, ['queue']); -// @phpstan-ignore -Http::setResource('queueForBuilds', function (Connection $queue) { - return new Build($queue); -}, ['queue']); -// @phpstan-ignore -Http::setResource('queueForDatabase', function (Connection $queue) { - return new EventDatabase($queue); -}, ['queue']); -// @phpstan-ignore -Http::setResource('queueForDeletes', function (Connection $queue) { - return new Delete($queue); -}, ['queue']); -// @phpstan-ignore -Http::setResource('queueForEvents', function (Connection $queue) { - return new Event($queue); -}, ['queue']); -// @phpstan-ignore -Http::setResource('queueForAudits', function (Connection $queue) { - return new Audit($queue); -}, ['queue']); -// @phpstan-ignore -Http::setResource('queueForFunctions', function (Connection $queue) { - return new Func($queue); -}, ['queue']); -// @phpstan-ignore -Http::setResource('queueForUsage', function (Connection $queue) { - return new Usage($queue); -}, ['queue']); -// @phpstan-ignore -Http::setResource('queueForCertificates', function (Connection $queue) { - return new Certificate($queue); -}, ['queue']); -// @phpstan-ignore -Http::setResource('queueForMigrations', function (Connection $queue) { - return new Migration($queue); -}, ['queue']); -// @phpstan-ignore -Http::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']); - -// @phpstan-ignore -Http::setResource('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $auth) { - - $auth->setDefaultStatus(true); - - Auth::setCookieName('a_session_' . $project->getId()); - - if (APP_MODE_ADMIN === $mode) { - Auth::setCookieName('a_session_' . $console->getId()); - } - - $session = Auth::decodeSession( - $request->getCookie( - Auth::$cookieName, // Get sessions - $request->getCookie(Auth::$cookieName . '_legacy', '') - ) - ); - - // Get session from header for SSR clients - if (empty($session['id']) && empty($session['secret'])) { - $sessionHeader = $request->getHeader('x-appwrite-session', ''); - - if (!empty($sessionHeader)) { - $session = Auth::decodeSession($sessionHeader); - } - } - - // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies - if ($response) { - $response->addHeader('X-Debug-Fallback', 'false'); - } - - if (empty($session['id']) && empty($session['secret'])) { - if ($response) { - $response->addHeader('X-Debug-Fallback', 'true'); - } - $fallback = $request->getHeader('x-fallback-cookies', ''); - $fallback = \json_decode($fallback, true); - $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); - } - - Auth::$unique = $session['id'] ?? ''; - Auth::$secret = $session['secret'] ?? ''; - - if (APP_MODE_ADMIN !== $mode) { - if ($project->isEmpty()) { - $user = new Document([]); - } else { - if ($project->getId() === 'console') { - $user = $dbForConsole->getDocument('users', Auth::$unique); - } else { - $user = $dbForProject->getDocument('users', Auth::$unique); - } - } - } else { - $user = $dbForConsole->getDocument('users', Auth::$unique); - } - - if ( - $user->isEmpty() // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) - ) { // Validate user has valid login token - $user = new Document([]); - } - - if (APP_MODE_ADMIN === $mode) { - if ($user->find('teamId', $project->getAttribute('teamId'), 'memberships')) { - $auth->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', 900, 10); // Instantiate with key, algo, maxAge and leeway. - - try { - $payload = $jwt->decode($authJWT); - } catch (JWTException $error) { - throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); - } - - $jwtUserId = $payload['userId'] ?? ''; - $jwtSessionId = $payload['sessionId'] ?? ''; - - if ($jwtUserId && $jwtSessionId) { - $user = $dbForProject->getDocument('users', $jwtUserId); - } - - if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token - $user = new Document([]); - } - } - - $dbForProject->setMetadata('user', $user->getId()); - $dbForConsole->setMetadata('user', $user->getId()); - - return $user; -}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole', 'auth']); - -// @phpstan-ignore -Http::setResource('project', function (Database $dbForConsole, Request $request, Document $console, Authorization $auth) { - - $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); - - if (empty($projectId) || $projectId === 'console') { - return $console; - } - - $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - - return $project; -}, ['dbForConsole', 'request', 'console', 'auth']); - -// @phpstan-ignore -Http::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']); - -// @phpstan-ignore -Http::setResource('console', function () { - return new Document([ - '$id' => ID::custom('console'), - '$internalId' => ID::custom('console'), - 'name' => 'Appwrite', - '$collection' => ID::custom('projects'), - 'description' => 'Appwrite core engine', - 'logo' => '', - 'teamId' => -1, - 'webhooks' => [], - 'keys' => [], - 'platforms' => [ - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Localhost', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => 'localhost', - ], // Current host is added on app init - ], - 'legalName' => '', - 'legalCountry' => '', - 'legalState' => '', - 'legalCity' => '', - 'legalAddress' => '', - 'legalTaxId' => '', - 'auths' => [ - '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 - ], - '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', '') - ], - ]); -}, []); - -// @phpstan-ignore -Http::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project, Authorization $auth, Connections $connections) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $connection = $pools->get($dsn->getHost())->pop(); - $connections->add($connection); - $dbAdapter = $connection->getResource(); - - $database = new Database($dbAdapter, $cache); - $database->setAuthorization($auth); - - $database - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; -}, ['pools', 'dbForConsole', 'cache', 'project', 'auth', 'connections']); - -// @phpstan-ignore -Http::setResource('dbForConsole', function (Group $pools, Cache $cache, Authorization $auth, Connections $connections) { - $connection = $pools->get('console')->pop(); - $connections->add($connection); - $dbAdapter = $connection->getResource(); - - $database = new Database($dbAdapter, $cache); - $database->setAuthorization($auth); - - $database - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - return $database; -}, ['pools', 'cache', 'auth', 'connections']); - -// @phpstan-ignore -Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth, Connections $connections) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - - $getProjectDB = function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $configure = (function (Database $database) use ($project, $dsn) { - $database - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - }); - - if (isset($databases[$dsn->getHost()])) { - $database = $databases[$dsn->getHost()]; - $configure($database); - return $database; - } - - $connection = $pools->get($dsn->getHost())->pop(); - $connections->add($connection); - $dbAdapter = $connection->getResource(); - - $database = new Database($dbAdapter, $cache); - $database->setAuthorization($auth); - $databases[$dsn->getHost()] = $database; - $configure($database); - - return $database; - }; - - return $getProjectDB; -}, ['pools', 'dbForConsole', 'cache', 'auth', 'connections']); - -// @phpstan-ignore -Http::setResource('cache', function (Group $pools, Connections $connections) { - $list = Config::getParam('pools-cache', []); - $adapters = []; - - foreach ($list as $value) { - $connection = $pools->get($value)->pop(); - $connections->add($connection); - $adapters[] = $connection->getResource(); - } - - return new Cache(new Sharding($adapters)); -}, ['pools', 'connections']); - -// @phpstan-ignore -Http::setResource('deviceForLocal', function () { - return new Local(); -}); - -// @phpstan-ignore -Http::setResource('deviceForFiles', function ($project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); -}, ['project']); - -// @phpstan-ignore -Http::setResource('deviceForFunctions', function ($project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); -}, ['project']); - -// @phpstan-ignore -Http::setResource('deviceForBuilds', function ($project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); -}, ['project']); - -function getDevice($root): Device -{ - $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); - - if (!empty($connection)) { - $acl = 'private'; - $device = Storage::DEVICE_LOCAL; - $accessKey = ''; - $accessSecret = ''; - $bucket = ''; - $region = ''; - - try { - $dsn = new DSN($connection); - $device = $dsn->getScheme(); - $accessKey = $dsn->getUser() ?? ''; - $accessSecret = $dsn->getPassword() ?? ''; - $bucket = $dsn->getPath() ?? ''; - $region = $dsn->getParam('region'); - } catch (\Throwable $e) { - Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); - } - - switch ($device) { - case Storage::DEVICE_S3: - return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case STORAGE::DEVICE_DO_SPACES: - $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); - $device->setHttpVersion(S3::HTTP_VERSION_1_1); - return $device; - case Storage::DEVICE_BACKBLAZE: - return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LINODE: - return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_WASABI: - return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - } - } else { - switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - case Storage::DEVICE_S3: - $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); - $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); - $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); - $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); - $s3Acl = 'private'; - return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); - case Storage::DEVICE_DO_SPACES: - $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); - $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); - $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); - $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); - $doSpacesAcl = 'private'; - $device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); - $device->setHttpVersion(S3::HTTP_VERSION_1_1); - return $device; - case Storage::DEVICE_BACKBLAZE: - $backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); - $backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); - $backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); - $backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); - $backblazeAcl = 'private'; - return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); - case Storage::DEVICE_LINODE: - $linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); - $linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', ''); - $linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', ''); - $linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); - $linodeAcl = 'private'; - return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); - case Storage::DEVICE_WASABI: - $wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); - $wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', ''); - $wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', ''); - $wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); - $wasabiAcl = 'private'; - return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); - } - } -} - -// @phpstan-ignore -Http::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']); - -// @phpstan-ignore -Http::setResource('geodb', function ($register) { - /** @var Utopia\Registry\Registry $register */ - return $register->get('geodb'); -}, ['register']); - -// @phpstan-ignore -Http::setResource('passwordsDictionary', function ($register) { - /** @var Utopia\Registry\Registry $register */ - return $register->get('passwordsDictionary'); -}, ['register']); - - -// @phpstan-ignore -Http::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; -}); - -// @phpstan-ignore -Http::setResource('promiseAdapter', function ($register) { - return $register->get('promiseAdapter'); -}, ['register']); - -// @phpstan-ignore -Http::setResource('schema', function (Http $utopia, Database $dbForProject, Authorization $auth) { - - $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, $auth) { - $attrs = $auth->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', 'auth']); - -// @phpstan-ignore -Http::setResource('contributors', function () { - $path = 'app/config/contributors.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -// @phpstan-ignore -Http::setResource('employees', function () { - $path = 'app/config/employees.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -// @phpstan-ignore -Http::setResource('heroes', function () { - $path = 'app/config/heroes.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -// @phpstan-ignore -Http::setResource('gitHub', function (Cache $cache) { - return new VcsGitHub($cache); -}, ['cache']); - -// @phpstan-ignore -Http::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']); - -// @phpstan-ignore -Http::setResource('plan', function (array $plan = []) { - return []; -}); - -// @phpstan-ignore -Http::setResource('auth', fn () => new Authorization()); - -// @phpstan-ignore -Http::setResource('pools', function ($register) { - return $register->get('pools'); -}, ['pools']); +///** +// * Init +// * +// * Initializes both Appwrite API entry point, queue workers, and CLI tasks. +// * Set configuration, framework resources & app constants +// * +// */ +// +//if (\file_exists(__DIR__ . '/../vendor/autoload.php')) { +// require_once __DIR__ . '/../vendor/autoload.php'; +//} +// +//\ini_set('memory_limit', '512M'); +//\ini_set('display_errors', 1); +//\ini_set('display_startup_errors', 1); +//\ini_set('default_socket_timeout', -1); +//\error_reporting(E_ALL); +// +//use Ahc\Jwt\JWT; +//use Ahc\Jwt\JWTException; +//use Appwrite\Auth\Auth; +//use Appwrite\Event\Audit; +//use Appwrite\Event\Build; +//use Appwrite\Event\Certificate; +//use Appwrite\Event\Database as EventDatabase; +//use Appwrite\Event\Delete; +//use Appwrite\Event\Event; +//use Appwrite\Event\Func; +//use Appwrite\Event\Mail; +//use Appwrite\Event\Messaging; +//use Appwrite\Event\Migration; +//use Appwrite\Event\Usage; +//use Appwrite\Extend\Exception; +//use Appwrite\GraphQL\Promises\Adapter\Swoole; +//use Appwrite\GraphQL\Schema; +//use Appwrite\Hooks\Hooks; +//use Appwrite\Network\Validator\Email; +//use Appwrite\Network\Validator\Origin; +//use Appwrite\OpenSSL\OpenSSL; +//use Appwrite\URL\URL as AppwriteURL; +//use Appwrite\Utopia\Queue\Connections; +//use MaxMind\Db\Reader; +//use PHPMailer\PHPMailer\PHPMailer; +//use Swoole\Database\PDOProxy; +//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\Http\Http; +//use Utopia\Http\Request; +//use Utopia\Http\Response; +//use Utopia\Http\Validator\Hostname; +//use Utopia\Http\Validator\IP; +//use Utopia\Http\Validator\Range; +//use Utopia\Http\Validator\URL; +//use Utopia\Http\Validator\WhiteList; +//use Utopia\Locale\Locale; +//use Utopia\Logger\Log; +//use Utopia\Logger\Logger; +//use Utopia\Pools\Group; +//use Utopia\Pools\Pool; +//use Utopia\Queue; +//use Utopia\Queue\Connection; +//use Utopia\Registry\Registry; +//use Utopia\Storage\Device; +//use Utopia\Storage\Device\Backblaze; +//use Utopia\Storage\Device\DOSpaces; +//use Utopia\Storage\Device\Linode; +//use Utopia\Storage\Device\Local; +//use Utopia\Storage\Device\S3; +//use Utopia\Storage\Device\Wasabi; +//use Utopia\Storage\Storage; +//use Utopia\System\System; +//use Utopia\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_ACCCESS = 24 * 60 * 60; // 24 hours +//const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours +//const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours +//const APP_CACHE_BUSTER = 443; +//const APP_VERSION_STABLE = '1.5.7'; +//const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; +//const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; +//const APP_DATABASE_ATTRIBUTE_IP = 'ip'; +//const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime'; +//const APP_DATABASE_ATTRIBUTE_URL = 'url'; +//const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange'; +//const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange'; +//const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char +//const APP_DATABASE_TIMEOUT_MILLISECONDS = 15_000; +//const APP_STORAGE_UPLOADS = '/storage/uploads'; +//const APP_STORAGE_FUNCTIONS = '/storage/functions'; +//const APP_STORAGE_BUILDS = '/storage/builds'; +//const APP_STORAGE_CACHE = '/storage/cache'; +//const APP_STORAGE_CERTIFICATES = '/storage/certificates'; +//const APP_STORAGE_CONFIG = '/storage/config'; +//const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT` +//const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite'; +//const APP_SOCIAL_TWITTER_HANDLE = 'appwrite'; +//const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io'; +//const APP_SOCIAL_LINKEDIN = 'https://www.linkedin.com/company/appwrite'; +//const APP_SOCIAL_INSTAGRAM = 'https://www.instagram.com/appwrite.io'; +//const APP_SOCIAL_GITHUB = 'https://github.com/appwrite'; +//const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord'; +//const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244'; +//const APP_SOCIAL_DEV = 'https://dev.to/appwrite'; +//const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite'; +//const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1'; +//const APP_HOSTNAME_INTERNAL = 'appwrite'; +// +//// Databases +//const DATABASE_SHARED_TABLES = 'database_db_fra1_self_hosted_16_0'; +// +//// 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_TEAMS = 'teams'; +//const DELETE_TYPE_EXECUTIONS = 'executions'; +//const DELETE_TYPE_AUDIT = 'audit'; +//const DELETE_TYPE_ABUSE = 'abuse'; +//const DELETE_TYPE_USAGE = 'usage'; +//const DELETE_TYPE_REALTIME = 'realtime'; +//const DELETE_TYPE_BUCKETS = 'buckets'; +//const DELETE_TYPE_INSTALLATIONS = 'installations'; +//const DELETE_TYPE_RULES = 'rules'; +//const DELETE_TYPE_SESSIONS = 'sessions'; +//const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp'; +//const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource'; +//const DELETE_TYPE_SCHEDULES = 'schedules'; +//const DELETE_TYPE_TOPIC = 'topic'; +//const DELETE_TYPE_TARGET = 'target'; +//const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets'; +//const DELETE_TYPE_SESSION_TARGETS = 'session_targets'; +// +//// Message types +//const MESSAGE_SEND_TYPE_INTERNAL = 'internal'; +//const MESSAGE_SEND_TYPE_EXTERNAL = 'external'; +//// Mail Types +//const MAIL_TYPE_VERIFICATION = 'verification'; +//const MAIL_TYPE_MAGIC_SESSION = 'magicSession'; +//const MAIL_TYPE_RECOVERY = 'recovery'; +//const MAIL_TYPE_INVITATION = 'invitation'; +//const MAIL_TYPE_CERTIFICATE = 'certificate'; +//// Auth Types +//const APP_AUTH_TYPE_SESSION = 'Session'; +//const APP_AUTH_TYPE_JWT = 'JWT'; +//const APP_AUTH_TYPE_KEY = 'Key'; +//const APP_AUTH_TYPE_ADMIN = 'Admin'; +//// Response related +//const MAX_OUTPUT_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB +//// Function headers +//const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host']; +//const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length']; +//// Message types +//const MESSAGE_TYPE_EMAIL = 'email'; +//const MESSAGE_TYPE_SMS = 'sms'; +//const MESSAGE_TYPE_PUSH = 'push'; +//// Usage metrics +//const METRIC_TEAMS = 'teams'; +//const METRIC_USERS = 'users'; +//const METRIC_MESSAGES = 'messages'; +//const METRIC_MESSAGES_COUNTRY_CODE = '{countryCode}.messages'; +//const METRIC_SESSIONS = 'sessions'; +//const METRIC_DATABASES = 'databases'; +//const METRIC_COLLECTIONS = 'collections'; +//const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections'; +//const METRIC_DOCUMENTS = 'documents'; +//const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents'; +//const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents'; +//const METRIC_BUCKETS = 'buckets'; +//const METRIC_FILES = 'files'; +//const METRIC_FILES_STORAGE = 'files.storage'; +//const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files'; +//const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage'; +//const METRIC_FUNCTIONS = 'functions'; +//const METRIC_DEPLOYMENTS = 'deployments'; +//const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage'; +//const METRIC_BUILDS = 'builds'; +//const METRIC_BUILDS_STORAGE = 'builds.storage'; +//const METRIC_BUILDS_COMPUTE = 'builds.compute'; +//const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds'; +//const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage'; +//const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute'; +//const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments'; +//const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage'; +//const METRIC_EXECUTIONS = 'executions'; +//const METRIC_EXECUTIONS_COMPUTE = 'executions.compute'; +//const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions'; +//const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute'; +//const METRIC_NETWORK_REQUESTS = 'network.requests'; +//const METRIC_NETWORK_INBOUND = 'network.inbound'; +//const METRIC_NETWORK_OUTBOUND = 'network.outbound'; +// +//$register = new Registry(); +// +//Http::setMode(System::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION)); +// +//if (!Http::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'); +//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'); +// +///** +// * 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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 = $database->getAuthorization()->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->find('targets', [ +// Query::equal('$internalId', $targetIds) +// ]); +// } +// return []; +// } +//); +// +//Database::addFilter( +// 'providerSearch', +// function (mixed $value, Document $provider) { +// $searchValues = [ +// $provider->getId(), +// $provider->getAttribute('name', ''), +// $provider->getAttribute('provider', ''), +// $provider->getAttribute('type', '') +// ]; +// +// $search = \implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +//Database::addFilter( +// 'topicSearch', +// function (mixed $value, Document $topic) { +// $searchValues = [ +// $topic->getId(), +// $topic->getAttribute('name', ''), +// $topic->getAttribute('description', ''), +// ]; +// +// $search = \implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +//Database::addFilter( +// 'messageSearch', +// function (mixed $value, Document $message) { +// $searchValues = [ +// $message->getId(), +// $message->getAttribute('description', ''), +// $message->getAttribute('status', ''), +// ]; +// +// $data = \json_decode($message->getAttribute('data', []), true); +// $providerType = $message->getAttribute('providerType', ''); +// +// if ($providerType === MESSAGE_TYPE_EMAIL) { +// $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); +// } elseif ($providerType === MESSAGE_TYPE_SMS) { +// $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); +// } else { +// $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); +// } +// +// $search = \implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +///** +// * DB Formats +// */ +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () { +// return new Email(); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () { +// return new DatetimeValidator(); +//}, Database::VAR_DATETIME); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) { +// $elements = $attribute['formatOptions']['elements']; +// return new WhiteList($elements, true); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () { +// return new IP(); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () { +// return new URL(); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) { +// $min = $attribute['formatOptions']['min'] ?? -INF; +// $max = $attribute['formatOptions']['max'] ?? INF; +// return new Range($min, $max, Range::TYPE_INTEGER); +//}, Database::VAR_INTEGER); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) { +// $min = $attribute['formatOptions']['min'] ?? -INF; +// $max = $attribute['formatOptions']['max'] ?? INF; +// return new Range($min, $max, Range::TYPE_FLOAT); +//}, Database::VAR_FLOAT); +// +///* +// * Registry +// */ +//$register->set('logger', function () { +// // Register error logger +// $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); +// $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); +// +// if (empty($providerName) || empty($providerConfig)) { +// return; +// } +// +// if (!Logger::hasProvider($providerName)) { +// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); +// } +// +// // Old Sentry Format conversion. Fallback until the old syntax is completely deprecated. +// if (str_contains($providerConfig, ';') && strtolower($providerName) == 'sentry') { +// $configChunks = \explode(";", $providerConfig); +// +// $sentryKey = $configChunks[0]; +// $projectId = $configChunks[1]; +// +// $providerConfig = 'https://' . $sentryKey . '@sentry.io/' . $projectId; +// } +// +// $classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName); +// $adapter = new $classname($providerConfig); +// return new Logger($adapter); +//}); +//$register->set('pools', function () { +// $group = new Group(); +// +// $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ +// 'scheme' => 'mariadb', +// 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), +// 'port' => System::getEnv('_APP_DB_PORT', '3306'), +// 'user' => System::getEnv('_APP_DB_USER', ''), +// 'pass' => System::getEnv('_APP_DB_PASS', ''), +// 'path' => System::getEnv('_APP_DB_SCHEMA', ''), +// ]); +// $fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([ +// 'scheme' => 'redis', +// 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), +// 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), +// 'user' => System::getEnv('_APP_REDIS_USER', ''), +// 'pass' => System::getEnv('_APP_REDIS_PASS', ''), +// ]); +// +// $connections = [ +// 'console' => [ +// 'type' => 'database', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), +// 'multiple' => false, +// 'schemes' => ['mariadb', 'mysql'], +// ], +// 'database' => [ +// 'type' => 'database', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), +// 'multiple' => true, +// 'schemes' => ['mariadb', 'mysql'], +// ], +// 'queue' => [ +// 'type' => 'queue', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), +// 'multiple' => false, +// 'schemes' => ['redis'], +// ], +// 'pubsub' => [ +// 'type' => 'pubsub', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), +// 'multiple' => false, +// 'schemes' => ['redis'], +// ], +// 'cache' => [ +// 'type' => 'cache', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), +// 'multiple' => true, +// 'schemes' => ['redis'], +// ], +// ]; +// +// $maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151); +// $instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14); +// +// $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; +// +// if ($multiprocessing) { +// $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); +// } else { +// $workerCount = 1; +// } +// +// if ($workerCount > $instanceConnections) { +// throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); +// } +// +// $poolSize = (int)($instanceConnections / $workerCount); +// +// foreach ($connections as $key => $connection) { +// $type = $connection['type'] ?? ''; +// $multiple = $connection['multiple'] ?? false; +// $schemes = $connection['schemes'] ?? []; +// $config = []; +// $dsns = explode(',', $connection['dsns'] ?? ''); +// foreach ($dsns as &$dsn) { +// $dsn = explode('=', $dsn); +// $name = ($multiple) ? $key . '_' . $dsn[0] : $key; +// $dsn = $dsn[1] ?? ''; +// $config[] = $name; +// if (empty($dsn)) { +// //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); +// continue; +// } +// +// $dsn = new DSN($dsn); +// $dsnHost = $dsn->getHost(); +// $dsnPort = $dsn->getPort(); +// $dsnUser = $dsn->getUser(); +// $dsnPass = $dsn->getPassword(); +// $dsnScheme = $dsn->getScheme(); +// $dsnDatabase = $dsn->getPath(); +// +// if (!in_array($dsnScheme, $schemes)) { +// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); +// } +// +// /** +// * Get Resource +// * +// * Creation could be reused across connection types like database, cache, queue, etc. +// * +// * Resource assignment to an adapter will happen below. +// */ +// $resource = match ($dsnScheme) { +// 'mysql', +// 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { +// return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { +// return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( +// PDO::ATTR_TIMEOUT => 3, // Seconds +// PDO::ATTR_PERSISTENT => true, +// PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, +// PDO::ATTR_EMULATE_PREPARES => true, +// PDO::ATTR_STRINGIFY_FETCHES => true +// )); +// }); +// }, +// 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { +// $redis = new Redis(); +// @$redis->pconnect($dsnHost, (int)$dsnPort); +// if ($dsnPass) { +// $redis->auth($dsnPass); +// } +// $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); +// +// return $redis; +// }, +// default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'), +// }; +// +// $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { +// // Get Adapter +// switch ($type) { +// case 'database': +// $adapter = match ($dsn->getScheme()) { +// 'mariadb' => new MariaDB($resource()), +// 'mysql' => new MySQL($resource()), +// default => null +// }; +// +// $adapter->setDatabase($dsn->getPath()); +// break; +// case 'pubsub': +// $adapter = $resource(); +// break; +// case 'queue': +// $adapter = match ($dsn->getScheme()) { +// 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), +// default => null +// }; +// break; +// case 'cache': +// $adapter = match ($dsn->getScheme()) { +// 'redis' => new RedisCache($resource()), +// default => null +// }; +// break; +// +// default: +// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); +// } +// +// return $adapter; +// }); +// +// $group->add($pool); +// } +// +// Config::setParam('pools-' . $key, $config); +// } +// +// return $group; +//}); +// +//$register->set('db', function () { +// // This is usually for our workers or CLI commands scope +// $dbHost = System::getEnv('_APP_DB_HOST', ''); +// $dbPort = System::getEnv('_APP_DB_PORT', ''); +// $dbUser = System::getEnv('_APP_DB_USER', ''); +// $dbPass = System::getEnv('_APP_DB_PASS', ''); +// $dbScheme = System::getEnv('_APP_DB_SCHEMA', ''); +// +// return new PDO( +// "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", +// $dbUser, +// $dbPass, +// SQL::getPDOAttributes() +// ); +//}); +// +//$register->set('smtp', function () { +// $mail = new PHPMailer(true); +// +// $mail->isSMTP(); +// +// $username = System::getEnv('_APP_SMTP_USERNAME'); +// $password = System::getEnv('_APP_SMTP_PASSWORD'); +// +// $mail->XMailer = 'Appwrite Mailer'; +// $mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp'); +// $mail->Port = System::getEnv('_APP_SMTP_PORT', 25); +// $mail->SMTPAuth = !empty($username) && !empty($password); +// $mail->Username = $username; +// $mail->Password = $password; +// $mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', ''); +// $mail->SMTPAutoTLS = false; +// $mail->CharSet = 'UTF-8'; +// +// $from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); +// $email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); +// +// $mail->setFrom($email, $from); +// $mail->addReplyTo($email, $from); +// +// $mail->isHTML(true); +// +// return $mail; +//}); +//$register->set('geodb', function () { +// return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.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); +//} +// +//\stream_context_set_default([ // Set global user agent and http settings +// 'http' => [ +// 'method' => 'GET', +// 'user_agent' => \sprintf( +// APP_USERAGENT, +// System::getEnv('_APP_VERSION', 'UNKNOWN'), +// System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)) +// ), +// 'timeout' => 2, +// ], +//]); +// +//// Runtime Execution +//Http::setResource('log', fn () => new Log()); +//Http::setResource('logger', function ($register) { +// return $register->get('logger'); +//}, ['register']); +// +//Http::setResource('hooks', function ($register) { +// return $register->get('hooks'); +//}, ['register']); +// +//Http::setResource('register', fn () => $register); +//Http::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); +// +//Http::setResource('localeCodes', function () { +// return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); +//}); +// +//Http::setResource('connections', function () { +// return new Connections(); +//}); +// +//// Queues +//Http::setResource('queue', function (Group $pools, Connections $connections) { +// $connection = $pools->get('queue')->pop(); +// $connections->add($connection); +// return $connection->getResource(); +//}, ['pools', 'connections']); +//Http::setResource('queueForMessaging', function (Connection $queue) { +// return new Messaging($queue); +//}, ['queue']); +//Http::setResource('queueForMails', function (Connection $queue) { +// return new Mail($queue); +//}, ['queue']); +//Http::setResource('queueForBuilds', function (Connection $queue) { +// return new Build($queue); +//}, ['queue']); +//Http::setResource('queueForDatabase', function (Connection $queue) { +// return new EventDatabase($queue); +//}, ['queue']); +//Http::setResource('queueForDeletes', function (Connection $queue) { +// return new Delete($queue); +//}, ['queue']); +//Http::setResource('queueForEvents', function (Connection $queue) { +// return new Event($queue); +//}, ['queue']); +//Http::setResource('queueForAudits', function (Connection $queue) { +// return new Audit($queue); +//}, ['queue']); +//Http::setResource('queueForFunctions', function (Connection $queue) { +// return new Func($queue); +//}, ['queue']); +//Http::setResource('queueForUsage', function (Connection $queue) { +// return new Usage($queue); +//}, ['queue']); +//Http::setResource('queueForCertificates', function (Connection $queue) { +// return new Certificate($queue); +//}, ['queue']); +//Http::setResource('queueForMigrations', function (Connection $queue) { +// return new Migration($queue); +//}, ['queue']); +//Http::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']); +// +//Http::setResource('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $auth) { +// +// $auth->setDefaultStatus(true); +// +// Auth::setCookieName('a_session_' . $project->getId()); +// +// if (APP_MODE_ADMIN === $mode) { +// Auth::setCookieName('a_session_' . $console->getId()); +// } +// +// $session = Auth::decodeSession( +// $request->getCookie( +// Auth::$cookieName, // Get sessions +// $request->getCookie(Auth::$cookieName . '_legacy', '') +// ) +// ); +// +// // Get session from header for SSR clients +// if (empty($session['id']) && empty($session['secret'])) { +// $sessionHeader = $request->getHeader('x-appwrite-session', ''); +// +// if (!empty($sessionHeader)) { +// $session = Auth::decodeSession($sessionHeader); +// } +// } +// +// // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies +// if ($response) { +// $response->addHeader('X-Debug-Fallback', 'false'); +// } +// +// if (empty($session['id']) && empty($session['secret'])) { +// if ($response) { +// $response->addHeader('X-Debug-Fallback', 'true'); +// } +// $fallback = $request->getHeader('x-fallback-cookies', ''); +// $fallback = \json_decode($fallback, true); +// $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); +// } +// +// Auth::$unique = $session['id'] ?? ''; +// Auth::$secret = $session['secret'] ?? ''; +// +// if (APP_MODE_ADMIN !== $mode) { +// if ($project->isEmpty()) { +// $user = new Document([]); +// } else { +// if ($project->getId() === 'console') { +// $user = $dbForConsole->getDocument('users', Auth::$unique); +// } else { +// $user = $dbForProject->getDocument('users', Auth::$unique); +// } +// } +// } else { +// $user = $dbForConsole->getDocument('users', Auth::$unique); +// } +// +// if ( +// $user->isEmpty() // Check a document has been found in the DB +// || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) +// ) { // Validate user has valid login token +// $user = new Document([]); +// } +// +// if (APP_MODE_ADMIN === $mode) { +// if ($user->find('teamId', $project->getAttribute('teamId'), 'memberships')) { +// $auth->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', 900, 10); // Instantiate with key, algo, maxAge and leeway. +// +// try { +// $payload = $jwt->decode($authJWT); +// } catch (JWTException $error) { +// throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); +// } +// +// $jwtUserId = $payload['userId'] ?? ''; +// $jwtSessionId = $payload['sessionId'] ?? ''; +// +// if ($jwtUserId && $jwtSessionId) { +// $user = $dbForProject->getDocument('users', $jwtUserId); +// } +// +// if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token +// $user = new Document([]); +// } +// } +// +// $dbForProject->setMetadata('user', $user->getId()); +// $dbForConsole->setMetadata('user', $user->getId()); +// +// return $user; +//}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole', 'auth']); +// +//Http::setResource('project', function (Database $dbForConsole, Request $request, Document $console, Authorization $auth) { +// +// $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); +// +// if (empty($projectId) || $projectId === 'console') { +// return $console; +// } +// +// $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); +// +// return $project; +//}, ['dbForConsole', 'request', 'console', 'auth']); +// +//Http::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']); +// +//Http::setResource('console', function () { +// return new Document([ +// '$id' => ID::custom('console'), +// '$internalId' => ID::custom('console'), +// 'name' => 'Appwrite', +// '$collection' => ID::custom('projects'), +// 'description' => 'Appwrite core engine', +// 'logo' => '', +// 'teamId' => -1, +// 'webhooks' => [], +// 'keys' => [], +// 'platforms' => [ +// [ +// '$collection' => ID::custom('platforms'), +// 'name' => 'Localhost', +// 'type' => Origin::CLIENT_TYPE_WEB, +// 'hostname' => 'localhost', +// ], // Current host is added on app init +// ], +// 'legalName' => '', +// 'legalCountry' => '', +// 'legalState' => '', +// 'legalCity' => '', +// 'legalAddress' => '', +// 'legalTaxId' => '', +// 'auths' => [ +// '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 +// ], +// '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', '') +// ], +// ]); +//}, []); +// +//Http::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project, Authorization $auth, Connections $connections) { +// if ($project->isEmpty() || $project->getId() === 'console') { +// return $dbForConsole; +// } +// +// try { +// $dsn = new DSN($project->getAttribute('database')); +// } catch (\InvalidArgumentException) { +// // TODO: Temporary until all projects are using shared tables +// $dsn = new DSN('mysql://' . $project->getAttribute('database')); +// } +// +// $connection = $pools->get($dsn->getHost())->pop(); +// $connections->add($connection); +// $dbAdapter = $connection->getResource(); +// +// $database = new Database($dbAdapter, $cache); +// $database->setAuthorization($auth); +// +// $database +// ->setMetadata('host', \gethostname()) +// ->setMetadata('project', $project->getId()) +// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); +// +// try { +// $dsn = new DSN($project->getAttribute('database')); +// } catch (\InvalidArgumentException) { +// // TODO: Temporary until all projects are using shared tables +// $dsn = new DSN('mysql://' . $project->getAttribute('database')); +// } +// +// if ($dsn->getHost() === DATABASE_SHARED_TABLES) { +// $database +// ->setSharedTables(true) +// ->setTenant($project->getInternalId()) +// ->setNamespace($dsn->getParam('namespace')); +// } else { +// $database +// ->setSharedTables(false) +// ->setTenant(null) +// ->setNamespace('_' . $project->getInternalId()); +// } +// +// return $database; +//}, ['pools', 'dbForConsole', 'cache', 'project', 'auth', 'connections']); +// +//Http::setResource('dbForConsole', function (Group $pools, Cache $cache, Authorization $auth, Connections $connections) { +// $connection = $pools->get('console')->pop(); +// $connections->add($connection); +// $dbAdapter = $connection->getResource(); +// +// $database = new Database($dbAdapter, $cache); +// $database->setAuthorization($auth); +// +// $database +// ->setNamespace('_console') +// ->setMetadata('host', \gethostname()) +// ->setMetadata('project', 'console') +// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); +// +// return $database; +//}, ['pools', 'cache', 'auth', 'connections']); +// +//Http::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache, Authorization $auth, Connections $connections) { +// $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools +// +// $getProjectDB = function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $auth, $connections) { +// if ($project->isEmpty() || $project->getId() === 'console') { +// return $dbForConsole; +// } +// +// try { +// $dsn = new DSN($project->getAttribute('database')); +// } catch (\InvalidArgumentException) { +// // TODO: Temporary until all projects are using shared tables +// $dsn = new DSN('mysql://' . $project->getAttribute('database')); +// } +// +// $configure = (function (Database $database) use ($project, $dsn) { +// $database +// ->setMetadata('host', \gethostname()) +// ->setMetadata('project', $project->getId()) +// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); +// +// if ($dsn->getHost() === DATABASE_SHARED_TABLES) { +// $database +// ->setSharedTables(true) +// ->setTenant($project->getInternalId()) +// ->setNamespace($dsn->getParam('namespace')); +// } else { +// $database +// ->setSharedTables(false) +// ->setTenant(null) +// ->setNamespace('_' . $project->getInternalId()); +// } +// }); +// +// if (isset($databases[$dsn->getHost()])) { +// $database = $databases[$dsn->getHost()]; +// $configure($database); +// return $database; +// } +// +// $connection = $pools->get($dsn->getHost())->pop(); +// $connections->add($connection); +// $dbAdapter = $connection->getResource(); +// +// $database = new Database($dbAdapter, $cache); +// $database->setAuthorization($auth); +// $databases[$dsn->getHost()] = $database; +// $configure($database); +// +// return $database; +// }; +// +// return $getProjectDB; +//}, ['pools', 'dbForConsole', 'cache', 'auth', 'connections']); +// +//Http::setResource('cache', function (Group $pools, Connections $connections) { +// $list = Config::getParam('pools-cache', []); +// $adapters = []; +// +// foreach ($list as $value) { +// $connection = $pools->get($value)->pop(); +// $connections->add($connection); +// $adapters[] = $connection->getResource(); +// } +// +// return new Cache(new Sharding($adapters)); +//}, ['pools', 'connections']); +// +//Http::setResource('deviceForLocal', function () { +// return new Local(); +//}); +// +//Http::setResource('deviceForFiles', function ($project) { +// return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); +//}, ['project']); +// +//Http::setResource('deviceForFunctions', function ($project) { +// return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); +//}, ['project']); +// +//Http::setResource('deviceForBuilds', function ($project) { +// return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); +//}, ['project']); +// +//function getDevice($root): Device +//{ +// $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); +// +// if (!empty($connection)) { +// $acl = 'private'; +// $device = Storage::DEVICE_LOCAL; +// $accessKey = ''; +// $accessSecret = ''; +// $bucket = ''; +// $region = ''; +// +// try { +// $dsn = new DSN($connection); +// $device = $dsn->getScheme(); +// $accessKey = $dsn->getUser() ?? ''; +// $accessSecret = $dsn->getPassword() ?? ''; +// $bucket = $dsn->getPath() ?? ''; +// $region = $dsn->getParam('region'); +// } catch (\Throwable $e) { +// Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); +// } +// +// switch ($device) { +// case Storage::DEVICE_S3: +// return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case STORAGE::DEVICE_DO_SPACES: +// $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// $device->setHttpVersion(S3::HTTP_VERSION_1_1); +// return $device; +// case Storage::DEVICE_BACKBLAZE: +// return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case Storage::DEVICE_LINODE: +// return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case Storage::DEVICE_WASABI: +// return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case Storage::DEVICE_LOCAL: +// default: +// return new Local($root); +// } +// } else { +// switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { +// case Storage::DEVICE_LOCAL: +// default: +// return new Local($root); +// case Storage::DEVICE_S3: +// $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); +// $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); +// $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); +// $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); +// $s3Acl = 'private'; +// return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); +// case Storage::DEVICE_DO_SPACES: +// $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); +// $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); +// $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); +// $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); +// $doSpacesAcl = 'private'; +// $device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); +// $device->setHttpVersion(S3::HTTP_VERSION_1_1); +// return $device; +// case Storage::DEVICE_BACKBLAZE: +// $backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); +// $backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); +// $backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); +// $backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); +// $backblazeAcl = 'private'; +// return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); +// case Storage::DEVICE_LINODE: +// $linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); +// $linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', ''); +// $linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', ''); +// $linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); +// $linodeAcl = 'private'; +// return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); +// case Storage::DEVICE_WASABI: +// $wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); +// $wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', ''); +// $wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', ''); +// $wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); +// $wasabiAcl = 'private'; +// return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); +// } +// } +//} +// +//Http::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']); +// +//Http::setResource('geodb', function ($register) { +// /** @var Utopia\Registry\Registry $register */ +// return $register->get('geodb'); +//}, ['register']); +// +//Http::setResource('passwordsDictionary', function ($register) { +// /** @var Utopia\Registry\Registry $register */ +// return $register->get('passwordsDictionary'); +//}, ['register']); +// +// +//Http::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; +//}); +// +//Http::setResource('promiseAdapter', function ($register) { +// return $register->get('promiseAdapter'); +//}, ['register']); +// +//Http::setResource('schema', function (Http $utopia, Database $dbForProject, Authorization $auth) { +// +// $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, $auth) { +// $attrs = $auth->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', 'auth']); +// +//Http::setResource('contributors', function () { +// $path = 'app/config/contributors.json'; +// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; +// return $list; +//}); +// +//Http::setResource('employees', function () { +// $path = 'app/config/employees.json'; +// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; +// return $list; +//}); +// +//Http::setResource('heroes', function () { +// $path = 'app/config/heroes.json'; +// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; +// return $list; +//}); +// +//Http::setResource('gitHub', function (Cache $cache) { +// return new VcsGitHub($cache); +//}, ['cache']); +// +//Http::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']); +// +//Http::setResource('plan', function (array $plan = []) { +// return []; +//}); +// +//Http::setResource('auth', fn () => new Authorization()); +// +//Http::setResource('pools', function ($register) { +// return $register->get('pools'); +//}, ['pools']); diff --git a/app/init2.php b/app/init2.php index 9e179427ec..087ea37317 100644 --- a/app/init2.php +++ b/app/init2.php @@ -36,6 +36,7 @@ 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; @@ -380,6 +381,21 @@ $global->set('promiseAdapter', function () { return new Swoole(); }); +$global->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() + ); +}); $log = new Dependency(); $mode = new Dependency(); diff --git a/composer.lock b/composer.lock index ed40b64d09..ce4472d6ed 100644 --- a/composer.lock +++ b/composer.lock @@ -1626,12 +1626,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "bedbca08f451dc96f0321014e805a1f46f76f6b9" + "reference": "d48b696891dee1e46df2491d192bb91cf4df8f94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/bedbca08f451dc96f0321014e805a1f46f76f6b9", - "reference": "bedbca08f451dc96f0321014e805a1f46f76f6b9", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/d48b696891dee1e46df2491d192bb91cf4df8f94", + "reference": "d48b696891dee1e46df2491d192bb91cf4df8f94", "shasum": "" }, "require": { @@ -1669,7 +1669,7 @@ "issues": "https://github.com/utopia-php/cli/issues", "source": "https://github.com/utopia-php/cli/tree/dev-coroutines" }, - "time": "2024-06-07T18:51:16+00:00" + "time": "2024-06-24T13:24:20+00:00" }, { "name": "utopia-php/config", From 78d321b33ba88260c6f4da06b4ccfad21f1b5187 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 24 Jun 2024 15:52:22 -0400 Subject: [PATCH 109/195] chore: rearrange account.php methods --- app/cli.php | 2 +- app/controllers/api/account.php | 187 ++++++++++++++++---------------- 2 files changed, 95 insertions(+), 94 deletions(-) diff --git a/app/cli.php b/app/cli.php index 1b6756174f..75c4c96555 100644 --- a/app/cli.php +++ b/app/cli.php @@ -33,7 +33,7 @@ $queueForCertificates = new Dependency(); $context ->setName('context') - ->setCallback(fn() => $container); + ->setCallback(fn () => $container); $register ->setName('register') diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 05bf87829e..2a016951ff 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2487,6 +2487,100 @@ Http::get('/v1/account/logs') ]), Response::MODEL_LOG_LIST); }); +Http::patch('/v1/account/email') + ->desc('Update email') + ->groups(['api', 'account']) + ->label('event', 'users.[userId].update.email') + ->label('scope', 'account') + ->label('audits.event', 'user.update') + ->label('audits.resource', 'user/{response.$id}') + ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) + ->label('sdk.namespace', 'account') + ->label('sdk.method', 'updateEmail') + ->label('sdk.description', '/docs/references/account/update-email.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_USER) + ->label('sdk.offline.model', '/account') + ->label('sdk.offline.key', 'current') + ->param('email', '', new Email(), 'User email.') + ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') + ->inject('requestTimestamp') + ->inject('response') + ->inject('user') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('project') + ->inject('hooks') + ->inject('authorization') + ->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $authorization) { + // passwordUpdate will be empty if the user has never set a password + $passwordUpdate = $user->getAttribute('passwordUpdate'); + + if ( + !empty($passwordUpdate) && + !Auth::passwordVerify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions')) + ) { // Double check user password + throw new Exception(Exception::USER_INVALID_CREDENTIALS); + } + + $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]); + + $oldEmail = $user->getAttribute('email'); + + $email = \strtolower($email); + + // Makes sure this email is not already used in another identity + $identityWithMatchingEmail = $dbForProject->findOne('identities', [ + Query::equal('providerEmail', [$email]), + Query::notEqual('userInternalId', $user->getInternalId()), + ]); + if ($identityWithMatchingEmail !== false && !$identityWithMatchingEmail->isEmpty()) { + throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ + } + + $user + ->setAttribute('email', $email) + ->setAttribute('emailVerification', false) // After this user needs to confirm mail again + ; + + if (empty($passwordUpdate)) { + $user + ->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS)) + ->setAttribute('hash', Auth::DEFAULT_ALGO) + ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS) + ->setAttribute('passwordUpdate', DateTime::now()); + } + + $target = $authorization->skip(fn () => $dbForProject->findOne('targets', [ + Query::equal('identifier', [$email]), + ])); + + if ($target instanceof Document && !$target->isEmpty()) { + throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS); + } + + try { + $user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user)); + /** + * @var Document $oldTarget + */ + $oldTarget = $user->find('identifier', $oldEmail, 'targets'); + + if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) { + $authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email))); + } + $dbForProject->purgeCachedDocument('users', $user->getId()); + } catch (Duplicate) { + throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ + } + + $queueForEvents->setParam('userId', $user->getId()); + + $response->dynamic($user, Response::MODEL_ACCOUNT); + }); + + Http::patch('/v1/account/name') ->desc('Update name') ->groups(['api', 'account']) @@ -2589,99 +2683,6 @@ Http::patch('/v1/account/password') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -Http::patch('/v1/account/email') - ->desc('Update email') - ->groups(['api', 'account']) - ->label('event', 'users.[userId].update.email') - ->label('scope', 'account') - ->label('audits.event', 'user.update') - ->label('audits.resource', 'user/{response.$id}') - ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) - ->label('sdk.namespace', 'account') - ->label('sdk.method', 'updateEmail') - ->label('sdk.description', '/docs/references/account/update-email.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_USER) - ->label('sdk.offline.model', '/account') - ->label('sdk.offline.key', 'current') - ->param('email', '', new Email(), 'User email.') - ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') - ->inject('requestTimestamp') - ->inject('response') - ->inject('user') - ->inject('dbForProject') - ->inject('queueForEvents') - ->inject('project') - ->inject('hooks') - ->inject('authorization') - ->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $authorization) { - // passwordUpdate will be empty if the user has never set a password - $passwordUpdate = $user->getAttribute('passwordUpdate'); - - if ( - !empty($passwordUpdate) && - !Auth::passwordVerify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions')) - ) { // Double check user password - throw new Exception(Exception::USER_INVALID_CREDENTIALS); - } - - $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]); - - $oldEmail = $user->getAttribute('email'); - - $email = \strtolower($email); - - // Makes sure this email is not already used in another identity - $identityWithMatchingEmail = $dbForProject->findOne('identities', [ - Query::equal('providerEmail', [$email]), - Query::notEqual('userInternalId', $user->getInternalId()), - ]); - if ($identityWithMatchingEmail !== false && !$identityWithMatchingEmail->isEmpty()) { - throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ - } - - $user - ->setAttribute('email', $email) - ->setAttribute('emailVerification', false) // After this user needs to confirm mail again - ; - - if (empty($passwordUpdate)) { - $user - ->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS)) - ->setAttribute('hash', Auth::DEFAULT_ALGO) - ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS) - ->setAttribute('passwordUpdate', DateTime::now()); - } - - $target = $authorization->skip(fn () => $dbForProject->findOne('targets', [ - Query::equal('identifier', [$email]), - ])); - - if ($target instanceof Document && !$target->isEmpty()) { - throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS); - } - - try { - $user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user)); - /** - * @var Document $oldTarget - */ - $oldTarget = $user->find('identifier', $oldEmail, 'targets'); - - if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) { - $authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email))); - } - $dbForProject->purgeCachedDocument('users', $user->getId()); - } catch (Duplicate) { - throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ - } - - $queueForEvents->setParam('userId', $user->getId()); - - $response->dynamic($user, Response::MODEL_ACCOUNT); - }); - Http::patch('/v1/account/phone') ->desc('Update phone') ->groups(['api', 'account']) From 034814c92441a10e81d4cad9604be48ee970be13 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 25 Jun 2024 09:38:33 -0400 Subject: [PATCH 110/195] chore: Removing default function timeout --- app/controllers/api/functions.php | 3 +-- app/controllers/general.php | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index b5e02b9dad..7571b4eaf6 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1730,8 +1730,7 @@ Http::post('/v1/functions/:functionId/executions') path: $path, method: $method, headers: $headers, - runtimeEntrypoint: $command, - requestTimeout: 30 + runtimeEntrypoint: $command ); $headersFiltered = []; diff --git a/app/controllers/general.php b/app/controllers/general.php index 1839777a2e..f11943b9d6 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -268,8 +268,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request path: $path, method: $method, headers: $headers, - runtimeEntrypoint: $command, - requestTimeout: 30 + runtimeEntrypoint: $command ); $headersFiltered = []; From 6f62f915d9ae9acaedd8863f69cd793c0e09841d Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 28 Jun 2024 14:33:13 -0400 Subject: [PATCH 111/195] feat: Adding Authentication injection for user id and secret --- app/console | 2 +- app/controllers/api/account.php | 45 +++++++++++++++++----------- app/controllers/api/functions.php | 6 ++-- app/init2.php | 34 ++++++++++++++------- app/realtime.php | 10 ++++--- src/Appwrite/Auth/Auth.php | 14 --------- src/Appwrite/Auth/Authentication.php | 41 +++++++++++++++++++++++++ 7 files changed, 102 insertions(+), 50 deletions(-) create mode 100644 src/Appwrite/Auth/Authentication.php diff --git a/app/console b/app/console index 5169fe16d6..f483d9631d 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 5169fe16d63066f64ab5013c78953aea04e24b53 +Subproject commit f483d9631d6f21e94aedb20b5c37c56fea06c23e diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 2a016951ff..107c7d0d1f 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2,6 +2,7 @@ use Ahc\Jwt\JWT; use Appwrite\Auth\Auth; +use Appwrite\Auth\Authentication; use Appwrite\Auth\MFA\Challenge; use Appwrite\Auth\MFA\Type; use Appwrite\Auth\MFA\Type\TOTP; @@ -399,14 +400,15 @@ Http::get('/v1/account/sessions') ->inject('user') ->inject('locale') ->inject('authorization') - ->action(function (Response $response, Document $user, Locale $locale, Authorization $authorization) { + ->inject('authentication') + ->action(function (Response $response, Document $user, Locale $locale, Authorization $authorization, Authentication $authentication) { $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); $sessions = $user->getAttribute('sessions', []); - $current = Auth::sessionVerify($sessions, Auth::$secret); + $current = Auth::sessionVerify($sessions, $authentication->getSecret()); foreach ($sessions as $key => $session) {/** @var Document $session */ $countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); @@ -445,7 +447,8 @@ Http::delete('/v1/account/sessions') ->inject('locale') ->inject('queueForEvents') ->inject('queueForDeletes') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes) { + ->inject('authentication') + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes, Authentication $authentication) { $protocol = $request->getProtocol(); $sessions = $user->getAttribute('sessions', []); @@ -461,7 +464,7 @@ Http::delete('/v1/account/sessions') ->setAttribute('current', false) ->setAttribute('countryName', $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'))); - if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { + if ($session->getAttribute('secret') == Auth::hash($authentication->getSecret())) { $session->setAttribute('current', true); // If current session delete the cookies too @@ -507,7 +510,8 @@ Http::get('/v1/account/sessions/:sessionId') ->inject('user') ->inject('locale') ->inject('authorization') - ->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Authorization $authorization) { + ->inject('authentication') + ->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Authorization $authorization, Authentication $authentication) { $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); @@ -515,7 +519,7 @@ Http::get('/v1/account/sessions/:sessionId') $sessions = $user->getAttribute('sessions', []); $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) + ? Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret()) : $sessionId; foreach ($sessions as $session) {/** @var Document $session */ @@ -523,7 +527,7 @@ Http::get('/v1/account/sessions/:sessionId') $countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); $session - ->setAttribute('current', ($session->getAttribute('secret') == Auth::hash(Auth::$secret))) + ->setAttribute('current', ($session->getAttribute('secret') == Auth::hash($authentication->getSecret()))) ->setAttribute('countryName', $countryName) ->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $session->getAttribute('secret', '') : '') ; @@ -558,11 +562,12 @@ Http::delete('/v1/account/sessions/:sessionId') ->inject('locale') ->inject('queueForEvents') ->inject('queueForDeletes') - ->action(function (?string $sessionId, ?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes) { + ->inject('authentication') + ->action(function (?string $sessionId, ?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes, Authentication $authentication) { $protocol = $request->getProtocol(); $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) + ? Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret()) : $sessionId; $sessions = $user->getAttribute('sessions', []); @@ -581,7 +586,7 @@ Http::delete('/v1/account/sessions/:sessionId') $session->setAttribute('current', false); - if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + if ($session->getAttribute('secret') == Auth::hash($authentication->getSecret())) { // If current session delete the cookies too $session ->setAttribute('current', true) ->setAttribute('countryName', $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'))); @@ -636,10 +641,11 @@ Http::patch('/v1/account/sessions/:sessionId') ->inject('dbForProject') ->inject('project') ->inject('queueForEvents') - ->action(function (?string $sessionId, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents) { + ->inject('authentication') + ->action(function (?string $sessionId, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Authentication $authentication) { $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) + ? Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret()) : $sessionId; $sessions = $user->getAttribute('sessions', []); @@ -1142,7 +1148,8 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('geodb') ->inject('queueForEvents') ->inject('authorization') - ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization) use ($oauthDefaultSuccess) { + ->inject('authentication') + ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization, Authentication $authentication) use ($oauthDefaultSuccess) { $protocol = $request->getProtocol(); $callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId(); $defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => '']; @@ -1279,7 +1286,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') } $sessions = $user->getAttribute('sessions', []); - $current = Auth::sessionVerify($sessions, Auth::$secret); + $current = Auth::sessionVerify($sessions, $authentication->getSecret()); if ($current) { // Delete current session of new one. $currentDocument = $dbForProject->getDocument('sessions', $current); @@ -2369,14 +2376,15 @@ Http::post('/v1/account/jwt') ->inject('response') ->inject('user') ->inject('dbForProject') - ->action(function (Response $response, Document $user, Database $dbForProject) { + ->inject('authentication') + ->action(function (Response $response, Document $user, Database $dbForProject, Authentication $authentication) { $sessions = $user->getAttribute('sessions', []); $current = new Document(); foreach ($sessions as $session) { /** @var Utopia\Database\Document $session */ - if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + if ($session->getAttribute('secret') == Auth::hash($authentication->getSecret())) { // If current session delete the cookies too $current = $session; } } @@ -4197,7 +4205,8 @@ Http::post('/v1/account/targets/push') ->inject('response') ->inject('dbForProject') ->inject('authorization') - ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) { + ->inject('authentication') + ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization, Authentication $authentication) { $targetId = $targetId == 'unique()' ? ID::unique() : $targetId; $provider = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId)); @@ -4213,7 +4222,7 @@ Http::post('/v1/account/targets/push') $device = $detector->getDevice(); - $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); + $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret()); $session = $dbForProject->getDocument('sessions', $sessionId); try { diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 7571b4eaf6..36a5a76e89 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -2,6 +2,7 @@ use Ahc\Jwt\JWT; use Appwrite\Auth\Auth; +use Appwrite\Auth\Authentication; use Appwrite\Event\Build; use Appwrite\Event\Delete; use Appwrite\Event\Event; @@ -1532,7 +1533,8 @@ Http::post('/v1/functions/:functionId/executions') ->inject('queueForFunctions') ->inject('geodb') ->inject('authorization') - ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode, Func $queueForFunctions, Reader $geodb, Authorization $authorization) { + ->inject('authentication') + ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode, Func $queueForFunctions, Reader $geodb, Authorization $authorization, Authentication $authentication) { $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); @@ -1583,7 +1585,7 @@ Http::post('/v1/functions/:functionId/executions') foreach ($sessions as $session) { /** @var Utopia\Database\Document $session */ - if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + if ($session->getAttribute('secret') == Auth::hash($authentication->getSecret())) { // If current session delete the cookies too $current = $session; } } diff --git a/app/init2.php b/app/init2.php index 087ea37317..9e5c9ad25a 100644 --- a/app/init2.php +++ b/app/init2.php @@ -5,6 +5,7 @@ require_once __DIR__ . '/../vendor/autoload.php'; use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; use Appwrite\Auth\Auth; +use Appwrite\Auth\Authentication; use Appwrite\Event\Audit; use Appwrite\Event\Build; use Appwrite\Event\Certificate; @@ -423,8 +424,9 @@ $getProjectDB = new Dependency(); $dbForProject = new Dependency(); $dbForConsole = new Dependency(); $queueForUsage = new Dependency(); -$authorization = new Dependency(); $queueForMails = new Dependency(); +$authorization = new Dependency(); +$authentication = new Dependency(); $queueForBuilds = new Dependency(); $deviceForLocal = new Dependency(); $deviceForFiles = new Dependency(); @@ -470,7 +472,8 @@ $user ->inject('dbForProject') ->inject('dbForConsole') ->inject('authorization') - ->setCallback(function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization) { + ->inject('authentication') + ->setCallback(function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization, Authentication $authentication) { $authorization->setDefaultStatus(true); Auth::setCookieName('a_session_' . $project->getId()); @@ -509,26 +512,26 @@ $user $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); } - Auth::$unique = $session['id'] ?? ''; - Auth::$secret = $session['secret'] ?? ''; + $authentication->setUnique($session['id'] ?? ''); + $authentication->setSecret($session['secret'] ?? ''); if (APP_MODE_ADMIN !== $mode) { if ($project->isEmpty()) { $user = new Document([]); } else { if ($project->getId() === 'console') { - $user = $dbForConsole->getDocument('users', Auth::$unique); + $user = $dbForConsole->getDocument('users', $authentication->getUnique()); } else { - $user = $dbForProject->getDocument('users', Auth::$unique); + $user = $dbForProject->getDocument('users', $authentication->getUnique()); } } } else { - $user = $dbForConsole->getDocument('users', Auth::$unique); + $user = $dbForConsole->getDocument('users', $authentication->getUnique()); } if ( $user->isEmpty() // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) + || !Auth::sessionVerify($user->getAttribute('sessions', []), $authentication->getSecret()) ) { // Validate user has valid login token $user = new Document([]); } @@ -577,14 +580,16 @@ $session ->setName('session') ->inject('user') ->inject('project') - ->setCallback(function (Document $user, Document $project) { + ->inject('authorization') + ->inject('authentication') + ->setCallback(function (Document $user, Document $project, Authorization $authorization, Authentication $authentication) { if ($user->isEmpty()) { return; } $sessions = $user->getAttribute('sessions', []); $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; - $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret, $authDuration); + $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret(), $authDuration); if (!$sessionId) { return; @@ -784,6 +789,12 @@ $authorization return new Authorization(); }); +$authentication + ->setName('authentication') + ->setCallback(function (): Authentication { + return new Authentication(); + }); + $registry ->setName('registry') ->setCallback(function () use (&$global): Registry { @@ -1259,9 +1270,10 @@ $container->set($localeCodes); $container->set($dbForProject); $container->set($dbForConsole); $container->set($getProjectDB); -$container->set($authorization); $container->set($queueForUsage); $container->set($queueForMails); +$container->set($authorization); +$container->set($authentication); $container->set($schemaVariable); $container->set($queueForBuilds); $container->set($queueForEvents); diff --git a/app/realtime.php b/app/realtime.php index a30a094115..2e87b83634 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -483,6 +483,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $co $projectId = $realtime->connections[$connection]['projectId']; $database = $container->get('dbForConsole'); $authorization = $container->get('authorization'); + $authentication = $container->get('authentication'); if ($projectId !== 'console') { @@ -525,14 +526,15 @@ $server->onMessage(function (int $connection, string $message) use ($server, $co } $session = Auth::decodeSession($message['data']['session']); - Auth::$unique = $session['id'] ?? ''; - Auth::$secret = $session['secret'] ?? ''; - $user = $database->getDocument('users', Auth::$unique); + $authentication->setUnique($session['id'] ?? ''); + $authentication->setSecret($session['secret'] ?? ''); + + $user = $database->getDocument('users', $authorization->getUnique()); if ( empty($user->getId()) // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) // Validate user has valid login token + || !Auth::sessionVerify($user->getAttribute('sessions', []), $authentication->getSecret()) // Validate user has valid login token ) { // cookie not valid throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.'); diff --git a/src/Appwrite/Auth/Auth.php b/src/Appwrite/Auth/Auth.php index 291c16bf0d..877dbf5f78 100644 --- a/src/Appwrite/Auth/Auth.php +++ b/src/Appwrite/Auth/Auth.php @@ -96,20 +96,6 @@ class Auth */ public static $cookieName = 'a_session'; - /** - * User Unique ID. - * - * @var string - */ - public static $unique = ''; - - /** - * User Secret Key. - * - * @var string - */ - public static $secret = ''; - /** * Set Cookie Name. * diff --git a/src/Appwrite/Auth/Authentication.php b/src/Appwrite/Auth/Authentication.php new file mode 100644 index 0000000000..5ff33c247e --- /dev/null +++ b/src/Appwrite/Auth/Authentication.php @@ -0,0 +1,41 @@ +unique; + } + + public function setUnique(string $unique): void + { + $this->unique = $unique; + } + + public function getSecret(): string + { + return $this->secret; + } + + public function setSecret(string $secret): void + { + $this->secret = $secret; + } + +} From 5bcb22cd4f494aff853f3907f0b58c67a92ddd8c Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 28 Jun 2024 15:44:52 -0400 Subject: [PATCH 112/195] test: Latest swoole --- Dockerfile | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 229 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index af0d59390c..8ce696fc62 100755 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,235 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT RUN npm ci RUN npm run build -FROM appwrite/base:0.7.2 as final +FROM php:8.2.20-cli-alpine3.20 as compile + +ENV PHP_REDIS_VERSION="6.0.2" \ + PHP_MONGODB_VERSION="1.16.1" \ + PHP_SWOOLE_VERSION="v5.1.3" \ + PHP_IMAGICK_VERSION="3.7.0" \ + PHP_YAML_VERSION="2.2.3" \ + PHP_MAXMINDDB_VERSION="v1.11.1" \ + PHP_SCRYPT_VERSION="2.0.1" \ + PHP_ZSTD_VERSION="0.13.3" \ + PHP_BROTLI_VERSION="0.15.0" \ + PHP_SNAPPY_VERSION="c27f830dcfe6c41eb2619a374de10fd0597f4939" \ + PHP_LZ4_VERSION="2f006c3e4f1fb3a60d2656fc164f9ba26b71e995" \ + PHP_XDEBUG_VERSION="3.3.1" + +RUN \ + apk add --no-cache --virtual .deps \ + linux-headers \ + make \ + automake \ + autoconf \ + gcc \ + g++ \ + git \ + zlib-dev \ + openssl-dev \ + yaml-dev \ + imagemagick \ + imagemagick-dev \ + libjpeg-turbo-dev \ + jpeg-dev \ + libjxl-dev \ + libmaxminddb-dev \ + zstd-dev \ + brotli-dev \ + lz4-dev \ + curl-dev + +RUN docker-php-ext-install sockets + +FROM compile AS redis +RUN \ + # Redis Extension + git clone --depth 1 --branch $PHP_REDIS_VERSION https://github.com/phpredis/phpredis.git && \ + cd phpredis && \ + phpize && \ + ./configure && \ + make && make install + +## Swoole Extension +FROM compile AS swoole +RUN \ + git clone --depth 1 --branch $PHP_SWOOLE_VERSION https://github.com/swoole/swoole-src.git && \ + cd swoole-src && \ + phpize && \ + ./configure --enable-sockets --enable-http2 --enable-openssl --enable-swoole-curl && \ + make && make install && \ + cd .. + +## Imagick Extension +FROM compile AS imagick +RUN \ + git clone --depth 1 --branch $PHP_IMAGICK_VERSION https://github.com/imagick/imagick && \ + cd imagick && \ + phpize && \ + ./configure && \ + make && make install + +## YAML Extension +FROM compile AS yaml +RUN \ + git clone --depth 1 --branch $PHP_YAML_VERSION https://github.com/php/pecl-file_formats-yaml && \ + cd pecl-file_formats-yaml && \ + phpize && \ + ./configure && \ + make && make install + +## Maxminddb extension +FROM compile AS maxmind +RUN \ + git clone --depth 1 --branch $PHP_MAXMINDDB_VERSION https://github.com/maxmind/MaxMind-DB-Reader-php.git && \ + cd MaxMind-DB-Reader-php && \ + cd ext && \ + phpize && \ + ./configure && \ + make && make install + +# Mongodb Extension +FROM compile as mongodb +RUN \ + git clone --depth 1 --branch $PHP_MONGODB_VERSION https://github.com/mongodb/mongo-php-driver.git && \ + cd mongo-php-driver && \ + git submodule update --init && \ + phpize && \ + ./configure && \ + make && make install + +# Zstd Compression +FROM compile as zstd +RUN git clone --recursive -n https://github.com/kjdev/php-ext-zstd.git \ + && cd php-ext-zstd \ + && git checkout $PHP_ZSTD_VERSION \ + && phpize \ + && ./configure --with-libzstd \ + && make && make install + +## Brotli Extension +FROM compile as brotli +RUN git clone https://github.com/kjdev/php-ext-brotli.git \ + && cd php-ext-brotli \ + && git reset --hard $PHP_BROTLI_VERSION \ + && phpize \ + && ./configure --with-libbrotli \ + && make && make install + +## LZ4 Extension +FROM compile AS lz4 +RUN git clone --recursive https://github.com/kjdev/php-ext-lz4.git \ + && cd php-ext-lz4 \ + && git reset --hard $PHP_LZ4_VERSION \ + && phpize \ + && ./configure --with-lz4-includedir=/usr \ + && make && make install + +## Snappy Extension +FROM compile AS snappy +RUN git clone --recursive https://github.com/kjdev/php-ext-snappy.git \ + && cd php-ext-snappy \ + && git reset --hard $PHP_SNAPPY_VERSION \ + && phpize \ + && ./configure \ + && make && make install + +## Scrypt Extension +FROM compile AS scrypt +RUN git clone --depth 1 https://github.com/DomBlack/php-scrypt.git \ + && cd php-scrypt \ + && git reset --hard $PHP_SCRYPT_VERSION \ + && phpize \ + && ./configure --enable-scrypt \ + && make && make install + +## XDebug Extension +FROM compile AS xdebug +RUN \ + git clone --depth 1 --branch $PHP_XDEBUG_VERSION https://github.com/xdebug/xdebug && \ + cd xdebug && \ + phpize && \ + ./configure && \ + make && make install + +FROM php:8.2.20-cli-alpine3.20 as finald + +LABEL maintainer="team@appwrite.io" + +ENV DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker} +ENV DOCKER_COMPOSE_VERSION="v2.24.6" + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN set -ex \ + && apk --no-cache add \ + postgresql-dev + +RUN \ + apk update \ + && apk add --no-cache --virtual .deps \ + linux-headers \ + make \ + automake \ + autoconf \ + gcc \ + g++ \ + curl-dev \ + && apk add --no-cache \ + libstdc++ \ + rsync \ + brotli-dev \ + lz4-dev \ + yaml-dev \ + imagemagick \ + imagemagick-dev \ + libjpeg-turbo-dev \ + jpeg-dev \ + libjxl-dev \ + libavif \ + libheif \ + imagemagick-heic \ + libmaxminddb-dev \ + certbot \ + docker-cli \ + libgomp \ + git \ + && docker-php-ext-install sockets pdo_mysql pdo_pgsql intl \ + && apk del .deps \ + && rm -rf /var/cache/apk/* + +RUN \ + mkdir -p $DOCKER_CONFIG/cli-plugins \ + && ARCH=$(uname -m) && if [ $ARCH == "armv7l" ]; then ARCH="armv7"; fi \ + && curl -SL https://github.com/docker/compose/releases/download/$DOCKER_COMPOSE_VERSION/docker-compose-linux-$ARCH -o $DOCKER_CONFIG/cli-plugins/docker-compose \ + && chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose + +WORKDIR /usr/src/code + +COPY --from=swoole /usr/local/lib/php/extensions/no-debug-non-zts-20220829/swoole.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ +COPY --from=redis /usr/local/lib/php/extensions/no-debug-non-zts-20220829/redis.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ +COPY --from=imagick /usr/local/lib/php/extensions/no-debug-non-zts-20220829/imagick.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ +COPY --from=yaml /usr/local/lib/php/extensions/no-debug-non-zts-20220829/yaml.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ +COPY --from=maxmind /usr/local/lib/php/extensions/no-debug-non-zts-20220829/maxminddb.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ +COPY --from=mongodb /usr/local/lib/php/extensions/no-debug-non-zts-20220829/mongodb.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ +COPY --from=scrypt /usr/local/lib/php/extensions/no-debug-non-zts-20220829/scrypt.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ +COPY --from=zstd /usr/local/lib/php/extensions/no-debug-non-zts-20220829/zstd.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ +COPY --from=brotli /usr/local/lib/php/extensions/no-debug-non-zts-20220829/brotli.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ +COPY --from=lz4 /usr/local/lib/php/extensions/no-debug-non-zts-20220829/lz4.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ +COPY --from=snappy /usr/local/lib/php/extensions/no-debug-non-zts-20220829/snappy.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ +COPY --from=xdebug /usr/local/lib/php/extensions/no-debug-non-zts-20220829/xdebug.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ + +# Enable Extensions +RUN echo extension=swoole.so >> /usr/local/etc/php/conf.d/swoole.ini +RUN echo extension=redis.so >> /usr/local/etc/php/conf.d/redis.ini +RUN echo extension=imagick.so >> /usr/local/etc/php/conf.d/imagick.ini +RUN echo extension=yaml.so >> /usr/local/etc/php/conf.d/yaml.ini +RUN echo extension=maxminddb.so >> /usr/local/etc/php/conf.d/maxminddb.ini +RUN echo extension=scrypt.so >> /usr/local/etc/php/conf.d/scrypt.ini +RUN echo extension=zstd.so >> /usr/local/etc/php/conf.d/zstd.ini +RUN echo extension=brotli.so >> /usr/local/etc/php/conf.d/brotli.ini +RUN echo extension=lz4.so >> /usr/local/etc/php/conf.d/lz4.ini +RUN echo extension=snappy.so >> /usr/local/etc/php/conf.d/snappy.ini LABEL maintainer="team@appwrite.io" From b05671b8b3f1d49c878842c94f2b0e699958ab3e Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 1 Jul 2024 08:36:46 -0400 Subject: [PATCH 113/195] refactor: Rollback --- Dockerfile | 230 +---------------------------------------------------- 1 file changed, 1 insertion(+), 229 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8ce696fc62..af0d59390c 100755 --- a/Dockerfile +++ b/Dockerfile @@ -29,235 +29,7 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT RUN npm ci RUN npm run build -FROM php:8.2.20-cli-alpine3.20 as compile - -ENV PHP_REDIS_VERSION="6.0.2" \ - PHP_MONGODB_VERSION="1.16.1" \ - PHP_SWOOLE_VERSION="v5.1.3" \ - PHP_IMAGICK_VERSION="3.7.0" \ - PHP_YAML_VERSION="2.2.3" \ - PHP_MAXMINDDB_VERSION="v1.11.1" \ - PHP_SCRYPT_VERSION="2.0.1" \ - PHP_ZSTD_VERSION="0.13.3" \ - PHP_BROTLI_VERSION="0.15.0" \ - PHP_SNAPPY_VERSION="c27f830dcfe6c41eb2619a374de10fd0597f4939" \ - PHP_LZ4_VERSION="2f006c3e4f1fb3a60d2656fc164f9ba26b71e995" \ - PHP_XDEBUG_VERSION="3.3.1" - -RUN \ - apk add --no-cache --virtual .deps \ - linux-headers \ - make \ - automake \ - autoconf \ - gcc \ - g++ \ - git \ - zlib-dev \ - openssl-dev \ - yaml-dev \ - imagemagick \ - imagemagick-dev \ - libjpeg-turbo-dev \ - jpeg-dev \ - libjxl-dev \ - libmaxminddb-dev \ - zstd-dev \ - brotli-dev \ - lz4-dev \ - curl-dev - -RUN docker-php-ext-install sockets - -FROM compile AS redis -RUN \ - # Redis Extension - git clone --depth 1 --branch $PHP_REDIS_VERSION https://github.com/phpredis/phpredis.git && \ - cd phpredis && \ - phpize && \ - ./configure && \ - make && make install - -## Swoole Extension -FROM compile AS swoole -RUN \ - git clone --depth 1 --branch $PHP_SWOOLE_VERSION https://github.com/swoole/swoole-src.git && \ - cd swoole-src && \ - phpize && \ - ./configure --enable-sockets --enable-http2 --enable-openssl --enable-swoole-curl && \ - make && make install && \ - cd .. - -## Imagick Extension -FROM compile AS imagick -RUN \ - git clone --depth 1 --branch $PHP_IMAGICK_VERSION https://github.com/imagick/imagick && \ - cd imagick && \ - phpize && \ - ./configure && \ - make && make install - -## YAML Extension -FROM compile AS yaml -RUN \ - git clone --depth 1 --branch $PHP_YAML_VERSION https://github.com/php/pecl-file_formats-yaml && \ - cd pecl-file_formats-yaml && \ - phpize && \ - ./configure && \ - make && make install - -## Maxminddb extension -FROM compile AS maxmind -RUN \ - git clone --depth 1 --branch $PHP_MAXMINDDB_VERSION https://github.com/maxmind/MaxMind-DB-Reader-php.git && \ - cd MaxMind-DB-Reader-php && \ - cd ext && \ - phpize && \ - ./configure && \ - make && make install - -# Mongodb Extension -FROM compile as mongodb -RUN \ - git clone --depth 1 --branch $PHP_MONGODB_VERSION https://github.com/mongodb/mongo-php-driver.git && \ - cd mongo-php-driver && \ - git submodule update --init && \ - phpize && \ - ./configure && \ - make && make install - -# Zstd Compression -FROM compile as zstd -RUN git clone --recursive -n https://github.com/kjdev/php-ext-zstd.git \ - && cd php-ext-zstd \ - && git checkout $PHP_ZSTD_VERSION \ - && phpize \ - && ./configure --with-libzstd \ - && make && make install - -## Brotli Extension -FROM compile as brotli -RUN git clone https://github.com/kjdev/php-ext-brotli.git \ - && cd php-ext-brotli \ - && git reset --hard $PHP_BROTLI_VERSION \ - && phpize \ - && ./configure --with-libbrotli \ - && make && make install - -## LZ4 Extension -FROM compile AS lz4 -RUN git clone --recursive https://github.com/kjdev/php-ext-lz4.git \ - && cd php-ext-lz4 \ - && git reset --hard $PHP_LZ4_VERSION \ - && phpize \ - && ./configure --with-lz4-includedir=/usr \ - && make && make install - -## Snappy Extension -FROM compile AS snappy -RUN git clone --recursive https://github.com/kjdev/php-ext-snappy.git \ - && cd php-ext-snappy \ - && git reset --hard $PHP_SNAPPY_VERSION \ - && phpize \ - && ./configure \ - && make && make install - -## Scrypt Extension -FROM compile AS scrypt -RUN git clone --depth 1 https://github.com/DomBlack/php-scrypt.git \ - && cd php-scrypt \ - && git reset --hard $PHP_SCRYPT_VERSION \ - && phpize \ - && ./configure --enable-scrypt \ - && make && make install - -## XDebug Extension -FROM compile AS xdebug -RUN \ - git clone --depth 1 --branch $PHP_XDEBUG_VERSION https://github.com/xdebug/xdebug && \ - cd xdebug && \ - phpize && \ - ./configure && \ - make && make install - -FROM php:8.2.20-cli-alpine3.20 as finald - -LABEL maintainer="team@appwrite.io" - -ENV DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker} -ENV DOCKER_COMPOSE_VERSION="v2.24.6" - -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone - -RUN set -ex \ - && apk --no-cache add \ - postgresql-dev - -RUN \ - apk update \ - && apk add --no-cache --virtual .deps \ - linux-headers \ - make \ - automake \ - autoconf \ - gcc \ - g++ \ - curl-dev \ - && apk add --no-cache \ - libstdc++ \ - rsync \ - brotli-dev \ - lz4-dev \ - yaml-dev \ - imagemagick \ - imagemagick-dev \ - libjpeg-turbo-dev \ - jpeg-dev \ - libjxl-dev \ - libavif \ - libheif \ - imagemagick-heic \ - libmaxminddb-dev \ - certbot \ - docker-cli \ - libgomp \ - git \ - && docker-php-ext-install sockets pdo_mysql pdo_pgsql intl \ - && apk del .deps \ - && rm -rf /var/cache/apk/* - -RUN \ - mkdir -p $DOCKER_CONFIG/cli-plugins \ - && ARCH=$(uname -m) && if [ $ARCH == "armv7l" ]; then ARCH="armv7"; fi \ - && curl -SL https://github.com/docker/compose/releases/download/$DOCKER_COMPOSE_VERSION/docker-compose-linux-$ARCH -o $DOCKER_CONFIG/cli-plugins/docker-compose \ - && chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose - -WORKDIR /usr/src/code - -COPY --from=swoole /usr/local/lib/php/extensions/no-debug-non-zts-20220829/swoole.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ -COPY --from=redis /usr/local/lib/php/extensions/no-debug-non-zts-20220829/redis.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ -COPY --from=imagick /usr/local/lib/php/extensions/no-debug-non-zts-20220829/imagick.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ -COPY --from=yaml /usr/local/lib/php/extensions/no-debug-non-zts-20220829/yaml.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ -COPY --from=maxmind /usr/local/lib/php/extensions/no-debug-non-zts-20220829/maxminddb.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ -COPY --from=mongodb /usr/local/lib/php/extensions/no-debug-non-zts-20220829/mongodb.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ -COPY --from=scrypt /usr/local/lib/php/extensions/no-debug-non-zts-20220829/scrypt.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ -COPY --from=zstd /usr/local/lib/php/extensions/no-debug-non-zts-20220829/zstd.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ -COPY --from=brotli /usr/local/lib/php/extensions/no-debug-non-zts-20220829/brotli.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ -COPY --from=lz4 /usr/local/lib/php/extensions/no-debug-non-zts-20220829/lz4.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ -COPY --from=snappy /usr/local/lib/php/extensions/no-debug-non-zts-20220829/snappy.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ -COPY --from=xdebug /usr/local/lib/php/extensions/no-debug-non-zts-20220829/xdebug.so /usr/local/lib/php/extensions/no-debug-non-zts-20220829/ - -# Enable Extensions -RUN echo extension=swoole.so >> /usr/local/etc/php/conf.d/swoole.ini -RUN echo extension=redis.so >> /usr/local/etc/php/conf.d/redis.ini -RUN echo extension=imagick.so >> /usr/local/etc/php/conf.d/imagick.ini -RUN echo extension=yaml.so >> /usr/local/etc/php/conf.d/yaml.ini -RUN echo extension=maxminddb.so >> /usr/local/etc/php/conf.d/maxminddb.ini -RUN echo extension=scrypt.so >> /usr/local/etc/php/conf.d/scrypt.ini -RUN echo extension=zstd.so >> /usr/local/etc/php/conf.d/zstd.ini -RUN echo extension=brotli.so >> /usr/local/etc/php/conf.d/brotli.ini -RUN echo extension=lz4.so >> /usr/local/etc/php/conf.d/lz4.ini -RUN echo extension=snappy.so >> /usr/local/etc/php/conf.d/snappy.ini +FROM appwrite/base:0.7.2 as final LABEL maintainer="team@appwrite.io" From 77b593dae514445dd4d3c2f47224cbeedce60f67 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:25:32 -0400 Subject: [PATCH 114/195] feat: Coroutine adjustments --- .env | 4 +- Dockerfile | 2 +- app/http.php | 13 +- app/realtime.php | 2 +- docker-compose.local.prod.yml | 1059 +++++++++++++++++++++++++++++++++ 5 files changed, 1072 insertions(+), 8 deletions(-) create mode 100644 docker-compose.local.prod.yml diff --git a/.env b/.env index 2283ceeb30..4e211a95b7 100644 --- a/.env +++ b/.env @@ -1,7 +1,7 @@ _APP_ENV=development _APP_EDITION=self-hosted _APP_LOCALE=en -_APP_WORKER_PER_CORE=6 +_APP_WORKER_PER_CORE=4 _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= @@ -105,4 +105,4 @@ _APP_MESSAGE_SMS_TEST_DSN= _APP_MESSAGE_EMAIL_TEST_DSN= _APP_MESSAGE_PUSH_TEST_DSN= _APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10 -_APP_PROJECT_REGIONS=default \ No newline at end of file +_APP_PROJECT_REGIONS=default diff --git a/Dockerfile b/Dockerfile index af0d59390c..2d99d4ed3e 100755 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT RUN npm ci RUN npm run build -FROM appwrite/base:0.7.2 as final +FROM appwrite/base:0.9.1 as final LABEL maintainer="team@appwrite.io" diff --git a/app/http.php b/app/http.php index 546338555c..5b0d2b5c9e 100644 --- a/app/http.php +++ b/app/http.php @@ -26,7 +26,7 @@ use Utopia\System\System; global $global, $container; $payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing -$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); +$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 4)); $server = new Server('0.0.0.0', '80', [ 'open_http2_protocol' => true, @@ -35,17 +35,22 @@ $server = new Server('0.0.0.0', '80', [ 'package_max_length' => $payloadSize, 'buffer_output_size' => $payloadSize, + // TCP Keep-Alive check + 'open_tcp_keepalive' => true, + 'tcp_keepidle' => 4, + 'tcp_keepinterval' => 1, + 'tcp_keepcount' => 5, + // Server // 'log_level' => 0, - 'dispatch_mode' => 2, + 'dispatch_mode' => 1, 'worker_num' => $workerNumber, 'reactor_num' => swoole_cpu_num() * 2, - // 'task_worker_num' => $workerNumber, 'open_cpu_affinity' => true, - // Coroutine 'enable_coroutine' => true, 'max_coroutine' => 10000, + 'send_yield' => true ]); $http = new Http($server, $container, 'UTC'); diff --git a/app/realtime.php b/app/realtime.php index 2e87b83634..117941a86e 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -530,7 +530,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $co $authentication->setUnique($session['id'] ?? ''); $authentication->setSecret($session['secret'] ?? ''); - $user = $database->getDocument('users', $authorization->getUnique()); + $user = $database->getDocument('users', $authentication->getUnique()); if ( empty($user->getId()) // Check a document has been found in the DB diff --git a/docker-compose.local.prod.yml b/docker-compose.local.prod.yml new file mode 100644 index 0000000000..d4a87226e1 --- /dev/null +++ b/docker-compose.local.prod.yml @@ -0,0 +1,1059 @@ +# WARNING! +# This is a development version of THE Appwrite docker-compose.yml file. +# Avoid using this file in your production environment. +# We're exposing here sensitive ports and mounting code volumes for rapid development and debugging of the server stack. + +x-logging: &x-logging + logging: + driver: "json-file" + options: + max-file: "5" + max-size: "10m" + +services: + traefik: + image: traefik:2.11 + <<: *x-logging + container_name: appwrite-traefik + command: + - --providers.file.directory=/storage/config + - --providers.file.watch=true + - --providers.docker=true + - --providers.docker.exposedByDefault=false + - --providers.docker.constraints=Label(`traefik.constraint-label-stack`,`appwrite`) + - --entrypoints.appwrite_web.address=:80 + - --entrypoints.appwrite_websecure.address=:443 + - --accesslog=true + ports: + - 80:80 + - 8080:80 + - 443:443 + - 9500:8080 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - appwrite-config:/storage/config:ro + - appwrite-certificates:/storage/certificates:ro + depends_on: + - appwrite + networks: + - gateway + - appwrite + - runtimes + + appwrite: + container_name: appwrite + <<: *x-logging + image: appwrite-dev + build: + context: . + args: + DEBUG: false + TESTING: false + VERSION: production + ports: + - 9501:80 + networks: + - appwrite + labels: + - "traefik.enable=true" + - "traefik.constraint-label-stack=appwrite" + - "traefik.docker.network=appwrite" + - "traefik.http.services.appwrite_api.loadbalancer.server.port=80" + #http + - traefik.http.routers.appwrite_api_http.entrypoints=appwrite_web + - traefik.http.routers.appwrite_api_http.rule=PathPrefix(`/`) + - traefik.http.routers.appwrite_api_http.service=appwrite_api + # https + - traefik.http.routers.appwrite_api_https.entrypoints=appwrite_websecure + - traefik.http.routers.appwrite_api_https.rule=PathPrefix(`/`) + - traefik.http.routers.appwrite_api_https.service=appwrite_api + - traefik.http.routers.appwrite_api_https.tls=true + volumes: + - appwrite-uploads:/storage/uploads:rw + - appwrite-cache:/storage/cache:rw + - appwrite-config:/storage/config:rw + - appwrite-certificates:/storage/certificates:rw + - appwrite-functions:/storage/functions:rw + - appwrite-builds:/storage/builds:rw + - ./phpunit.xml:/usr/src/code/phpunit.xml + - ./tests:/usr/src/code/tests + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./docs:/usr/src/code/docs + - ./public:/usr/src/code/public + - ./src:/usr/src/code/src + - ./dev:/usr/src/code/dev + depends_on: + - mariadb + - redis + # - clamav + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_LOCALE + - _APP_CONSOLE_WHITELIST_ROOT + - _APP_CONSOLE_WHITELIST_EMAILS + - _APP_CONSOLE_WHITELIST_IPS + - _APP_CONSOLE_HOSTNAMES + - _APP_SYSTEM_EMAIL_NAME + - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_SYSTEM_RESPONSE_FORMAT + - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION + - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS + - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_DOMAIN_TARGET + - _APP_DOMAIN_FUNCTIONS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_SMTP_HOST + - _APP_SMTP_PORT + - _APP_SMTP_SECURE + - _APP_SMTP_USERNAME + - _APP_SMTP_PASSWORD + - _APP_USAGE_STATS + - _APP_STORAGE_LIMIT + - _APP_STORAGE_PREVIEW_LIMIT + - _APP_STORAGE_ANTIVIRUS + - _APP_STORAGE_ANTIVIRUS_HOST + - _APP_STORAGE_ANTIVIRUS_PORT + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + - _APP_FUNCTIONS_SIZE_LIMIT + - _APP_FUNCTIONS_TIMEOUT + - _APP_FUNCTIONS_BUILD_TIMEOUT + - _APP_FUNCTIONS_CPUS + - _APP_FUNCTIONS_MEMORY + - _APP_FUNCTIONS_RUNTIMES + - _APP_EXECUTOR_SECRET + - _APP_EXECUTOR_HOST + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + - _APP_MAINTENANCE_INTERVAL + - _APP_MAINTENANCE_RETENTION_EXECUTION + - _APP_MAINTENANCE_RETENTION_CACHE + - _APP_MAINTENANCE_RETENTION_ABUSE + - _APP_MAINTENANCE_RETENTION_AUDIT + - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY + - _APP_MAINTENANCE_RETENTION_SCHEDULES + - _APP_SMS_PROVIDER + - _APP_SMS_FROM + - _APP_GRAPHQL_MAX_BATCH_SIZE + - _APP_GRAPHQL_MAX_COMPLEXITY + - _APP_GRAPHQL_MAX_DEPTH + - _APP_VCS_GITHUB_APP_NAME + - _APP_VCS_GITHUB_PRIVATE_KEY + - _APP_VCS_GITHUB_APP_ID + - _APP_VCS_GITHUB_WEBHOOK_SECRET + - _APP_VCS_GITHUB_CLIENT_SECRET + - _APP_VCS_GITHUB_CLIENT_ID + - _APP_MIGRATIONS_FIREBASE_CLIENT_ID + - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET + - _APP_ASSISTANT_OPENAI_API_KEY + - _APP_MESSAGE_SMS_TEST_DSN + - _APP_MESSAGE_EMAIL_TEST_DSN + - _APP_MESSAGE_PUSH_TEST_DSN + - _APP_CONSOLE_COUNTRIES_DENYLIST + - _APP_EXPERIMENT_LOGGING_PROVIDER + - _APP_EXPERIMENT_LOGGING_CONFIG + + appwrite-realtime: + entrypoint: realtime + <<: *x-logging + container_name: appwrite-realtime + image: appwrite-dev + restart: unless-stopped + ports: + - 9505:80 + labels: + - "traefik.enable=true" + - "traefik.constraint-label-stack=appwrite" + - "traefik.docker.network=appwrite" + - "traefik.http.services.appwrite_realtime.loadbalancer.server.port=80" + #ws + - traefik.http.routers.appwrite_realtime_ws.entrypoints=appwrite_web + - traefik.http.routers.appwrite_realtime_ws.rule=PathPrefix(`/v1/realtime`) + - traefik.http.routers.appwrite_realtime_ws.service=appwrite_realtime + # wss + - traefik.http.routers.appwrite_realtime_wss.entrypoints=appwrite_websecure + - traefik.http.routers.appwrite_realtime_wss.rule=PathPrefix(`/v1/realtime`) + - traefik.http.routers.appwrite_realtime_wss.service=appwrite_realtime + - traefik.http.routers.appwrite_realtime_wss.tls=true + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - mariadb + - redis + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + + appwrite-worker-audits: + entrypoint: worker-audits + <<: *x-logging + container_name: appwrite-worker-audits + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + + appwrite-worker-webhooks: + entrypoint: worker-webhooks + <<: *x-logging + container_name: appwrite-worker-webhooks + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + - request-catcher + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + - _APP_WEBHOOK_MAX_FAILED_ATTEMPTS + + appwrite-worker-deletes: + entrypoint: worker-deletes + <<: *x-logging + container_name: appwrite-worker-deletes + image: appwrite-dev + networks: + - appwrite + depends_on: + - redis + - mariadb + volumes: + - appwrite-uploads:/storage/uploads:rw + - appwrite-cache:/storage/cache:rw + - appwrite-functions:/storage/functions:rw + - appwrite-builds:/storage/builds:rw + - appwrite-certificates:/storage/certificates:rw + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + - _APP_EXECUTOR_SECRET + - _APP_EXECUTOR_HOST + + appwrite-worker-databases: + entrypoint: worker-databases + <<: *x-logging + container_name: appwrite-worker-databases + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + - _APP_WORKERS_NUM + - _APP_QUEUE_NAME + + appwrite-worker-builds: + entrypoint: worker-builds + <<: *x-logging + container_name: appwrite-worker-builds + image: appwrite-dev + networks: + - appwrite + volumes: + - appwrite-functions:/storage/functions:rw + - appwrite-builds:/storage/builds:rw + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_EXECUTOR_SECRET + - _APP_EXECUTOR_HOST + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + - _APP_VCS_GITHUB_APP_NAME + - _APP_VCS_GITHUB_PRIVATE_KEY + - _APP_VCS_GITHUB_APP_ID + - _APP_FUNCTIONS_TIMEOUT + - _APP_FUNCTIONS_BUILD_TIMEOUT + - _APP_FUNCTIONS_CPUS + - _APP_FUNCTIONS_MEMORY + - _APP_FUNCTIONS_SIZE_LIMIT + - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS + - _APP_DOMAIN + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + + appwrite-worker-certificates: + entrypoint: worker-certificates + <<: *x-logging + container_name: appwrite-worker-certificates + image: appwrite-dev + networks: + - appwrite + depends_on: + - redis + - mariadb + volumes: + - appwrite-config:/storage/config:rw + - appwrite-certificates:/storage/certificates:rw + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_DOMAIN_TARGET + - _APP_DOMAIN_FUNCTIONS + - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + + appwrite-worker-functions: + entrypoint: worker-functions + <<: *x-logging + container_name: appwrite-worker-functions + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + - openruntimes-executor + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_FUNCTIONS_TIMEOUT + - _APP_FUNCTIONS_BUILD_TIMEOUT + - _APP_FUNCTIONS_CPUS + - _APP_FUNCTIONS_MEMORY + - _APP_EXECUTOR_SECRET + - _APP_EXECUTOR_HOST + - _APP_USAGE_STATS + - _APP_DOCKER_HUB_USERNAME + - _APP_DOCKER_HUB_PASSWORD + - _APP_LOGGING_CONFIG + - _APP_LOGGING_PROVIDER + + appwrite-worker-mails: + entrypoint: worker-mails + <<: *x-logging + container_name: appwrite-worker-mails + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - redis + - maildev + # - smtp + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_SYSTEM_EMAIL_NAME + - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_SMTP_HOST + - _APP_SMTP_PORT + - _APP_SMTP_SECURE + - _APP_SMTP_USERNAME + - _APP_SMTP_PASSWORD + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + - _APP_DOMAIN + - _APP_OPTIONS_FORCE_HTTPS + + appwrite-worker-messaging: + entrypoint: worker-messaging + <<: *x-logging + container_name: appwrite-worker-messaging + restart: unless-stopped + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - redis + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + - _APP_SMS_FROM + - _APP_SMS_PROVIDER + - _APP_SMS_PROJECTS_DENY_LIST + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + + appwrite-worker-migrations: + entrypoint: worker-migrations + <<: *x-logging + container_name: appwrite-worker-migrations + restart: unless-stopped + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + - ./tests:/usr/src/code/tests + depends_on: + - mariadb + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_DOMAIN_TARGET + - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + - _APP_MIGRATIONS_FIREBASE_CLIENT_ID + - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET + + appwrite-task-maintenance: + entrypoint: maintenance + <<: *x-logging + container_name: appwrite-task-maintenance + restart: unless-stopped + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - redis + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_DOMAIN + - _APP_DOMAIN_TARGET + - _APP_DOMAIN_FUNCTIONS + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_MAINTENANCE_INTERVAL + - _APP_MAINTENANCE_RETENTION_EXECUTION + - _APP_MAINTENANCE_RETENTION_CACHE + - _APP_MAINTENANCE_RETENTION_ABUSE + - _APP_MAINTENANCE_RETENTION_AUDIT + - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY + - _APP_MAINTENANCE_RETENTION_SCHEDULES + - _APP_MAINTENANCE_DELAY + + appwrite-worker-usage: + entrypoint: worker-usage + <<: *x-logging + container_name: appwrite-worker-usage + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + - _APP_USAGE_AGGREGATION_INTERVAL + + appwrite-worker-usage-dump: + entrypoint: worker-usage-dump + <<: *x-logging + container_name: appwrite-worker-usage-dump + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + - _APP_USAGE_AGGREGATION_INTERVAL + + appwrite-task-scheduler-functions: + entrypoint: schedule-functions + <<: *x-logging + container_name: appwrite-task-scheduler-functions + restart: unless-stopped + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - mariadb + - redis + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + + appwrite-task-scheduler-messages: + entrypoint: schedule-messages + <<: *x-logging + container_name: appwrite-task-scheduler-messages + restart: unless-stopped + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - mariadb + - redis + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + + appwrite-assistant: + container_name: appwrite-assistant + image: appwrite/assistant:0.4.0 + networks: + - appwrite + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - _APP_ASSISTANT_OPENAI_API_KEY + + openruntimes-executor: + container_name: openruntimes-executor + hostname: exc1 + <<: *x-logging + stop_signal: SIGINT + image: openruntimes/executor:0.5.6 + restart: unless-stopped + networks: + - appwrite + - runtimes + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - appwrite-builds:/storage/builds:rw + - appwrite-functions:/storage/functions:rw + # Host mount nessessary to share files between executor and runtimes. + # It's not possible to share mount file between 2 containers without host mount (copying is too slow) + - /tmp:/tmp:rw + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD + - OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL + - OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK + - OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME + - OPR_EXECUTOR_DOCKER_HUB_PASSWORD=$_APP_DOCKER_HUB_PASSWORD + - OPR_EXECUTOR_ENV=$_APP_ENV + - OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES + - OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET + - OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v3 + - OPR_EXECUTOR_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER + - OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG + - OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE + - OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_S3_SECRET=$_APP_STORAGE_S3_SECRET + - OPR_EXECUTOR_STORAGE_S3_REGION=$_APP_STORAGE_S3_REGION + - OPR_EXECUTOR_STORAGE_S3_BUCKET=$_APP_STORAGE_S3_BUCKET + - OPR_EXECUTOR_STORAGE_DO_SPACES_ACCESS_KEY=$_APP_STORAGE_DO_SPACES_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=$_APP_STORAGE_DO_SPACES_SECRET + - OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=$_APP_STORAGE_DO_SPACES_REGION + - OPR_EXECUTOR_STORAGE_DO_SPACES_BUCKET=$_APP_STORAGE_DO_SPACES_BUCKET + - OPR_EXECUTOR_STORAGE_BACKBLAZE_ACCESS_KEY=$_APP_STORAGE_BACKBLAZE_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_BACKBLAZE_SECRET=$_APP_STORAGE_BACKBLAZE_SECRET + - OPR_EXECUTOR_STORAGE_BACKBLAZE_REGION=$_APP_STORAGE_BACKBLAZE_REGION + - OPR_EXECUTOR_STORAGE_BACKBLAZE_BUCKET=$_APP_STORAGE_BACKBLAZE_BUCKET + - OPR_EXECUTOR_STORAGE_LINODE_ACCESS_KEY=$_APP_STORAGE_LINODE_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_LINODE_SECRET=$_APP_STORAGE_LINODE_SECRET + - OPR_EXECUTOR_STORAGE_LINODE_REGION=$_APP_STORAGE_LINODE_REGION + - OPR_EXECUTOR_STORAGE_LINODE_BUCKET=$_APP_STORAGE_LINODE_BUCKET + - OPR_EXECUTOR_STORAGE_WASABI_ACCESS_KEY=$_APP_STORAGE_WASABI_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_WASABI_SECRET=$_APP_STORAGE_WASABI_SECRET + - OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION + - OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET + + openruntimes-proxy: + container_name: openruntimes-proxy + hostname: proxy + <<: *x-logging + stop_signal: SIGINT + image: openruntimes/proxy:0.3.1 + networks: + - appwrite + - runtimes + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - OPR_PROXY_WORKER_PER_CORE=$_APP_WORKER_PER_CORE + - OPR_PROXY_ENV=$_APP_ENV + - OPR_PROXY_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET + - OPR_PROXY_SECRET=$_APP_EXECUTOR_SECRET + - OPR_PROXY_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER + - OPR_PROXY_LOGGING_CONFIG=$_APP_LOGGING_CONFIG + - OPR_PROXY_ALGORITHM=random + - OPR_PROXY_EXECUTORS=exc1 + - OPR_PROXY_HEALTHCHECK_INTERVAL=10000 + - OPR_PROXY_MAX_TIMEOUT=600 + - OPR_PROXY_HEALTHCHECK=enabled + + mariadb: + image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p + container_name: appwrite-mariadb + <<: *x-logging + networks: + - appwrite + volumes: + - appwrite-mariadb:/var/lib/mysql:rw + ports: + - "3306:3306" + environment: + - MYSQL_ROOT_PASSWORD=${_APP_DB_ROOT_PASS} + - MYSQL_DATABASE=${_APP_DB_SCHEMA} + - MYSQL_USER=${_APP_DB_USER} + - MYSQL_PASSWORD=${_APP_DB_PASS} + - MARIADB_AUTO_UPGRADE=1 +# command: "mysqld --innodb-flush-method=fsync" # add ' --query_cache_size=0' for DB tests + command: 'mysqld --innodb-flush-method=fsync --max_connections=5000' + + # command: mv /var/lib/mysql/ib_logfile0 /var/lib/mysql/ib_logfile0.bu && mv /var/lib/mysql/ib_logfile1 /var/lib/mysql/ib_logfile1.bu + + # smtp: + # image: appwrite/smtp:1.2.0 + # container_name: appwrite-smtp + # restart: unless-stopped + # networks: + # - appwrite + # environment: + # - LOCAL_DOMAINS=@ + # - RELAY_FROM_HOSTS=192.168.0.0/16 ; *.yourdomain.com + # - SMARTHOST_HOST=smtp + # - SMARTHOST_PORT=587 + + redis: + image: redis:7.2.4-alpine + <<: *x-logging + container_name: appwrite-redis + command: > + redis-server + --maxmemory 512mb + --maxmemory-policy allkeys-lru + --maxmemory-samples 5 + ports: + - "6379:6379" + networks: + - appwrite + volumes: + - appwrite-redis:/data:rw + + # clamav: + # image: appwrite/clamav:1.2.0 + # container_name: appwrite-clamav + # networks: + # - appwrite + # volumes: + # - appwrite-uploads:/storage/uploads + + # Dev Tools Start ------------------------------------------------------------------------------------------ + # + # The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack + # + # Here is a description of the different tools and why are we using them: + # + # MailCatcher - An SMTP server. Catches all system emails and displays them in a nice UI. + # RequestCatcher - An HTTP server. Catches all system https calls and displays them using a simple HTTP API. Used to debug & tests webhooks and HTTP tasks + # Redis Insight - A nice UI for exploring Redis data + # Adminer - A nice UI for exploring MariaDB data + # GraphQl Explorer - A nice UI for exploring GraphQL API + + maildev: # used mainly for dev tests + image: appwrite/mailcatcher:1.0.0 + container_name: appwrite-mailcatcher + <<: *x-logging + ports: + - "9503:1080" + networks: + - appwrite + + request-catcher: # used mainly for dev tests + image: appwrite/requestcatcher:1.0.0 + container_name: appwrite-requestcatcher + <<: *x-logging + ports: + - "9504:5000" + networks: + - appwrite + + adminer: + image: adminer + container_name: appwrite-adminer + <<: *x-logging + restart: always + ports: + - 9506:8080 + networks: + - appwrite + + redis-insight: + image: redis/redisinsight:latest + restart: unless-stopped + networks: + - appwrite + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - REDIS_HOSTS=redis + ports: + - "8081:5540" + + graphql-explorer: + container_name: appwrite-graphql-explorer + image: appwrite/altair:0.3.0 + restart: unless-stopped + networks: + - appwrite + ports: + - "9509:3000" + environment: + - PHP_IDE_CONFIG=serverName=Appwrite + - SERVER_URL=http://localhost/v1/graphql + + # Dev Tools End ------------------------------------------------------------------------------------------ + +networks: + gateway: + name: gateway + appwrite: + name: appwrite + runtimes: + name: runtimes + +volumes: + appwrite-mariadb: + appwrite-redis: + appwrite-cache: + appwrite-uploads: + appwrite-certificates: + appwrite-functions: + appwrite-builds: + appwrite-config: From a4f675c6324eee8137697bf9baa30be1c120cbaf Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:16:44 -0400 Subject: [PATCH 115/195] feat: Coroutine adjustments --- .env | 2 +- app/http.php | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.env b/.env index 4e211a95b7..23d221d0d6 100644 --- a/.env +++ b/.env @@ -1,7 +1,7 @@ _APP_ENV=development _APP_EDITION=self-hosted _APP_LOCALE=en -_APP_WORKER_PER_CORE=4 +_APP_WORKER_PER_CORE=6 _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= diff --git a/app/http.php b/app/http.php index 5b0d2b5c9e..5ae2e40e1a 100644 --- a/app/http.php +++ b/app/http.php @@ -26,7 +26,7 @@ use Utopia\System\System; global $global, $container; $payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing -$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 4)); +$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); $server = new Server('0.0.0.0', '80', [ 'open_http2_protocol' => true, @@ -34,13 +34,6 @@ $server = new Server('0.0.0.0', '80', [ 'http_compression_level' => 6, 'package_max_length' => $payloadSize, 'buffer_output_size' => $payloadSize, - - // TCP Keep-Alive check - 'open_tcp_keepalive' => true, - 'tcp_keepidle' => 4, - 'tcp_keepinterval' => 1, - 'tcp_keepcount' => 5, - // Server // 'log_level' => 0, 'dispatch_mode' => 1, From 92668fda0db77bc7f452357bc0f6c08ecf31ca78 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:24:04 -0400 Subject: [PATCH 116/195] feat: Moving to coroutine dedicated http server --- app/http.php | 429 ++++++++++++++------------ app/init2.php | 5 +- composer.lock | 69 ++--- src/Appwrite/Platform/Tasks/Specs.php | 3 +- 4 files changed, 257 insertions(+), 249 deletions(-) diff --git a/app/http.php b/app/http.php index 5ae2e40e1a..bb50c46230 100644 --- a/app/http.php +++ b/app/http.php @@ -19,8 +19,10 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; -use Utopia\Http\Adapter\Swoole\Server; +use Utopia\DI\Container; +use Utopia\Http\Adapter\SwooleCoroutine\Server; use Utopia\Http\Http; +use Utopia\Registry\Registry; use Utopia\System\System; global $global, $container; @@ -28,219 +30,236 @@ global $global, $container; $payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing $workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); -$server = new Server('0.0.0.0', '80', [ - 'open_http2_protocol' => true, - 'http_compression' => true, - 'http_compression_level' => 6, - 'package_max_length' => $payloadSize, - 'buffer_output_size' => $payloadSize, - // Server - // 'log_level' => 0, - 'dispatch_mode' => 1, - 'worker_num' => $workerNumber, - 'reactor_num' => swoole_cpu_num() * 2, - 'open_cpu_affinity' => true, - // Coroutine - 'enable_coroutine' => true, - 'max_coroutine' => 10000, - 'send_yield' => true -]); +$pool = new Swoole\Process\Pool($workerNumber, 0, 0, true); -$http = new Http($server, $container, 'UTC'); +function startCoroutineServer(float|int $payloadSize, float|int $workerNumber, Registry $global, Container $container, $workerId) +{ + $server = new Server('0.0.0.0', '80', [ + 'open_http2_protocol' => true, + 'http_compression' => true, + 'http_compression_level' => 6, + 'package_max_length' => $payloadSize, + 'buffer_output_size' => $payloadSize, + // Server + // 'log_level' => 0, + 'dispatch_mode' => 1, + 'worker_num' => $workerNumber, + 'reactor_num' => swoole_cpu_num() * 2, + 'open_cpu_affinity' => true, + // Coroutine + 'enable_coroutine' => true, + 'max_coroutine' => 10000, + 'send_yield' => true + ]); -$http->loadFiles(__DIR__ . '/../console'); -$http->setRequestClass(Request::class); -$http->setResponseClass(Response::class); + $http = new Http($server, $container, 'UTC'); -global $global, $container; - -http::onStart() - ->inject('authorization') - ->inject('cache') - ->inject('pools') - ->inject('connections') - ->action(function (Authorization $authorization, Cache $cache, array $pools, Connections $connections) { - try { - // wait for database to be ready - $attempts = 0; - $max = 15; - $sleep = 2; - - do { - try { - $attempts++; - $pool = $pools['pools-console-main']['pool']; - $dsn = $pools['pools-console-main']['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - - $dbForConsole = new Database($adapter, $cache); - $dbForConsole->setAuthorization($authorization); - - $dbForConsole - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - $dbForConsole->ping(); - break; // leave the do-while if successful - } catch (\Throwable $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: ' . $e->getMessage()); - } - sleep($sleep); - } - } while ($attempts < $max); - - Console::success('[Setup] - Server database init started...'); + $http->loadFiles(__DIR__ . '/../console'); + $http->setRequestClass(Request::class); + $http->setResponseClass(Response::class); + Http::onStart() + ->inject('authorization') + ->inject('cache') + ->inject('pools') + ->inject('connections') + ->action(function (Authorization $authorization, Cache $cache, array $pools, Connections $connections) use ($workerId) { + if($workerId !== 0) { + return; + } try { - Console::success('[Setup] - Creating database: appwrite...'); - $dbForConsole->create(); + // wait for database to be ready + $attempts = 0; + $max = 15; + $sleep = 2; + + do { + try { + $attempts++; + $pool = $pools['pools-console-main']['pool']; + $dsn = $pools['pools-console-main']['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); + + $dbForConsole = new Database($adapter, $cache); + $dbForConsole->setAuthorization($authorization); + + $dbForConsole + ->setNamespace('_console') + ->setMetadata('host', \gethostname()) + ->setMetadata('project', 'console') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + $dbForConsole->ping(); + break; // leave the do-while if successful + } catch (\Throwable $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: ' . $e->getMessage()); + } + sleep($sleep); + } + } while ($attempts < $max); + + Console::success('[Setup] - Server database init started...'); + + try { + Console::success('[Setup] - Creating database: appwrite...'); + $dbForConsole->create(); + } catch (\Throwable $e) { + Console::success('[Setup] - Skip: metadata table already exists'); + return true; + } + + if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) { + $audit = new Audit($dbForConsole); + $audit->setup(); + } + + if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { + $abuse = new TimeLimit("", 0, 1, $dbForConsole); + $abuse->setup(); + } + + /** @var array $collections */ + $collections = Config::getParam('collections', []); + $consoleCollections = $collections['console']; + foreach ($consoleCollections as $key => $collection) { + if (($collection['$collection'] ?? '') !== Database::METADATA) { + continue; + } + if (!$dbForConsole->getCollection($key)->isEmpty()) { + continue; + } + + Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); + + $attributes = []; + $indexes = []; + + foreach ($collection['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]); + } + + foreach ($collection['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); + } + + $dbForConsole->createCollection($key, $attributes, $indexes); + } + + if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) { + Console::success('[Setup] - Creating default bucket...'); + $dbForConsole->createDocument('buckets', new Document([ + '$id' => ID::custom('default'), + '$collection' => ID::custom('buckets'), + 'name' => 'Default', + 'maximumFileSize' => (int)System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB + 'allowedFileExtensions' => [], + 'enabled' => true, + 'compression' => 'gzip', + 'encryption' => true, + 'antivirus' => true, + 'fileSecurity' => true, + '$permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'search' => 'buckets Default', + ])); + + $bucket = $dbForConsole->getDocument('buckets', 'default'); + + Console::success('[Setup] - Creating files collection for default bucket...'); + + $files = $collections['buckets']['files'] ?? []; + if (empty($files)) { + throw new Exception('Files collection is not configured.'); + } + + $attributes = []; + $indexes = []; + + foreach ($files['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]); + } + + foreach ($files['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); + } + + $dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); + } + + $connections->reclaim(); + + Console::success('[Setup] - Server database init completed...'); + Console::success('Server started successfully'); } catch (\Throwable $e) { - Console::success('[Setup] - Skip: metadata table already exists'); - return true; + Console::warning('Database not ready: ' . $e->getMessage()); + exit(1); } + }); - if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) { - $audit = new Audit($dbForConsole); - $audit->setup(); - } + Http::init() + ->inject('authorization') + ->action(function (Authorization $authorization) { + $authorization->cleanRoles(); + $authorization->addRole(Role::any()->toString()); + }); - if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { - $abuse = new TimeLimit("", 0, 1, $dbForConsole); - $abuse->setup(); - } + $http->start(); +} - /** @var array $collections */ - $collections = Config::getParam('collections', []); - $consoleCollections = $collections['console']; - foreach ($consoleCollections as $key => $collection) { - if (($collection['$collection'] ?? '') !== Database::METADATA) { - continue; - } - if (!$dbForConsole->getCollection($key)->isEmpty()) { - continue; - } +$pool->on("WorkerStart", function ($pool, $workerId) use ($payloadSize, $workerNumber, $global, $container) { + Console::success("Worker " . $workerId . " started"); + startCoroutineServer($payloadSize, $workerNumber, $global, $container, $workerId); +}); - Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); +$pool->on("WorkerStop", function ($pool, $workerId) { + Console::success("Worker " . $workerId . " stopped"); +}); - $attributes = []; - $indexes = []; - - foreach ($collection['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]); - } - - foreach ($collection['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } - - $dbForConsole->createCollection($key, $attributes, $indexes); - } - - if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) { - Console::success('[Setup] - Creating default bucket...'); - $dbForConsole->createDocument('buckets', new Document([ - '$id' => ID::custom('default'), - '$collection' => ID::custom('buckets'), - 'name' => 'Default', - 'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB - 'allowedFileExtensions' => [], - 'enabled' => true, - 'compression' => 'gzip', - 'encryption' => true, - 'antivirus' => true, - 'fileSecurity' => true, - '$permissions' => [ - Permission::create(Role::any()), - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'search' => 'buckets Default', - ])); - - $bucket = $dbForConsole->getDocument('buckets', 'default'); - - Console::success('[Setup] - Creating files collection for default bucket...'); - - $files = $collections['buckets']['files'] ?? []; - if (empty($files)) { - throw new Exception('Files collection is not configured.'); - } - - $attributes = []; - $indexes = []; - - foreach ($files['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]); - } - - foreach ($files['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } - - $dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); - } - - $connections->reclaim(); - - Console::success('[Setup] - Server database init completed...'); - Console::success('Server started successfully'); - } catch (\Throwable $e) { - Console::warning('Database not ready: ' . $e->getMessage()); - exit(1); - } - }); - -Http::init() - ->inject('authorization') - ->action(function (Authorization $authorization) { - $authorization->cleanRoles(); - $authorization->addRole(Role::any()->toString()); - }); - -$http->start(); +$pool->start(); diff --git a/app/init2.php b/app/init2.php index 9e5c9ad25a..234a4f210d 100644 --- a/app/init2.php +++ b/app/init2.php @@ -1153,13 +1153,12 @@ $schemaVariable $schema ->setName('schema') ->inject('http') - ->inject('context') ->inject('request') ->inject('response') ->inject('dbForProject') ->inject('authorization') ->inject('schemaVariable') - ->setCallback(function (Http $http, Container $context, Request $request, Response $response, Database $dbForProject, Authorization $authorization, $schemaVariable) { + ->setCallback(function (Http $http, Request $request, Response $response, Database $dbForProject, Authorization $authorization, $schemaVariable) { $complexity = function (int $complexity, array $args) { $queries = Query::parseQueries($args['queries'] ?? []); $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; @@ -1237,7 +1236,7 @@ $schema $http, $request, $response, - $context, + $http->getContainer(), $complexity, $attributes, $urls, diff --git a/composer.lock b/composer.lock index ce4472d6ed..ffb651e0fd 100644 --- a/composer.lock +++ b/composer.lock @@ -1572,16 +1572,16 @@ }, { "name": "utopia-php/cache", - "version": "0.10.1", + "version": "0.10.2", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "87ee4fc91e50d4ddfef650aa999ea12be3a99583" + "reference": "b22c6eb6d308de246b023efd0fc9758aee8b8247" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/87ee4fc91e50d4ddfef650aa999ea12be3a99583", - "reference": "87ee4fc91e50d4ddfef650aa999ea12be3a99583", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/b22c6eb6d308de246b023efd0fc9758aee8b8247", + "reference": "b22c6eb6d308de246b023efd0fc9758aee8b8247", "shasum": "" }, "require": { @@ -1616,9 +1616,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.10.1" + "source": "https://github.com/utopia-php/cache/tree/0.10.2" }, - "time": "2024-06-18T13:20:25+00:00" + "time": "2024-06-25T20:36:35+00:00" }, { "name": "utopia-php/cli", @@ -1783,7 +1783,7 @@ "version": "dev-feat-framework-v2", "source": { "type": "git", - "url": "https://github.com/utopia-php/di.git", + "url": "git@github.com:utopia-php/di.git", "reference": "1fb7da5ead16de5d795ef10c1e22e39726eb6943" }, "dist": { @@ -1833,10 +1833,6 @@ "php", "upf" ], - "support": { - "source": "https://github.com/utopia-php/di/tree/feat-framework-v2", - "issues": "https://github.com/utopia-php/di/issues" - }, "time": "2024-06-11T16:03:00+00:00" }, { @@ -1991,12 +1987,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "bf2474554f78d870c74aaaa1dfb4f54795ae9497" + "reference": "f4ce8a42a5613f40992e6fe84ff2e23584465adb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/bf2474554f78d870c74aaaa1dfb4f54795ae9497", - "reference": "bf2474554f78d870c74aaaa1dfb4f54795ae9497", + "url": "https://api.github.com/repos/utopia-php/http/zipball/f4ce8a42a5613f40992e6fe84ff2e23584465adb", + "reference": "f4ce8a42a5613f40992e6fe84ff2e23584465adb", "shasum": "" }, "require": { @@ -2033,7 +2029,7 @@ "issues": "https://github.com/utopia-php/http/issues", "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-06-11T16:38:45+00:00" + "time": "2024-07-03T20:21:21+00:00" }, { "name": "utopia-php/image", @@ -2670,7 +2666,7 @@ "version": "dev-dev", "source": { "type": "git", - "url": "https://github.com/utopia-php/servers.git", + "url": "git@github.com:utopia-php/servers.git", "reference": "4565c1c111f6da6b18bc0f00f350377a1e691e48" }, "dist": { @@ -2730,10 +2726,6 @@ "upf", "utopia" ], - "support": { - "source": "https://github.com/utopia-php/servers/tree/dev", - "issues": "https://github.com/utopia-php/servers/issues" - }, "time": "2024-06-07T18:49:59+00:00" }, { @@ -3124,16 +3116,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.38.7", + "version": "0.38.8", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a" + "reference": "6367c57ddbcf7b88cacb900c4fe7ef3f28bf38ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a", - "reference": "0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/6367c57ddbcf7b88cacb900c4fe7ef3f28bf38ef", + "reference": "6367c57ddbcf7b88cacb900c4fe7ef3f28bf38ef", "shasum": "" }, "require": { @@ -3169,9 +3161,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.38.7" + "source": "https://github.com/appwrite/sdk-generator/tree/0.38.8" }, - "time": "2024-06-10T00:23:02+00:00" + "time": "2024-06-17T00:42:27+00:00" }, { "name": "doctrine/deprecations", @@ -3544,16 +3536,16 @@ }, { "name": "nikic/php-parser", - "version": "dev-master", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3ef0811e45ba7e91fb0f066af5af7d52c3b24469" + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3ef0811e45ba7e91fb0f066af5af7d52c3b24469", - "reference": "3ef0811e45ba7e91fb0f066af5af7d52c3b24469", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", "shasum": "" }, "require": { @@ -3566,7 +3558,6 @@ "ircmaxell/php-yacc": "^0.0.7", "phpunit/phpunit": "^9.0" }, - "default-branch": true, "bin": [ "bin/php-parse" ], @@ -3597,9 +3588,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/master" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" }, - "time": "2024-06-12T18:31:58+00:00" + "time": "2024-07-01T20:03:41+00:00" }, { "name": "phar-io/manifest", @@ -4079,12 +4070,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "3352293d9e91513d5508c415835014881b420218" + "reference": "39d628812d8d83344a6c1b07799e3700d830d355" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/3352293d9e91513d5508c415835014881b420218", - "reference": "3352293d9e91513d5508c415835014881b420218", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/39d628812d8d83344a6c1b07799e3700d830d355", + "reference": "39d628812d8d83344a6c1b07799e3700d830d355", "shasum": "" }, "require": { @@ -4103,7 +4094,7 @@ "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -4112,7 +4103,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2-dev" } }, "autoload": { @@ -4149,7 +4140,7 @@ "type": "github" } ], - "time": "2024-03-22T05:16:32+00:00" + "time": "2024-06-29T07:23:05+00:00" }, { "name": "phpunit/php-file-iterator", diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 7c4c91fc60..31db47098b 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -41,8 +41,7 @@ class Specs extends Action ->param('version', 'latest', new Text(16), 'Spec version', true) ->param('mode', 'normal', new WhiteList(['normal', 'mocks']), 'Spec Mode', true) ->inject('register') - ->inject('context') - ->callback(fn (string $version, string $mode, Registry $register, Container $context) => $this->action($version, $mode, $register, $context)); + ->callback(fn (string $version, string $mode, Registry $register) => $this->action($version, $mode, $register)); } public function action(string $version, string $mode, Registry $register, Container $container): void From ea13375109e6a8cd85923d85139ece5d99116dde Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 4 Jul 2024 11:02:34 -0400 Subject: [PATCH 117/195] chore: Merge tasks --- app/controllers/general.php | 1 + app/init2.php | 5 +- composer.json | 1 - composer.lock | 807 ++++++++++++++++++++++++------------ 4 files changed, 549 insertions(+), 265 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 9dab136c42..5c4255328e 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1,5 +1,6 @@ setName('schema') ->inject('http') + ->inject('context') ->inject('request') ->inject('response') ->inject('dbForProject') ->inject('authorization') ->inject('schemaVariable') - ->setCallback(function (Http $http, Request $request, Response $response, Database $dbForProject, Authorization $authorization, $schemaVariable) { + ->setCallback(function (Http $http, Container $context, Request $request, Response $response, Database $dbForProject, Authorization $authorization, $schemaVariable) { $complexity = function (int $complexity, array $args) { $queries = Query::parseQueries($args['queries'] ?? []); $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; @@ -1236,7 +1237,7 @@ $schema $http, $request, $response, - $http->getContainer(), + $context, $complexity, $attributes, $urls, diff --git a/composer.json b/composer.json index 6e4c411a78..7807425128 100644 --- a/composer.json +++ b/composer.json @@ -70,7 +70,6 @@ "utopia-php/queue": "dev-feat-coroutine-and-di as 0.7.99", "utopia-php/registry": "0.5.*", "utopia-php/storage": "dev-feat-framework-v2-v2 as 0.18.99", - "utopia-php/swoole": "0.8.*", "utopia-php/system": "0.8.*", "utopia-php/vcs": "0.6.*", "utopia-php/websocket": "0.1.*", diff --git a/composer.lock b/composer.lock index 7d72f0072a..179dbaa1ca 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": "89f2552eaa152516be5bf89628c0f86e", + "content-hash": "6cd124cc3eeca5b62a56909ea4fab324", "packages": [ { "name": "adhocore/jwt", @@ -210,16 +210,16 @@ }, { "name": "beberlei/assert", - "version": "v3.3.2", + "version": "v3.x-dev", "source": { "type": "git", "url": "https://github.com/beberlei/assert.git", - "reference": "cb70015c04be1baee6f5f5c953703347c0ac1655" + "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/beberlei/assert/zipball/cb70015c04be1baee6f5f5c953703347c0ac1655", - "reference": "cb70015c04be1baee6f5f5c953703347c0ac1655", + "url": "https://api.github.com/repos/beberlei/assert/zipball/d63a6943fc4fd1a2aedb65994e3548715105abcf", + "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf", "shasum": "" }, "require": { @@ -227,13 +227,12 @@ "ext-json": "*", "ext-mbstring": "*", "ext-simplexml": "*", - "php": "^7.0 || ^8.0" + "php": "^7" }, "require-dev": { "friendsofphp/php-cs-fixer": "*", - "phpstan/phpstan": "*", - "phpunit/phpunit": ">=6.0.0", - "yoast/phpunit-polyfills": "^0.1.0" + "phpstan/phpstan-shim": "*", + "phpunit/phpunit": ">=6.0.0 <8" }, "suggest": { "ext-intl": "Needed to allow Assertion::count(), Assertion::isCountable(), Assertion::minCount(), and Assertion::maxCount() to operate on ResourceBundles" @@ -271,9 +270,9 @@ ], "support": { "issues": "https://github.com/beberlei/assert/issues", - "source": "https://github.com/beberlei/assert/tree/v3.3.2" + "source": "https://github.com/beberlei/assert/tree/v3" }, - "time": "2021-12-16T21:41:27+00:00" + "time": "2019-12-19T17:51:41+00:00" }, { "name": "chillerlan/php-qrcode", @@ -565,16 +564,16 @@ }, { "name": "jean85/pretty-package-versions", - "version": "2.0.6", + "version": "2.x-dev", "source": { "type": "git", "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" + "reference": "4ea62fbb39a29d65ef6cda413158baa7f1d98550" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4ea62fbb39a29d65ef6cda413158baa7f1d98550", + "reference": "4ea62fbb39a29d65ef6cda413158baa7f1d98550", "shasum": "" }, "require": { @@ -586,8 +585,9 @@ "jean85/composer-provided-replaced-stub-package": "^1.0", "phpstan/phpstan": "^1.4", "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" + "vimeo/psalm": "^4.3 || ^5.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -618,9 +618,9 @@ ], "support": { "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.x" }, - "time": "2024-03-08T09:58:59+00:00" + "time": "2024-04-08T08:58:14+00:00" }, { "name": "league/csv", @@ -905,7 +905,7 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.7.0", + "version": "v2.x-dev", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", @@ -1053,7 +1053,7 @@ }, { "name": "spomky-labs/otphp", - "version": "v10.0.3", + "version": "v10.0.x-dev", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/otphp.git", @@ -1082,6 +1082,7 @@ "phpunit/phpunit": "^8.0", "thecodingmachine/phpstan-safe-rule": "^1.0 || ^2.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -1128,16 +1129,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "8740a072b86292957feb42703edde77fcfca84fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8740a072b86292957feb42703edde77fcfca84fb", + "reference": "8740a072b86292957feb42703edde77fcfca84fb", "shasum": "" }, "require": { @@ -1149,6 +1150,7 @@ "suggest": { "ext-mbstring": "For best performance" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -1188,7 +1190,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/1.x" }, "funding": [ { @@ -1204,11 +1206,11 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-06-20T08:18:00+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -1223,6 +1225,7 @@ "require": { "php": ">=7.1" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -1427,23 +1430,23 @@ }, { "name": "utopia-php/abuse", - "version": "0.38.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "b7be9086c9d9b4561d810cbd42fdda798742f56c" + "reference": "a2292d71da901ea13129d56f89876626ba92adf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b7be9086c9d9b4561d810cbd42fdda798742f56c", - "reference": "b7be9086c9d9b4561d810cbd42fdda798742f56c", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/a2292d71da901ea13129d56f89876626ba92adf0", + "reference": "a2292d71da901ea13129d56f89876626ba92adf0", "shasum": "" }, "require": { "ext-curl": "*", "ext-pdo": "*", "php": ">=8.0", - "utopia-php/database": "0.50.*" + "utopia-php/database": "dev-feat-framework-v2 as 0.49.99" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1470,27 +1473,27 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.38.0" + "source": "https://github.com/utopia-php/abuse/tree/feat-framework-v2" }, - "time": "2024-06-24T00:52:02+00:00" + "time": "2024-04-18T17:04:17+00:00" }, { "name": "utopia-php/analytics", - "version": "0.10.2", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + "reference": "5d59c2e50381a25adecbca979ed5a7c81307442f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/5d59c2e50381a25adecbca979ed5a7c81307442f", + "reference": "5d59c2e50381a25adecbca979ed5a7c81307442f", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "^0.15.0" + "utopia-php/cli": "0.17.*" }, "require-dev": { "laravel/pint": "dev-main", @@ -1516,27 +1519,27 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + "source": "https://github.com/utopia-php/analytics/tree/feat-framework-v2" }, - "time": "2023-03-22T12:01:09+00:00" + "time": "2024-03-07T15:54:19+00:00" }, { "name": "utopia-php/audit", - "version": "0.40.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "735ae211ce5fee5b52b736731571b4030b1d7cdc" + "reference": "49c2a113277bfa0d7d1774c150de2d2fa07349e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/735ae211ce5fee5b52b736731571b4030b1d7cdc", - "reference": "735ae211ce5fee5b52b736731571b4030b1d7cdc", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/49c2a113277bfa0d7d1774c150de2d2fa07349e7", + "reference": "49c2a113277bfa0d7d1774c150de2d2fa07349e7", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.50.*" + "utopia-php/database": "dev-feat-framework-v2 as 0.49.99" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1563,9 +1566,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.40.0" + "source": "https://github.com/utopia-php/audit/tree/feat-framework-v2" }, - "time": "2024-06-24T00:52:17+00:00" + "time": "2024-04-18T17:02:14+00:00" }, { "name": "utopia-php/cache", @@ -1619,27 +1622,29 @@ }, { "name": "utopia-php/cli", - "version": "0.15.0", + "version": "dev-dev-coroutines", "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + "reference": "d48b696891dee1e46df2491d192bb91cf4df8f94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/d48b696891dee1e46df2491d192bb91cf4df8f94", + "reference": "d48b696891dee1e46df2491d192bb91cf4df8f94", "shasum": "" }, "require": { "php": ">=7.4", - "utopia-php/framework": "0.*.*" + "utopia-php/di": "dev-feat-framework-v2", + "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99" }, "require-dev": { "laravel/pint": "1.2.*", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.3", "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" + "swoole/ide-helper": "4.8.8" }, "type": "library", "autoload": { @@ -1662,9 +1667,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" + "source": "https://github.com/utopia-php/cli/tree/dev-coroutines" }, - "time": "2023-03-01T05:55:14+00:00" + "time": "2024-06-24T13:24:20+00:00" }, { "name": "utopia-php/config", @@ -1719,16 +1724,16 @@ }, { "name": "utopia-php/database", - "version": "0.50.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "ce3eaccb2f3bbd34b2b97419836fec633b26b8f7" + "reference": "5b0d6ba40141dd9d4983d3aac018782f8ecdfc8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/ce3eaccb2f3bbd34b2b97419836fec633b26b8f7", - "reference": "ce3eaccb2f3bbd34b2b97419836fec633b26b8f7", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5b0d6ba40141dd9d4983d3aac018782f8ecdfc8a", + "reference": "5b0d6ba40141dd9d4983d3aac018782f8ecdfc8a", "shasum": "" }, "require": { @@ -1736,7 +1741,7 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.10.*", - "utopia-php/framework": "0.33.*", + "utopia-php/framework": "0.34.*", "utopia-php/mongo": "0.3.*" }, "require-dev": { @@ -1747,7 +1752,7 @@ "phpunit/phpunit": "^9.4", "rregeer/phpunit-coverage-check": "^0.3.1", "swoole/ide-helper": "4.8.0", - "utopia-php/cli": "^0.14.0" + "utopia-php/cli": "0.17.*" }, "type": "library", "autoload": { @@ -1769,27 +1774,88 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.50.0" + "source": "https://github.com/utopia-php/database/tree/feat-framework-v2" }, - "time": "2024-06-21T03:21:42+00:00" + "time": "2024-06-06T00:07:47+00:00" }, { - "name": "utopia-php/domains", - "version": "0.5.0", + "name": "utopia-php/di", + "version": "dev-feat-framework-v2", "source": { "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c" + "url": "https://github.com/utopia-php/di.git", + "reference": "1fb7da5ead16de5d795ef10c1e22e39726eb6943" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/bf07f60326f8389f378ddf6fcde86217e5cfe18c", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c", + "url": "https://api.github.com/repos/utopia-php/di/zipball/1fb7da5ead16de5d795ef10c1e22e39726eb6943", + "reference": "1fb7da5ead16de5d795ef10c1e22e39726eb6943", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/", + "Tests\\E2E\\": "tests/e2e" + } + }, + "scripts": { + "lint": [ + "vendor/bin/pint --test" + ], + "format": [ + "vendor/bin/pint" + ], + "check": [ + "vendor/bin/phpstan analyse -c phpstan.neon" + ], + "test": [ + "vendor/bin/phpunit --configuration phpunit.xml" + ] + }, + "license": [ + "MIT" + ], + "description": "A simple and lite library for managing dependency injections", + "keywords": [ + "framework", + "http", + "php", + "upf" + ], + "support": { + "source": "https://github.com/utopia-php/di/tree/feat-framework-v2", + "issues": "https://github.com/utopia-php/di/issues" + }, + "time": "2024-06-11T16:03:00+00:00" + }, + { + "name": "utopia-php/domains", + "version": "dev-feat-framework-v2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "4e7055f0aaba0c16ae60c972faefb9189fa0db1c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/4e7055f0aaba0c16ae60c972faefb9189fa0db1c", + "reference": "4e7055f0aaba0c16ae60c972faefb9189fa0db1c", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/framework": "0.*.*" + "utopia-php/framework": "0.34.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -1829,9 +1895,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.5.0" + "source": "https://github.com/utopia-php/domains/tree/feat-framework-v2" }, - "time": "2024-01-03T22:04:27+00:00" + "time": "2024-03-08T09:24:35+00:00" }, { "name": "utopia-php/dsn", @@ -1921,26 +1987,30 @@ }, { "name": "utopia-php/framework", - "version": "0.33.6", + "version": "dev-feat-di-upgrade", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "8fe57da0cecd57e3b17cd395b4a666a24f4c07a6" + "reference": "d72069f810521fd90a378791adc7e1cf8fc9a378" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/8fe57da0cecd57e3b17cd395b4a666a24f4c07a6", - "reference": "8fe57da0cecd57e3b17cd395b4a666a24f4c07a6", + "url": "https://api.github.com/repos/utopia-php/http/zipball/d72069f810521fd90a378791adc7e1cf8fc9a378", + "reference": "d72069f810521fd90a378791adc7e1cf8fc9a378", "shasum": "" }, "require": { - "php": ">=8.0" + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/servers": "dev-dev" }, "require-dev": { + "ext-xdebug": "*", "laravel/pint": "^1.2", "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" }, "type": "library", "autoload": { @@ -1952,17 +2022,18 @@ "license": [ "MIT" ], - "description": "A simple, light and advanced PHP framework", + "description": "A simple, light and advanced PHP HTTP framework", "keywords": [ "framework", + "http", "php", "upf" ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.6" + "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-03-21T18:10:57+00:00" + "time": "2024-07-04T15:01:24+00:00" }, { "name": "utopia-php/image", @@ -2277,21 +2348,21 @@ }, { "name": "utopia-php/orchestration", - "version": "0.9.1", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + "reference": "ede2b7a60bd1630ef8bd7fdbbc3cf73275fc9f28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/ede2b7a60bd1630ef8bd7fdbbc3cf73275fc9f28", + "reference": "ede2b7a60bd1630ef8bd7fdbbc3cf73275fc9f28", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*" + "utopia-php/cli": "0.17.*" }, "require-dev": { "laravel/pint": "^1.2", @@ -2321,31 +2392,31 @@ ], "support": { "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + "source": "https://github.com/utopia-php/orchestration/tree/feat-framework-v2" }, - "time": "2023-03-17T15:05:06+00:00" + "time": "2024-03-07T15:56:18+00:00" }, { "name": "utopia-php/platform", - "version": "0.7.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52" + "reference": "f40b23bf84f8fd95bc954ef9297663c01fed5c4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/f40b23bf84f8fd95bc954ef9297663c01fed5c4c", + "reference": "f40b23bf84f8fd95bc954ef9297663c01fed5c4c", "shasum": "" }, "require": { "ext-json": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.33.*", - "utopia-php/queue": "0.7.*" + "utopia-php/cli": "dev-dev-coroutines as 0.17.99", + "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99", + "utopia-php/queue": "dev-feat-coroutine-and-di as 0.7.99" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2371,9 +2442,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.7.0" + "source": "https://github.com/utopia-php/platform/tree/feat-framework-v2" }, - "time": "2024-05-08T17:00:55+00:00" + "time": "2024-06-21T19:25:15+00:00" }, { "name": "utopia-php/pools", @@ -2481,22 +2552,23 @@ }, { "name": "utopia-php/queue", - "version": "0.7.0", + "version": "dev-feat-coroutine-and-di", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "917565256eb94bcab7246f7a746b1a486813761b" + "reference": "cad5651b38f0f69e20e805424d0c29818c15c174" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/917565256eb94bcab7246f7a746b1a486813761b", - "reference": "917565256eb94bcab7246f7a746b1a486813761b", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/cad5651b38f0f69e20e805424d0c29818c15c174", + "reference": "cad5651b38f0f69e20e805424d0c29818c15c174", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" + "utopia-php/cli": "0.17.*", + "utopia-php/di": "dev-feat-framework-v2", + "utopia-php/servers": "dev-dev" }, "require-dev": { "laravel/pint": "^0.2.3", @@ -2506,6 +2578,7 @@ "workerman/workerman": "^4.0" }, "suggest": { + "ext-redis": "Needed to support Redis connections", "ext-swoole": "Needed to support Swoole.", "workerman/workerman": "Needed to support Workerman." }, @@ -2536,9 +2609,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.7.0" + "source": "https://github.com/utopia-php/queue/tree/feat-coroutine-and-di" }, - "time": "2024-01-17T19:00:43+00:00" + "time": "2024-06-07T18:50:32+00:00" }, { "name": "utopia-php/registry", @@ -2593,17 +2666,88 @@ "time": "2021-03-10T10:45:22+00:00" }, { - "name": "utopia-php/storage", - "version": "0.18.4", + "name": "utopia-php/servers", + "version": "dev-dev", "source": { "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "94ab8758fabcefee5c5fa723616e45719833f922" + "url": "https://github.com/utopia-php/servers.git", + "reference": "4565c1c111f6da6b18bc0f00f350377a1e691e48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/94ab8758fabcefee5c5fa723616e45719833f922", - "reference": "94ab8758fabcefee5c5fa723616e45719833f922", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/4565c1c111f6da6b18bc0f00f350377a1e691e48", + "reference": "4565c1c111f6da6b18bc0f00f350377a1e691e48", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/di": "dev-feat-framework-v2" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Servers\\": "src/Servers" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\E2E\\": "tests/Servers/Unit" + } + }, + "scripts": { + "test": [ + "phpunit" + ], + "analyse": [ + "vendor/bin/phpstan analyse" + ], + "format": [ + "vendor/bin/pint" + ], + "lint": [ + "vendor/bin/pint --test" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A base library for building Utopia style servers.", + "keywords": [ + "framework", + "php", + "servers", + "upf", + "utopia" + ], + "support": { + "source": "https://github.com/utopia-php/servers/tree/dev", + "issues": "https://github.com/utopia-php/servers/issues" + }, + "time": "2024-06-07T18:49:59+00:00" + }, + { + "name": "utopia-php/storage", + "version": "dev-feat-framework-v2-v2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "80eafa63cb86b33ce35d48c0189bae6ebdc02734" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/80eafa63cb86b33ce35d48c0189bae6ebdc02734", + "reference": "80eafa63cb86b33ce35d48c0189bae6ebdc02734", "shasum": "" }, "require": { @@ -2615,7 +2759,7 @@ "ext-zlib": "*", "ext-zstd": "*", "php": ">=8.0", - "utopia-php/framework": "0.*.*", + "utopia-php/framework": "0.34.*", "utopia-php/system": "0.*.*" }, "require-dev": { @@ -2643,60 +2787,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.4" + "source": "https://github.com/utopia-php/storage/tree/feat-framework-v2-v2" }, - "time": "2024-04-02T08:24:09+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.8.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.33.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "5.0.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.8.2" - }, - "time": "2024-02-01T14:54:12+00:00" + "time": "2024-03-08T09:39:46+00:00" }, { "name": "utopia-php/system", @@ -2803,6 +2896,49 @@ }, "time": "2024-06-05T17:38:29+00:00" }, + { + "name": "utopia-php/view", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/view.git", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/view/zipball/6ee55e83bc014c39ed6b69390f6d399116f65e88", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\View\\": "src/View" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP rendering engine", + "keywords": [ + "php", + "view" + ], + "support": { + "issues": "https://github.com/utopia-php/view/issues", + "source": "https://github.com/utopia-php/view/tree/0.2.0" + }, + "time": "2024-04-01T17:21:29+00:00" + }, { "name": "utopia-php/websocket", "version": "0.1.0", @@ -2988,16 +3124,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.38.7", + "version": "0.38.8", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a" + "reference": "6367c57ddbcf7b88cacb900c4fe7ef3f28bf38ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a", - "reference": "0a66c1149ef05ed9f45ce1c897c4a0ce9ee5e95a", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/6367c57ddbcf7b88cacb900c4fe7ef3f28bf38ef", + "reference": "6367c57ddbcf7b88cacb900c4fe7ef3f28bf38ef", "shasum": "" }, "require": { @@ -3033,13 +3169,13 @@ "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.38.7" + "source": "https://github.com/appwrite/sdk-generator/tree/0.38.8" }, - "time": "2024-06-10T00:23:02+00:00" + "time": "2024-06-17T00:42:27+00:00" }, { "name": "doctrine/deprecations", - "version": "1.1.3", + "version": "1.1.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", @@ -3066,6 +3202,7 @@ "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -3086,29 +3223,29 @@ }, { "name": "doctrine/instantiator", - "version": "1.5.0", + "version": "1.5.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "reference": "12be2483e1f0e850b353e26869e4e6c038459501" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/12be2483e1f0e850b353e26869e4e6c038459501", + "reference": "12be2483e1f0e850b353e26869e4e6c038459501", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", + "doctrine/coding-standard": "^9 || ^12", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.16 || ^1", "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", @@ -3136,7 +3273,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "source": "https://github.com/doctrine/instantiator/tree/1.5.x" }, "funding": [ { @@ -3152,7 +3289,7 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2023-12-09T14:16:53+00:00" }, { "name": "laravel/pint", @@ -3346,7 +3483,7 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", @@ -3371,6 +3508,7 @@ "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, + "default-branch": true, "type": "library", "autoload": { "files": [ @@ -3406,16 +3544,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.0.2", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", "shasum": "" }, "require": { @@ -3426,7 +3564,7 @@ }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -3458,13 +3596,13 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" }, - "time": "2024-03-05T20:51:40+00:00" + "time": "2024-07-01T20:03:41+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.4", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", @@ -3484,6 +3622,7 @@ "phar-io/version": "^3.0.1", "php": "^7.2 || ^8.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3582,25 +3721,25 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "reference": "a0eeab580cbdf4414fef6978732510a36ed0a9d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/a0eeab580cbdf4414fef6978732510a36ed0a9d6", + "reference": "a0eeab580cbdf4414fef6978732510a36ed0a9d6", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-2.x": "2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -3629,22 +3768,22 @@ ], "support": { "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master" }, - "time": "2020-06-27T09:03:43+00:00" + "time": "2021-06-25T13:47:51+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.4.1", + "version": "5.x-dev", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + "reference": "aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39", + "reference": "aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39", "shasum": "" }, "require": { @@ -3665,6 +3804,7 @@ "phpunit/phpunit": "^9.5", "vimeo/psalm": "^5.13" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3693,29 +3833,29 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.x" }, - "time": "2024-05-21T05:55:05+00:00" + "time": "2024-05-21T06:14:15+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "reference": "eee054a3d40f09920f5b27c9b09a6483f88d9d24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/eee054a3d40f09920f5b27c9b09a6483f88d9d24", + "reference": "eee054a3d40f09920f5b27c9b09a6483f88d9d24", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "phpstan/phpdoc-parser": "^1.18" }, "require-dev": { "ext-tokenizer": "*", @@ -3727,6 +3867,7 @@ "rector/rector": "^0.13.9", "vimeo/psalm": "^4.25" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3751,22 +3892,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.x" }, - "time": "2024-02-23T11:10:43+00:00" + "time": "2024-05-24T14:24:30+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.19.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87" + "reference": "f9e07be0992e7bf1cad210829055b99318df142f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/67a759e7d8746d501c41536ba40cd9c0a07d6a87", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f9e07be0992e7bf1cad210829055b99318df142f", + "reference": "f9e07be0992e7bf1cad210829055b99318df142f", "shasum": "" }, "require": { @@ -3781,6 +3922,7 @@ "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3820,9 +3962,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.19.0" + "source": "https://github.com/phpspec/prophecy/tree/master" }, - "time": "2024-02-29T11:52:51+00:00" + "time": "2024-03-29T09:25:04+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -3872,17 +4014,76 @@ "time": "2024-05-31T08:52:43+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "9.2.31", + "name": "phpstan/phpstan", + "version": "1.8.x-dev", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + "url": "https://github.com/phpstan/phpstan.git", + "reference": "b20042710baa0d9c07636cc66d4c400f03f1477a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b20042710baa0d9c07636cc66d4c400f03f1477a", + "reference": "b20042710baa0d9c07636cc66d4c400f03f1477a", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.8.x" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-10-29T12:56:57+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "39d628812d8d83344a6c1b07799e3700d830d355" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/39d628812d8d83344a6c1b07799e3700d830d355", + "reference": "39d628812d8d83344a6c1b07799e3700d830d355", "shasum": "" }, "require": { @@ -3901,7 +4102,7 @@ "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -3910,7 +4111,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2-dev" } }, "autoload": { @@ -3939,7 +4140,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2" }, "funding": [ { @@ -3947,20 +4148,20 @@ "type": "github" } ], - "time": "2024-03-02T06:37:42+00:00" + "time": "2024-06-29T07:23:05+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "3.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "38b24367e1b340aa78b96d7cab042942d917bb84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/38b24367e1b340aa78b96d7cab042942d917bb84", + "reference": "38b24367e1b340aa78b96d7cab042942d917bb84", "shasum": "" }, "require": { @@ -3999,7 +4200,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0" }, "funding": [ { @@ -4007,7 +4208,7 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2022-02-11T16:23:04+00:00" }, { "name": "phpunit/php-invoker", @@ -4295,7 +4496,7 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", @@ -4310,6 +4511,7 @@ "require": { "php": ">=8.0.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4345,7 +4547,7 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.2", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", @@ -4512,16 +4714,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "b247957a1c8dc81a671770f74b479c0a78a818f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b247957a1c8dc81a671770f74b479c0a78a818f1", + "reference": "b247957a1c8dc81a671770f74b479c0a78a818f1", "shasum": "" }, "require": { @@ -4574,7 +4776,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0" }, "funding": [ { @@ -4582,11 +4784,11 @@ "type": "github" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2022-09-14T12:46:14+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.3", + "version": "2.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", @@ -4643,7 +4845,7 @@ }, { "name": "sebastian/diff", - "version": "4.0.6", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", @@ -4709,7 +4911,7 @@ }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "5.1.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", @@ -4760,7 +4962,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1" }, "funding": [ { @@ -4772,7 +4974,7 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", @@ -4849,7 +5051,7 @@ }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", @@ -4913,7 +5115,7 @@ }, { "name": "sebastian/lines-of-code", - "version": "1.0.4", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", @@ -5082,7 +5284,7 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", @@ -5145,16 +5347,16 @@ }, { "name": "sebastian/resource-operations", - "version": "3.0.4", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + "reference": "ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25", + "reference": "ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25", "shasum": "" }, "require": { @@ -5163,6 +5365,7 @@ "require-dev": { "phpunit/phpunit": "^9.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -5187,7 +5390,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/main" }, "funding": [ { @@ -5195,11 +5398,11 @@ "type": "github" } ], - "time": "2024-03-14T16:00:52+00:00" + "time": "2024-03-14T18:47:08+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "3.2.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", @@ -5243,7 +5446,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "source": "https://github.com/sebastianbergmann/type/tree/3.2" }, "funding": [ { @@ -5255,7 +5458,7 @@ }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "3.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", @@ -5340,7 +5543,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -5361,6 +5564,7 @@ "suggest": { "ext-ctype": "For best performance" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -5589,9 +5793,88 @@ "time": "2023-11-21T18:54:41+00:00" } ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], + "aliases": [ + { + "package": "utopia-php/abuse", + "version": "dev-feat-framework-v2", + "alias": "0.37.99", + "alias_normalized": "0.37.99.0" + }, + { + "package": "utopia-php/analytics", + "version": "dev-feat-framework-v2", + "alias": "0.10.99", + "alias_normalized": "0.10.99.0" + }, + { + "package": "utopia-php/audit", + "version": "dev-feat-framework-v2", + "alias": "0.39.99", + "alias_normalized": "0.39.99.0" + }, + { + "package": "utopia-php/cli", + "version": "dev-dev-coroutines", + "alias": "0.17.99", + "alias_normalized": "0.17.99.0" + }, + { + "package": "utopia-php/database", + "version": "dev-feat-framework-v2", + "alias": "0.49.99", + "alias_normalized": "0.49.99.0" + }, + { + "package": "utopia-php/domains", + "version": "dev-feat-framework-v2", + "alias": "0.5.99", + "alias_normalized": "0.5.99.0" + }, + { + "package": "utopia-php/framework", + "version": "dev-feat-di-upgrade", + "alias": "0.34.99", + "alias_normalized": "0.34.99.0" + }, + { + "package": "utopia-php/orchestration", + "version": "dev-feat-framework-v2", + "alias": "0.9.99", + "alias_normalized": "0.9.99.0" + }, + { + "package": "utopia-php/platform", + "version": "dev-feat-framework-v2", + "alias": "0.5.99", + "alias_normalized": "0.5.99.0" + }, + { + "package": "utopia-php/queue", + "version": "dev-feat-coroutine-and-di", + "alias": "0.7.99", + "alias_normalized": "0.7.99.0" + }, + { + "package": "utopia-php/storage", + "version": "dev-feat-framework-v2-v2", + "alias": "0.18.99", + "alias_normalized": "0.18.99.0" + } + ], + "minimum-stability": "dev", + "stability-flags": { + "utopia-php/abuse": 20, + "utopia-php/analytics": 20, + "utopia-php/audit": 20, + "utopia-php/cli": 20, + "utopia-php/database": 20, + "utopia-php/domains": 20, + "utopia-php/framework": 20, + "utopia-php/orchestration": 20, + "utopia-php/platform": 20, + "utopia-php/queue": 20, + "utopia-php/storage": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From 664c3ad4666cbeec7c9e13fce762ae5bfb9b3ee9 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 4 Jul 2024 15:04:20 -0400 Subject: [PATCH 118/195] chore: Cleanup --- app/cli.php | 1 + app/controllers/api/projects.php | 46 +- app/controllers/general.php | 4 +- app/controllers/shared/api.php | 7 - app/init.php | 3397 +++++++++++++------------ composer.json | 2 +- composer.lock | 37 +- src/Appwrite/Platform/Tasks/Specs.php | 7 +- 8 files changed, 1721 insertions(+), 1780 deletions(-) diff --git a/app/cli.php b/app/cli.php index adff98aeeb..10d36291ee 100644 --- a/app/cli.php +++ b/app/cli.php @@ -5,6 +5,7 @@ require_once __DIR__ . '/controllers/general.php'; use Appwrite\Event\Certificate; use Appwrite\Event\Delete; +use Appwrite\Event\Func; use Appwrite\Platform\Appwrite; use Swoole\Runtime; use Utopia\CLI\Adapters\Swoole as SwooleCLI; diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 0b9d462f80..b66d1205fc 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -185,50 +185,6 @@ Http::post('/v1/projects') $dsn = new DSN('mysql://' . $dsn); } - try { - $project = $dbForConsole->createDocument('projects', new Document([ - '$id' => $projectId, - '$permissions' => [ - Permission::read(Role::team(ID::custom($teamId))), - Permission::update(Role::team(ID::custom($teamId), 'owner')), - Permission::update(Role::team(ID::custom($teamId), 'developer')), - Permission::delete(Role::team(ID::custom($teamId), 'owner')), - Permission::delete(Role::team(ID::custom($teamId), 'developer')), - ], - 'name' => $name, - 'teamInternalId' => $team->getInternalId(), - 'teamId' => $team->getId(), - 'region' => $region, - 'description' => $description, - 'logo' => $logo, - 'url' => $url, - 'version' => APP_VERSION_STABLE, - 'legalName' => $legalName, - 'legalCountry' => $legalCountry, - 'legalState' => $legalState, - 'legalCity' => $legalCity, - 'legalAddress' => $legalAddress, - 'legalTaxId' => ID::custom($legalTaxId), - 'services' => new stdClass(), - 'platforms' => null, - 'oAuthProviders' => [], - 'webhooks' => null, - 'keys' => null, - 'auths' => $auths, - 'search' => implode(' ', [$projectId, $name]), - 'database' => $dsn, - ])); - } catch (Duplicate) { - throw new Exception(Exception::PROJECT_ALREADY_EXISTS); - } - - try { - $dsn = new DSN($dsn); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $dsn); - } - $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; $connection = $pool->get(); @@ -285,7 +241,7 @@ Http::post('/v1/projects') // Collection already exists } } - + $connections->reclaim(); // Hook allowing instant project mirroring during migration // Outside of migration, hook is not registered and has no effect $hooks->trigger('afterProjectCreation', [$project, $pools, $cache]); diff --git a/app/controllers/general.php b/app/controllers/general.php index 5c4255328e..2e5f953b5f 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -668,7 +668,7 @@ Http::error() ->inject('authorization') ->inject('connections') ->inject('queueForUsage') - ->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization, Connections $connections, Usage $queueForUsage) { + ->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization, Connections $connections, Usage $queueForUsage) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); if (is_null($route)) { @@ -762,7 +762,7 @@ Http::error() } if ($publish && $project->getId() !== 'console') { - if (!Auth::isPrivilegedUser(Authorization::getRoles())) { + if (!Auth::isPrivilegedUser($authorization->getRoles())) { $fileSize = 0; $file = $request->getFiles('file'); if (!empty($file)) { diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 0c6267dd69..70873ddeaa 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -761,10 +761,3 @@ Http::shutdown() ->action(function (Connections $connections) { $connections->reclaim(); }); - - -Http::error() - ->inject('connections') - ->action(function (Connections $connections) { - $connections->reclaim(); - }); diff --git a/app/init.php b/app/init.php index ea8b087faa..d1257b22b9 100644 --- a/app/init.php +++ b/app/init.php @@ -1,1700 +1,1701 @@ $value], JSON_PRESERVE_ZERO_FRACTION); - }, - function (mixed $value) { - if (is_null($value)) { - return; - } - - return json_decode($value, true)['value']; - } -); - -Database::addFilter( - 'enum', - function (mixed $value, Document $attribute) { - if ($attribute->isSet('elements')) { - $attribute->removeAttribute('elements'); - } - - return $value; - }, - function (mixed $value, Document $attribute) { - $formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true); - if (isset($formatOptions['elements'])) { - $attribute->setAttribute('elements', $formatOptions['elements']); - } - - return $value; - } -); - -Database::addFilter( - 'range', - function (mixed $value, Document $attribute) { - if ($attribute->isSet('min')) { - $attribute->removeAttribute('min'); - } - if ($attribute->isSet('max')) { - $attribute->removeAttribute('max'); - } - - return $value; - }, - function (mixed $value, Document $attribute) { - $formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true); - if (isset($formatOptions['min']) || isset($formatOptions['max'])) { - $attribute - ->setAttribute('min', $formatOptions['min']) - ->setAttribute('max', $formatOptions['max']) - ; - } - - return $value; - } -); - -Database::addFilter( - 'subQueryAttributes', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - $attributes = $database->find('attributes', [ - Query::equal('collectionInternalId', [$document->getInternalId()]), - Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), - Query::limit($database->getLimitForAttributes()), - ]); - - foreach ($attributes as $attribute) { - if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { - $options = $attribute->getAttribute('options'); - foreach ($options as $key => $value) { - $attribute->setAttribute($key, $value); - } - $attribute->removeAttribute('options'); - } - } - - return $attributes; - } -); - -Database::addFilter( - 'subQueryIndexes', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('indexes', [ - Query::equal('collectionInternalId', [$document->getInternalId()]), - Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), - Query::limit($database->getLimitForIndexes()), - ]); - } -); - -Database::addFilter( - 'subQueryPlatforms', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('platforms', [ - Query::equal('projectInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'subQueryKeys', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('keys', [ - Query::equal('projectInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'subQueryWebhooks', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('webhooks', [ - Query::equal('projectInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'subQuerySessions', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database->find('sessions', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryTokens', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('tokens', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryChallenges', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('challenges', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryAuthenticators', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('authenticators', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryMemberships', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('memberships', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryVariables', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('variables', [ - Query::equal('resourceInternalId', [$document->getInternalId()]), - Query::equal('resourceType', ['function']), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'encrypt', - function (mixed $value) { - $key = System::getEnv('_APP_OPENSSL_KEY_V1'); - $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); - $tag = null; - - return json_encode([ - 'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag), - 'method' => OpenSSL::CIPHER_AES_128_GCM, - 'iv' => \bin2hex($iv), - 'tag' => \bin2hex($tag ?? ''), - 'version' => '1', - ]); - }, - function (mixed $value) { - if (is_null($value)) { - return; - } - $value = json_decode($value, true); - $key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']); - - return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag'])); - } -); - -Database::addFilter( - 'subQueryProjectVariables', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('variables', [ - Query::equal('resourceType', ['project']), - Query::limit(APP_LIMIT_SUBQUERY) - ]); - } -); - -Database::addFilter( - 'userSearch', - function (mixed $value, Document $user) { - $searchValues = [ - $user->getId(), - $user->getAttribute('email', ''), - $user->getAttribute('name', ''), - $user->getAttribute('phone', '') - ]; - - foreach ($user->getAttribute('labels', []) as $label) { - $searchValues[] = 'label:' . $label; - } - - $search = implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'subQueryTargets', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('targets', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY) - ])); - } -); - -Database::addFilter( - 'subQueryTopicTargets', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - $targetIds = Authorization::skip(fn () => \array_map( - fn ($document) => $document->getAttribute('targetInternalId'), - $database->find('subscribers', [ - Query::equal('topicInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY) - ]) - )); - if (\count($targetIds) > 0) { - return $database->skipValidation(fn () => $database->find('targets', [ - Query::equal('$internalId', $targetIds) - ])); - } - return []; - } -); - -Database::addFilter( - 'providerSearch', - function (mixed $value, Document $provider) { - $searchValues = [ - $provider->getId(), - $provider->getAttribute('name', ''), - $provider->getAttribute('provider', ''), - $provider->getAttribute('type', '') - ]; - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'topicSearch', - function (mixed $value, Document $topic) { - $searchValues = [ - $topic->getId(), - $topic->getAttribute('name', ''), - $topic->getAttribute('description', ''), - ]; - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'messageSearch', - function (mixed $value, Document $message) { - $searchValues = [ - $message->getId(), - $message->getAttribute('description', ''), - $message->getAttribute('status', ''), - ]; - - $data = \json_decode($message->getAttribute('data', []), true); - $providerType = $message->getAttribute('providerType', ''); - - if ($providerType === MESSAGE_TYPE_EMAIL) { - $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); - } elseif ($providerType === MESSAGE_TYPE_SMS) { - $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); - } else { - $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); - } - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -/** - * DB Formats - */ -Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () { - return new Email(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () { - return new DatetimeValidator(); -}, Database::VAR_DATETIME); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) { - $elements = $attribute['formatOptions']['elements']; - return new WhiteList($elements, true); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () { - return new IP(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () { - return new URL(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) { - $min = $attribute['formatOptions']['min'] ?? -INF; - $max = $attribute['formatOptions']['max'] ?? INF; - return new Range($min, $max, Range::TYPE_INTEGER); -}, Database::VAR_INTEGER); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) { - $min = $attribute['formatOptions']['min'] ?? -INF; - $max = $attribute['formatOptions']['max'] ?? INF; - return new Range($min, $max, Range::TYPE_FLOAT); -}, Database::VAR_FLOAT); - -/* - * Registry - */ -$register->set('logger', function () { - // Register error logger - $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); - $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); - - if (empty($providerName) || empty($providerConfig)) { - return; - } - - if (!Logger::hasProvider($providerName)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); - } - - // Old Sentry Format conversion. Fallback until the old syntax is completely deprecated. - if (str_contains($providerConfig, ';') && strtolower($providerName) == 'sentry') { - $configChunks = \explode(";", $providerConfig); - - $sentryKey = $configChunks[0]; - $projectId = $configChunks[1]; - - $providerConfig = 'https://' . $sentryKey . '@sentry.io/' . $projectId; - } - - $classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName); - $adapter = new $classname($providerConfig); - return new Logger($adapter); -}); -$register->set('pools', function () { - $group = new Group(); - - $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ - 'scheme' => 'mariadb', - 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), - 'port' => System::getEnv('_APP_DB_PORT', '3306'), - 'user' => System::getEnv('_APP_DB_USER', ''), - 'pass' => System::getEnv('_APP_DB_PASS', ''), - 'path' => System::getEnv('_APP_DB_SCHEMA', ''), - ]); - $fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([ - 'scheme' => 'redis', - 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => System::getEnv('_APP_REDIS_USER', ''), - 'pass' => System::getEnv('_APP_REDIS_PASS', ''), - ]); - - $connections = [ - 'console' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), - 'multiple' => false, - 'schemes' => ['mariadb', 'mysql'], - ], - 'database' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), - 'multiple' => true, - 'schemes' => ['mariadb', 'mysql'], - ], - 'queue' => [ - 'type' => 'queue', - 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'pubsub' => [ - 'type' => 'pubsub', - 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'cache' => [ - 'type' => 'cache', - 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), - 'multiple' => true, - 'schemes' => ['redis'], - ], - ]; - - $maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151); - $instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14); - - $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; - - if ($multiprocessing) { - $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); - } else { - $workerCount = 1; - } - - if ($workerCount > $instanceConnections) { - throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); - } - - $poolSize = (int)($instanceConnections / $workerCount); - - foreach ($connections as $key => $connection) { - $type = $connection['type'] ?? ''; - $multiple = $connection['multiple'] ?? false; - $schemes = $connection['schemes'] ?? []; - $config = []; - $dsns = explode(',', $connection['dsns'] ?? ''); - foreach ($dsns as &$dsn) { - $dsn = explode('=', $dsn); - $name = ($multiple) ? $key . '_' . $dsn[0] : $key; - $dsn = $dsn[1] ?? ''; - $config[] = $name; - if (empty($dsn)) { - //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); - continue; - } - - $dsn = new DSN($dsn); - $dsnHost = $dsn->getHost(); - $dsnPort = $dsn->getPort(); - $dsnUser = $dsn->getUser(); - $dsnPass = $dsn->getPassword(); - $dsnScheme = $dsn->getScheme(); - $dsnDatabase = $dsn->getPath(); - - if (!in_array($dsnScheme, $schemes)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); - } - - /** - * Get Resource - * - * Creation could be reused across connection types like database, cache, queue, etc. - * - * Resource assignment to an adapter will happen below. - */ - $resource = match ($dsnScheme) { - 'mysql', - 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( - PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_EMULATE_PREPARES => true, - PDO::ATTR_STRINGIFY_FETCHES => true - )); - }); - }, - 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { - $redis = new Redis(); - @$redis->pconnect($dsnHost, (int)$dsnPort); - if ($dsnPass) { - $redis->auth($dsnPass); - } - $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); - - return $redis; - }, - default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'), - }; - - $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { - // Get Adapter - switch ($type) { - case 'database': - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($resource()), - 'mysql' => new MySQL($resource()), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - break; - case 'pubsub': - $adapter = $resource(); - break; - case 'queue': - $adapter = match ($dsn->getScheme()) { - 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), - default => null - }; - break; - case 'cache': - $adapter = match ($dsn->getScheme()) { - 'redis' => new RedisCache($resource()), - default => null - }; - break; - - default: - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); - } - - return $adapter; - }); - - $group->add($pool); - } - - Config::setParam('pools-' . $key, $config); - } - - return $group; -}); - -$register->set('db', function () { - // This is usually for our workers or CLI commands scope - $dbHost = System::getEnv('_APP_DB_HOST', ''); - $dbPort = System::getEnv('_APP_DB_PORT', ''); - $dbUser = System::getEnv('_APP_DB_USER', ''); - $dbPass = System::getEnv('_APP_DB_PASS', ''); - $dbScheme = System::getEnv('_APP_DB_SCHEMA', ''); - - return new PDO( - "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", - $dbUser, - $dbPass, - SQL::getPDOAttributes() - ); -}); - -$register->set('smtp', function () { - $mail = new PHPMailer(true); - - $mail->isSMTP(); - - $username = System::getEnv('_APP_SMTP_USERNAME'); - $password = System::getEnv('_APP_SMTP_PASSWORD'); - - $mail->XMailer = 'Appwrite Mailer'; - $mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp'); - $mail->Port = System::getEnv('_APP_SMTP_PORT', 25); - $mail->SMTPAuth = !empty($username) && !empty($password); - $mail->Username = $username; - $mail->Password = $password; - $mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', ''); - $mail->SMTPAutoTLS = false; - $mail->CharSet = 'UTF-8'; - - $from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); - $email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - - $mail->setFrom($email, $from); - $mail->addReplyTo($email, $from); - - $mail->isHTML(true); - - return $mail; -}); -$register->set('geodb', function () { - return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.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); -} - -\stream_context_set_default([ // Set global user agent and http settings - 'http' => [ - 'method' => 'GET', - 'user_agent' => \sprintf( - APP_USERAGENT, - System::getEnv('_APP_VERSION', 'UNKNOWN'), - System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)) - ), - 'timeout' => 2, - ], -]); - -// Runtime Execution -App::setResource('log', fn () => new Log()); -App::setResource('logger', function ($register) { - return $register->get('logger'); -}, ['register']); - -App::setResource('hooks', function ($register) { - return $register->get('hooks'); -}, ['register']); - -App::setResource('register', fn () => $register); -App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); - -App::setResource('localeCodes', function () { - return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); -}); - -// Queues -App::setResource('queue', function (Group $pools) { - return $pools->get('queue')->pop()->getResource(); -}, ['pools']); -App::setResource('queueForMessaging', function (Connection $queue) { - return new Messaging($queue); -}, ['queue']); -App::setResource('queueForMails', function (Connection $queue) { - return new Mail($queue); -}, ['queue']); -App::setResource('queueForBuilds', function (Connection $queue) { - return new Build($queue); -}, ['queue']); -App::setResource('queueForDatabase', function (Connection $queue) { - return new EventDatabase($queue); -}, ['queue']); -App::setResource('queueForDeletes', function (Connection $queue) { - return new Delete($queue); -}, ['queue']); -App::setResource('queueForEvents', function (Connection $queue) { - return new Event($queue); -}, ['queue']); -App::setResource('queueForAudits', function (Connection $queue) { - return new Audit($queue); -}, ['queue']); -App::setResource('queueForFunctions', function (Connection $queue) { - return new Func($queue); -}, ['queue']); -App::setResource('queueForUsage', function (Connection $queue) { - return new Usage($queue); -}, ['queue']); -App::setResource('queueForCertificates', function (Connection $queue) { - return new Certificate($queue); -}, ['queue']); -App::setResource('queueForMigrations', function (Connection $queue) { - return new Migration($queue); -}, ['queue']); -App::setResource('clients', function ($request, $console, $project) { - $console->setAttribute('platforms', [ // Always allow current host - '$collection' => ID::custom('platforms'), - 'name' => 'Current Host', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => $request->getHostname(), - ], Document::SET_TYPE_APPEND); - - $hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', '')); - $validator = new Hostname(); - foreach ($hostnames as $hostname) { - $hostname = trim($hostname); - if (!$validator->isValid($hostname)) { - continue; - } - $console->setAttribute('platforms', [ - '$collection' => ID::custom('platforms'), - 'type' => Origin::CLIENT_TYPE_WEB, - 'name' => $hostname, - 'hostname' => $hostname, - ], Document::SET_TYPE_APPEND); - } - - /** - * Get All verified client URLs for both console and current projects - * + Filter for duplicated entries - */ - $clientsConsole = \array_map( - fn ($node) => $node['hostname'], - \array_filter( - $console->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname'])) - ) - ); - - $clients = $clientsConsole; - $platforms = $project->getAttribute('platforms', []); - - foreach ($platforms as $node) { - if ( - isset($node['type']) && - ($node['type'] === Origin::CLIENT_TYPE_WEB || - $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && - !empty($node['hostname']) - ) { - $clients[] = $node['hostname']; - } - } - - return \array_unique($clients); -}, ['request', 'console', 'project']); - -App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $project */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var string $mode */ - - Authorization::setDefaultStatus(true); - - Auth::setCookieName('a_session_' . $project->getId()); - - if (APP_MODE_ADMIN === $mode) { - Auth::setCookieName('a_session_' . $console->getId()); - } - - $session = Auth::decodeSession( - $request->getCookie( - Auth::$cookieName, // Get sessions - $request->getCookie(Auth::$cookieName . '_legacy', '') - ) - ); - - // Get session from header for SSR clients - if (empty($session['id']) && empty($session['secret'])) { - $sessionHeader = $request->getHeader('x-appwrite-session', ''); - - if (!empty($sessionHeader)) { - $session = Auth::decodeSession($sessionHeader); - } - } - - // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies - if ($response) { - $response->addHeader('X-Debug-Fallback', 'false'); - } - - if (empty($session['id']) && empty($session['secret'])) { - if ($response) { - $response->addHeader('X-Debug-Fallback', 'true'); - } - $fallback = $request->getHeader('x-fallback-cookies', ''); - $fallback = \json_decode($fallback, true); - $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); - } - - Auth::$unique = $session['id'] ?? ''; - Auth::$secret = $session['secret'] ?? ''; - - if (APP_MODE_ADMIN !== $mode) { - if ($project->isEmpty()) { - $user = new Document([]); - } else { - if ($project->getId() === 'console') { - $user = $dbForConsole->getDocument('users', Auth::$unique); - } else { - $user = $dbForProject->getDocument('users', Auth::$unique); - } - } - } else { - $user = $dbForConsole->getDocument('users', Auth::$unique); - } - - if ( - $user->isEmpty() // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) - ) { // Validate user has valid login token - $user = new Document([]); - } - - if (APP_MODE_ADMIN === $mode) { - if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { - Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. - } else { - $user = new Document([]); - } - } - - $authJWT = $request->getHeader('x-appwrite-jwt', ''); - - if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication - $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. - - try { - $payload = $jwt->decode($authJWT); - } catch (JWTException $error) { - throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); - } - - $jwtUserId = $payload['userId'] ?? ''; - $jwtSessionId = $payload['sessionId'] ?? ''; - - if ($jwtUserId && $jwtSessionId) { - $user = $dbForProject->getDocument('users', $jwtUserId); - } - - if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token - $user = new Document([]); - } - } - - $dbForProject->setMetadata('user', $user->getId()); - $dbForConsole->setMetadata('user', $user->getId()); - - return $user; -}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); - -App::setResource('project', function ($dbForConsole, $request, $console) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var Utopia\Database\Document $console */ - - $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); - - if (empty($projectId) || $projectId === 'console') { - return $console; - } - - $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - - return $project; -}, ['dbForConsole', 'request', 'console']); - -App::setResource('session', function (Document $user) { - if ($user->isEmpty()) { - return; - } - - $sessions = $user->getAttribute('sessions', []); - $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); - - if (!$sessionId) { - return; - } - - foreach ($sessions as $session) {/** @var Document $session */ - if ($sessionId === $session->getId()) { - return $session; - } - } - - return; -}, ['user']); - -App::setResource('console', function () { - return new Document([ - '$id' => ID::custom('console'), - '$internalId' => ID::custom('console'), - 'name' => 'Appwrite', - '$collection' => ID::custom('projects'), - 'description' => 'Appwrite core engine', - 'logo' => '', - 'teamId' => -1, - 'webhooks' => [], - 'keys' => [], - 'platforms' => [ - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Localhost', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => 'localhost', - ], // Current host is added on app init - ], - 'legalName' => '', - 'legalCountry' => '', - 'legalState' => '', - 'legalCity' => '', - 'legalAddress' => '', - 'legalTaxId' => '', - 'auths' => [ - '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 - ], - 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], - 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], - 'oAuthProviders' => [ - 'githubEnabled' => true, - 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), - 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') - ], - ]); -}, []); - -App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); - - $database - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; -}, ['pools', 'dbForConsole', 'cache', 'project']); - -App::setResource('dbForConsole', function (Group $pools, Cache $cache) { - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); - - $database - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - return $database; -}, ['pools', 'cache']); - -App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $configure = (function (Database $database) use ($project, $dsn) { - $database - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - }); - - if (isset($databases[$dsn->getHost()])) { - $database = $databases[$dsn->getHost()]; - $configure($database); - return $database; - } - - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); - $databases[$dsn->getHost()] = $database; - $configure($database); - - return $database; - }; -}, ['pools', 'dbForConsole', 'cache']); - -App::setResource('cache', function (Group $pools) { - $list = Config::getParam('pools-cache', []); - $adapters = []; - - foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource() - ; - } - - return new Cache(new Sharding($adapters)); -}, ['pools']); - -App::setResource('deviceForLocal', function () { - return new Local(); -}); - -App::setResource('deviceForFiles', function ($project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); -}, ['project']); - -App::setResource('deviceForFunctions', function ($project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); -}, ['project']); - -App::setResource('deviceForBuilds', function ($project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); -}, ['project']); - -function getDevice($root): Device -{ - $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); - - if (!empty($connection)) { - $acl = 'private'; - $device = Storage::DEVICE_LOCAL; - $accessKey = ''; - $accessSecret = ''; - $bucket = ''; - $region = ''; - - try { - $dsn = new DSN($connection); - $device = $dsn->getScheme(); - $accessKey = $dsn->getUser() ?? ''; - $accessSecret = $dsn->getPassword() ?? ''; - $bucket = $dsn->getPath() ?? ''; - $region = $dsn->getParam('region'); - } catch (\Throwable $e) { - Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); - } - - switch ($device) { - case Storage::DEVICE_S3: - return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case STORAGE::DEVICE_DO_SPACES: - $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); - $device->setHttpVersion(S3::HTTP_VERSION_1_1); - return $device; - case Storage::DEVICE_BACKBLAZE: - return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LINODE: - return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_WASABI: - return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - } - } else { - switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - case Storage::DEVICE_S3: - $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); - $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); - $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); - $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); - $s3Acl = 'private'; - return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); - case Storage::DEVICE_DO_SPACES: - $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); - $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); - $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); - $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); - $doSpacesAcl = 'private'; - $device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); - $device->setHttpVersion(S3::HTTP_VERSION_1_1); - return $device; - case Storage::DEVICE_BACKBLAZE: - $backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); - $backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); - $backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); - $backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); - $backblazeAcl = 'private'; - return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); - case Storage::DEVICE_LINODE: - $linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); - $linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', ''); - $linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', ''); - $linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); - $linodeAcl = 'private'; - return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); - case Storage::DEVICE_WASABI: - $wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); - $wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', ''); - $wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', ''); - $wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); - $wasabiAcl = 'private'; - return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); - } - } -} - -App::setResource('mode', function ($request) { - /** @var Appwrite\Utopia\Request $request */ - - /** - * Defines the mode for the request: - * - 'default' => Requests for Client and Server Side - * - 'admin' => Request from the Console on non-console projects - */ - return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); -}, ['request']); - -App::setResource('geodb', function ($register) { - /** @var Utopia\Registry\Registry $register */ - return $register->get('geodb'); -}, ['register']); - -App::setResource('passwordsDictionary', function ($register) { - /** @var Utopia\Registry\Registry $register */ - return $register->get('passwordsDictionary'); -}, ['register']); - - -App::setResource('servers', function () { - $platforms = Config::getParam('platforms'); - $server = $platforms[APP_PLATFORM_SERVER]; - - $languages = array_map(function ($language) { - return strtolower($language['name']); - }, $server['sdks']); - - return $languages; -}); - -App::setResource('promiseAdapter', function ($register) { - return $register->get('promiseAdapter'); -}, ['register']); - -App::setResource('schema', function ($utopia, $dbForProject) { - - $complexity = function (int $complexity, array $args) { - $queries = Query::parseQueries($args['queries'] ?? []); - $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; - $limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT; - - return $complexity * $limit; - }; - - $attributes = function (int $limit, int $offset) use ($dbForProject) { - $attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [ - Query::limit($limit), - Query::offset($offset), - ])); - - return \array_map(function ($attr) { - return $attr->getArrayCopy(); - }, $attrs); - }; - - $urls = [ - 'list' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents"; - }, - 'create' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents"; - }, - 'read' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; - }, - 'update' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; - }, - 'delete' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; - }, - ]; - - $params = [ - 'list' => function (string $databaseId, string $collectionId, array $args) { - return [ 'queries' => $args['queries']]; - }, - 'create' => function (string $databaseId, string $collectionId, array $args) { - $id = $args['id'] ?? 'unique()'; - $permissions = $args['permissions'] ?? null; - - unset($args['id']); - unset($args['permissions']); - - // Order must be the same as the route params - return [ - 'databaseId' => $databaseId, - 'documentId' => $id, - 'collectionId' => $collectionId, - 'data' => $args, - 'permissions' => $permissions, - ]; - }, - 'update' => function (string $databaseId, string $collectionId, array $args) { - $documentId = $args['id']; - $permissions = $args['permissions'] ?? null; - - unset($args['id']); - unset($args['permissions']); - - // Order must be the same as the route params - return [ - 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'documentId' => $documentId, - 'data' => $args, - 'permissions' => $permissions, - ]; - }, - ]; - - return Schema::build( - $utopia, - $complexity, - $attributes, - $urls, - $params, - ); -}, ['utopia', 'dbForProject']); - -App::setResource('contributors', function () { - $path = 'app/config/contributors.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -App::setResource('employees', function () { - $path = 'app/config/employees.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -App::setResource('heroes', function () { - $path = 'app/config/heroes.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -App::setResource('gitHub', function (Cache $cache) { - return new VcsGitHub($cache); -}, ['cache']); - -App::setResource('requestTimestamp', function ($request) { - //TODO: Move this to the Request class itself - $timestampHeader = $request->getHeader('x-appwrite-timestamp'); - $requestTimestamp = null; - if (!empty($timestampHeader)) { - try { - $requestTimestamp = new \DateTime($timestampHeader); - } catch (\Throwable $e) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value'); - } - } - return $requestTimestamp; -}, ['request']); -App::setResource('plan', function (array $plan = []) { - return []; -}); +// +///** +// * Init +// * +// * Initializes both Appwrite API entry point, queue workers, and CLI tasks. +// * Set configuration, framework resources & app constants +// * +// */ +// +//if (\file_exists(__DIR__ . '/../vendor/autoload.php')) { +// require_once __DIR__ . '/../vendor/autoload.php'; +//} +// +//\ini_set('memory_limit', '512M'); +//\ini_set('display_errors', 1); +//\ini_set('display_startup_errors', 1); +//\ini_set('default_socket_timeout', -1); +//\error_reporting(E_ALL); +// +//use Ahc\Jwt\JWT; +//use Ahc\Jwt\JWTException; +//use Appwrite\Auth\Auth; +//use Appwrite\Event\Audit; +//use Appwrite\Event\Build; +//use Appwrite\Event\Certificate; +//use Appwrite\Event\Database as EventDatabase; +//use Appwrite\Event\Delete; +//use Appwrite\Event\Event; +//use Appwrite\Event\Func; +//use Appwrite\Event\Mail; +//use Appwrite\Event\Messaging; +//use Appwrite\Event\Migration; +//use Appwrite\Event\Usage; +//use Appwrite\Extend\Exception; +//use Appwrite\GraphQL\Promises\Adapter\Swoole; +//use Appwrite\GraphQL\Schema; +//use Appwrite\Hooks\Hooks; +//use Appwrite\Network\Validator\Email; +//use Appwrite\Network\Validator\Origin; +//use Appwrite\OpenSSL\OpenSSL; +//use Appwrite\URL\URL as AppwriteURL; +//use MaxMind\Db\Reader; +//use PHPMailer\PHPMailer\PHPMailer; +//use Swoole\Database\PDOProxy; +//use Utopia\App; +//use Utopia\Cache\Adapter\Redis as RedisCache; +//use Utopia\Cache\Adapter\Sharding; +//use Utopia\Cache\Cache; +//use Utopia\CLI\Console; +//use Utopia\Config\Config; +//use Utopia\Database\Adapter\MariaDB; +//use Utopia\Database\Adapter\MySQL; +//use Utopia\Database\Adapter\SQL; +//use Utopia\Database\Database; +//use Utopia\Database\Document; +//use Utopia\Database\Helpers\ID; +//use Utopia\Database\Query; +//use Utopia\Database\Validator\Authorization; +//use Utopia\Database\Validator\Datetime as DatetimeValidator; +//use Utopia\Database\Validator\Structure; +//use Utopia\Domains\Validator\PublicDomain; +//use Utopia\DSN\DSN; +//use Utopia\Locale\Locale; +//use Utopia\Logger\Log; +//use Utopia\Logger\Logger; +//use Utopia\Pools\Group; +//use Utopia\Pools\Pool; +//use Utopia\Queue; +//use Utopia\Queue\Connection; +//use Utopia\Registry\Registry; +//use Utopia\Storage\Device; +//use Utopia\Storage\Device\Backblaze; +//use Utopia\Storage\Device\DOSpaces; +//use Utopia\Storage\Device\Linode; +//use Utopia\Storage\Device\Local; +//use Utopia\Storage\Device\S3; +//use Utopia\Storage\Device\Wasabi; +//use Utopia\Storage\Storage; +//use Utopia\System\System; +//use Utopia\Validator\Hostname; +//use Utopia\Validator\IP; +//use Utopia\Validator\Range; +//use Utopia\Validator\URL; +//use Utopia\Validator\WhiteList; +//use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; +// +//const APP_NAME = 'Appwrite'; +//const APP_DOMAIN = 'appwrite.io'; +//const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address +//const APP_EMAIL_SECURITY = ''; // Default security email address +//const APP_USERAGENT = APP_NAME . '-Server v%s. Please report abuse at %s'; +//const APP_MODE_DEFAULT = 'default'; +//const APP_MODE_ADMIN = 'admin'; +//const APP_PAGING_LIMIT = 12; +//const APP_LIMIT_COUNT = 5000; +//const APP_LIMIT_USERS = 10_000; +//const APP_LIMIT_USER_PASSWORD_HISTORY = 20; +//const APP_LIMIT_USER_SESSIONS_MAX = 100; +//const APP_LIMIT_USER_SESSIONS_DEFAULT = 10; +//const APP_LIMIT_ANTIVIRUS = 20_000_000; //20MB +//const APP_LIMIT_ENCRYPTION = 20_000_000; //20MB +//const APP_LIMIT_COMPRESSION = 20_000_000; //20MB +//const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value +//const APP_LIMIT_ARRAY_LABELS_SIZE = 1000; // Default maximum of how many labels elements can there be in API parameter that expects array value +//const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length. +//const APP_LIMIT_SUBQUERY = 1000; +//const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000; +//const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period +//const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds +//const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls +//const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours +//const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours +//const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours +//const APP_CACHE_BUSTER = 4314; +//const APP_VERSION_STABLE = '1.5.7'; +//const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; +//const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; +//const APP_DATABASE_ATTRIBUTE_IP = 'ip'; +//const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime'; +//const APP_DATABASE_ATTRIBUTE_URL = 'url'; +//const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange'; +//const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange'; +//const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char +//const APP_DATABASE_TIMEOUT_MILLISECONDS = 15_000; +//const APP_STORAGE_UPLOADS = '/storage/uploads'; +//const APP_STORAGE_FUNCTIONS = '/storage/functions'; +//const APP_STORAGE_BUILDS = '/storage/builds'; +//const APP_STORAGE_CACHE = '/storage/cache'; +//const APP_STORAGE_CERTIFICATES = '/storage/certificates'; +//const APP_STORAGE_CONFIG = '/storage/config'; +//const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT` +//const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite'; +//const APP_SOCIAL_TWITTER_HANDLE = 'appwrite'; +//const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io'; +//const APP_SOCIAL_LINKEDIN = 'https://www.linkedin.com/company/appwrite'; +//const APP_SOCIAL_INSTAGRAM = 'https://www.instagram.com/appwrite.io'; +//const APP_SOCIAL_GITHUB = 'https://github.com/appwrite'; +//const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord'; +//const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244'; +//const APP_SOCIAL_DEV = 'https://dev.to/appwrite'; +//const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite'; +//const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1'; +//const APP_HOSTNAME_INTERNAL = 'appwrite'; +// +//// 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_TEAMS = 'teams'; +//const DELETE_TYPE_EXECUTIONS = 'executions'; +//const DELETE_TYPE_AUDIT = 'audit'; +//const DELETE_TYPE_ABUSE = 'abuse'; +//const DELETE_TYPE_USAGE = 'usage'; +//const DELETE_TYPE_REALTIME = 'realtime'; +//const DELETE_TYPE_BUCKETS = 'buckets'; +//const DELETE_TYPE_INSTALLATIONS = 'installations'; +//const DELETE_TYPE_RULES = 'rules'; +//const DELETE_TYPE_SESSIONS = 'sessions'; +//const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp'; +//const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource'; +//const DELETE_TYPE_SCHEDULES = 'schedules'; +//const DELETE_TYPE_TOPIC = 'topic'; +//const DELETE_TYPE_TARGET = 'target'; +//const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets'; +//const DELETE_TYPE_SESSION_TARGETS = 'session_targets'; +// +//// Message types +//const MESSAGE_SEND_TYPE_INTERNAL = 'internal'; +//const MESSAGE_SEND_TYPE_EXTERNAL = 'external'; +//// Mail Types +//const MAIL_TYPE_VERIFICATION = 'verification'; +//const MAIL_TYPE_MAGIC_SESSION = 'magicSession'; +//const MAIL_TYPE_RECOVERY = 'recovery'; +//const MAIL_TYPE_INVITATION = 'invitation'; +//const MAIL_TYPE_CERTIFICATE = 'certificate'; +//// Auth Types +//const APP_AUTH_TYPE_SESSION = 'Session'; +//const APP_AUTH_TYPE_JWT = 'JWT'; +//const APP_AUTH_TYPE_KEY = 'Key'; +//const APP_AUTH_TYPE_ADMIN = 'Admin'; +//// Response related +//const MAX_OUTPUT_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB +//// Function headers +//const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host']; +//const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length']; +//// Message types +//const MESSAGE_TYPE_EMAIL = 'email'; +//const MESSAGE_TYPE_SMS = 'sms'; +//const MESSAGE_TYPE_PUSH = 'push'; +//// Usage metrics +//const METRIC_TEAMS = 'teams'; +//const METRIC_USERS = 'users'; +//const METRIC_MESSAGES = 'messages'; +//const METRIC_MESSAGES_COUNTRY_CODE = '{countryCode}.messages'; +//const METRIC_SESSIONS = 'sessions'; +//const METRIC_DATABASES = 'databases'; +//const METRIC_COLLECTIONS = 'collections'; +//const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections'; +//const METRIC_DOCUMENTS = 'documents'; +//const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents'; +//const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents'; +//const METRIC_BUCKETS = 'buckets'; +//const METRIC_FILES = 'files'; +//const METRIC_FILES_STORAGE = 'files.storage'; +//const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files'; +//const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage'; +//const METRIC_FUNCTIONS = 'functions'; +//const METRIC_DEPLOYMENTS = 'deployments'; +//const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage'; +//const METRIC_BUILDS = 'builds'; +//const METRIC_BUILDS_STORAGE = 'builds.storage'; +//const METRIC_BUILDS_COMPUTE = 'builds.compute'; +//const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds'; +//const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage'; +//const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute'; +//const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments'; +//const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage'; +//const METRIC_EXECUTIONS = 'executions'; +//const METRIC_EXECUTIONS_COMPUTE = 'executions.compute'; +//const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions'; +//const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute'; +//const METRIC_NETWORK_REQUESTS = 'network.requests'; +//const METRIC_NETWORK_INBOUND = 'network.inbound'; +//const METRIC_NETWORK_OUTBOUND = 'network.outbound'; +// +//$register = new Registry(); +// +//App::setMode(System::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION)); +// +//if (!App::isProduction()) { +// // Allow specific domains to skip public domain validation in dev environment +// // Useful for existing tests involving webhooks +// PublicDomain::allow(['request-catcher']); +//} +// +///* +// * ENV vars +// */ +//Config::load('events', __DIR__ . '/config/events.php'); +//Config::load('auth', __DIR__ . '/config/auth.php'); +//Config::load('apis', __DIR__ . '/config/apis.php'); // List of APIs +//Config::load('errors', __DIR__ . '/config/errors.php'); +//Config::load('oAuthProviders', __DIR__ . '/config/oAuthProviders.php'); +//Config::load('platforms', __DIR__ . '/config/platforms.php'); +//Config::load('collections', __DIR__ . '/config/collections.php'); +//Config::load('runtimes', __DIR__ . '/config/runtimes.php'); +//Config::load('runtimes-v2', __DIR__ . '/config/runtimes-v2.php'); +//Config::load('usage', __DIR__ . '/config/usage.php'); +//Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes +//Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes +//Config::load('services', __DIR__ . '/config/services.php'); // List of services +//Config::load('variables', __DIR__ . '/config/variables.php'); // List of env variables +//Config::load('regions', __DIR__ . '/config/regions.php'); // List of available regions +//Config::load('avatar-browsers', __DIR__ . '/config/avatars/browsers.php'); +//Config::load('avatar-credit-cards', __DIR__ . '/config/avatars/credit-cards.php'); +//Config::load('avatar-flags', __DIR__ . '/config/avatars/flags.php'); +//Config::load('locale-codes', __DIR__ . '/config/locale/codes.php'); +//Config::load('locale-currencies', __DIR__ . '/config/locale/currencies.php'); +//Config::load('locale-eu', __DIR__ . '/config/locale/eu.php'); +//Config::load('locale-languages', __DIR__ . '/config/locale/languages.php'); +//Config::load('locale-phones', __DIR__ . '/config/locale/phones.php'); +//Config::load('locale-countries', __DIR__ . '/config/locale/countries.php'); +//Config::load('locale-continents', __DIR__ . '/config/locale/continents.php'); +//Config::load('locale-templates', __DIR__ . '/config/locale/templates.php'); +//Config::load('storage-logos', __DIR__ . '/config/storage/logos.php'); +//Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php'); +//Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php'); +//Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php'); +// +///** +// * New DB Filters +// */ +//Database::addFilter( +// 'casting', +// function (mixed $value) { +// return json_encode(['value' => $value], JSON_PRESERVE_ZERO_FRACTION); +// }, +// function (mixed $value) { +// if (is_null($value)) { +// return; +// } +// +// return json_decode($value, true)['value']; +// } +//); +// +//Database::addFilter( +// 'enum', +// function (mixed $value, Document $attribute) { +// if ($attribute->isSet('elements')) { +// $attribute->removeAttribute('elements'); +// } +// +// return $value; +// }, +// function (mixed $value, Document $attribute) { +// $formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true); +// if (isset($formatOptions['elements'])) { +// $attribute->setAttribute('elements', $formatOptions['elements']); +// } +// +// return $value; +// } +//); +// +//Database::addFilter( +// 'range', +// function (mixed $value, Document $attribute) { +// if ($attribute->isSet('min')) { +// $attribute->removeAttribute('min'); +// } +// if ($attribute->isSet('max')) { +// $attribute->removeAttribute('max'); +// } +// +// return $value; +// }, +// function (mixed $value, Document $attribute) { +// $formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true); +// if (isset($formatOptions['min']) || isset($formatOptions['max'])) { +// $attribute +// ->setAttribute('min', $formatOptions['min']) +// ->setAttribute('max', $formatOptions['max']) +// ; +// } +// +// return $value; +// } +//); +// +//Database::addFilter( +// 'subQueryAttributes', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// $attributes = $database->find('attributes', [ +// Query::equal('collectionInternalId', [$document->getInternalId()]), +// Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), +// Query::limit($database->getLimitForAttributes()), +// ]); +// +// foreach ($attributes as $attribute) { +// if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { +// $options = $attribute->getAttribute('options'); +// foreach ($options as $key => $value) { +// $attribute->setAttribute($key, $value); +// } +// $attribute->removeAttribute('options'); +// } +// } +// +// return $attributes; +// } +//); +// +//Database::addFilter( +// 'subQueryIndexes', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('indexes', [ +// Query::equal('collectionInternalId', [$document->getInternalId()]), +// Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), +// Query::limit($database->getLimitForIndexes()), +// ]); +// } +//); +// +//Database::addFilter( +// 'subQueryPlatforms', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('platforms', [ +// Query::equal('projectInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ]); +// } +//); +// +//Database::addFilter( +// 'subQueryKeys', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('keys', [ +// Query::equal('projectInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ]); +// } +//); +// +//Database::addFilter( +// 'subQueryWebhooks', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('webhooks', [ +// Query::equal('projectInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ]); +// } +//); +// +//Database::addFilter( +// 'subQuerySessions', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database->find('sessions', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryTokens', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('tokens', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryChallenges', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('challenges', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryAuthenticators', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('authenticators', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryMemberships', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('memberships', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryVariables', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('variables', [ +// Query::equal('resourceInternalId', [$document->getInternalId()]), +// Query::equal('resourceType', ['function']), +// Query::limit(APP_LIMIT_SUBQUERY), +// ]); +// } +//); +// +//Database::addFilter( +// 'encrypt', +// function (mixed $value) { +// $key = System::getEnv('_APP_OPENSSL_KEY_V1'); +// $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); +// $tag = null; +// +// return json_encode([ +// 'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag), +// 'method' => OpenSSL::CIPHER_AES_128_GCM, +// 'iv' => \bin2hex($iv), +// 'tag' => \bin2hex($tag ?? ''), +// 'version' => '1', +// ]); +// }, +// function (mixed $value) { +// if (is_null($value)) { +// return; +// } +// $value = json_decode($value, true); +// $key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']); +// +// return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag'])); +// } +//); +// +//Database::addFilter( +// 'subQueryProjectVariables', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('variables', [ +// Query::equal('resourceType', ['project']), +// Query::limit(APP_LIMIT_SUBQUERY) +// ]); +// } +//); +// +//Database::addFilter( +// 'userSearch', +// function (mixed $value, Document $user) { +// $searchValues = [ +// $user->getId(), +// $user->getAttribute('email', ''), +// $user->getAttribute('name', ''), +// $user->getAttribute('phone', '') +// ]; +// +// foreach ($user->getAttribute('labels', []) as $label) { +// $searchValues[] = 'label:' . $label; +// } +// +// $search = implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +//Database::addFilter( +// 'subQueryTargets', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('targets', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY) +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryTopicTargets', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// $targetIds = Authorization::skip(fn () => \array_map( +// fn ($document) => $document->getAttribute('targetInternalId'), +// $database->find('subscribers', [ +// Query::equal('topicInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY) +// ]) +// )); +// if (\count($targetIds) > 0) { +// return $database->skipValidation(fn () => $database->find('targets', [ +// Query::equal('$internalId', $targetIds) +// ])); +// } +// return []; +// } +//); +// +//Database::addFilter( +// 'providerSearch', +// function (mixed $value, Document $provider) { +// $searchValues = [ +// $provider->getId(), +// $provider->getAttribute('name', ''), +// $provider->getAttribute('provider', ''), +// $provider->getAttribute('type', '') +// ]; +// +// $search = \implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +//Database::addFilter( +// 'topicSearch', +// function (mixed $value, Document $topic) { +// $searchValues = [ +// $topic->getId(), +// $topic->getAttribute('name', ''), +// $topic->getAttribute('description', ''), +// ]; +// +// $search = \implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +//Database::addFilter( +// 'messageSearch', +// function (mixed $value, Document $message) { +// $searchValues = [ +// $message->getId(), +// $message->getAttribute('description', ''), +// $message->getAttribute('status', ''), +// ]; +// +// $data = \json_decode($message->getAttribute('data', []), true); +// $providerType = $message->getAttribute('providerType', ''); +// +// if ($providerType === MESSAGE_TYPE_EMAIL) { +// $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); +// } elseif ($providerType === MESSAGE_TYPE_SMS) { +// $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); +// } else { +// $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); +// } +// +// $search = \implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +///** +// * DB Formats +// */ +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () { +// return new Email(); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () { +// return new DatetimeValidator(); +//}, Database::VAR_DATETIME); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) { +// $elements = $attribute['formatOptions']['elements']; +// return new WhiteList($elements, true); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () { +// return new IP(); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () { +// return new URL(); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) { +// $min = $attribute['formatOptions']['min'] ?? -INF; +// $max = $attribute['formatOptions']['max'] ?? INF; +// return new Range($min, $max, Range::TYPE_INTEGER); +//}, Database::VAR_INTEGER); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) { +// $min = $attribute['formatOptions']['min'] ?? -INF; +// $max = $attribute['formatOptions']['max'] ?? INF; +// return new Range($min, $max, Range::TYPE_FLOAT); +//}, Database::VAR_FLOAT); +// +///* +// * Registry +// */ +//$register->set('logger', function () { +// // Register error logger +// $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); +// $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); +// +// if (empty($providerName) || empty($providerConfig)) { +// return; +// } +// +// if (!Logger::hasProvider($providerName)) { +// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); +// } +// +// // Old Sentry Format conversion. Fallback until the old syntax is completely deprecated. +// if (str_contains($providerConfig, ';') && strtolower($providerName) == 'sentry') { +// $configChunks = \explode(";", $providerConfig); +// +// $sentryKey = $configChunks[0]; +// $projectId = $configChunks[1]; +// +// $providerConfig = 'https://' . $sentryKey . '@sentry.io/' . $projectId; +// } +// +// $classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName); +// $adapter = new $classname($providerConfig); +// return new Logger($adapter); +//}); +//$register->set('pools', function () { +// $group = new Group(); +// +// $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ +// 'scheme' => 'mariadb', +// 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), +// 'port' => System::getEnv('_APP_DB_PORT', '3306'), +// 'user' => System::getEnv('_APP_DB_USER', ''), +// 'pass' => System::getEnv('_APP_DB_PASS', ''), +// 'path' => System::getEnv('_APP_DB_SCHEMA', ''), +// ]); +// $fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([ +// 'scheme' => 'redis', +// 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), +// 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), +// 'user' => System::getEnv('_APP_REDIS_USER', ''), +// 'pass' => System::getEnv('_APP_REDIS_PASS', ''), +// ]); +// +// $connections = [ +// 'console' => [ +// 'type' => 'database', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), +// 'multiple' => false, +// 'schemes' => ['mariadb', 'mysql'], +// ], +// 'database' => [ +// 'type' => 'database', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), +// 'multiple' => true, +// 'schemes' => ['mariadb', 'mysql'], +// ], +// 'queue' => [ +// 'type' => 'queue', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), +// 'multiple' => false, +// 'schemes' => ['redis'], +// ], +// 'pubsub' => [ +// 'type' => 'pubsub', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), +// 'multiple' => false, +// 'schemes' => ['redis'], +// ], +// 'cache' => [ +// 'type' => 'cache', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), +// 'multiple' => true, +// 'schemes' => ['redis'], +// ], +// ]; +// +// $maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151); +// $instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14); +// +// $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; +// +// if ($multiprocessing) { +// $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); +// } else { +// $workerCount = 1; +// } +// +// if ($workerCount > $instanceConnections) { +// throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); +// } +// +// $poolSize = (int)($instanceConnections / $workerCount); +// +// foreach ($connections as $key => $connection) { +// $type = $connection['type'] ?? ''; +// $multiple = $connection['multiple'] ?? false; +// $schemes = $connection['schemes'] ?? []; +// $config = []; +// $dsns = explode(',', $connection['dsns'] ?? ''); +// foreach ($dsns as &$dsn) { +// $dsn = explode('=', $dsn); +// $name = ($multiple) ? $key . '_' . $dsn[0] : $key; +// $dsn = $dsn[1] ?? ''; +// $config[] = $name; +// if (empty($dsn)) { +// //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); +// continue; +// } +// +// $dsn = new DSN($dsn); +// $dsnHost = $dsn->getHost(); +// $dsnPort = $dsn->getPort(); +// $dsnUser = $dsn->getUser(); +// $dsnPass = $dsn->getPassword(); +// $dsnScheme = $dsn->getScheme(); +// $dsnDatabase = $dsn->getPath(); +// +// if (!in_array($dsnScheme, $schemes)) { +// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); +// } +// +// /** +// * Get Resource +// * +// * Creation could be reused across connection types like database, cache, queue, etc. +// * +// * Resource assignment to an adapter will happen below. +// */ +// $resource = match ($dsnScheme) { +// 'mysql', +// 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { +// return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { +// return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( +// PDO::ATTR_TIMEOUT => 3, // Seconds +// PDO::ATTR_PERSISTENT => true, +// PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, +// PDO::ATTR_EMULATE_PREPARES => true, +// PDO::ATTR_STRINGIFY_FETCHES => true +// )); +// }); +// }, +// 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { +// $redis = new Redis(); +// @$redis->pconnect($dsnHost, (int)$dsnPort); +// if ($dsnPass) { +// $redis->auth($dsnPass); +// } +// $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); +// +// return $redis; +// }, +// default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'), +// }; +// +// $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { +// // Get Adapter +// switch ($type) { +// case 'database': +// $adapter = match ($dsn->getScheme()) { +// 'mariadb' => new MariaDB($resource()), +// 'mysql' => new MySQL($resource()), +// default => null +// }; +// +// $adapter->setDatabase($dsn->getPath()); +// break; +// case 'pubsub': +// $adapter = $resource(); +// break; +// case 'queue': +// $adapter = match ($dsn->getScheme()) { +// 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), +// default => null +// }; +// break; +// case 'cache': +// $adapter = match ($dsn->getScheme()) { +// 'redis' => new RedisCache($resource()), +// default => null +// }; +// break; +// +// default: +// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); +// } +// +// return $adapter; +// }); +// +// $group->add($pool); +// } +// +// Config::setParam('pools-' . $key, $config); +// } +// +// return $group; +//}); +// +//$register->set('db', function () { +// // This is usually for our workers or CLI commands scope +// $dbHost = System::getEnv('_APP_DB_HOST', ''); +// $dbPort = System::getEnv('_APP_DB_PORT', ''); +// $dbUser = System::getEnv('_APP_DB_USER', ''); +// $dbPass = System::getEnv('_APP_DB_PASS', ''); +// $dbScheme = System::getEnv('_APP_DB_SCHEMA', ''); +// +// return new PDO( +// "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", +// $dbUser, +// $dbPass, +// SQL::getPDOAttributes() +// ); +//}); +// +//$register->set('smtp', function () { +// $mail = new PHPMailer(true); +// +// $mail->isSMTP(); +// +// $username = System::getEnv('_APP_SMTP_USERNAME'); +// $password = System::getEnv('_APP_SMTP_PASSWORD'); +// +// $mail->XMailer = 'Appwrite Mailer'; +// $mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp'); +// $mail->Port = System::getEnv('_APP_SMTP_PORT', 25); +// $mail->SMTPAuth = !empty($username) && !empty($password); +// $mail->Username = $username; +// $mail->Password = $password; +// $mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', ''); +// $mail->SMTPAutoTLS = false; +// $mail->CharSet = 'UTF-8'; +// +// $from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); +// $email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); +// +// $mail->setFrom($email, $from); +// $mail->addReplyTo($email, $from); +// +// $mail->isHTML(true); +// +// return $mail; +//}); +//$register->set('geodb', function () { +// return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.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); +//} +// +//\stream_context_set_default([ // Set global user agent and http settings +// 'http' => [ +// 'method' => 'GET', +// 'user_agent' => \sprintf( +// APP_USERAGENT, +// System::getEnv('_APP_VERSION', 'UNKNOWN'), +// System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)) +// ), +// 'timeout' => 2, +// ], +//]); +// +//// Runtime Execution +//App::setResource('log', fn () => new Log()); +//App::setResource('logger', function ($register) { +// return $register->get('logger'); +//}, ['register']); +// +//App::setResource('hooks', function ($register) { +// return $register->get('hooks'); +//}, ['register']); +// +//App::setResource('register', fn () => $register); +//App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); +// +//App::setResource('localeCodes', function () { +// return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); +//}); +// +//// Queues +//App::setResource('queue', function (Group $pools) { +// return $pools->get('queue')->pop()->getResource(); +//}, ['pools']); +//App::setResource('queueForMessaging', function (Connection $queue) { +// return new Messaging($queue); +//}, ['queue']); +//App::setResource('queueForMails', function (Connection $queue) { +// return new Mail($queue); +//}, ['queue']); +//App::setResource('queueForBuilds', function (Connection $queue) { +// return new Build($queue); +//}, ['queue']); +//App::setResource('queueForDatabase', function (Connection $queue) { +// return new EventDatabase($queue); +//}, ['queue']); +//App::setResource('queueForDeletes', function (Connection $queue) { +// return new Delete($queue); +//}, ['queue']); +//App::setResource('queueForEvents', function (Connection $queue) { +// return new Event($queue); +//}, ['queue']); +//App::setResource('queueForAudits', function (Connection $queue) { +// return new Audit($queue); +//}, ['queue']); +//App::setResource('queueForFunctions', function (Connection $queue) { +// return new Func($queue); +//}, ['queue']); +//App::setResource('queueForUsage', function (Connection $queue) { +// return new Usage($queue); +//}, ['queue']); +//App::setResource('queueForCertificates', function (Connection $queue) { +// return new Certificate($queue); +//}, ['queue']); +//App::setResource('queueForMigrations', function (Connection $queue) { +// return new Migration($queue); +//}, ['queue']); +//App::setResource('clients', function ($request, $console, $project) { +// $console->setAttribute('platforms', [ // Always allow current host +// '$collection' => ID::custom('platforms'), +// 'name' => 'Current Host', +// 'type' => Origin::CLIENT_TYPE_WEB, +// 'hostname' => $request->getHostname(), +// ], Document::SET_TYPE_APPEND); +// +// $hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', '')); +// $validator = new Hostname(); +// foreach ($hostnames as $hostname) { +// $hostname = trim($hostname); +// if (!$validator->isValid($hostname)) { +// continue; +// } +// $console->setAttribute('platforms', [ +// '$collection' => ID::custom('platforms'), +// 'type' => Origin::CLIENT_TYPE_WEB, +// 'name' => $hostname, +// 'hostname' => $hostname, +// ], Document::SET_TYPE_APPEND); +// } +// +// /** +// * Get All verified client URLs for both console and current projects +// * + Filter for duplicated entries +// */ +// $clientsConsole = \array_map( +// fn ($node) => $node['hostname'], +// \array_filter( +// $console->getAttribute('platforms', []), +// fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname'])) +// ) +// ); +// +// $clients = $clientsConsole; +// $platforms = $project->getAttribute('platforms', []); +// +// foreach ($platforms as $node) { +// if ( +// isset($node['type']) && +// ($node['type'] === Origin::CLIENT_TYPE_WEB || +// $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && +// !empty($node['hostname']) +// ) { +// $clients[] = $node['hostname']; +// } +// } +// +// return \array_unique($clients); +//}, ['request', 'console', 'project']); +// +//App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { +// /** @var Appwrite\Utopia\Request $request */ +// /** @var Appwrite\Utopia\Response $response */ +// /** @var Utopia\Database\Document $project */ +// /** @var Utopia\Database\Database $dbForProject */ +// /** @var Utopia\Database\Database $dbForConsole */ +// /** @var string $mode */ +// +// Authorization::setDefaultStatus(true); +// +// Auth::setCookieName('a_session_' . $project->getId()); +// +// if (APP_MODE_ADMIN === $mode) { +// Auth::setCookieName('a_session_' . $console->getId()); +// } +// +// $session = Auth::decodeSession( +// $request->getCookie( +// Auth::$cookieName, // Get sessions +// $request->getCookie(Auth::$cookieName . '_legacy', '') +// ) +// ); +// +// // Get session from header for SSR clients +// if (empty($session['id']) && empty($session['secret'])) { +// $sessionHeader = $request->getHeader('x-appwrite-session', ''); +// +// if (!empty($sessionHeader)) { +// $session = Auth::decodeSession($sessionHeader); +// } +// } +// +// // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies +// if ($response) { +// $response->addHeader('X-Debug-Fallback', 'false'); +// } +// +// if (empty($session['id']) && empty($session['secret'])) { +// if ($response) { +// $response->addHeader('X-Debug-Fallback', 'true'); +// } +// $fallback = $request->getHeader('x-fallback-cookies', ''); +// $fallback = \json_decode($fallback, true); +// $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); +// } +// +// Auth::$unique = $session['id'] ?? ''; +// Auth::$secret = $session['secret'] ?? ''; +// +// if (APP_MODE_ADMIN !== $mode) { +// if ($project->isEmpty()) { +// $user = new Document([]); +// } else { +// if ($project->getId() === 'console') { +// $user = $dbForConsole->getDocument('users', Auth::$unique); +// } else { +// $user = $dbForProject->getDocument('users', Auth::$unique); +// } +// } +// } else { +// $user = $dbForConsole->getDocument('users', Auth::$unique); +// } +// +// if ( +// $user->isEmpty() // Check a document has been found in the DB +// || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) +// ) { // Validate user has valid login token +// $user = new Document([]); +// } +// +// if (APP_MODE_ADMIN === $mode) { +// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { +// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. +// } else { +// $user = new Document([]); +// } +// } +// +// $authJWT = $request->getHeader('x-appwrite-jwt', ''); +// +// if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication +// $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. +// +// try { +// $payload = $jwt->decode($authJWT); +// } catch (JWTException $error) { +// throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); +// } +// +// $jwtUserId = $payload['userId'] ?? ''; +// $jwtSessionId = $payload['sessionId'] ?? ''; +// +// if ($jwtUserId && $jwtSessionId) { +// $user = $dbForProject->getDocument('users', $jwtUserId); +// } +// +// if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token +// $user = new Document([]); +// } +// } +// +// $dbForProject->setMetadata('user', $user->getId()); +// $dbForConsole->setMetadata('user', $user->getId()); +// +// return $user; +//}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); +// +//App::setResource('project', function ($dbForConsole, $request, $console) { +// /** @var Appwrite\Utopia\Request $request */ +// /** @var Utopia\Database\Database $dbForConsole */ +// /** @var Utopia\Database\Document $console */ +// +// $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); +// +// if (empty($projectId) || $projectId === 'console') { +// return $console; +// } +// +// $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); +// +// return $project; +//}, ['dbForConsole', 'request', 'console']); +// +//App::setResource('session', function (Document $user) { +// if ($user->isEmpty()) { +// return; +// } +// +// $sessions = $user->getAttribute('sessions', []); +// $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); +// +// if (!$sessionId) { +// return; +// } +// +// foreach ($sessions as $session) {/** @var Document $session */ +// if ($sessionId === $session->getId()) { +// return $session; +// } +// } +// +// return; +//}, ['user']); +// +//App::setResource('console', function () { +// return new Document([ +// '$id' => ID::custom('console'), +// '$internalId' => ID::custom('console'), +// 'name' => 'Appwrite', +// '$collection' => ID::custom('projects'), +// 'description' => 'Appwrite core engine', +// 'logo' => '', +// 'teamId' => -1, +// 'webhooks' => [], +// 'keys' => [], +// 'platforms' => [ +// [ +// '$collection' => ID::custom('platforms'), +// 'name' => 'Localhost', +// 'type' => Origin::CLIENT_TYPE_WEB, +// 'hostname' => 'localhost', +// ], // Current host is added on app init +// ], +// 'legalName' => '', +// 'legalCountry' => '', +// 'legalState' => '', +// 'legalCity' => '', +// 'legalAddress' => '', +// 'legalTaxId' => '', +// 'auths' => [ +// '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 +// ], +// 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], +// 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], +// 'oAuthProviders' => [ +// 'githubEnabled' => true, +// 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), +// 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') +// ], +// ]); +//}, []); +// +//App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { +// if ($project->isEmpty() || $project->getId() === 'console') { +// return $dbForConsole; +// } +// +// try { +// $dsn = new DSN($project->getAttribute('database')); +// } catch (\InvalidArgumentException) { +// // TODO: Temporary until all projects are using shared tables +// $dsn = new DSN('mysql://' . $project->getAttribute('database')); +// } +// +// $dbAdapter = $pools +// ->get($dsn->getHost()) +// ->pop() +// ->getResource(); +// +// $database = new Database($dbAdapter, $cache); +// +// $database +// ->setMetadata('host', \gethostname()) +// ->setMetadata('project', $project->getId()) +// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); +// +// try { +// $dsn = new DSN($project->getAttribute('database')); +// } catch (\InvalidArgumentException) { +// // TODO: Temporary until all projects are using shared tables +// $dsn = new DSN('mysql://' . $project->getAttribute('database')); +// } +// +// if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { +// $database +// ->setSharedTables(true) +// ->setTenant($project->getInternalId()) +// ->setNamespace($dsn->getParam('namespace')); +// } else { +// $database +// ->setSharedTables(false) +// ->setTenant(null) +// ->setNamespace('_' . $project->getInternalId()); +// } +// +// return $database; +//}, ['pools', 'dbForConsole', 'cache', 'project']); +// +//App::setResource('dbForConsole', function (Group $pools, Cache $cache) { +// $dbAdapter = $pools +// ->get('console') +// ->pop() +// ->getResource(); +// +// $database = new Database($dbAdapter, $cache); +// +// $database +// ->setNamespace('_console') +// ->setMetadata('host', \gethostname()) +// ->setMetadata('project', 'console') +// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); +// +// return $database; +//}, ['pools', 'cache']); +// +//App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { +// $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools +// +// return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { +// if ($project->isEmpty() || $project->getId() === 'console') { +// return $dbForConsole; +// } +// +// try { +// $dsn = new DSN($project->getAttribute('database')); +// } catch (\InvalidArgumentException) { +// // TODO: Temporary until all projects are using shared tables +// $dsn = new DSN('mysql://' . $project->getAttribute('database')); +// } +// +// $configure = (function (Database $database) use ($project, $dsn) { +// $database +// ->setMetadata('host', \gethostname()) +// ->setMetadata('project', $project->getId()) +// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); +// +// if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { +// $database +// ->setSharedTables(true) +// ->setTenant($project->getInternalId()) +// ->setNamespace($dsn->getParam('namespace')); +// } else { +// $database +// ->setSharedTables(false) +// ->setTenant(null) +// ->setNamespace('_' . $project->getInternalId()); +// } +// }); +// +// if (isset($databases[$dsn->getHost()])) { +// $database = $databases[$dsn->getHost()]; +// $configure($database); +// return $database; +// } +// +// $dbAdapter = $pools +// ->get($dsn->getHost()) +// ->pop() +// ->getResource(); +// +// $database = new Database($dbAdapter, $cache); +// $databases[$dsn->getHost()] = $database; +// $configure($database); +// +// return $database; +// }; +//}, ['pools', 'dbForConsole', 'cache']); +// +//App::setResource('cache', function (Group $pools) { +// $list = Config::getParam('pools-cache', []); +// $adapters = []; +// +// foreach ($list as $value) { +// $adapters[] = $pools +// ->get($value) +// ->pop() +// ->getResource() +// ; +// } +// +// return new Cache(new Sharding($adapters)); +//}, ['pools']); +// +//App::setResource('deviceForLocal', function () { +// return new Local(); +//}); +// +//App::setResource('deviceForFiles', function ($project) { +// return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); +//}, ['project']); +// +//App::setResource('deviceForFunctions', function ($project) { +// return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); +//}, ['project']); +// +//App::setResource('deviceForBuilds', function ($project) { +// return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); +//}, ['project']); +// +//function getDevice($root): Device +//{ +// $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); +// +// if (!empty($connection)) { +// $acl = 'private'; +// $device = Storage::DEVICE_LOCAL; +// $accessKey = ''; +// $accessSecret = ''; +// $bucket = ''; +// $region = ''; +// +// try { +// $dsn = new DSN($connection); +// $device = $dsn->getScheme(); +// $accessKey = $dsn->getUser() ?? ''; +// $accessSecret = $dsn->getPassword() ?? ''; +// $bucket = $dsn->getPath() ?? ''; +// $region = $dsn->getParam('region'); +// } catch (\Throwable $e) { +// Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); +// } +// +// switch ($device) { +// case Storage::DEVICE_S3: +// return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case STORAGE::DEVICE_DO_SPACES: +// $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// $device->setHttpVersion(S3::HTTP_VERSION_1_1); +// return $device; +// case Storage::DEVICE_BACKBLAZE: +// return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case Storage::DEVICE_LINODE: +// return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case Storage::DEVICE_WASABI: +// return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case Storage::DEVICE_LOCAL: +// default: +// return new Local($root); +// } +// } else { +// switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { +// case Storage::DEVICE_LOCAL: +// default: +// return new Local($root); +// case Storage::DEVICE_S3: +// $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); +// $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); +// $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); +// $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); +// $s3Acl = 'private'; +// return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); +// case Storage::DEVICE_DO_SPACES: +// $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); +// $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); +// $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); +// $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); +// $doSpacesAcl = 'private'; +// $device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); +// $device->setHttpVersion(S3::HTTP_VERSION_1_1); +// return $device; +// case Storage::DEVICE_BACKBLAZE: +// $backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); +// $backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); +// $backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); +// $backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); +// $backblazeAcl = 'private'; +// return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); +// case Storage::DEVICE_LINODE: +// $linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); +// $linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', ''); +// $linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', ''); +// $linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); +// $linodeAcl = 'private'; +// return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); +// case Storage::DEVICE_WASABI: +// $wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); +// $wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', ''); +// $wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', ''); +// $wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); +// $wasabiAcl = 'private'; +// return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); +// } +// } +//} +// +//App::setResource('mode', function ($request) { +// /** @var Appwrite\Utopia\Request $request */ +// +// /** +// * Defines the mode for the request: +// * - 'default' => Requests for Client and Server Side +// * - 'admin' => Request from the Console on non-console projects +// */ +// return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); +//}, ['request']); +// +//App::setResource('geodb', function ($register) { +// /** @var Utopia\Registry\Registry $register */ +// return $register->get('geodb'); +//}, ['register']); +// +//App::setResource('passwordsDictionary', function ($register) { +// /** @var Utopia\Registry\Registry $register */ +// return $register->get('passwordsDictionary'); +//}, ['register']); +// +// +//App::setResource('servers', function () { +// $platforms = Config::getParam('platforms'); +// $server = $platforms[APP_PLATFORM_SERVER]; +// +// $languages = array_map(function ($language) { +// return strtolower($language['name']); +// }, $server['sdks']); +// +// return $languages; +//}); +// +//App::setResource('promiseAdapter', function ($register) { +// return $register->get('promiseAdapter'); +//}, ['register']); +// +//App::setResource('schema', function ($utopia, $dbForProject) { +// +// $complexity = function (int $complexity, array $args) { +// $queries = Query::parseQueries($args['queries'] ?? []); +// $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; +// $limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT; +// +// return $complexity * $limit; +// }; +// +// $attributes = function (int $limit, int $offset) use ($dbForProject) { +// $attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [ +// Query::limit($limit), +// Query::offset($offset), +// ])); +// +// return \array_map(function ($attr) { +// return $attr->getArrayCopy(); +// }, $attrs); +// }; +// +// $urls = [ +// 'list' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents"; +// }, +// 'create' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents"; +// }, +// 'read' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; +// }, +// 'update' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; +// }, +// 'delete' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; +// }, +// ]; +// +// $params = [ +// 'list' => function (string $databaseId, string $collectionId, array $args) { +// return [ 'queries' => $args['queries']]; +// }, +// 'create' => function (string $databaseId, string $collectionId, array $args) { +// $id = $args['id'] ?? 'unique()'; +// $permissions = $args['permissions'] ?? null; +// +// unset($args['id']); +// unset($args['permissions']); +// +// // Order must be the same as the route params +// return [ +// 'databaseId' => $databaseId, +// 'documentId' => $id, +// 'collectionId' => $collectionId, +// 'data' => $args, +// 'permissions' => $permissions, +// ]; +// }, +// 'update' => function (string $databaseId, string $collectionId, array $args) { +// $documentId = $args['id']; +// $permissions = $args['permissions'] ?? null; +// +// unset($args['id']); +// unset($args['permissions']); +// +// // Order must be the same as the route params +// return [ +// 'databaseId' => $databaseId, +// 'collectionId' => $collectionId, +// 'documentId' => $documentId, +// 'data' => $args, +// 'permissions' => $permissions, +// ]; +// }, +// ]; +// +// return Schema::build( +// $utopia, +// $complexity, +// $attributes, +// $urls, +// $params, +// ); +//}, ['utopia', 'dbForProject']); +// +//App::setResource('contributors', function () { +// $path = 'app/config/contributors.json'; +// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; +// return $list; +//}); +// +//App::setResource('employees', function () { +// $path = 'app/config/employees.json'; +// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; +// return $list; +//}); +// +//App::setResource('heroes', function () { +// $path = 'app/config/heroes.json'; +// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; +// return $list; +//}); +// +//App::setResource('gitHub', function (Cache $cache) { +// return new VcsGitHub($cache); +//}, ['cache']); +// +//App::setResource('requestTimestamp', function ($request) { +// //TODO: Move this to the Request class itself +// $timestampHeader = $request->getHeader('x-appwrite-timestamp'); +// $requestTimestamp = null; +// if (!empty($timestampHeader)) { +// try { +// $requestTimestamp = new \DateTime($timestampHeader); +// } catch (\Throwable $e) { +// throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value'); +// } +// } +// return $requestTimestamp; +//}, ['request']); +//App::setResource('plan', function (array $plan = []) { +// return []; +//}); diff --git a/composer.json b/composer.json index 7807425128..c8d4465ed2 100644 --- a/composer.json +++ b/composer.json @@ -72,7 +72,7 @@ "utopia-php/storage": "dev-feat-framework-v2-v2 as 0.18.99", "utopia-php/system": "0.8.*", "utopia-php/vcs": "0.6.*", - "utopia-php/websocket": "0.1.*", + "utopia-php/websocket": "0.2.*", "matomo/device-detector": "6.1.*", "dragonmantank/cron-expression": "3.3.2", "phpmailer/phpmailer": "6.9.1", diff --git a/composer.lock b/composer.lock index 179dbaa1ca..9ba085ba83 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": "6cd124cc3eeca5b62a56909ea4fab324", + "content-hash": "2d1ca49eab2982e8b3ceea8b388e249a", "packages": [ { "name": "adhocore/jwt", @@ -2556,12 +2556,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "cad5651b38f0f69e20e805424d0c29818c15c174" + "reference": "41b76eacae43bb3e46824ffc8e9aa14536771b88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/cad5651b38f0f69e20e805424d0c29818c15c174", - "reference": "cad5651b38f0f69e20e805424d0c29818c15c174", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/41b76eacae43bb3e46824ffc8e9aa14536771b88", + "reference": "41b76eacae43bb3e46824ffc8e9aa14536771b88", "shasum": "" }, "require": { @@ -2611,7 +2611,7 @@ "issues": "https://github.com/utopia-php/queue/issues", "source": "https://github.com/utopia-php/queue/tree/feat-coroutine-and-di" }, - "time": "2024-06-07T18:50:32+00:00" + "time": "2024-07-04T18:02:23+00:00" }, { "name": "utopia-php/registry", @@ -2941,26 +2941,27 @@ }, { "name": "utopia-php/websocket", - "version": "0.1.0", + "version": "0.2.0", "source": { "type": "git", "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + "reference": "e9d0919b321744a61f12563f5791c47ba9f57810" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/e9d0919b321744a61f12563f5791c47ba9f57810", + "reference": "e9d0919b321744a61f12563f5791c47ba9f57810", "shasum": "" }, "require": { "php": ">=8.0" }, "require-dev": { + "laravel/pint": "^1.15", + "phpstan/phpstan": "^1.8", "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", + "swoole/ide-helper": "5.1.2", "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", "workerman/workerman": "^4.0" }, "type": "library", @@ -2973,16 +2974,6 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], "description": "A simple abstraction for WebSocket servers.", "keywords": [ "framework", @@ -2993,9 +2984,9 @@ ], "support": { "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + "source": "https://github.com/utopia-php/websocket/tree/0.2.0" }, - "time": "2021-12-20T10:50:09+00:00" + "time": "2024-04-09T08:28:11+00:00" }, { "name": "webmozart/assert", diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 31db47098b..c7c0fc5760 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -24,7 +24,6 @@ use Utopia\Http\Http; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; use Utopia\Platform\Action; -use Utopia\Registry\Registry; use Utopia\System\System; class Specs extends Action @@ -40,11 +39,11 @@ class Specs extends Action ->desc('Generate Appwrite API specifications') ->param('version', 'latest', new Text(16), 'Spec version', true) ->param('mode', 'normal', new WhiteList(['normal', 'mocks']), 'Spec Mode', true) - ->inject('register') - ->callback(fn (string $version, string $mode, Registry $register) => $this->action($version, $mode, $register)); + ->inject('context') + ->callback(fn (string $version, string $mode, Container $context) => $this->action($version, $mode, $context)); } - public function action(string $version, string $mode, Registry $register, Container $container): void + public function action(string $version, string $mode, Container $container): void { $appRoutes = Http::getRoutes(); $response = new Response(new HttpResponse(new SwooleHttpResponse())); From 1217aee2961adfe1d172aaead73fbf49573847f7 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 4 Jul 2024 15:04:36 -0400 Subject: [PATCH 119/195] refactor: Connection pools --- app/controllers/api/health.php | 10 ++++++++-- app/http.php | 7 ++++++- app/init2.php | 6 ++---- app/realtime.php | 18 ++++++++++++++---- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 24acaf96cc..47d80dbf71 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -179,6 +179,8 @@ Http::get('/v1/health/cache') 'status' => 'fail', 'ping' => \round((\microtime(true) - $checkStart) / 1000) ]); + } finally { + $connections->reclaim(); } } } @@ -241,6 +243,8 @@ Http::get('/v1/health/queue') 'status' => 'fail', 'ping' => \round((\microtime(true) - $checkStart) / 1000) ]); + } finally { + $connections->reclaim(); } } } @@ -277,11 +281,11 @@ Http::get('/v1/health/pubsub') foreach ($config as $database) { try { $pool = $pools['pools-pubsub-' . $database]['pool']; - $dsn = $pools['pools-pubsub-' . $database]['dsn']; + $connection = $pool->get(); $connections->add($connection, $pool); - $adapter = new Connection\Redis($dsn->getHost(), $dsn->getPort()); + $adapter = new Connection\Redis($connection); $checkStart = \microtime(true); @@ -304,6 +308,8 @@ Http::get('/v1/health/pubsub') 'status' => 'fail', 'ping' => \round((\microtime(true) - $checkStart) / 1000) ]); + } finally { + $connections->reclaim(); } } } diff --git a/app/http.php b/app/http.php index bb50c46230..5bf67108bc 100644 --- a/app/http.php +++ b/app/http.php @@ -58,13 +58,18 @@ function startCoroutineServer(float|int $payloadSize, float|int $workerNumber, R $http->setRequestClass(Request::class); $http->setResponseClass(Response::class); + Http::onEnd() + ->inject('connections') + ->action(function (Connections $connections) use ($workerId) { + $connections->reclaim(); + }); Http::onStart() ->inject('authorization') ->inject('cache') ->inject('pools') ->inject('connections') ->action(function (Authorization $authorization, Cache $cache, array $pools, Connections $connections) use ($workerId) { - if($workerId !== 0) { + if ($workerId !== 0) { return; } try { diff --git a/app/init2.php b/app/init2.php index 9e5c9ad25a..8441a84f81 100644 --- a/app/init2.php +++ b/app/init2.php @@ -257,8 +257,7 @@ $global->set( ]; $pools = []; - $poolSize = (int)System::getEnv('_APP_POOL_CLIENTS', 9000); - $poolSize = 9000; + $poolSize = (int)System::getEnv('_APP_POOL_CLIENTS', 64); foreach ($connections as $key => $connection) { $dsns = $connection['dsns'] ?? ''; @@ -841,11 +840,10 @@ $queue ->inject('connections') ->setCallback(function (array $pools, Connections $connections) { $pool = $pools['pools-queue-main']['pool']; - $dsn = $pools['pools-queue-main']['dsn']; $connection = $pool->get(); $connections->add($connection, $pool); - return new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()); + return new Queue\Connection\Redis($connection); }); $queueForMessaging diff --git a/app/realtime.php b/app/realtime.php index 117941a86e..0e95ce1735 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -5,6 +5,7 @@ use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Network\Validator\Origin; +use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Swoole\Http\Request as SwooleRequest; @@ -28,6 +29,7 @@ use Utopia\Http\Adapter\Swoole\Response as HttpResponse; use Utopia\Http\Adapter\Swoole\Response as UtopiaResponse; use Utopia\Http\Http; use Utopia\Logger\Log; +use Utopia\Pools\Connection; use Utopia\Registry\Registry; use Utopia\System\System; use Utopia\WebSocket\Adapter; @@ -270,10 +272,14 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats $start = time(); $pools = $container->get('pools'); + /** @var Connections $connections */ + $connections = $container->get('connections'); + $pool = $pools['pools-pubsub-main']['pool']; - $dsn = $pools['pools-pubsub-main']['dsn']; - $redis = new \Redis(); - $redis->connect($dsn->getHost(), $dsn->getPort()); + $connection = $pool->get(); + $connections->add($connection, $pool); + + $redis = $connection; /** @var Redis $redis */ $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); @@ -477,6 +483,11 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, } }); +$server->onWorkerStop(function (int $workerId) use ($container) { + $connections = $container->get('connections'); + $connections->reclaim(); +}); + $server->onMessage(function (int $connection, string $message) use ($server, $container, $realtime, $containerId) { try { $response = new Response(new HttpResponse(new SwooleHttpResponse())); @@ -486,7 +497,6 @@ $server->onMessage(function (int $connection, string $message) use ($server, $co $authentication = $container->get('authentication'); if ($projectId !== 'console') { - $project = $authorization->skip(fn () => $database->getDocument('projects', $projectId)); $database = $container->get('getProjectDB')($project); } else { From a60000d6319c1169d067dd0c7ba8e5063cffe3d2 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 4 Jul 2024 17:07:33 -0400 Subject: [PATCH 120/195] fix: Small fix --- app/init2.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/init2.php b/app/init2.php index 8441a84f81..dfbee1b26b 100644 --- a/app/init2.php +++ b/app/init2.php @@ -1247,7 +1247,6 @@ $container->set($log); $container->set($mode); $container->set($user); $container->set($plan); -$container->set($pools); $container->set($cache); $container->set($pools); $container->set($queue); From 66b9467e6dd81048eb387d06e89e6b5e2d625185 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:44:58 -0400 Subject: [PATCH 121/195] rollback: Reverting to more stable server --- app/http.php | 424 ++++++++++++++++++++++++--------------------------- 1 file changed, 200 insertions(+), 224 deletions(-) diff --git a/app/http.php b/app/http.php index 5bf67108bc..5ae2e40e1a 100644 --- a/app/http.php +++ b/app/http.php @@ -19,10 +19,8 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; -use Utopia\DI\Container; -use Utopia\Http\Adapter\SwooleCoroutine\Server; +use Utopia\Http\Adapter\Swoole\Server; use Utopia\Http\Http; -use Utopia\Registry\Registry; use Utopia\System\System; global $global, $container; @@ -30,241 +28,219 @@ global $global, $container; $payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing $workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); -$pool = new Swoole\Process\Pool($workerNumber, 0, 0, true); +$server = new Server('0.0.0.0', '80', [ + 'open_http2_protocol' => true, + 'http_compression' => true, + 'http_compression_level' => 6, + 'package_max_length' => $payloadSize, + 'buffer_output_size' => $payloadSize, + // Server + // 'log_level' => 0, + 'dispatch_mode' => 1, + 'worker_num' => $workerNumber, + 'reactor_num' => swoole_cpu_num() * 2, + 'open_cpu_affinity' => true, + // Coroutine + 'enable_coroutine' => true, + 'max_coroutine' => 10000, + 'send_yield' => true +]); -function startCoroutineServer(float|int $payloadSize, float|int $workerNumber, Registry $global, Container $container, $workerId) -{ - $server = new Server('0.0.0.0', '80', [ - 'open_http2_protocol' => true, - 'http_compression' => true, - 'http_compression_level' => 6, - 'package_max_length' => $payloadSize, - 'buffer_output_size' => $payloadSize, - // Server - // 'log_level' => 0, - 'dispatch_mode' => 1, - 'worker_num' => $workerNumber, - 'reactor_num' => swoole_cpu_num() * 2, - 'open_cpu_affinity' => true, - // Coroutine - 'enable_coroutine' => true, - 'max_coroutine' => 10000, - 'send_yield' => true - ]); +$http = new Http($server, $container, 'UTC'); - $http = new Http($server, $container, 'UTC'); +$http->loadFiles(__DIR__ . '/../console'); +$http->setRequestClass(Request::class); +$http->setResponseClass(Response::class); - $http->loadFiles(__DIR__ . '/../console'); - $http->setRequestClass(Request::class); - $http->setResponseClass(Response::class); +global $global, $container; - Http::onEnd() - ->inject('connections') - ->action(function (Connections $connections) use ($workerId) { - $connections->reclaim(); - }); - Http::onStart() - ->inject('authorization') - ->inject('cache') - ->inject('pools') - ->inject('connections') - ->action(function (Authorization $authorization, Cache $cache, array $pools, Connections $connections) use ($workerId) { - if ($workerId !== 0) { - return; - } - try { - // wait for database to be ready - $attempts = 0; - $max = 15; - $sleep = 2; - - do { - try { - $attempts++; - $pool = $pools['pools-console-main']['pool']; - $dsn = $pools['pools-console-main']['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - - $dbForConsole = new Database($adapter, $cache); - $dbForConsole->setAuthorization($authorization); - - $dbForConsole - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - $dbForConsole->ping(); - break; // leave the do-while if successful - } catch (\Throwable $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: ' . $e->getMessage()); - } - sleep($sleep); - } - } while ($attempts < $max); - - Console::success('[Setup] - Server database init started...'); +http::onStart() + ->inject('authorization') + ->inject('cache') + ->inject('pools') + ->inject('connections') + ->action(function (Authorization $authorization, Cache $cache, array $pools, Connections $connections) { + try { + // wait for database to be ready + $attempts = 0; + $max = 15; + $sleep = 2; + do { try { - Console::success('[Setup] - Creating database: appwrite...'); - $dbForConsole->create(); + $attempts++; + $pool = $pools['pools-console-main']['pool']; + $dsn = $pools['pools-console-main']['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); + + $dbForConsole = new Database($adapter, $cache); + $dbForConsole->setAuthorization($authorization); + + $dbForConsole + ->setNamespace('_console') + ->setMetadata('host', \gethostname()) + ->setMetadata('project', 'console') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + $dbForConsole->ping(); + break; // leave the do-while if successful } catch (\Throwable $e) { - Console::success('[Setup] - Skip: metadata table already exists'); - return true; + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: ' . $e->getMessage()); + } + sleep($sleep); } + } while ($attempts < $max); - if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) { - $audit = new Audit($dbForConsole); - $audit->setup(); - } + Console::success('[Setup] - Server database init started...'); - if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { - $abuse = new TimeLimit("", 0, 1, $dbForConsole); - $abuse->setup(); - } - - /** @var array $collections */ - $collections = Config::getParam('collections', []); - $consoleCollections = $collections['console']; - foreach ($consoleCollections as $key => $collection) { - if (($collection['$collection'] ?? '') !== Database::METADATA) { - continue; - } - if (!$dbForConsole->getCollection($key)->isEmpty()) { - continue; - } - - Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); - - $attributes = []; - $indexes = []; - - foreach ($collection['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]); - } - - foreach ($collection['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } - - $dbForConsole->createCollection($key, $attributes, $indexes); - } - - if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) { - Console::success('[Setup] - Creating default bucket...'); - $dbForConsole->createDocument('buckets', new Document([ - '$id' => ID::custom('default'), - '$collection' => ID::custom('buckets'), - 'name' => 'Default', - 'maximumFileSize' => (int)System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB - 'allowedFileExtensions' => [], - 'enabled' => true, - 'compression' => 'gzip', - 'encryption' => true, - 'antivirus' => true, - 'fileSecurity' => true, - '$permissions' => [ - Permission::create(Role::any()), - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'search' => 'buckets Default', - ])); - - $bucket = $dbForConsole->getDocument('buckets', 'default'); - - Console::success('[Setup] - Creating files collection for default bucket...'); - - $files = $collections['buckets']['files'] ?? []; - if (empty($files)) { - throw new Exception('Files collection is not configured.'); - } - - $attributes = []; - $indexes = []; - - foreach ($files['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]); - } - - foreach ($files['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } - - $dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); - } - - $connections->reclaim(); - - Console::success('[Setup] - Server database init completed...'); - Console::success('Server started successfully'); + try { + Console::success('[Setup] - Creating database: appwrite...'); + $dbForConsole->create(); } catch (\Throwable $e) { - Console::warning('Database not ready: ' . $e->getMessage()); - exit(1); + Console::success('[Setup] - Skip: metadata table already exists'); + return true; } - }); - Http::init() - ->inject('authorization') - ->action(function (Authorization $authorization) { - $authorization->cleanRoles(); - $authorization->addRole(Role::any()->toString()); - }); + if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) { + $audit = new Audit($dbForConsole); + $audit->setup(); + } - $http->start(); -} + if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { + $abuse = new TimeLimit("", 0, 1, $dbForConsole); + $abuse->setup(); + } -$pool->on("WorkerStart", function ($pool, $workerId) use ($payloadSize, $workerNumber, $global, $container) { - Console::success("Worker " . $workerId . " started"); - startCoroutineServer($payloadSize, $workerNumber, $global, $container, $workerId); -}); + /** @var array $collections */ + $collections = Config::getParam('collections', []); + $consoleCollections = $collections['console']; + foreach ($consoleCollections as $key => $collection) { + if (($collection['$collection'] ?? '') !== Database::METADATA) { + continue; + } + if (!$dbForConsole->getCollection($key)->isEmpty()) { + continue; + } -$pool->on("WorkerStop", function ($pool, $workerId) { - Console::success("Worker " . $workerId . " stopped"); -}); + Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); -$pool->start(); + $attributes = []; + $indexes = []; + + foreach ($collection['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]); + } + + foreach ($collection['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); + } + + $dbForConsole->createCollection($key, $attributes, $indexes); + } + + if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) { + Console::success('[Setup] - Creating default bucket...'); + $dbForConsole->createDocument('buckets', new Document([ + '$id' => ID::custom('default'), + '$collection' => ID::custom('buckets'), + 'name' => 'Default', + 'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB + 'allowedFileExtensions' => [], + 'enabled' => true, + 'compression' => 'gzip', + 'encryption' => true, + 'antivirus' => true, + 'fileSecurity' => true, + '$permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'search' => 'buckets Default', + ])); + + $bucket = $dbForConsole->getDocument('buckets', 'default'); + + Console::success('[Setup] - Creating files collection for default bucket...'); + + $files = $collections['buckets']['files'] ?? []; + if (empty($files)) { + throw new Exception('Files collection is not configured.'); + } + + $attributes = []; + $indexes = []; + + foreach ($files['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]); + } + + foreach ($files['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); + } + + $dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); + } + + $connections->reclaim(); + + Console::success('[Setup] - Server database init completed...'); + Console::success('Server started successfully'); + } catch (\Throwable $e) { + Console::warning('Database not ready: ' . $e->getMessage()); + exit(1); + } + }); + +Http::init() + ->inject('authorization') + ->action(function (Authorization $authorization) { + $authorization->cleanRoles(); + $authorization->addRole(Role::any()->toString()); + }); + +$http->start(); From 9b94a4a1ed5f5e80df3c31e87e2bfe9fd977f3e8 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 12 Jul 2024 18:11:43 -0400 Subject: [PATCH 122/195] chore: improvements --- .env | 2 +- app/config/variables.php | 2 +- app/http.php | 6 +++--- app/realtime.php | 2 +- composer.lock | 10 +++++----- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.env b/.env index 23d221d0d6..a541e255b9 100644 --- a/.env +++ b/.env @@ -1,7 +1,7 @@ _APP_ENV=development _APP_EDITION=self-hosted _APP_LOCALE=en -_APP_WORKER_PER_CORE=6 +_APP_WORKER_PER_CORE=2 _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_WHITELIST_IPS= diff --git a/app/config/variables.php b/app/config/variables.php index ff19b94a91..8e85e9714a 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -245,7 +245,7 @@ return [ 'name' => '_APP_WORKER_PER_CORE', 'description' => 'Internal Worker per core for the API, Realtime and Executor containers. Can be configured to optimize performance.', 'introduction' => '0.13.0', - 'default' => 6, + 'default' => 2, 'required' => false, 'question' => '', 'filter' => '' diff --git a/app/http.php b/app/http.php index 5ae2e40e1a..63896015ab 100644 --- a/app/http.php +++ b/app/http.php @@ -26,7 +26,7 @@ use Utopia\System\System; global $global, $container; $payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing -$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); +$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 2)); $server = new Server('0.0.0.0', '80', [ 'open_http2_protocol' => true, @@ -42,8 +42,8 @@ $server = new Server('0.0.0.0', '80', [ 'open_cpu_affinity' => true, // Coroutine 'enable_coroutine' => true, - 'max_coroutine' => 10000, - 'send_yield' => true + 'send_yield' => true, + 'tcp_fastopen' => true, ]); $http = new Http($server, $container, 'UTC'); diff --git a/app/realtime.php b/app/realtime.php index 0e95ce1735..12c26c1e3d 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -61,7 +61,7 @@ $stats->create(); $containerId = uniqid(); $statsDocument = null; -$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); +$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 2)); $adapter = new Adapter\Swoole(port: System::getEnv('PORT', 80)); $adapter diff --git a/composer.lock b/composer.lock index 9ba085ba83..d9ea057fad 100644 --- a/composer.lock +++ b/composer.lock @@ -3284,16 +3284,16 @@ }, { "name": "laravel/pint", - "version": "v1.16.1", + "version": "v1.16.2", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "9266a47f1b9231b83e0cfd849009547329d871b1" + "reference": "51f1ba679a6afe0315621ad143d788bd7ded0eca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/9266a47f1b9231b83e0cfd849009547329d871b1", - "reference": "9266a47f1b9231b83e0cfd849009547329d871b1", + "url": "https://api.github.com/repos/laravel/pint/zipball/51f1ba679a6afe0315621ad143d788bd7ded0eca", + "reference": "51f1ba679a6afe0315621ad143d788bd7ded0eca", "shasum": "" }, "require": { @@ -3346,7 +3346,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-06-18T16:50:05+00:00" + "time": "2024-07-09T15:58:08+00:00" }, { "name": "matthiasmullie/minify", From 55d4cd2aa582853eee4c79254c5decee0d73dd4d Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 15 Jul 2024 10:34:59 -0400 Subject: [PATCH 123/195] refactor: response models to static --- app/http.php | 1 + src/Appwrite/GraphQL/Schema.php | 2 +- src/Appwrite/Platform/Tasks/Specs.php | 2 +- src/Appwrite/Utopia/Response.php | 294 +----------------------- src/Appwrite/Utopia/Response/Models.php | 287 +++++++++++++++++++++++ tests/unit/GraphQL/BuilderTest.php | 4 +- tests/unit/Utopia/ResponseTest.php | 6 +- 7 files changed, 300 insertions(+), 296 deletions(-) create mode 100644 src/Appwrite/Utopia/Response/Models.php diff --git a/app/http.php b/app/http.php index 63896015ab..6f72f81d47 100644 --- a/app/http.php +++ b/app/http.php @@ -27,6 +27,7 @@ global $global, $container; $payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing $workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 2)); +Response\Models::init(); $server = new Server('0.0.0.0', '80', [ 'open_http2_protocol' => true, diff --git a/src/Appwrite/GraphQL/Schema.php b/src/Appwrite/GraphQL/Schema.php index 01b9711dec..1ffd6edf91 100644 --- a/src/Appwrite/GraphQL/Schema.php +++ b/src/Appwrite/GraphQL/Schema.php @@ -98,7 +98,7 @@ class Schema */ protected function api(Http $http, Request $request, UtopiaHttpResponse $response, Container $container, callable $complexity): array { - Mapper::init((new Response($response))->getModels()); + Mapper::init(Response\Models::getModels()); $mapper = new Mapper(); diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index c7c0fc5760..e57587d637 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -256,7 +256,7 @@ class Specs extends Action ]; } - $models = $response->getModels(); + $models = Response\Models::getModels(); foreach ($models as $key => $value) { if ($platform !== APP_PLATFORM_CONSOLE && !$value->isPublic()) { diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 0f23874fc7..0bc281a8e3 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -3,109 +3,12 @@ namespace Appwrite\Utopia; use Appwrite\Utopia\Response\Filter; -use Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Response\Model\Account; -use Appwrite\Utopia\Response\Model\AlgoArgon2; -use Appwrite\Utopia\Response\Model\AlgoBcrypt; -use Appwrite\Utopia\Response\Model\AlgoMd5; -use Appwrite\Utopia\Response\Model\AlgoPhpass; -use Appwrite\Utopia\Response\Model\AlgoScrypt; -use Appwrite\Utopia\Response\Model\AlgoScryptModified; -use Appwrite\Utopia\Response\Model\AlgoSha; -use Appwrite\Utopia\Response\Model\Any; -use Appwrite\Utopia\Response\Model\Attribute; -use Appwrite\Utopia\Response\Model\AttributeBoolean; -use Appwrite\Utopia\Response\Model\AttributeDatetime; -use Appwrite\Utopia\Response\Model\AttributeEmail; -use Appwrite\Utopia\Response\Model\AttributeEnum; -use Appwrite\Utopia\Response\Model\AttributeFloat; -use Appwrite\Utopia\Response\Model\AttributeInteger; -use Appwrite\Utopia\Response\Model\AttributeIP; -use Appwrite\Utopia\Response\Model\AttributeList; -use Appwrite\Utopia\Response\Model\AttributeRelationship; -use Appwrite\Utopia\Response\Model\AttributeString; -use Appwrite\Utopia\Response\Model\AttributeURL; -use Appwrite\Utopia\Response\Model\AuthProvider; -use Appwrite\Utopia\Response\Model\BaseList; -use Appwrite\Utopia\Response\Model\Branch; -use Appwrite\Utopia\Response\Model\Bucket; -use Appwrite\Utopia\Response\Model\Build; -use Appwrite\Utopia\Response\Model\Collection; -use Appwrite\Utopia\Response\Model\ConsoleVariables; -use Appwrite\Utopia\Response\Model\Continent; -use Appwrite\Utopia\Response\Model\Country; -use Appwrite\Utopia\Response\Model\Currency; -use Appwrite\Utopia\Response\Model\Database; -use Appwrite\Utopia\Response\Model\Deployment; -use Appwrite\Utopia\Response\Model\Detection; -use Appwrite\Utopia\Response\Model\Document as ModelDocument; -use Appwrite\Utopia\Response\Model\Error; -use Appwrite\Utopia\Response\Model\ErrorDev; -use Appwrite\Utopia\Response\Model\Execution; -use Appwrite\Utopia\Response\Model\File; -use Appwrite\Utopia\Response\Model\Func; -use Appwrite\Utopia\Response\Model\Headers; -use Appwrite\Utopia\Response\Model\HealthAntivirus; -use Appwrite\Utopia\Response\Model\HealthCertificate; -use Appwrite\Utopia\Response\Model\HealthQueue; -use Appwrite\Utopia\Response\Model\HealthStatus; -use Appwrite\Utopia\Response\Model\HealthTime; -use Appwrite\Utopia\Response\Model\HealthVersion; -use Appwrite\Utopia\Response\Model\Identity; -use Appwrite\Utopia\Response\Model\Index; -use Appwrite\Utopia\Response\Model\Installation; -use Appwrite\Utopia\Response\Model\JWT; -use Appwrite\Utopia\Response\Model\Key; -use Appwrite\Utopia\Response\Model\Language; -use Appwrite\Utopia\Response\Model\Locale; -use Appwrite\Utopia\Response\Model\LocaleCode; -use Appwrite\Utopia\Response\Model\Log; -use Appwrite\Utopia\Response\Model\Membership; -use Appwrite\Utopia\Response\Model\Message; -use Appwrite\Utopia\Response\Model\Metric; -use Appwrite\Utopia\Response\Model\MetricBreakdown; -use Appwrite\Utopia\Response\Model\MFAChallenge; -use Appwrite\Utopia\Response\Model\MFAFactors; -use Appwrite\Utopia\Response\Model\MFARecoveryCodes; -use Appwrite\Utopia\Response\Model\MFAType; -use Appwrite\Utopia\Response\Model\Migration; -use Appwrite\Utopia\Response\Model\MigrationFirebaseProject; -use Appwrite\Utopia\Response\Model\MigrationReport; -use Appwrite\Utopia\Response\Model\Mock; -use Appwrite\Utopia\Response\Model\None; -use Appwrite\Utopia\Response\Model\Phone; -use Appwrite\Utopia\Response\Model\Platform; -use Appwrite\Utopia\Response\Model\Preferences; -use Appwrite\Utopia\Response\Model\Project; -use Appwrite\Utopia\Response\Model\Provider; -use Appwrite\Utopia\Response\Model\ProviderRepository; -use Appwrite\Utopia\Response\Model\Rule; -use Appwrite\Utopia\Response\Model\Runtime; -use Appwrite\Utopia\Response\Model\Session; -use Appwrite\Utopia\Response\Model\Subscriber; -use Appwrite\Utopia\Response\Model\Target; -use Appwrite\Utopia\Response\Model\Team; -use Appwrite\Utopia\Response\Model\TemplateEmail; -use Appwrite\Utopia\Response\Model\TemplateSMS; -use Appwrite\Utopia\Response\Model\Token; -use Appwrite\Utopia\Response\Model\Topic; -use Appwrite\Utopia\Response\Model\UsageBuckets; -use Appwrite\Utopia\Response\Model\UsageCollection; -use Appwrite\Utopia\Response\Model\UsageDatabase; -use Appwrite\Utopia\Response\Model\UsageDatabases; -use Appwrite\Utopia\Response\Model\UsageFunction; -use Appwrite\Utopia\Response\Model\UsageFunctions; -use Appwrite\Utopia\Response\Model\UsageProject; -use Appwrite\Utopia\Response\Model\UsageStorage; -use Appwrite\Utopia\Response\Model\UsageUsers; -use Appwrite\Utopia\Response\Model\User; -use Appwrite\Utopia\Response\Model\Variable; -use Appwrite\Utopia\Response\Model\Webhook; use Exception; -// Keep last use Utopia\Database\Document; use Utopia\Http\Adapter\Swoole\Response as HttpResponse; +// Keep last + /** * @method int getStatusCode() * @method Response setStatusCode(int $code = 200) @@ -314,150 +217,6 @@ class Response extends HttpResponse */ public function __construct(HttpResponse $response) { - $this - // General - ->setModel(new None()) - ->setModel(new Any()) - ->setModel(new Error()) - ->setModel(new ErrorDev()) - // Lists - ->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT)) - ->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION)) - ->setModel(new BaseList('Databases List', self::MODEL_DATABASE_LIST, 'databases', self::MODEL_DATABASE)) - ->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX)) - ->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER)) - ->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION)) - ->setModel(new BaseList('Identities List', self::MODEL_IDENTITY_LIST, 'identities', self::MODEL_IDENTITY)) - ->setModel(new BaseList('Logs List', self::MODEL_LOG_LIST, 'logs', self::MODEL_LOG)) - ->setModel(new BaseList('Files List', self::MODEL_FILE_LIST, 'files', self::MODEL_FILE)) - ->setModel(new BaseList('Buckets List', self::MODEL_BUCKET_LIST, 'buckets', self::MODEL_BUCKET)) - ->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM)) - ->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP)) - ->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION)) - ->setModel(new BaseList('Installations List', self::MODEL_INSTALLATION_LIST, 'installations', self::MODEL_INSTALLATION)) - ->setModel(new BaseList('Provider Repositories List', self::MODEL_PROVIDER_REPOSITORY_LIST, 'providerRepositories', self::MODEL_PROVIDER_REPOSITORY)) - ->setModel(new BaseList('Branches List', self::MODEL_BRANCH_LIST, 'branches', self::MODEL_BRANCH)) - ->setModel(new BaseList('Runtimes List', self::MODEL_RUNTIME_LIST, 'runtimes', self::MODEL_RUNTIME)) - ->setModel(new BaseList('Deployments List', self::MODEL_DEPLOYMENT_LIST, 'deployments', self::MODEL_DEPLOYMENT)) - ->setModel(new BaseList('Executions List', self::MODEL_EXECUTION_LIST, 'executions', self::MODEL_EXECUTION)) - ->setModel(new BaseList('Builds List', self::MODEL_BUILD_LIST, 'builds', self::MODEL_BUILD)) // Not used anywhere yet - ->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false)) - ->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false)) - ->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY, true, false)) - ->setModel(new BaseList('Auth Providers List', self::MODEL_AUTH_PROVIDER_LIST, 'platforms', self::MODEL_AUTH_PROVIDER, true, false)) - ->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false)) - ->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY)) - ->setModel(new BaseList('Continents List', self::MODEL_CONTINENT_LIST, 'continents', self::MODEL_CONTINENT)) - ->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE)) - ->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY)) - ->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE)) - ->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metrics', self::MODEL_METRIC, true, false)) - ->setModel(new BaseList('Variables List', self::MODEL_VARIABLE_LIST, 'variables', self::MODEL_VARIABLE)) - ->setModel(new BaseList('Status List', self::MODEL_HEALTH_STATUS_LIST, 'statuses', self::MODEL_HEALTH_STATUS)) - ->setModel(new BaseList('Rule List', self::MODEL_PROXY_RULE_LIST, 'rules', self::MODEL_PROXY_RULE)) - ->setModel(new BaseList('Locale codes list', self::MODEL_LOCALE_CODE_LIST, 'localeCodes', self::MODEL_LOCALE_CODE)) - ->setModel(new BaseList('Provider list', self::MODEL_PROVIDER_LIST, 'providers', self::MODEL_PROVIDER)) - ->setModel(new BaseList('Message list', self::MODEL_MESSAGE_LIST, 'messages', self::MODEL_MESSAGE)) - ->setModel(new BaseList('Topic list', self::MODEL_TOPIC_LIST, 'topics', self::MODEL_TOPIC)) - ->setModel(new BaseList('Subscriber list', self::MODEL_SUBSCRIBER_LIST, 'subscribers', self::MODEL_SUBSCRIBER)) - ->setModel(new BaseList('Target list', self::MODEL_TARGET_LIST, 'targets', self::MODEL_TARGET)) - ->setModel(new BaseList('Migrations List', self::MODEL_MIGRATION_LIST, 'migrations', self::MODEL_MIGRATION)) - ->setModel(new BaseList('Migrations Firebase Projects List', self::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', self::MODEL_MIGRATION_FIREBASE_PROJECT)) - // Entities - ->setModel(new Database()) - ->setModel(new Collection()) - ->setModel(new Attribute()) - ->setModel(new AttributeList()) - ->setModel(new AttributeString()) - ->setModel(new AttributeInteger()) - ->setModel(new AttributeFloat()) - ->setModel(new AttributeBoolean()) - ->setModel(new AttributeEmail()) - ->setModel(new AttributeEnum()) - ->setModel(new AttributeIP()) - ->setModel(new AttributeURL()) - ->setModel(new AttributeDatetime()) - ->setModel(new AttributeRelationship()) - ->setModel(new Index()) - ->setModel(new ModelDocument()) - ->setModel(new Log()) - ->setModel(new User()) - ->setModel(new AlgoMd5()) - ->setModel(new AlgoSha()) - ->setModel(new AlgoPhpass()) - ->setModel(new AlgoBcrypt()) - ->setModel(new AlgoScrypt()) - ->setModel(new AlgoScryptModified()) - ->setModel(new AlgoArgon2()) - ->setModel(new Account()) - ->setModel(new Preferences()) - ->setModel(new Session()) - ->setModel(new Identity()) - ->setModel(new Token()) - ->setModel(new JWT()) - ->setModel(new Locale()) - ->setModel(new LocaleCode()) - ->setModel(new File()) - ->setModel(new Bucket()) - ->setModel(new Team()) - ->setModel(new Membership()) - ->setModel(new Func()) - ->setModel(new Installation()) - ->setModel(new ProviderRepository()) - ->setModel(new Detection()) - ->setModel(new Branch()) - ->setModel(new Runtime()) - ->setModel(new Deployment()) - ->setModel(new Execution()) - ->setModel(new Build()) - ->setModel(new Project()) - ->setModel(new Webhook()) - ->setModel(new Key()) - ->setModel(new AuthProvider()) - ->setModel(new Platform()) - ->setModel(new Variable()) - ->setModel(new Country()) - ->setModel(new Continent()) - ->setModel(new Language()) - ->setModel(new Currency()) - ->setModel(new Phone()) - ->setModel(new HealthAntivirus()) - ->setModel(new HealthQueue()) - ->setModel(new HealthStatus()) - ->setModel(new HealthCertificate()) - ->setModel(new HealthTime()) - ->setModel(new HealthVersion()) - ->setModel(new Metric()) - ->setModel(new MetricBreakdown()) - ->setModel(new UsageDatabases()) - ->setModel(new UsageDatabase()) - ->setModel(new UsageCollection()) - ->setModel(new UsageUsers()) - ->setModel(new UsageStorage()) - ->setModel(new UsageBuckets()) - ->setModel(new UsageFunctions()) - ->setModel(new UsageFunction()) - ->setModel(new UsageProject()) - ->setModel(new Headers()) - ->setModel(new Rule()) - ->setModel(new TemplateSMS()) - ->setModel(new TemplateEmail()) - ->setModel(new ConsoleVariables()) - ->setModel(new MFAChallenge()) - ->setModel(new MFARecoveryCodes()) - ->setModel(new MFAType()) - ->setModel(new MFAFactors()) - ->setModel(new Provider()) - ->setModel(new Message()) - ->setModel(new Topic()) - ->setModel(new Subscriber()) - ->setModel(new Target()) - ->setModel(new Migration()) - ->setModel(new MigrationReport()) - ->setModel(new MigrationFirebaseProject()) - // Tests (keep last) - ->setModel(new Mock()); - parent::__construct($response->swoole); } @@ -467,49 +226,6 @@ class Response extends HttpResponse public const CONTENT_TYPE_YAML = 'application/x-yaml'; public const CONTENT_TYPE_NULL = 'null'; - /** - * List of defined output objects - */ - protected $models = []; - - /** - * Set Model Object - * - * @return self - */ - public function setModel(Model $instance) - { - $this->models[$instance->getType()] = $instance; - - return $this; - } - - /** - * Get Model Object - * - * @param string $key - * @return Model - * @throws Exception - */ - public function getModel(string $key): Model - { - if (!isset($this->models[$key])) { - throw new Exception('Undefined model: ' . $key); - } - - return $this->models[$key]; - } - - /** - * Get Models List - * - * @return Model[] - */ - public function getModels(): array - { - return $this->models; - } - public function applyFilters(array $data, string $model): array { foreach ($this->filters as $filter) { @@ -569,7 +285,7 @@ class Response extends HttpResponse public function output(Document $document, string $model): array { $data = clone $document; - $model = $this->getModel($model); + $model = Response\Models::getModel($model); $output = []; $data = $model->filter($data); @@ -599,7 +315,7 @@ class Response extends HttpResponse if (\is_array($rule['type'])) { foreach ($rule['type'] as $type) { $condition = false; - foreach ($this->getModel($type)->conditions as $attribute => $val) { + foreach (Response\Models::getModel($type)->conditions as $attribute => $val) { $condition = $item->getAttribute($attribute) === $val; if (!$condition) { break; @@ -614,7 +330,7 @@ class Response extends HttpResponse $ruleType = $rule['type']; } - if (!array_key_exists($ruleType, $this->models)) { + if (!array_key_exists($ruleType, Response\Models::getModels())) { throw new Exception('Missing model for rule: ' . $ruleType); } diff --git a/src/Appwrite/Utopia/Response/Models.php b/src/Appwrite/Utopia/Response/Models.php new file mode 100644 index 0000000000..5970265005 --- /dev/null +++ b/src/Appwrite/Utopia/Response/Models.php @@ -0,0 +1,287 @@ +getType()] = $instance; + } + + /** + * Get Model Object + * + * @param string $key + * @return Model + * @throws Exception + */ + public static function getModel(string $key): Model + { + if (!isset(self::$models[$key])) { + throw new Exception('Undefined model: ' . $key); + } + + return self::$models[$key]; + } + + public static function getModels(): array + { + return self::$models; + } +} diff --git a/tests/unit/GraphQL/BuilderTest.php b/tests/unit/GraphQL/BuilderTest.php index 22350c150b..87dfaae12e 100644 --- a/tests/unit/GraphQL/BuilderTest.php +++ b/tests/unit/GraphQL/BuilderTest.php @@ -15,7 +15,7 @@ class BuilderTest extends TestCase public function setUp(): void { $this->response = new Response(new UtopiaSwooleResponse(new SwooleResponse())); - Mapper::init($this->response->getModels()); + Mapper::init(Response\Models::getModels()); } /** @@ -23,7 +23,7 @@ class BuilderTest extends TestCase */ public function testCreateTypeMapping() { - $model = $this->response->getModel(Response::MODEL_COLLECTION); + $model = Response\Models::getModel(Response::MODEL_COLLECTION); $type = Mapper::model(\ucfirst($model->getType())); $this->assertEquals('Collection', $type->name); } diff --git a/tests/unit/Utopia/ResponseTest.php b/tests/unit/Utopia/ResponseTest.php index b1d9f97752..9ab61cb4f0 100644 --- a/tests/unit/Utopia/ResponseTest.php +++ b/tests/unit/Utopia/ResponseTest.php @@ -18,9 +18,9 @@ class ResponseTest extends TestCase public function setUp(): void { $this->response = new Response(new UtopiaSwooleResponse(new SwooleResponse())); - $this->response->setModel(new Single()); - $this->response->setModel(new Lists()); - $this->response->setModel(new Nested()); + Response\Models::setModel(new Single()); + Response\Models::setModel(new Lists()); + Response\Models::setModel(new Nested()); } public function testFilters(): void From b86f1c0aa435013b9dc6064d249d1349eb03ee43 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 15 Jul 2024 12:52:18 -0400 Subject: [PATCH 124/195] refactor: Auto load inline-classes --- app/init2.php | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/app/init2.php b/app/init2.php index dfbee1b26b..ae67a6bb38 100644 --- a/app/init2.php +++ b/app/init2.php @@ -6,6 +6,7 @@ use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; use Appwrite\Auth\Auth; use Appwrite\Auth\Authentication; +use Appwrite\Auth\MFA\Type\TOTP; use Appwrite\Event\Audit; use Appwrite\Event\Build; use Appwrite\Event\Certificate; @@ -30,6 +31,8 @@ use Swoole\Database\PDOConfig; use Swoole\Database\PDOPool; use Swoole\Database\RedisConfig; use Swoole\Database\RedisPool; +use Utopia\Abuse\Abuse; +use Utopia\Abuse\Adapters\TimeLimit; use Utopia\Cache\Adapter\Redis as CacheRedis; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; @@ -45,6 +48,7 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\DI\Container; use Utopia\DI\Dependency; +use Utopia\Domains\Domain; use Utopia\Domains\Validator\PublicDomain; use Utopia\DSN\DSN; use Utopia\Http\Http; @@ -397,6 +401,44 @@ $global->set('db', function () { ); }); +// Autoload +class_exists(JWT::class, true); +class_exists(DSN::class, true); +class_exists(Log::class, true); +class_exists(TOTP::class, true); +class_exists(Mail::class, true); +class_exists(Func::class, true); +class_exists(Cache::class, true); +class_exists(Abuse::class, true); +class_exists(MySQL::class, true); +class_exists(Event::class, true); +class_exists(Audit::class, true); +class_exists(Usage::class, true); +class_exists(Local::class, true); +class_exists(Build::class, true); +class_exists(Locale::class, true); +class_exists(Delete::class, true); +class_exists(GitHub::class, true); +class_exists(Schema::class, true); +class_exists(Domain::class, true); +class_exists(Console::class, true); +class_exists(Request::class, true); +class_exists(MariaDB::class, true); +class_exists(Document::class, true); +class_exists(Sharding::class, true); +class_exists(Database::class, true); +class_exists(Hostname::class, true); +class_exists(TimeLimit::class, true); +class_exists(Migration::class, true); +class_exists(Messaging::class, true); +class_exists(CacheRedis::class, true); +class_exists(Connections::class, true); +class_exists(Certificate::class, true); +class_exists(EventDatabase::class, true); +class_exists(Authorization::class, true); +class_exists(Authentication::class, true); +class_exists(Queue\Connection\Redis::class, true); + $log = new Dependency(); $mode = new Dependency(); $user = new Dependency(); From 6d22474af9093299ac0509e6c4ee2f97c7209bd3 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 15 Jul 2024 13:26:18 -0400 Subject: [PATCH 125/195] fix: Initializing response model --- tests/unit/GraphQL/BuilderTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/GraphQL/BuilderTest.php b/tests/unit/GraphQL/BuilderTest.php index 87dfaae12e..348b7ac4a4 100644 --- a/tests/unit/GraphQL/BuilderTest.php +++ b/tests/unit/GraphQL/BuilderTest.php @@ -14,6 +14,7 @@ class BuilderTest extends TestCase public function setUp(): void { + Response\Models::init(); $this->response = new Response(new UtopiaSwooleResponse(new SwooleResponse())); Mapper::init(Response\Models::getModels()); } From b1e766d61a82fcc532e39e357d234f3ef80ac7f9 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 15 Jul 2024 13:26:32 -0400 Subject: [PATCH 126/195] refactor: Response Models init --- app/http.php | 1 - app/init2.php | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/http.php b/app/http.php index 6f72f81d47..63896015ab 100644 --- a/app/http.php +++ b/app/http.php @@ -27,7 +27,6 @@ global $global, $container; $payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing $workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 2)); -Response\Models::init(); $server = new Server('0.0.0.0', '80', [ 'open_http2_protocol' => true, diff --git a/app/init2.php b/app/init2.php index ae67a6bb38..8a66302131 100644 --- a/app/init2.php +++ b/app/init2.php @@ -25,6 +25,7 @@ use Appwrite\Hooks\Hooks; use Appwrite\Network\Validator\Origin; use Appwrite\URL\URL; use Appwrite\Utopia\Queue\Connections; +use Appwrite\Utopia\Response\Models; use MaxMind\Db\Reader; use PHPMailer\PHPMailer\PHPMailer; use Swoole\Database\PDOConfig; @@ -1330,3 +1331,5 @@ $container->set($queueForMigrations); $container->set($deviceForFunctions); $container->set($passwordsDictionary); $container->set($queueForCertificates); + +Models::init(); From acef27909163f45441f42bc06487489f36e358b2 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:15:02 -0400 Subject: [PATCH 127/195] refactor: headers to instance --- src/Appwrite/Utopia/Request.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Utopia/Request.php b/src/Appwrite/Utopia/Request.php index 94a82c6041..c974243608 100644 --- a/src/Appwrite/Utopia/Request.php +++ b/src/Appwrite/Utopia/Request.php @@ -124,10 +124,14 @@ class Request extends HttpRequest */ public function getHeaders(): array { - $headers = $this->generateHeaders(); + if($this->headers !== null) { + return $this->headers; + } + + $this->headers = $this->generateHeaders(); if (empty($this->swoole->cookie)) { - return $headers; + return $this->headers; } $cookieHeaders = []; @@ -136,10 +140,10 @@ class Request extends HttpRequest } if (!empty($cookieHeaders)) { - $headers['cookie'] = \implode('; ', $cookieHeaders); + $this->headers['cookie'] = \implode('; ', $cookieHeaders); } - return $headers; + return $this->headers; } /** From 19119b573d29d8ecf8e637cbb7bceda94d6e6b01 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:06:06 -0400 Subject: [PATCH 128/195] chore: Merge and lint --- app/controllers/api/functions.php | 16 +- app/controllers/api/users.php | 2 +- app/controllers/shared/api.php | 6 +- app/init.php | 3465 ++++++++++++------------ app/init/constants.php | 19 +- app/init2.php | 39 +- src/Appwrite/Platform/Tasks/Doctor.php | 2 +- 7 files changed, 1799 insertions(+), 1750 deletions(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 198eb062dc..1278d0572b 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1522,7 +1522,8 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId/build') ->inject('dbForProject') ->inject('project') ->inject('queueForEvents') - ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents) { + ->inject('authorization') + ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Authorization $authorization) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -1535,7 +1536,7 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId/build') throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } - $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); + $build = $authorization->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); if ($build->isEmpty()) { $buildId = ID::unique(); @@ -1614,7 +1615,7 @@ Http::post('/v1/functions/:functionId/executions') ->inject('geodb') ->inject('authorization') ->inject('authentication') - ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, ?string $scheduledAt, Response $response, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, , Authorization $authorization, Authentication $authentication) { + ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, ?string $scheduledAt, Response $response, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, Authorization $authorization, Authentication $authentication) { if(!$async && !is_null($scheduledAt)) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Scheduled executions must run asynchronously. Set scheduledAt to a future date, or set async to true.'); @@ -1753,7 +1754,7 @@ Http::post('/v1/functions/:functionId/executions') ->setContext('function', $function); if ($async) { - $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); if(is_null($scheduledAt)) { $queueForFunctions @@ -1896,7 +1897,7 @@ Http::post('/v1/functions/:functionId/executions') ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function ; - $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); } $roles = $authorization->getRoles(); @@ -2070,7 +2071,8 @@ Http::delete('/v1/functions/:functionId/executions/:executionId') ->inject('dbForProject') ->inject('dbForConsole') ->inject('queueForEvents') - ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, Database $dbForConsole, Event $queueForEvents) { + ->inject('authorization') + ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, Database $dbForConsole, Event $queueForEvents, Authorization $authorization) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -2107,7 +2109,7 @@ Http::delete('/v1/functions/:functionId/executions/:executionId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('active', false); - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); } } diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 61bb1411b7..6206ee3ee4 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -40,7 +40,6 @@ use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; use Utopia\Http\Http; use Utopia\Http\Validator\ArrayList; -use Utopia\System\System; use Utopia\Http\Validator\Assoc; use Utopia\Http\Validator\Boolean; use Utopia\Http\Validator\Integer; @@ -48,6 +47,7 @@ use Utopia\Http\Validator\Range; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; use Utopia\Locale\Locale; +use Utopia\System\System; /** TODO: Remove function when we move to using utopia/platform */ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks): Document diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index c2b970015f..adaac618d4 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -268,8 +268,8 @@ Http::init() throw new Exception(Exception::PROJECT_KEY_EXPIRED); } - $authorization->addRole(Auth::USER_ROLE_APPS); - $authorization->setDefaultStatus(false); // Cancel security segmentation for API keys. + $authorization->addRole(Auth::USER_ROLE_APPS); + $authorization->setDefaultStatus(false); // Cancel security segmentation for API keys. $accessedAt = $key->getAttribute('accessedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { @@ -783,7 +783,7 @@ Http::shutdown() $accessedAt = $project->getAttribute('accessedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) { $project->setAttribute('accessedAt', DateTime::now()); - Authorization::skip(fn () => $dbForConsole->updateDocument('projects', $project->getId(), $project)); + $authorization->skip(fn () => $dbForConsole->updateDocument('projects', $project->getId(), $project)); } } diff --git a/app/init.php b/app/init.php index a0e71f041b..9fca6503c9 100644 --- a/app/init.php +++ b/app/init.php @@ -1,1734 +1,1735 @@ $value], JSON_PRESERVE_ZERO_FRACTION); - }, - function (mixed $value) { - if (is_null($value)) { - return; - } - - return json_decode($value, true)['value']; - } -); - -Database::addFilter( - 'enum', - function (mixed $value, Document $attribute) { - if ($attribute->isSet('elements')) { - $attribute->removeAttribute('elements'); - } - - return $value; - }, - function (mixed $value, Document $attribute) { - $formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true); - if (isset($formatOptions['elements'])) { - $attribute->setAttribute('elements', $formatOptions['elements']); - } - - return $value; - } -); - -Database::addFilter( - 'range', - function (mixed $value, Document $attribute) { - if ($attribute->isSet('min')) { - $attribute->removeAttribute('min'); - } - if ($attribute->isSet('max')) { - $attribute->removeAttribute('max'); - } - - return $value; - }, - function (mixed $value, Document $attribute) { - $formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true); - if (isset($formatOptions['min']) || isset($formatOptions['max'])) { - $attribute - ->setAttribute('min', $formatOptions['min']) - ->setAttribute('max', $formatOptions['max']); - } - - return $value; - } -); - -Database::addFilter( - 'subQueryAttributes', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - $attributes = $database->find('attributes', [ - Query::equal('collectionInternalId', [$document->getInternalId()]), - Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), - Query::limit($database->getLimitForAttributes()), - ]); - - foreach ($attributes as $attribute) { - if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { - $options = $attribute->getAttribute('options'); - foreach ($options as $key => $value) { - $attribute->setAttribute($key, $value); - } - $attribute->removeAttribute('options'); - } - } - - return $attributes; - } -); - -Database::addFilter( - 'subQueryIndexes', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('indexes', [ - Query::equal('collectionInternalId', [$document->getInternalId()]), - Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), - Query::limit($database->getLimitForIndexes()), - ]); - } -); - -Database::addFilter( - 'subQueryPlatforms', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('platforms', [ - Query::equal('projectInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'subQueryKeys', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('keys', [ - Query::equal('projectInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'subQueryWebhooks', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('webhooks', [ - Query::equal('projectInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'subQuerySessions', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database->find('sessions', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryTokens', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('tokens', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryChallenges', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('challenges', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryAuthenticators', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('authenticators', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryMemberships', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('memberships', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryVariables', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('variables', [ - Query::equal('resourceInternalId', [$document->getInternalId()]), - Query::equal('resourceType', ['function']), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'encrypt', - function (mixed $value) { - $key = System::getEnv('_APP_OPENSSL_KEY_V1'); - $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); - $tag = null; - - return json_encode([ - 'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag), - 'method' => OpenSSL::CIPHER_AES_128_GCM, - 'iv' => \bin2hex($iv), - 'tag' => \bin2hex($tag ?? ''), - 'version' => '1', - ]); - }, - function (mixed $value) { - if (is_null($value)) { - return; - } - $value = json_decode($value, true); - $key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']); - - return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag'])); - } -); - -Database::addFilter( - 'subQueryProjectVariables', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('variables', [ - Query::equal('resourceType', ['project']), - Query::limit(APP_LIMIT_SUBQUERY) - ]); - } -); - -Database::addFilter( - 'userSearch', - function (mixed $value, Document $user) { - $searchValues = [ - $user->getId(), - $user->getAttribute('email', ''), - $user->getAttribute('name', ''), - $user->getAttribute('phone', '') - ]; - - foreach ($user->getAttribute('labels', []) as $label) { - $searchValues[] = 'label:' . $label; - } - - $search = implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'subQueryTargets', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('targets', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY) - ])); - } -); - -Database::addFilter( - 'subQueryTopicTargets', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - $targetIds = Authorization::skip(fn () => \array_map( - fn ($document) => $document->getAttribute('targetInternalId'), - $database->find('subscribers', [ - Query::equal('topicInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY) - ]) - )); - if (\count($targetIds) > 0) { - return $database->skipValidation(fn () => $database->find('targets', [ - Query::equal('$internalId', $targetIds) - ])); - } - return []; - } -); - -Database::addFilter( - 'providerSearch', - function (mixed $value, Document $provider) { - $searchValues = [ - $provider->getId(), - $provider->getAttribute('name', ''), - $provider->getAttribute('provider', ''), - $provider->getAttribute('type', '') - ]; - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'topicSearch', - function (mixed $value, Document $topic) { - $searchValues = [ - $topic->getId(), - $topic->getAttribute('name', ''), - $topic->getAttribute('description', ''), - ]; - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'messageSearch', - function (mixed $value, Document $message) { - $searchValues = [ - $message->getId(), - $message->getAttribute('description', ''), - $message->getAttribute('status', ''), - ]; - - $data = \json_decode($message->getAttribute('data', []), true); - $providerType = $message->getAttribute('providerType', ''); - - if ($providerType === MESSAGE_TYPE_EMAIL) { - $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); - } elseif ($providerType === MESSAGE_TYPE_SMS) { - $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); - } else { - $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); - } - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -/** - * DB Formats - */ -Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () { - return new Email(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () { - return new DatetimeValidator(); -}, Database::VAR_DATETIME); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) { - $elements = $attribute['formatOptions']['elements']; - return new WhiteList($elements, true); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () { - return new IP(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () { - return new URL(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) { - $min = $attribute['formatOptions']['min'] ?? -INF; - $max = $attribute['formatOptions']['max'] ?? INF; - return new Range($min, $max, Range::TYPE_INTEGER); -}, Database::VAR_INTEGER); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) { - $min = $attribute['formatOptions']['min'] ?? -INF; - $max = $attribute['formatOptions']['max'] ?? INF; - return new Range($min, $max, Range::TYPE_FLOAT); -}, Database::VAR_FLOAT); - -/* - * Registry - */ -$register->set('logger', function () { - // Register error logger - $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); - $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); - - try { - $loggingProvider = new DSN($providerConfig ?? ''); - - $providerName = $loggingProvider->getScheme(); - $providerConfig = match ($providerName) { - 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], - 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], - default => ['key' => $loggingProvider->getHost()], - }; - } catch (Throwable) { - // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables - $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"); - } - - $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 => throw new Exception('Provider "' . $providerName . '" not supported.') - }; - - return new Logger($adapter); -}); - -$register->set('pools', function () { - $group = new Group(); - - $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ - 'scheme' => 'mariadb', - 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), - 'port' => System::getEnv('_APP_DB_PORT', '3306'), - 'user' => System::getEnv('_APP_DB_USER', ''), - 'pass' => System::getEnv('_APP_DB_PASS', ''), - 'path' => System::getEnv('_APP_DB_SCHEMA', ''), - ]); - $fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([ - 'scheme' => 'redis', - 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => System::getEnv('_APP_REDIS_USER', ''), - 'pass' => System::getEnv('_APP_REDIS_PASS', ''), - ]); - - $connections = [ - 'console' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), - 'multiple' => false, - 'schemes' => ['mariadb', 'mysql'], - ], - 'database' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), - 'multiple' => true, - 'schemes' => ['mariadb', 'mysql'], - ], - 'queue' => [ - 'type' => 'queue', - 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'pubsub' => [ - 'type' => 'pubsub', - 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'cache' => [ - 'type' => 'cache', - 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), - 'multiple' => true, - 'schemes' => ['redis'], - ], - ]; - - $maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151); - $instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14); - - $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; - - if ($multiprocessing) { - $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); - } else { - $workerCount = 1; - } - - if ($workerCount > $instanceConnections) { - throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); - } - - $poolSize = (int)($instanceConnections / $workerCount); - - foreach ($connections as $key => $connection) { - $type = $connection['type'] ?? ''; - $multiple = $connection['multiple'] ?? false; - $schemes = $connection['schemes'] ?? []; - $config = []; - $dsns = explode(',', $connection['dsns'] ?? ''); - foreach ($dsns as &$dsn) { - $dsn = explode('=', $dsn); - $name = ($multiple) ? $key . '_' . $dsn[0] : $key; - $dsn = $dsn[1] ?? ''; - $config[] = $name; - if (empty($dsn)) { - //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); - continue; - } - - $dsn = new DSN($dsn); - $dsnHost = $dsn->getHost(); - $dsnPort = $dsn->getPort(); - $dsnUser = $dsn->getUser(); - $dsnPass = $dsn->getPassword(); - $dsnScheme = $dsn->getScheme(); - $dsnDatabase = $dsn->getPath(); - - if (!in_array($dsnScheme, $schemes)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); - } - - /** - * Get Resource - * - * Creation could be reused across connection types like database, cache, queue, etc. - * - * Resource assignment to an adapter will happen below. - */ - $resource = match ($dsnScheme) { - 'mysql', - 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( - PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_EMULATE_PREPARES => true, - PDO::ATTR_STRINGIFY_FETCHES => true - )); - }); - }, - 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { - $redis = new Redis(); - @$redis->pconnect($dsnHost, (int)$dsnPort); - if ($dsnPass) { - $redis->auth($dsnPass); - } - $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); - - return $redis; - }, - default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'), - }; - - $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { - // Get Adapter - switch ($type) { - case 'database': - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($resource()), - 'mysql' => new MySQL($resource()), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - break; - case 'pubsub': - $adapter = $resource(); - break; - case 'queue': - $adapter = match ($dsn->getScheme()) { - 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), - default => null - }; - break; - case 'cache': - $adapter = match ($dsn->getScheme()) { - 'redis' => new RedisCache($resource()), - default => null - }; - break; - - default: - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); - } - - return $adapter; - }); - - $group->add($pool); - } - - Config::setParam('pools-' . $key, $config); - } - - return $group; -}); - -$register->set('db', function () { - // This is usually for our workers or CLI commands scope - $dbHost = System::getEnv('_APP_DB_HOST', ''); - $dbPort = System::getEnv('_APP_DB_PORT', ''); - $dbUser = System::getEnv('_APP_DB_USER', ''); - $dbPass = System::getEnv('_APP_DB_PASS', ''); - $dbScheme = System::getEnv('_APP_DB_SCHEMA', ''); - - return new PDO( - "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", - $dbUser, - $dbPass, - SQL::getPDOAttributes() - ); -}); - -$register->set('smtp', function () { - $mail = new PHPMailer(true); - - $mail->isSMTP(); - - $username = System::getEnv('_APP_SMTP_USERNAME'); - $password = System::getEnv('_APP_SMTP_PASSWORD'); - - $mail->XMailer = 'Appwrite Mailer'; - $mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp'); - $mail->Port = System::getEnv('_APP_SMTP_PORT', 25); - $mail->SMTPAuth = !empty($username) && !empty($password); - $mail->Username = $username; - $mail->Password = $password; - $mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', ''); - $mail->SMTPAutoTLS = false; - $mail->CharSet = 'UTF-8'; - - $from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); - $email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - - $mail->setFrom($email, $from); - $mail->addReplyTo($email, $from); - - $mail->isHTML(true); - - return $mail; -}); -$register->set('geodb', function () { - return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.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); -} - -\stream_context_set_default([ // Set global user agent and http settings - 'http' => [ - 'method' => 'GET', - 'user_agent' => \sprintf( - APP_USERAGENT, - System::getEnv('_APP_VERSION', 'UNKNOWN'), - System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)) - ), - 'timeout' => 2, - ], -]); - -// Runtime Execution -App::setResource('log', fn () => new Log()); -App::setResource('logger', function ($register) { - return $register->get('logger'); -}, ['register']); - -App::setResource('hooks', function ($register) { - return $register->get('hooks'); -}, ['register']); - -App::setResource('register', fn () => $register); -App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); - -App::setResource('localeCodes', function () { - return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); -}); - -// Queues -App::setResource('queue', function (Group $pools) { - return $pools->get('queue')->pop()->getResource(); -}, ['pools']); -App::setResource('queueForMessaging', function (Connection $queue) { - return new Messaging($queue); -}, ['queue']); -App::setResource('queueForMails', function (Connection $queue) { - return new Mail($queue); -}, ['queue']); -App::setResource('queueForBuilds', function (Connection $queue) { - return new Build($queue); -}, ['queue']); -App::setResource('queueForDatabase', function (Connection $queue) { - return new EventDatabase($queue); -}, ['queue']); -App::setResource('queueForDeletes', function (Connection $queue) { - return new Delete($queue); -}, ['queue']); -App::setResource('queueForEvents', function (Connection $queue) { - return new Event($queue); -}, ['queue']); -App::setResource('queueForAudits', function (Connection $queue) { - return new Audit($queue); -}, ['queue']); -App::setResource('queueForFunctions', function (Connection $queue) { - return new Func($queue); -}, ['queue']); -App::setResource('queueForUsage', function (Connection $queue) { - return new Usage($queue); -}, ['queue']); -App::setResource('queueForCertificates', function (Connection $queue) { - return new Certificate($queue); -}, ['queue']); -App::setResource('queueForMigrations', function (Connection $queue) { - return new Migration($queue); -}, ['queue']); -App::setResource('clients', function ($request, $console, $project) { - $console->setAttribute('platforms', [ // Always allow current host - '$collection' => ID::custom('platforms'), - 'name' => 'Current Host', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => $request->getHostname(), - ], Document::SET_TYPE_APPEND); - - $hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', '')); - $validator = new Hostname(); - foreach ($hostnames as $hostname) { - $hostname = trim($hostname); - if (!$validator->isValid($hostname)) { - continue; - } - $console->setAttribute('platforms', [ - '$collection' => ID::custom('platforms'), - 'type' => Origin::CLIENT_TYPE_WEB, - 'name' => $hostname, - 'hostname' => $hostname, - ], Document::SET_TYPE_APPEND); - } - - /** - * Get All verified client URLs for both console and current projects - * + Filter for duplicated entries - */ - $clientsConsole = \array_map( - fn ($node) => $node['hostname'], - \array_filter( - $console->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname'])) - ) - ); - - $clients = $clientsConsole; - $platforms = $project->getAttribute('platforms', []); - - foreach ($platforms as $node) { - if ( - isset($node['type']) && - ($node['type'] === Origin::CLIENT_TYPE_WEB || - $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && - !empty($node['hostname']) - ) { - $clients[] = $node['hostname']; - } - } - - return \array_unique($clients); -}, ['request', 'console', 'project']); - -App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $project */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var string $mode */ - - Authorization::setDefaultStatus(true); - - Auth::setCookieName('a_session_' . $project->getId()); - - if (APP_MODE_ADMIN === $mode) { - Auth::setCookieName('a_session_' . $console->getId()); - } - - $session = Auth::decodeSession( - $request->getCookie( - Auth::$cookieName, // Get sessions - $request->getCookie(Auth::$cookieName . '_legacy', '') - ) - ); - - // Get session from header for SSR clients - if (empty($session['id']) && empty($session['secret'])) { - $sessionHeader = $request->getHeader('x-appwrite-session', ''); - - if (!empty($sessionHeader)) { - $session = Auth::decodeSession($sessionHeader); - } - } - - // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies - if ($response) { - $response->addHeader('X-Debug-Fallback', 'false'); - } - - if (empty($session['id']) && empty($session['secret'])) { - if ($response) { - $response->addHeader('X-Debug-Fallback', 'true'); - } - $fallback = $request->getHeader('x-fallback-cookies', ''); - $fallback = \json_decode($fallback, true); - $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); - } - - Auth::$unique = $session['id'] ?? ''; - Auth::$secret = $session['secret'] ?? ''; - - if (APP_MODE_ADMIN !== $mode) { - if ($project->isEmpty()) { - $user = new Document([]); - } else { - if ($project->getId() === 'console') { - $user = $dbForConsole->getDocument('users', Auth::$unique); - } else { - $user = $dbForProject->getDocument('users', Auth::$unique); - } - } - } else { - $user = $dbForConsole->getDocument('users', Auth::$unique); - } - - if ( - $user->isEmpty() // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) - ) { // Validate user has valid login token - $user = new Document([]); - } - - if (APP_MODE_ADMIN === $mode) { - if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { - Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. - } else { - $user = new Document([]); - } - } - - $authJWT = $request->getHeader('x-appwrite-jwt', ''); - - if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication - $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); - - try { - $payload = $jwt->decode($authJWT); - } catch (JWTException $error) { - throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); - } - - $jwtUserId = $payload['userId'] ?? ''; - $jwtSessionId = $payload['sessionId'] ?? ''; - - if ($jwtUserId && $jwtSessionId) { - $user = $dbForProject->getDocument('users', $jwtUserId); - } - - if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token - $user = new Document([]); - } - } - - $dbForProject->setMetadata('user', $user->getId()); - $dbForConsole->setMetadata('user', $user->getId()); - - return $user; -}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); - -App::setResource('project', function ($dbForConsole, $request, $console) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var Utopia\Database\Document $console */ - - $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); - - if (empty($projectId) || $projectId === 'console') { - return $console; - } - - $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - - return $project; -}, ['dbForConsole', 'request', 'console']); - -App::setResource('session', function (Document $user) { - if ($user->isEmpty()) { - return; - } - - $sessions = $user->getAttribute('sessions', []); - $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); - - if (!$sessionId) { - return; - } - - foreach ($sessions as $session) {/** @var Document $session */ - if ($sessionId === $session->getId()) { - return $session; - } - } - - return; -}, ['user']); - -App::setResource('console', function () { - return new Document([ - '$id' => ID::custom('console'), - '$internalId' => ID::custom('console'), - 'name' => 'Appwrite', - '$collection' => ID::custom('projects'), - 'description' => 'Appwrite core engine', - 'logo' => '', - 'teamId' => -1, - 'webhooks' => [], - 'keys' => [], - 'platforms' => [ - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Localhost', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => 'localhost', - ], // Current host is added on app init - ], - 'legalName' => '', - 'legalCountry' => '', - 'legalState' => '', - 'legalCity' => '', - 'legalAddress' => '', - 'legalTaxId' => '', - 'auths' => [ - 'mockNumbers' => [], - 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', - 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds - 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' - ], - 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], - 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], - 'oAuthProviders' => [ - 'githubEnabled' => true, - 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), - 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') - ], - ]); -}, []); - -App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); - - $database - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; -}, ['pools', 'dbForConsole', 'cache', 'project']); - -App::setResource('dbForConsole', function (Group $pools, Cache $cache) { - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); - - $database - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - return $database; -}, ['pools', 'cache']); - -App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $configure = (function (Database $database) use ($project, $dsn) { - $database - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - }); - - if (isset($databases[$dsn->getHost()])) { - $database = $databases[$dsn->getHost()]; - $configure($database); - return $database; - } - - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); - $databases[$dsn->getHost()] = $database; - $configure($database); - - return $database; - }; -}, ['pools', 'dbForConsole', 'cache']); - -App::setResource('cache', function (Group $pools) { - $list = Config::getParam('pools-cache', []); - $adapters = []; - - foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource() - ; - } - - return new Cache(new Sharding($adapters)); -}, ['pools']); - -App::setResource('deviceForLocal', function () { - return new Local(); -}); - -App::setResource('deviceForFiles', function ($project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); -}, ['project']); - -App::setResource('deviceForFunctions', function ($project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); -}, ['project']); - -App::setResource('deviceForBuilds', function ($project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); -}, ['project']); - -function getDevice($root): Device -{ - $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); - - if (!empty($connection)) { - $acl = 'private'; - $device = Storage::DEVICE_LOCAL; - $accessKey = ''; - $accessSecret = ''; - $bucket = ''; - $region = ''; - - try { - $dsn = new DSN($connection); - $device = $dsn->getScheme(); - $accessKey = $dsn->getUser() ?? ''; - $accessSecret = $dsn->getPassword() ?? ''; - $bucket = $dsn->getPath() ?? ''; - $region = $dsn->getParam('region'); - } catch (\Throwable $e) { - Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); - } - - switch ($device) { - case Storage::DEVICE_S3: - return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case STORAGE::DEVICE_DO_SPACES: - $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); - $device->setHttpVersion(S3::HTTP_VERSION_1_1); - return $device; - case Storage::DEVICE_BACKBLAZE: - return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LINODE: - return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_WASABI: - return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - } - } else { - switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - case Storage::DEVICE_S3: - $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); - $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); - $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); - $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); - $s3Acl = 'private'; - return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); - case Storage::DEVICE_DO_SPACES: - $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); - $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); - $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); - $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); - $doSpacesAcl = 'private'; - $device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); - $device->setHttpVersion(S3::HTTP_VERSION_1_1); - return $device; - case Storage::DEVICE_BACKBLAZE: - $backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); - $backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); - $backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); - $backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); - $backblazeAcl = 'private'; - return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); - case Storage::DEVICE_LINODE: - $linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); - $linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', ''); - $linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', ''); - $linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); - $linodeAcl = 'private'; - return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); - case Storage::DEVICE_WASABI: - $wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); - $wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', ''); - $wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', ''); - $wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); - $wasabiAcl = 'private'; - return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); - } - } -} - -App::setResource('mode', function ($request) { - /** @var Appwrite\Utopia\Request $request */ - - /** - * Defines the mode for the request: - * - 'default' => Requests for Client and Server Side - * - 'admin' => Request from the Console on non-console projects - */ - return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); -}, ['request']); - -App::setResource('geodb', function ($register) { - /** @var Utopia\Registry\Registry $register */ - return $register->get('geodb'); -}, ['register']); - -App::setResource('passwordsDictionary', function ($register) { - /** @var Utopia\Registry\Registry $register */ - return $register->get('passwordsDictionary'); -}, ['register']); - - -App::setResource('servers', function () { - $platforms = Config::getParam('platforms'); - $server = $platforms[APP_PLATFORM_SERVER]; - - $languages = array_map(function ($language) { - return strtolower($language['name']); - }, $server['sdks']); - - return $languages; -}); - -App::setResource('promiseAdapter', function ($register) { - return $register->get('promiseAdapter'); -}, ['register']); - -App::setResource('schema', function ($utopia, $dbForProject) { - - $complexity = function (int $complexity, array $args) { - $queries = Query::parseQueries($args['queries'] ?? []); - $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; - $limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT; - - return $complexity * $limit; - }; - - $attributes = function (int $limit, int $offset) use ($dbForProject) { - $attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [ - Query::limit($limit), - Query::offset($offset), - ])); - - return \array_map(function ($attr) { - return $attr->getArrayCopy(); - }, $attrs); - }; - - $urls = [ - 'list' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents"; - }, - 'create' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents"; - }, - 'read' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; - }, - 'update' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; - }, - 'delete' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; - }, - ]; - - $params = [ - 'list' => function (string $databaseId, string $collectionId, array $args) { - return [ 'queries' => $args['queries']]; - }, - 'create' => function (string $databaseId, string $collectionId, array $args) { - $id = $args['id'] ?? 'unique()'; - $permissions = $args['permissions'] ?? null; - - unset($args['id']); - unset($args['permissions']); - - // Order must be the same as the route params - return [ - 'databaseId' => $databaseId, - 'documentId' => $id, - 'collectionId' => $collectionId, - 'data' => $args, - 'permissions' => $permissions, - ]; - }, - 'update' => function (string $databaseId, string $collectionId, array $args) { - $documentId = $args['id']; - $permissions = $args['permissions'] ?? null; - - unset($args['id']); - unset($args['permissions']); - - // Order must be the same as the route params - return [ - 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'documentId' => $documentId, - 'data' => $args, - 'permissions' => $permissions, - ]; - }, - ]; - - return Schema::build( - $utopia, - $complexity, - $attributes, - $urls, - $params, - ); -}, ['utopia', 'dbForProject']); - -App::setResource('contributors', function () { - $path = 'app/config/contributors.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -App::setResource('employees', function () { - $path = 'app/config/employees.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -App::setResource('heroes', function () { - $path = 'app/config/heroes.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -App::setResource('gitHub', function (Cache $cache) { - return new VcsGitHub($cache); -}, ['cache']); - -App::setResource('requestTimestamp', function ($request) { - //TODO: Move this to the Request class itself - $timestampHeader = $request->getHeader('x-appwrite-timestamp'); - $requestTimestamp = null; - if (!empty($timestampHeader)) { - try { - $requestTimestamp = new \DateTime($timestampHeader); - } catch (\Throwable $e) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value'); - } - } - return $requestTimestamp; -}, ['request']); -App::setResource('plan', function (array $plan = []) { - return []; -}); +///* +///** +// * Init +// * +// * Initializes both Appwrite API entry point, queue workers, and CLI tasks. +// * Set configuration, framework resources & app constants +// * +// */ +// +//if (\file_exists(__DIR__ . '/../vendor/autoload.php')) { +// require_once __DIR__ . '/../vendor/autoload.php'; +//} +// +//\ini_set('memory_limit', '512M'); +//\ini_set('display_errors', 1); +//\ini_set('display_startup_errors', 1); +//\ini_set('default_socket_timeout', -1); +//\error_reporting(E_ALL); +// +//use Ahc\Jwt\JWT; +//use Ahc\Jwt\JWTException; +//use Appwrite\Auth\Auth; +//use Appwrite\Event\Audit; +//use Appwrite\Event\Build; +//use Appwrite\Event\Certificate; +//use Appwrite\Event\Database as EventDatabase; +//use Appwrite\Event\Delete; +//use Appwrite\Event\Event; +//use Appwrite\Event\Func; +//use Appwrite\Event\Mail; +//use Appwrite\Event\Messaging; +//use Appwrite\Event\Migration; +//use Appwrite\Event\Usage; +//use Appwrite\Extend\Exception; +//use Appwrite\GraphQL\Promises\Adapter\Swoole; +//use Appwrite\GraphQL\Schema; +//use Appwrite\Hooks\Hooks; +//use Appwrite\Network\Validator\Email; +//use Appwrite\Network\Validator\Origin; +//use Appwrite\OpenSSL\OpenSSL; +//use Appwrite\URL\URL as AppwriteURL; +//use MaxMind\Db\Reader; +//use PHPMailer\PHPMailer\PHPMailer; +//use Swoole\Database\PDOProxy; +//use Utopia\App; +//use Utopia\Cache\Adapter\Redis as RedisCache; +//use Utopia\Cache\Adapter\Sharding; +//use Utopia\Cache\Cache; +//use Utopia\CLI\Console; +//use Utopia\Config\Config; +//use Utopia\Database\Adapter\MariaDB; +//use Utopia\Database\Adapter\MySQL; +//use Utopia\Database\Adapter\SQL; +//use Utopia\Database\Database; +//use Utopia\Database\Document; +//use Utopia\Database\Helpers\ID; +//use Utopia\Database\Query; +//use Utopia\Database\Validator\Authorization; +//use Utopia\Database\Validator\Datetime as DatetimeValidator; +//use Utopia\Database\Validator\Structure; +//use Utopia\Domains\Validator\PublicDomain; +//use Utopia\DSN\DSN; +//use Utopia\Locale\Locale; +//use Utopia\Logger\Adapter\AppSignal; +//use Utopia\Logger\Adapter\LogOwl; +//use Utopia\Logger\Adapter\Raygun; +//use Utopia\Logger\Adapter\Sentry; +//use Utopia\Logger\Log; +//use Utopia\Logger\Logger; +//use Utopia\Pools\Group; +//use Utopia\Pools\Pool; +//use Utopia\Queue; +//use Utopia\Queue\Connection; +//use Utopia\Registry\Registry; +//use Utopia\Storage\Device; +//use Utopia\Storage\Device\Backblaze; +//use Utopia\Storage\Device\DOSpaces; +//use Utopia\Storage\Device\Linode; +//use Utopia\Storage\Device\Local; +//use Utopia\Storage\Device\S3; +//use Utopia\Storage\Device\Wasabi; +//use Utopia\Storage\Storage; +//use Utopia\System\System; +//use Utopia\Validator\Hostname; +//use Utopia\Validator\IP; +//use Utopia\Validator\Range; +//use Utopia\Validator\URL; +//use Utopia\Validator\WhiteList; +//use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; +// +//const APP_NAME = 'Appwrite'; +//const APP_DOMAIN = 'appwrite.io'; +//const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address +//const APP_EMAIL_SECURITY = ''; // Default security email address +//const APP_USERAGENT = APP_NAME . '-Server v%s. Please report abuse at %s'; +//const APP_MODE_DEFAULT = 'default'; +//const APP_MODE_ADMIN = 'admin'; +//const APP_PAGING_LIMIT = 12; +//const APP_LIMIT_COUNT = 5000; +//const APP_LIMIT_USERS = 10_000; +//const APP_LIMIT_USER_PASSWORD_HISTORY = 20; +//const APP_LIMIT_USER_SESSIONS_MAX = 100; +//const APP_LIMIT_USER_SESSIONS_DEFAULT = 10; +//const APP_LIMIT_ANTIVIRUS = 20_000_000; //20MB +//const APP_LIMIT_ENCRYPTION = 20_000_000; //20MB +//const APP_LIMIT_COMPRESSION = 20_000_000; //20MB +//const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value +//const APP_LIMIT_ARRAY_LABELS_SIZE = 1000; // Default maximum of how many labels elements can there be in API parameter that expects array value +//const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length. +//const APP_LIMIT_SUBQUERY = 1000; +//const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000; +//const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period +//const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds +//const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls +//const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours +//const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours +//const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours +//const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours +//const APP_CACHE_BUSTER = 4314; +//const APP_VERSION_STABLE = '1.5.7'; +//const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; +//const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; +//const APP_DATABASE_ATTRIBUTE_IP = 'ip'; +//const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime'; +//const APP_DATABASE_ATTRIBUTE_URL = 'url'; +//const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange'; +//const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange'; +//const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char +//const APP_DATABASE_TIMEOUT_MILLISECONDS = 15_000; +//const APP_STORAGE_UPLOADS = '/storage/uploads'; +//const APP_STORAGE_FUNCTIONS = '/storage/functions'; +//const APP_STORAGE_BUILDS = '/storage/builds'; +//const APP_STORAGE_CACHE = '/storage/cache'; +//const APP_STORAGE_CERTIFICATES = '/storage/certificates'; +//const APP_STORAGE_CONFIG = '/storage/config'; +//const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT` +//const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite'; +//const APP_SOCIAL_TWITTER_HANDLE = 'appwrite'; +//const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io'; +//const APP_SOCIAL_LINKEDIN = 'https://www.linkedin.com/company/appwrite'; +//const APP_SOCIAL_INSTAGRAM = 'https://www.instagram.com/appwrite.io'; +//const APP_SOCIAL_GITHUB = 'https://github.com/appwrite'; +//const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord'; +//const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244'; +//const APP_SOCIAL_DEV = 'https://dev.to/appwrite'; +//const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite'; +//const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1'; +//const APP_HOSTNAME_INTERNAL = 'appwrite'; +// +//// 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_TEAMS = 'teams'; +//const DELETE_TYPE_EXECUTIONS = 'executions'; +//const DELETE_TYPE_AUDIT = 'audit'; +//const DELETE_TYPE_ABUSE = 'abuse'; +//const DELETE_TYPE_USAGE = 'usage'; +//const DELETE_TYPE_REALTIME = 'realtime'; +//const DELETE_TYPE_BUCKETS = 'buckets'; +//const DELETE_TYPE_INSTALLATIONS = 'installations'; +//const DELETE_TYPE_RULES = 'rules'; +//const DELETE_TYPE_SESSIONS = 'sessions'; +//const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp'; +//const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource'; +//const DELETE_TYPE_SCHEDULES = 'schedules'; +//const DELETE_TYPE_TOPIC = 'topic'; +//const DELETE_TYPE_TARGET = 'target'; +//const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets'; +//const DELETE_TYPE_SESSION_TARGETS = 'session_targets'; +// +//// Message types +//const MESSAGE_SEND_TYPE_INTERNAL = 'internal'; +//const MESSAGE_SEND_TYPE_EXTERNAL = 'external'; +//// Mail Types +//const MAIL_TYPE_VERIFICATION = 'verification'; +//const MAIL_TYPE_MAGIC_SESSION = 'magicSession'; +//const MAIL_TYPE_RECOVERY = 'recovery'; +//const MAIL_TYPE_INVITATION = 'invitation'; +//const MAIL_TYPE_CERTIFICATE = 'certificate'; +//// Auth Types +//const APP_AUTH_TYPE_SESSION = 'Session'; +//const APP_AUTH_TYPE_JWT = 'JWT'; +//const APP_AUTH_TYPE_KEY = 'Key'; +//const APP_AUTH_TYPE_ADMIN = 'Admin'; +//// Response related +//const MAX_OUTPUT_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB +//// Function headers +//const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host']; +//const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length']; +//// Message types +//const MESSAGE_TYPE_EMAIL = 'email'; +//const MESSAGE_TYPE_SMS = 'sms'; +//const MESSAGE_TYPE_PUSH = 'push'; +//// API key types +//const API_KEY_STANDARD = 'standard'; +//const API_KEY_DYNAMIC = 'dynamic'; +//// Usage metrics +//const METRIC_TEAMS = 'teams'; +//const METRIC_USERS = 'users'; +//const METRIC_MESSAGES = 'messages'; +//const METRIC_MESSAGES_COUNTRY_CODE = '{countryCode}.messages'; +//const METRIC_SESSIONS = 'sessions'; +//const METRIC_DATABASES = 'databases'; +//const METRIC_COLLECTIONS = 'collections'; +//const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections'; +//const METRIC_DOCUMENTS = 'documents'; +//const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents'; +//const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents'; +//const METRIC_BUCKETS = 'buckets'; +//const METRIC_FILES = 'files'; +//const METRIC_FILES_STORAGE = 'files.storage'; +//const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files'; +//const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage'; +//const METRIC_FUNCTIONS = 'functions'; +//const METRIC_DEPLOYMENTS = 'deployments'; +//const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage'; +//const METRIC_BUILDS = 'builds'; +//const METRIC_BUILDS_SUCCESS = 'builds.success'; +//const METRIC_BUILDS_FAILED = 'builds.failed'; +//const METRIC_BUILDS_STORAGE = 'builds.storage'; +//const METRIC_BUILDS_COMPUTE = 'builds.compute'; +//const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success'; +//const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed'; +//const METRIC_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_EXECUTIONS = 'executions'; +//const METRIC_EXECUTIONS_COMPUTE = 'executions.compute'; +//const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions'; +//const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute'; +//const METRIC_NETWORK_REQUESTS = 'network.requests'; +//const METRIC_NETWORK_INBOUND = 'network.inbound'; +//const METRIC_NETWORK_OUTBOUND = 'network.outbound'; +// +//$register = new Registry(); +// +//App::setMode(System::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION)); +// +//if (!App::isProduction()) { +// // Allow specific domains to skip public domain validation in dev environment +// // Useful for existing tests involving webhooks +// PublicDomain::allow(['request-catcher']); +//} +// +///* +// * ENV vars +// */ +//Config::load('events', __DIR__ . '/config/events.php'); +//Config::load('auth', __DIR__ . '/config/auth.php'); +//Config::load('apis', __DIR__ . '/config/apis.php'); // List of APIs +//Config::load('errors', __DIR__ . '/config/errors.php'); +//Config::load('oAuthProviders', __DIR__ . '/config/oAuthProviders.php'); +//Config::load('platforms', __DIR__ . '/config/platforms.php'); +//Config::load('collections', __DIR__ . '/config/collections.php'); +//Config::load('runtimes', __DIR__ . '/config/runtimes.php'); +//Config::load('runtimes-v2', __DIR__ . '/config/runtimes-v2.php'); +//Config::load('usage', __DIR__ . '/config/usage.php'); +//Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes +//Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes +//Config::load('services', __DIR__ . '/config/services.php'); // List of services +//Config::load('variables', __DIR__ . '/config/variables.php'); // List of env variables +//Config::load('regions', __DIR__ . '/config/regions.php'); // List of available regions +//Config::load('avatar-browsers', __DIR__ . '/config/avatars/browsers.php'); +//Config::load('avatar-credit-cards', __DIR__ . '/config/avatars/credit-cards.php'); +//Config::load('avatar-flags', __DIR__ . '/config/avatars/flags.php'); +//Config::load('locale-codes', __DIR__ . '/config/locale/codes.php'); +//Config::load('locale-currencies', __DIR__ . '/config/locale/currencies.php'); +//Config::load('locale-eu', __DIR__ . '/config/locale/eu.php'); +//Config::load('locale-languages', __DIR__ . '/config/locale/languages.php'); +//Config::load('locale-phones', __DIR__ . '/config/locale/phones.php'); +//Config::load('locale-countries', __DIR__ . '/config/locale/countries.php'); +//Config::load('locale-continents', __DIR__ . '/config/locale/continents.php'); +//Config::load('locale-templates', __DIR__ . '/config/locale/templates.php'); +//Config::load('storage-logos', __DIR__ . '/config/storage/logos.php'); +//Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php'); +//Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php'); +//Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php'); +// +///** +// * New DB Filters +// */ +//Database::addFilter( +// 'casting', +// function (mixed $value) { +// return json_encode(['value' => $value], JSON_PRESERVE_ZERO_FRACTION); +// }, +// function (mixed $value) { +// if (is_null($value)) { +// return; +// } +// +// return json_decode($value, true)['value']; +// } +//); +// +//Database::addFilter( +// 'enum', +// function (mixed $value, Document $attribute) { +// if ($attribute->isSet('elements')) { +// $attribute->removeAttribute('elements'); +// } +// +// return $value; +// }, +// function (mixed $value, Document $attribute) { +// $formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true); +// if (isset($formatOptions['elements'])) { +// $attribute->setAttribute('elements', $formatOptions['elements']); +// } +// +// return $value; +// } +//); +// +//Database::addFilter( +// 'range', +// function (mixed $value, Document $attribute) { +// if ($attribute->isSet('min')) { +// $attribute->removeAttribute('min'); +// } +// if ($attribute->isSet('max')) { +// $attribute->removeAttribute('max'); +// } +// +// return $value; +// }, +// function (mixed $value, Document $attribute) { +// $formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true); +// if (isset($formatOptions['min']) || isset($formatOptions['max'])) { +// $attribute +// ->setAttribute('min', $formatOptions['min']) +// ->setAttribute('max', $formatOptions['max']); +// } +// +// return $value; +// } +//); +// +//Database::addFilter( +// 'subQueryAttributes', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// $attributes = $database->find('attributes', [ +// Query::equal('collectionInternalId', [$document->getInternalId()]), +// Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), +// Query::limit($database->getLimitForAttributes()), +// ]); +// +// foreach ($attributes as $attribute) { +// if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { +// $options = $attribute->getAttribute('options'); +// foreach ($options as $key => $value) { +// $attribute->setAttribute($key, $value); +// } +// $attribute->removeAttribute('options'); +// } +// } +// +// return $attributes; +// } +//); +// +//Database::addFilter( +// 'subQueryIndexes', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('indexes', [ +// Query::equal('collectionInternalId', [$document->getInternalId()]), +// Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), +// Query::limit($database->getLimitForIndexes()), +// ]); +// } +//); +// +//Database::addFilter( +// 'subQueryPlatforms', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('platforms', [ +// Query::equal('projectInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ]); +// } +//); +// +//Database::addFilter( +// 'subQueryKeys', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('keys', [ +// Query::equal('projectInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ]); +// } +//); +// +//Database::addFilter( +// 'subQueryWebhooks', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('webhooks', [ +// Query::equal('projectInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ]); +// } +//); +// +//Database::addFilter( +// 'subQuerySessions', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database->find('sessions', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryTokens', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('tokens', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryChallenges', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('challenges', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryAuthenticators', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('authenticators', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryMemberships', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('memberships', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryVariables', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('variables', [ +// Query::equal('resourceInternalId', [$document->getInternalId()]), +// Query::equal('resourceType', ['function']), +// Query::limit(APP_LIMIT_SUBQUERY), +// ]); +// } +//); +// +//Database::addFilter( +// 'encrypt', +// function (mixed $value) { +// $key = System::getEnv('_APP_OPENSSL_KEY_V1'); +// $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); +// $tag = null; +// +// return json_encode([ +// 'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag), +// 'method' => OpenSSL::CIPHER_AES_128_GCM, +// 'iv' => \bin2hex($iv), +// 'tag' => \bin2hex($tag ?? ''), +// 'version' => '1', +// ]); +// }, +// function (mixed $value) { +// if (is_null($value)) { +// return; +// } +// $value = json_decode($value, true); +// $key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']); +// +// return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag'])); +// } +//); +// +//Database::addFilter( +// 'subQueryProjectVariables', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('variables', [ +// Query::equal('resourceType', ['project']), +// Query::limit(APP_LIMIT_SUBQUERY) +// ]); +// } +//); +// +//Database::addFilter( +// 'userSearch', +// function (mixed $value, Document $user) { +// $searchValues = [ +// $user->getId(), +// $user->getAttribute('email', ''), +// $user->getAttribute('name', ''), +// $user->getAttribute('phone', '') +// ]; +// +// foreach ($user->getAttribute('labels', []) as $label) { +// $searchValues[] = 'label:' . $label; +// } +// +// $search = implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +//Database::addFilter( +// 'subQueryTargets', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('targets', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY) +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryTopicTargets', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// $targetIds = Authorization::skip(fn () => \array_map( +// fn ($document) => $document->getAttribute('targetInternalId'), +// $database->find('subscribers', [ +// Query::equal('topicInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY) +// ]) +// )); +// if (\count($targetIds) > 0) { +// return $database->skipValidation(fn () => $database->find('targets', [ +// Query::equal('$internalId', $targetIds) +// ])); +// } +// return []; +// } +//); +// +//Database::addFilter( +// 'providerSearch', +// function (mixed $value, Document $provider) { +// $searchValues = [ +// $provider->getId(), +// $provider->getAttribute('name', ''), +// $provider->getAttribute('provider', ''), +// $provider->getAttribute('type', '') +// ]; +// +// $search = \implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +//Database::addFilter( +// 'topicSearch', +// function (mixed $value, Document $topic) { +// $searchValues = [ +// $topic->getId(), +// $topic->getAttribute('name', ''), +// $topic->getAttribute('description', ''), +// ]; +// +// $search = \implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +//Database::addFilter( +// 'messageSearch', +// function (mixed $value, Document $message) { +// $searchValues = [ +// $message->getId(), +// $message->getAttribute('description', ''), +// $message->getAttribute('status', ''), +// ]; +// +// $data = \json_decode($message->getAttribute('data', []), true); +// $providerType = $message->getAttribute('providerType', ''); +// +// if ($providerType === MESSAGE_TYPE_EMAIL) { +// $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); +// } elseif ($providerType === MESSAGE_TYPE_SMS) { +// $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); +// } else { +// $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); +// } +// +// $search = \implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +///** +// * DB Formats +// */ +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () { +// return new Email(); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () { +// return new DatetimeValidator(); +//}, Database::VAR_DATETIME); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) { +// $elements = $attribute['formatOptions']['elements']; +// return new WhiteList($elements, true); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () { +// return new IP(); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () { +// return new URL(); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) { +// $min = $attribute['formatOptions']['min'] ?? -INF; +// $max = $attribute['formatOptions']['max'] ?? INF; +// return new Range($min, $max, Range::TYPE_INTEGER); +//}, Database::VAR_INTEGER); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) { +// $min = $attribute['formatOptions']['min'] ?? -INF; +// $max = $attribute['formatOptions']['max'] ?? INF; +// return new Range($min, $max, Range::TYPE_FLOAT); +//}, Database::VAR_FLOAT); +// +///* +// * Registry +// */ +//$register->set('logger', function () { +// // Register error logger +// $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); +// $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); +// +// try { +// $loggingProvider = new DSN($providerConfig ?? ''); +// +// $providerName = $loggingProvider->getScheme(); +// $providerConfig = match ($providerName) { +// 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], +// 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], +// default => ['key' => $loggingProvider->getHost()], +// }; +// } catch (Throwable) { +// // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables +// $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"); +// } +// +// $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 => throw new Exception('Provider "' . $providerName . '" not supported.') +// }; +// +// return new Logger($adapter); +//}); +// +//$register->set('pools', function () { +// $group = new Group(); +// +// $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ +// 'scheme' => 'mariadb', +// 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), +// 'port' => System::getEnv('_APP_DB_PORT', '3306'), +// 'user' => System::getEnv('_APP_DB_USER', ''), +// 'pass' => System::getEnv('_APP_DB_PASS', ''), +// 'path' => System::getEnv('_APP_DB_SCHEMA', ''), +// ]); +// $fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([ +// 'scheme' => 'redis', +// 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), +// 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), +// 'user' => System::getEnv('_APP_REDIS_USER', ''), +// 'pass' => System::getEnv('_APP_REDIS_PASS', ''), +// ]); +// +// $connections = [ +// 'console' => [ +// 'type' => 'database', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), +// 'multiple' => false, +// 'schemes' => ['mariadb', 'mysql'], +// ], +// 'database' => [ +// 'type' => 'database', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), +// 'multiple' => true, +// 'schemes' => ['mariadb', 'mysql'], +// ], +// 'queue' => [ +// 'type' => 'queue', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), +// 'multiple' => false, +// 'schemes' => ['redis'], +// ], +// 'pubsub' => [ +// 'type' => 'pubsub', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), +// 'multiple' => false, +// 'schemes' => ['redis'], +// ], +// 'cache' => [ +// 'type' => 'cache', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), +// 'multiple' => true, +// 'schemes' => ['redis'], +// ], +// ]; +// +// $maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151); +// $instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14); +// +// $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; +// +// if ($multiprocessing) { +// $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); +// } else { +// $workerCount = 1; +// } +// +// if ($workerCount > $instanceConnections) { +// throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); +// } +// +// $poolSize = (int)($instanceConnections / $workerCount); +// +// foreach ($connections as $key => $connection) { +// $type = $connection['type'] ?? ''; +// $multiple = $connection['multiple'] ?? false; +// $schemes = $connection['schemes'] ?? []; +// $config = []; +// $dsns = explode(',', $connection['dsns'] ?? ''); +// foreach ($dsns as &$dsn) { +// $dsn = explode('=', $dsn); +// $name = ($multiple) ? $key . '_' . $dsn[0] : $key; +// $dsn = $dsn[1] ?? ''; +// $config[] = $name; +// if (empty($dsn)) { +// //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); +// continue; +// } +// +// $dsn = new DSN($dsn); +// $dsnHost = $dsn->getHost(); +// $dsnPort = $dsn->getPort(); +// $dsnUser = $dsn->getUser(); +// $dsnPass = $dsn->getPassword(); +// $dsnScheme = $dsn->getScheme(); +// $dsnDatabase = $dsn->getPath(); +// +// if (!in_array($dsnScheme, $schemes)) { +// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); +// } +// +// /** +// * Get Resource +// * +// * Creation could be reused across connection types like database, cache, queue, etc. +// * +// * Resource assignment to an adapter will happen below. +// */ +// $resource = match ($dsnScheme) { +// 'mysql', +// 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { +// return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { +// return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( +// PDO::ATTR_TIMEOUT => 3, // Seconds +// PDO::ATTR_PERSISTENT => true, +// PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, +// PDO::ATTR_EMULATE_PREPARES => true, +// PDO::ATTR_STRINGIFY_FETCHES => true +// )); +// }); +// }, +// 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { +// $redis = new Redis(); +// @$redis->pconnect($dsnHost, (int)$dsnPort); +// if ($dsnPass) { +// $redis->auth($dsnPass); +// } +// $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); +// +// return $redis; +// }, +// default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'), +// }; +// +// $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { +// // Get Adapter +// switch ($type) { +// case 'database': +// $adapter = match ($dsn->getScheme()) { +// 'mariadb' => new MariaDB($resource()), +// 'mysql' => new MySQL($resource()), +// default => null +// }; +// +// $adapter->setDatabase($dsn->getPath()); +// break; +// case 'pubsub': +// $adapter = $resource(); +// break; +// case 'queue': +// $adapter = match ($dsn->getScheme()) { +// 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), +// default => null +// }; +// break; +// case 'cache': +// $adapter = match ($dsn->getScheme()) { +// 'redis' => new RedisCache($resource()), +// default => null +// }; +// break; +// +// default: +// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); +// } +// +// return $adapter; +// }); +// +// $group->add($pool); +// } +// +// Config::setParam('pools-' . $key, $config); +// } +// +// return $group; +//}); +// +//$register->set('db', function () { +// // This is usually for our workers or CLI commands scope +// $dbHost = System::getEnv('_APP_DB_HOST', ''); +// $dbPort = System::getEnv('_APP_DB_PORT', ''); +// $dbUser = System::getEnv('_APP_DB_USER', ''); +// $dbPass = System::getEnv('_APP_DB_PASS', ''); +// $dbScheme = System::getEnv('_APP_DB_SCHEMA', ''); +// +// return new PDO( +// "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", +// $dbUser, +// $dbPass, +// SQL::getPDOAttributes() +// ); +//}); +// +//$register->set('smtp', function () { +// $mail = new PHPMailer(true); +// +// $mail->isSMTP(); +// +// $username = System::getEnv('_APP_SMTP_USERNAME'); +// $password = System::getEnv('_APP_SMTP_PASSWORD'); +// +// $mail->XMailer = 'Appwrite Mailer'; +// $mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp'); +// $mail->Port = System::getEnv('_APP_SMTP_PORT', 25); +// $mail->SMTPAuth = !empty($username) && !empty($password); +// $mail->Username = $username; +// $mail->Password = $password; +// $mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', ''); +// $mail->SMTPAutoTLS = false; +// $mail->CharSet = 'UTF-8'; +// +// $from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); +// $email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); +// +// $mail->setFrom($email, $from); +// $mail->addReplyTo($email, $from); +// +// $mail->isHTML(true); +// +// return $mail; +//}); +//$register->set('geodb', function () { +// return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.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); +//} +// +//\stream_context_set_default([ // Set global user agent and http settings +// 'http' => [ +// 'method' => 'GET', +// 'user_agent' => \sprintf( +// APP_USERAGENT, +// System::getEnv('_APP_VERSION', 'UNKNOWN'), +// System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)) +// ), +// 'timeout' => 2, +// ], +//]); +// +//// Runtime Execution +//App::setResource('log', fn () => new Log()); +//App::setResource('logger', function ($register) { +// return $register->get('logger'); +//}, ['register']); +// +//App::setResource('hooks', function ($register) { +// return $register->get('hooks'); +//}, ['register']); +// +//App::setResource('register', fn () => $register); +//App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); +// +//App::setResource('localeCodes', function () { +// return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); +//}); +// +//// Queues +//App::setResource('queue', function (Group $pools) { +// return $pools->get('queue')->pop()->getResource(); +//}, ['pools']); +//App::setResource('queueForMessaging', function (Connection $queue) { +// return new Messaging($queue); +//}, ['queue']); +//App::setResource('queueForMails', function (Connection $queue) { +// return new Mail($queue); +//}, ['queue']); +//App::setResource('queueForBuilds', function (Connection $queue) { +// return new Build($queue); +//}, ['queue']); +//App::setResource('queueForDatabase', function (Connection $queue) { +// return new EventDatabase($queue); +//}, ['queue']); +//App::setResource('queueForDeletes', function (Connection $queue) { +// return new Delete($queue); +//}, ['queue']); +//App::setResource('queueForEvents', function (Connection $queue) { +// return new Event($queue); +//}, ['queue']); +//App::setResource('queueForAudits', function (Connection $queue) { +// return new Audit($queue); +//}, ['queue']); +//App::setResource('queueForFunctions', function (Connection $queue) { +// return new Func($queue); +//}, ['queue']); +//App::setResource('queueForUsage', function (Connection $queue) { +// return new Usage($queue); +//}, ['queue']); +//App::setResource('queueForCertificates', function (Connection $queue) { +// return new Certificate($queue); +//}, ['queue']); +//App::setResource('queueForMigrations', function (Connection $queue) { +// return new Migration($queue); +//}, ['queue']); +//App::setResource('clients', function ($request, $console, $project) { +// $console->setAttribute('platforms', [ // Always allow current host +// '$collection' => ID::custom('platforms'), +// 'name' => 'Current Host', +// 'type' => Origin::CLIENT_TYPE_WEB, +// 'hostname' => $request->getHostname(), +// ], Document::SET_TYPE_APPEND); +// +// $hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', '')); +// $validator = new Hostname(); +// foreach ($hostnames as $hostname) { +// $hostname = trim($hostname); +// if (!$validator->isValid($hostname)) { +// continue; +// } +// $console->setAttribute('platforms', [ +// '$collection' => ID::custom('platforms'), +// 'type' => Origin::CLIENT_TYPE_WEB, +// 'name' => $hostname, +// 'hostname' => $hostname, +// ], Document::SET_TYPE_APPEND); +// } +// +// /** +// * Get All verified client URLs for both console and current projects +// * + Filter for duplicated entries +// */ +// $clientsConsole = \array_map( +// fn ($node) => $node['hostname'], +// \array_filter( +// $console->getAttribute('platforms', []), +// fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname'])) +// ) +// ); +// +// $clients = $clientsConsole; +// $platforms = $project->getAttribute('platforms', []); +// +// foreach ($platforms as $node) { +// if ( +// isset($node['type']) && +// ($node['type'] === Origin::CLIENT_TYPE_WEB || +// $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && +// !empty($node['hostname']) +// ) { +// $clients[] = $node['hostname']; +// } +// } +// +// return \array_unique($clients); +//}, ['request', 'console', 'project']); +// +//App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { +// /** @var Appwrite\Utopia\Request $request */ +// /** @var Appwrite\Utopia\Response $response */ +// /** @var Utopia\Database\Document $project */ +// /** @var Utopia\Database\Database $dbForProject */ +// /** @var Utopia\Database\Database $dbForConsole */ +// /** @var string $mode */ +// +// Authorization::setDefaultStatus(true); +// +// Auth::setCookieName('a_session_' . $project->getId()); +// +// if (APP_MODE_ADMIN === $mode) { +// Auth::setCookieName('a_session_' . $console->getId()); +// } +// +// $session = Auth::decodeSession( +// $request->getCookie( +// Auth::$cookieName, // Get sessions +// $request->getCookie(Auth::$cookieName . '_legacy', '') +// ) +// ); +// +// // Get session from header for SSR clients +// if (empty($session['id']) && empty($session['secret'])) { +// $sessionHeader = $request->getHeader('x-appwrite-session', ''); +// +// if (!empty($sessionHeader)) { +// $session = Auth::decodeSession($sessionHeader); +// } +// } +// +// // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies +// if ($response) { +// $response->addHeader('X-Debug-Fallback', 'false'); +// } +// +// if (empty($session['id']) && empty($session['secret'])) { +// if ($response) { +// $response->addHeader('X-Debug-Fallback', 'true'); +// } +// $fallback = $request->getHeader('x-fallback-cookies', ''); +// $fallback = \json_decode($fallback, true); +// $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); +// } +// +// Auth::$unique = $session['id'] ?? ''; +// Auth::$secret = $session['secret'] ?? ''; +// +// if (APP_MODE_ADMIN !== $mode) { +// if ($project->isEmpty()) { +// $user = new Document([]); +// } else { +// if ($project->getId() === 'console') { +// $user = $dbForConsole->getDocument('users', Auth::$unique); +// } else { +// $user = $dbForProject->getDocument('users', Auth::$unique); +// } +// } +// } else { +// $user = $dbForConsole->getDocument('users', Auth::$unique); +// } +// +// if ( +// $user->isEmpty() // Check a document has been found in the DB +// || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) +// ) { // Validate user has valid login token +// $user = new Document([]); +// } +// +// if (APP_MODE_ADMIN === $mode) { +// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { +// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. +// } else { +// $user = new Document([]); +// } +// } +// +// $authJWT = $request->getHeader('x-appwrite-jwt', ''); +// +// if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication +// $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); +// +// try { +// $payload = $jwt->decode($authJWT); +// } catch (JWTException $error) { +// throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); +// } +// +// $jwtUserId = $payload['userId'] ?? ''; +// $jwtSessionId = $payload['sessionId'] ?? ''; +// +// if ($jwtUserId && $jwtSessionId) { +// $user = $dbForProject->getDocument('users', $jwtUserId); +// } +// +// if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token +// $user = new Document([]); +// } +// } +// +// $dbForProject->setMetadata('user', $user->getId()); +// $dbForConsole->setMetadata('user', $user->getId()); +// +// return $user; +//}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); +// +//App::setResource('project', function ($dbForConsole, $request, $console) { +// /** @var Appwrite\Utopia\Request $request */ +// /** @var Utopia\Database\Database $dbForConsole */ +// /** @var Utopia\Database\Document $console */ +// +// $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); +// +// if (empty($projectId) || $projectId === 'console') { +// return $console; +// } +// +// $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); +// +// return $project; +//}, ['dbForConsole', 'request', 'console']); +// +//App::setResource('session', function (Document $user) { +// if ($user->isEmpty()) { +// return; +// } +// +// $sessions = $user->getAttribute('sessions', []); +// $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); +// +// if (!$sessionId) { +// return; +// } +// +// foreach ($sessions as $session) {/** @var Document $session */ +// if ($sessionId === $session->getId()) { +// return $session; +// } +// } +// +// return; +//}, ['user']); +// +//App::setResource('console', function () { +// return new Document([ +// '$id' => ID::custom('console'), +// '$internalId' => ID::custom('console'), +// 'name' => 'Appwrite', +// '$collection' => ID::custom('projects'), +// 'description' => 'Appwrite core engine', +// 'logo' => '', +// 'teamId' => -1, +// 'webhooks' => [], +// 'keys' => [], +// 'platforms' => [ +// [ +// '$collection' => ID::custom('platforms'), +// 'name' => 'Localhost', +// 'type' => Origin::CLIENT_TYPE_WEB, +// 'hostname' => 'localhost', +// ], // Current host is added on app init +// ], +// 'legalName' => '', +// 'legalCountry' => '', +// 'legalState' => '', +// 'legalCity' => '', +// 'legalAddress' => '', +// 'legalTaxId' => '', +// 'auths' => [ +// 'mockNumbers' => [], +// 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', +// 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user +// 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds +// 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' +// ], +// 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], +// 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], +// 'oAuthProviders' => [ +// 'githubEnabled' => true, +// 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), +// 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') +// ], +// ]); +//}, []); +// +//App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { +// if ($project->isEmpty() || $project->getId() === 'console') { +// return $dbForConsole; +// } +// +// try { +// $dsn = new DSN($project->getAttribute('database')); +// } catch (\InvalidArgumentException) { +// // TODO: Temporary until all projects are using shared tables +// $dsn = new DSN('mysql://' . $project->getAttribute('database')); +// } +// +// $dbAdapter = $pools +// ->get($dsn->getHost()) +// ->pop() +// ->getResource(); +// +// $database = new Database($dbAdapter, $cache); +// +// $database +// ->setMetadata('host', \gethostname()) +// ->setMetadata('project', $project->getId()) +// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); +// +// try { +// $dsn = new DSN($project->getAttribute('database')); +// } catch (\InvalidArgumentException) { +// // TODO: Temporary until all projects are using shared tables +// $dsn = new DSN('mysql://' . $project->getAttribute('database')); +// } +// +// if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { +// $database +// ->setSharedTables(true) +// ->setTenant($project->getInternalId()) +// ->setNamespace($dsn->getParam('namespace')); +// } else { +// $database +// ->setSharedTables(false) +// ->setTenant(null) +// ->setNamespace('_' . $project->getInternalId()); +// } +// +// return $database; +//}, ['pools', 'dbForConsole', 'cache', 'project']); +// +//App::setResource('dbForConsole', function (Group $pools, Cache $cache) { +// $dbAdapter = $pools +// ->get('console') +// ->pop() +// ->getResource(); +// +// $database = new Database($dbAdapter, $cache); +// +// $database +// ->setNamespace('_console') +// ->setMetadata('host', \gethostname()) +// ->setMetadata('project', 'console') +// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); +// +// return $database; +//}, ['pools', 'cache']); +// +//App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { +// $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools +// +// return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { +// if ($project->isEmpty() || $project->getId() === 'console') { +// return $dbForConsole; +// } +// +// try { +// $dsn = new DSN($project->getAttribute('database')); +// } catch (\InvalidArgumentException) { +// // TODO: Temporary until all projects are using shared tables +// $dsn = new DSN('mysql://' . $project->getAttribute('database')); +// } +// +// $configure = (function (Database $database) use ($project, $dsn) { +// $database +// ->setMetadata('host', \gethostname()) +// ->setMetadata('project', $project->getId()) +// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); +// +// if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { +// $database +// ->setSharedTables(true) +// ->setTenant($project->getInternalId()) +// ->setNamespace($dsn->getParam('namespace')); +// } else { +// $database +// ->setSharedTables(false) +// ->setTenant(null) +// ->setNamespace('_' . $project->getInternalId()); +// } +// }); +// +// if (isset($databases[$dsn->getHost()])) { +// $database = $databases[$dsn->getHost()]; +// $configure($database); +// return $database; +// } +// +// $dbAdapter = $pools +// ->get($dsn->getHost()) +// ->pop() +// ->getResource(); +// +// $database = new Database($dbAdapter, $cache); +// $databases[$dsn->getHost()] = $database; +// $configure($database); +// +// return $database; +// }; +//}, ['pools', 'dbForConsole', 'cache']); +// +//App::setResource('cache', function (Group $pools) { +// $list = Config::getParam('pools-cache', []); +// $adapters = []; +// +// foreach ($list as $value) { +// $adapters[] = $pools +// ->get($value) +// ->pop() +// ->getResource() +// ; +// } +// +// return new Cache(new Sharding($adapters)); +//}, ['pools']); +// +//App::setResource('deviceForLocal', function () { +// return new Local(); +//}); +// +//App::setResource('deviceForFiles', function ($project) { +// return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); +//}, ['project']); +// +//App::setResource('deviceForFunctions', function ($project) { +// return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); +//}, ['project']); +// +//App::setResource('deviceForBuilds', function ($project) { +// return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); +//}, ['project']); +// +//function getDevice($root): Device +//{ +// $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); +// +// if (!empty($connection)) { +// $acl = 'private'; +// $device = Storage::DEVICE_LOCAL; +// $accessKey = ''; +// $accessSecret = ''; +// $bucket = ''; +// $region = ''; +// +// try { +// $dsn = new DSN($connection); +// $device = $dsn->getScheme(); +// $accessKey = $dsn->getUser() ?? ''; +// $accessSecret = $dsn->getPassword() ?? ''; +// $bucket = $dsn->getPath() ?? ''; +// $region = $dsn->getParam('region'); +// } catch (\Throwable $e) { +// Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); +// } +// +// switch ($device) { +// case Storage::DEVICE_S3: +// return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case STORAGE::DEVICE_DO_SPACES: +// $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// $device->setHttpVersion(S3::HTTP_VERSION_1_1); +// return $device; +// case Storage::DEVICE_BACKBLAZE: +// return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case Storage::DEVICE_LINODE: +// return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case Storage::DEVICE_WASABI: +// return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case Storage::DEVICE_LOCAL: +// default: +// return new Local($root); +// } +// } else { +// switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { +// case Storage::DEVICE_LOCAL: +// default: +// return new Local($root); +// case Storage::DEVICE_S3: +// $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); +// $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); +// $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); +// $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); +// $s3Acl = 'private'; +// return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); +// case Storage::DEVICE_DO_SPACES: +// $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); +// $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); +// $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); +// $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); +// $doSpacesAcl = 'private'; +// $device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); +// $device->setHttpVersion(S3::HTTP_VERSION_1_1); +// return $device; +// case Storage::DEVICE_BACKBLAZE: +// $backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); +// $backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); +// $backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); +// $backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); +// $backblazeAcl = 'private'; +// return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); +// case Storage::DEVICE_LINODE: +// $linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); +// $linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', ''); +// $linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', ''); +// $linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); +// $linodeAcl = 'private'; +// return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); +// case Storage::DEVICE_WASABI: +// $wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); +// $wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', ''); +// $wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', ''); +// $wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); +// $wasabiAcl = 'private'; +// return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); +// } +// } +//} +// +//App::setResource('mode', function ($request) { +// /** @var Appwrite\Utopia\Request $request */ +// +// /** +// * Defines the mode for the request: +// * - 'default' => Requests for Client and Server Side +// * - 'admin' => Request from the Console on non-console projects +// */ +// return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); +//}, ['request']); +// +//App::setResource('geodb', function ($register) { +// /** @var Utopia\Registry\Registry $register */ +// return $register->get('geodb'); +//}, ['register']); +// +//App::setResource('passwordsDictionary', function ($register) { +// /** @var Utopia\Registry\Registry $register */ +// return $register->get('passwordsDictionary'); +//}, ['register']); +// +// +//App::setResource('servers', function () { +// $platforms = Config::getParam('platforms'); +// $server = $platforms[APP_PLATFORM_SERVER]; +// +// $languages = array_map(function ($language) { +// return strtolower($language['name']); +// }, $server['sdks']); +// +// return $languages; +//}); +// +//App::setResource('promiseAdapter', function ($register) { +// return $register->get('promiseAdapter'); +//}, ['register']); +// +//App::setResource('schema', function ($utopia, $dbForProject) { +// +// $complexity = function (int $complexity, array $args) { +// $queries = Query::parseQueries($args['queries'] ?? []); +// $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; +// $limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT; +// +// return $complexity * $limit; +// }; +// +// $attributes = function (int $limit, int $offset) use ($dbForProject) { +// $attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [ +// Query::limit($limit), +// Query::offset($offset), +// ])); +// +// return \array_map(function ($attr) { +// return $attr->getArrayCopy(); +// }, $attrs); +// }; +// +// $urls = [ +// 'list' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents"; +// }, +// 'create' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents"; +// }, +// 'read' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; +// }, +// 'update' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; +// }, +// 'delete' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; +// }, +// ]; +// +// $params = [ +// 'list' => function (string $databaseId, string $collectionId, array $args) { +// return [ 'queries' => $args['queries']]; +// }, +// 'create' => function (string $databaseId, string $collectionId, array $args) { +// $id = $args['id'] ?? 'unique()'; +// $permissions = $args['permissions'] ?? null; +// +// unset($args['id']); +// unset($args['permissions']); +// +// // Order must be the same as the route params +// return [ +// 'databaseId' => $databaseId, +// 'documentId' => $id, +// 'collectionId' => $collectionId, +// 'data' => $args, +// 'permissions' => $permissions, +// ]; +// }, +// 'update' => function (string $databaseId, string $collectionId, array $args) { +// $documentId = $args['id']; +// $permissions = $args['permissions'] ?? null; +// +// unset($args['id']); +// unset($args['permissions']); +// +// // Order must be the same as the route params +// return [ +// 'databaseId' => $databaseId, +// 'collectionId' => $collectionId, +// 'documentId' => $documentId, +// 'data' => $args, +// 'permissions' => $permissions, +// ]; +// }, +// ]; +// +// return Schema::build( +// $utopia, +// $complexity, +// $attributes, +// $urls, +// $params, +// ); +//}, ['utopia', 'dbForProject']); +// +//App::setResource('contributors', function () { +// $path = 'app/config/contributors.json'; +// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; +// return $list; +//}); +// +//App::setResource('employees', function () { +// $path = 'app/config/employees.json'; +// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; +// return $list; +//}); +// +//App::setResource('heroes', function () { +// $path = 'app/config/heroes.json'; +// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; +// return $list; +//}); +// +//App::setResource('gitHub', function (Cache $cache) { +// return new VcsGitHub($cache); +//}, ['cache']); +// +//App::setResource('requestTimestamp', function ($request) { +// //TODO: Move this to the Request class itself +// $timestampHeader = $request->getHeader('x-appwrite-timestamp'); +// $requestTimestamp = null; +// if (!empty($timestampHeader)) { +// try { +// $requestTimestamp = new \DateTime($timestampHeader); +// } catch (\Throwable $e) { +// throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value'); +// } +// } +// return $requestTimestamp; +//}, ['request']); +//App::setResource('plan', function (array $plan = []) { +// return []; +//});*/ diff --git a/app/init/constants.php b/app/init/constants.php index ca1790f71f..8c53bd1aa4 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -24,8 +24,9 @@ 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_ACCCESS = 24 * 60 * 60; // 24 hours -const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours +const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours +const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours +const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 331; const APP_VERSION_STABLE = '1.5.0'; @@ -123,6 +124,9 @@ const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length']; 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'; @@ -143,9 +147,20 @@ 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_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS = '{functionInternalId}.builds.compute.success'; +const METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED = '{functionInternalId}.builds.compute.failed'; 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_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments'; diff --git a/app/init2.php b/app/init2.php index 8a66302131..9df610ab02 100644 --- a/app/init2.php +++ b/app/init2.php @@ -57,6 +57,10 @@ use Utopia\Http\Request; use Utopia\Http\Response; use Utopia\Http\Validator\Hostname; 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\Queue; @@ -185,6 +189,26 @@ $global->set('logger', function () { $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' => $loggingProvider->getHost()], + 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], + default => ['key' => $loggingProvider->getHost()], + }; + } catch (Throwable) { + // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables + $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; } @@ -193,8 +217,14 @@ $global->set('logger', function () { throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); } - $classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName); - $adapter = new $classname($providerConfig); + $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 => throw new Exception('Provider "' . $providerName . '" not supported.') + }; + return new Logger($adapter); }); @@ -517,7 +547,6 @@ $user ->inject('authentication') ->setCallback(function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization, Authentication $authentication) { $authorization->setDefaultStatus(true); - Auth::setCookieName('a_session_' . $project->getId()); if (APP_MODE_ADMIN === $mode) { @@ -589,7 +618,7 @@ $user $authJWT = $request->getHeader('x-appwrite-jwt', ''); if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication - $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. + $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); try { $payload = $jwt->decode($authJWT); @@ -674,9 +703,11 @@ $console '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)) : [], diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index 59a6eae6bd..3bf9e0d33b 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -9,8 +9,8 @@ use Utopia\Config\Config; use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Adapter\MySQL; use Utopia\Domains\Domain; -use Utopia\Http\Http; use Utopia\DSN\DSN; +use Utopia\Http\Http; use Utopia\Logger\Logger; use Utopia\Platform\Action; use Utopia\Queue\Connection\Redis; From d1af8cfbe0070c8e0cdeda6cddb1c7f9674a27cd Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:21:47 -0400 Subject: [PATCH 129/195] feat: Extracting authentication cookie --- app/controllers/api/account.php | 52 ++++++++++++++++------------ app/controllers/api/teams.php | 10 +++--- app/init2.php | 10 +++--- src/Appwrite/Auth/Auth.php | 17 --------- src/Appwrite/Auth/Authentication.php | 16 +++++++++ tests/unit/Auth/AuthTest.php | 7 ++-- 6 files changed, 61 insertions(+), 51 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 739480095d..369b6879de 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -143,7 +143,7 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc ->trigger(); }; -$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { +$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Authorization $authorization, Authentication $authentication) { $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -232,15 +232,15 @@ $createSession = function (string $userId, string $secret, Request $request, Res ->setParam('sessionId', $session->getId()); if (!Config::getParam('domainVerification')) { - $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $sessionSecret)])); + $response->addHeader('X-Fallback-Cookies', \json_encode([$authentication->getCookieName() => Auth::encodeSession($user->getId(), $sessionSecret)])); } $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $protocol = $request->getProtocol(); $response - ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $sessionSecret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $sessionSecret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie($authentication->getCookieName() . '_legacy', Auth::encodeSession($user->getId(), $sessionSecret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($authentication->getCookieName(), Auth::encodeSession($user->getId(), $sessionSecret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ->setStatusCode(Response::STATUS_CODE_CREATED); $countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); @@ -557,8 +557,8 @@ Http::delete('/v1/account/sessions') // If current session delete the cookies too $response - ->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); + ->addCookie($authentication->getCookieName() . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($authentication->getCookieName(), '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); // Use current session for events. $queueForEvents @@ -684,8 +684,8 @@ Http::delete('/v1/account/sessions/:sessionId') } $response - ->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); + ->addCookie($authentication->getCookieName() . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($authentication->getCookieName(), '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); } $dbForProject->purgeCachedDocument('users', $user->getId()); @@ -816,7 +816,8 @@ Http::post('/v1/account/sessions/email') ->inject('queueForMails') ->inject('hooks') ->inject('authorization') - ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Authorization $authorization) { + ->inject('authentication') + ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Authorization $authorization, Authentication $authentication) { $email = \strtolower($email); $protocol = $request->getProtocol(); @@ -884,15 +885,15 @@ Http::post('/v1/account/sessions/email') if (!Config::getParam('domainVerification')) { $response - ->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])) + ->addHeader('X-Fallback-Cookies', \json_encode([$authentication->getCookieName() => Auth::encodeSession($user->getId(), $secret)])) ; } $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $response - ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie($authentication->getCookieName() . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($authentication->getCookieName(), Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ->setStatusCode(Response::STATUS_CODE_CREATED) ; @@ -943,7 +944,8 @@ Http::post('/v1/account/sessions/anonymous') ->inject('geodb') ->inject('queueForEvents') ->inject('authorization') - ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization) { + ->inject('authentication') + ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization, Authentication $authentication) { $protocol = $request->getProtocol(); $roles = $authorization->getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); @@ -1033,14 +1035,14 @@ Http::post('/v1/account/sessions/anonymous') ; if (!Config::getParam('domainVerification')) { - $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])); + $response->addHeader('X-Fallback-Cookies', \json_encode([$authentication->getCookieName() => Auth::encodeSession($user->getId(), $secret)])); } $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $response - ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie($authentication->getCookieName() . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($authentication->getCookieName(), Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ->setStatusCode(Response::STATUS_CODE_CREATED) ; @@ -1084,6 +1086,7 @@ Http::post('/v1/account/sessions/token') ->inject('queueForEvents') ->inject('queueForMails') ->inject('authorization') + ->inject('authentication') ->action($createSession); Http::get('/v1/account/sessions/oauth2/:provider') @@ -1628,7 +1631,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') $session->setAttribute('expire', $expire); if (!Config::getParam('domainVerification')) { - $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])); + $response->addHeader('X-Fallback-Cookies', \json_encode([$authentication->getCookieName() => Auth::encodeSession($user->getId(), $secret)])); } $queueForEvents @@ -1641,13 +1644,13 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') if ($state['success']['path'] == $oauthDefaultSuccess) { $query['project'] = $project->getId(); $query['domain'] = Config::getParam('cookieDomain'); - $query['key'] = Auth::$cookieName; + $query['key'] = $authentication->getCookieName(); $query['secret'] = Auth::encodeSession($user->getId(), $secret); } $response - ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); + ->addCookie($authentication->getCookieName() . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($authentication->getCookieName(), Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); } if (isset($sessionUpgrade) && $sessionUpgrade) { @@ -2252,6 +2255,7 @@ Http::put('/v1/account/sessions/magic-url') ->inject('queueForEvents') ->inject('queueForMails') ->inject('authorization') + ->inject('authentication') ->action($createSession); Http::put('/v1/account/sessions/phone') @@ -2284,6 +2288,7 @@ Http::put('/v1/account/sessions/phone') ->inject('queueForEvents') ->inject('queueForMails') ->inject('authorization') + ->inject('authentication') ->action($createSession); Http::post('/v1/account/tokens/phone') @@ -2933,7 +2938,8 @@ Http::patch('/v1/account/status') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->inject('authentication') + ->action(function (?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authentication $authentication) { $user->setAttribute('status', false); @@ -2949,8 +2955,8 @@ Http::patch('/v1/account/status') $protocol = $request->getProtocol(); $response - ->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie($authentication->getCookieName() . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($authentication->getCookieName(), '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ; $response->dynamic($user, Response::MODEL_ACCOUNT); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 2bab10d0f1..a016839420 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -1,6 +1,7 @@ inject('geodb') ->inject('queueForEvents') ->inject('authorization') - ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Authorization $authorization) { + ->inject('authentication') + ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Authorization $authorization, Authentication $authentication) { $protocol = $request->getProtocol(); $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -1036,13 +1038,13 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') if (!Config::getParam('domainVerification')) { $response - ->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])) + ->addHeader('X-Fallback-Cookies', \json_encode([$authentication->getCookieName() => Auth::encodeSession($user->getId(), $secret)])) ; } $response - ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie($authentication->getCookieName() . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($authentication->getCookieName(), Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ; $response->dynamic( diff --git a/app/init2.php b/app/init2.php index 9df610ab02..d7e27db9ac 100644 --- a/app/init2.php +++ b/app/init2.php @@ -547,16 +547,16 @@ $user ->inject('authentication') ->setCallback(function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization, Authentication $authentication) { $authorization->setDefaultStatus(true); - Auth::setCookieName('a_session_' . $project->getId()); + $authentication->setCookieName('a_session_' . $project->getId()); if (APP_MODE_ADMIN === $mode) { - Auth::setCookieName('a_session_' . $console->getId()); + $authentication->setCookieName('a_session_' . $console->getId()); } $session = Auth::decodeSession( $request->getCookie( - Auth::$cookieName, // Get sessions - $request->getCookie(Auth::$cookieName . '_legacy', '') + $authentication->getCookieName(), // Get sessions + $request->getCookie($authentication->getCookieName() . '_legacy', '') ) ); @@ -580,7 +580,7 @@ $user } $fallback = $request->getHeader('x-fallback-cookies', ''); $fallback = \json_decode($fallback, true); - $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); + $session = Auth::decodeSession(((isset($fallback[$authentication->getCookieName()])) ? $fallback[$authentication->getCookieName()] : '')); } $authentication->setUnique($session['id'] ?? ''); diff --git a/src/Appwrite/Auth/Auth.php b/src/Appwrite/Auth/Auth.php index 877dbf5f78..0135020765 100644 --- a/src/Appwrite/Auth/Auth.php +++ b/src/Appwrite/Auth/Auth.php @@ -91,23 +91,6 @@ class Auth */ public const MFA_RECENT_DURATION = 1800; // 30 mins - /** - * @var string - */ - public static $cookieName = 'a_session'; - - /** - * Set Cookie Name. - * - * @param $string - * - * @return string - */ - public static function setCookieName($string) - { - return self::$cookieName = $string; - } - /** * Encode Session. * diff --git a/src/Appwrite/Auth/Authentication.php b/src/Appwrite/Auth/Authentication.php index 5ff33c247e..ef372309da 100644 --- a/src/Appwrite/Auth/Authentication.php +++ b/src/Appwrite/Auth/Authentication.php @@ -18,6 +18,22 @@ class Authentication */ private $secret = ''; + + /** + * @var string + */ + private $cookieName = 'a_session'; + + public function setCookieName($string): string + { + return $this->cookieName = $string; + } + + public function getCookieName(): string + { + return $this->cookieName; + } + public function getUnique(): string { return $this->unique; diff --git a/tests/unit/Auth/AuthTest.php b/tests/unit/Auth/AuthTest.php index 54cce740a7..1ff5850402 100644 --- a/tests/unit/Auth/AuthTest.php +++ b/tests/unit/Auth/AuthTest.php @@ -3,6 +3,7 @@ namespace Tests\Unit\Auth; use Appwrite\Auth\Auth; +use Appwrite\Auth\Authentication; use PHPUnit\Framework\TestCase; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -14,11 +15,13 @@ use Utopia\Database\Validator\Roles; class AuthTest extends TestCase { protected Authorization $auth; + protected Authentication $authentication; public function setUp(): void { parent::setUp(); $this->auth = new Authorization(); + $this->authentication = new Authentication(); } /** @@ -34,8 +37,8 @@ class AuthTest extends TestCase { $name = 'cookie-name'; - $this->assertEquals(Auth::setCookieName($name), $name); - $this->assertEquals(Auth::$cookieName, $name); + $this->assertEquals($this->authentication->setCookieName($name), $name); + $this->assertEquals($this->authentication->getCookieName(), $name); } public function testEncodeDecodeSession(): void From 8d66536e7fc11c940577946a99ba0ea8e2c28b16 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:34:57 -0400 Subject: [PATCH 130/195] fix: Adjusting --- src/Appwrite/Auth/Validator/MockNumber.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Auth/Validator/MockNumber.php b/src/Appwrite/Auth/Validator/MockNumber.php index 2c1c81f863..0a8db26ab2 100644 --- a/src/Appwrite/Auth/Validator/MockNumber.php +++ b/src/Appwrite/Auth/Validator/MockNumber.php @@ -2,8 +2,7 @@ namespace Appwrite\Auth\Validator; -use Utopia\Validator; -use Utopia\Validator\Text; +use Utopia\Http\Validator; /** * MockNumber. @@ -46,7 +45,7 @@ class MockNumber extends Validator return false; } - $otp = new Text(6, 6); + $otp = new Validator\Text(6, 6); if (!$otp->isValid($value['otp'])) { $this->message = 'OTP must be a valid string and exactly 6 characters.'; return false; From 023e2220291ffe1f427ed8057ea49d930bfd94c6 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:48:38 -0400 Subject: [PATCH 131/195] fix: Adjusting --- app/init2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/init2.php b/app/init2.php index d7e27db9ac..44ca9c2ba2 100644 --- a/app/init2.php +++ b/app/init2.php @@ -590,7 +590,7 @@ $user if ($project->isEmpty()) { $user = new Document([]); } else { - if ($project->getId() === 'console') { + if ($project->getId() === 'console') { // $user = $dbForConsole->getDocument('users', $authentication->getUnique()); } else { $user = $dbForProject->getDocument('users', $authentication->getUnique()); From bbdd886a6c852c706b43b3f6807c8ab13183f2be Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:48:44 -0400 Subject: [PATCH 132/195] fix: Adjusting --- app/init2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/init2.php b/app/init2.php index 44ca9c2ba2..d7e27db9ac 100644 --- a/app/init2.php +++ b/app/init2.php @@ -590,7 +590,7 @@ $user if ($project->isEmpty()) { $user = new Document([]); } else { - if ($project->getId() === 'console') { // + if ($project->getId() === 'console') { $user = $dbForConsole->getDocument('users', $authentication->getUnique()); } else { $user = $dbForProject->getDocument('users', $authentication->getUnique()); From c15d362865c2a7cef90d265c8b0cf016adadb5f8 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:50:15 -0400 Subject: [PATCH 133/195] chore: Updating composer --- composer.lock | 781 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 532 insertions(+), 249 deletions(-) diff --git a/composer.lock b/composer.lock index 68f109ed72..d512000fd3 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": "fc07cbc344782534962fd7bf0769f7b9", + "content-hash": "a96663831d99bf0afd34ac18a3099c1d", "packages": [ { "name": "adhocore/jwt", @@ -211,16 +211,16 @@ }, { "name": "beberlei/assert", - "version": "v3.3.2", + "version": "v3.x-dev", "source": { "type": "git", "url": "https://github.com/beberlei/assert.git", - "reference": "cb70015c04be1baee6f5f5c953703347c0ac1655" + "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/beberlei/assert/zipball/cb70015c04be1baee6f5f5c953703347c0ac1655", - "reference": "cb70015c04be1baee6f5f5c953703347c0ac1655", + "url": "https://api.github.com/repos/beberlei/assert/zipball/d63a6943fc4fd1a2aedb65994e3548715105abcf", + "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf", "shasum": "" }, "require": { @@ -228,13 +228,12 @@ "ext-json": "*", "ext-mbstring": "*", "ext-simplexml": "*", - "php": "^7.0 || ^8.0" + "php": "^7" }, "require-dev": { "friendsofphp/php-cs-fixer": "*", - "phpstan/phpstan": "*", - "phpunit/phpunit": ">=6.0.0", - "yoast/phpunit-polyfills": "^0.1.0" + "phpstan/phpstan-shim": "*", + "phpunit/phpunit": ">=6.0.0 <8" }, "suggest": { "ext-intl": "Needed to allow Assertion::count(), Assertion::isCountable(), Assertion::minCount(), and Assertion::maxCount() to operate on ResourceBundles" @@ -272,9 +271,9 @@ ], "support": { "issues": "https://github.com/beberlei/assert/issues", - "source": "https://github.com/beberlei/assert/tree/v3.3.2" + "source": "https://github.com/beberlei/assert/tree/v3" }, - "time": "2021-12-16T21:41:27+00:00" + "time": "2019-12-19T17:51:41+00:00" }, { "name": "chillerlan/php-qrcode", @@ -567,16 +566,16 @@ }, { "name": "jean85/pretty-package-versions", - "version": "2.0.6", + "version": "2.x-dev", "source": { "type": "git", "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" + "reference": "4ea62fbb39a29d65ef6cda413158baa7f1d98550" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4ea62fbb39a29d65ef6cda413158baa7f1d98550", + "reference": "4ea62fbb39a29d65ef6cda413158baa7f1d98550", "shasum": "" }, "require": { @@ -588,8 +587,9 @@ "jean85/composer-provided-replaced-stub-package": "^1.0", "phpstan/phpstan": "^1.4", "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" + "vimeo/psalm": "^4.3 || ^5.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -620,9 +620,9 @@ ], "support": { "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.x" }, - "time": "2024-03-08T09:58:59+00:00" + "time": "2024-04-08T08:58:14+00:00" }, { "name": "league/csv", @@ -907,7 +907,7 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.7.0", + "version": "v2.x-dev", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", @@ -1055,7 +1055,7 @@ }, { "name": "spomky-labs/otphp", - "version": "v10.0.3", + "version": "v10.0.x-dev", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/otphp.git", @@ -1084,6 +1084,7 @@ "phpunit/phpunit": "^8.0", "thecodingmachine/phpstan-safe-rule": "^1.0 || ^2.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -1130,16 +1131,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "8740a072b86292957feb42703edde77fcfca84fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8740a072b86292957feb42703edde77fcfca84fb", + "reference": "8740a072b86292957feb42703edde77fcfca84fb", "shasum": "" }, "require": { @@ -1151,6 +1152,7 @@ "suggest": { "ext-mbstring": "For best performance" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -1190,7 +1192,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/1.x" }, "funding": [ { @@ -1206,11 +1208,11 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-06-20T08:18:00+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -1225,6 +1227,7 @@ "require": { "php": ">=7.1" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -1429,23 +1432,23 @@ }, { "name": "utopia-php/abuse", - "version": "0.38.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "b7be9086c9d9b4561d810cbd42fdda798742f56c" + "reference": "a2292d71da901ea13129d56f89876626ba92adf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b7be9086c9d9b4561d810cbd42fdda798742f56c", - "reference": "b7be9086c9d9b4561d810cbd42fdda798742f56c", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/a2292d71da901ea13129d56f89876626ba92adf0", + "reference": "a2292d71da901ea13129d56f89876626ba92adf0", "shasum": "" }, "require": { "ext-curl": "*", "ext-pdo": "*", "php": ">=8.0", - "utopia-php/database": "0.50.*" + "utopia-php/database": "dev-feat-framework-v2 as 0.49.99" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1472,27 +1475,27 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.38.0" + "source": "https://github.com/utopia-php/abuse/tree/feat-framework-v2" }, - "time": "2024-06-24T00:52:02+00:00" + "time": "2024-04-18T17:04:17+00:00" }, { "name": "utopia-php/analytics", - "version": "0.10.2", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + "reference": "5d59c2e50381a25adecbca979ed5a7c81307442f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/5d59c2e50381a25adecbca979ed5a7c81307442f", + "reference": "5d59c2e50381a25adecbca979ed5a7c81307442f", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "^0.15.0" + "utopia-php/cli": "0.17.*" }, "require-dev": { "laravel/pint": "dev-main", @@ -1518,27 +1521,27 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + "source": "https://github.com/utopia-php/analytics/tree/feat-framework-v2" }, - "time": "2023-03-22T12:01:09+00:00" + "time": "2024-03-07T15:54:19+00:00" }, { "name": "utopia-php/audit", - "version": "0.40.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "735ae211ce5fee5b52b736731571b4030b1d7cdc" + "reference": "49c2a113277bfa0d7d1774c150de2d2fa07349e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/735ae211ce5fee5b52b736731571b4030b1d7cdc", - "reference": "735ae211ce5fee5b52b736731571b4030b1d7cdc", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/49c2a113277bfa0d7d1774c150de2d2fa07349e7", + "reference": "49c2a113277bfa0d7d1774c150de2d2fa07349e7", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.50.*" + "utopia-php/database": "dev-feat-framework-v2 as 0.49.99" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1565,9 +1568,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.40.0" + "source": "https://github.com/utopia-php/audit/tree/feat-framework-v2" }, - "time": "2024-06-24T00:52:17+00:00" + "time": "2024-04-18T17:02:14+00:00" }, { "name": "utopia-php/cache", @@ -1621,27 +1624,29 @@ }, { "name": "utopia-php/cli", - "version": "0.15.0", + "version": "dev-dev-coroutines", "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + "reference": "d48b696891dee1e46df2491d192bb91cf4df8f94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/d48b696891dee1e46df2491d192bb91cf4df8f94", + "reference": "d48b696891dee1e46df2491d192bb91cf4df8f94", "shasum": "" }, "require": { "php": ">=7.4", - "utopia-php/framework": "0.*.*" + "utopia-php/di": "dev-feat-framework-v2", + "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99" }, "require-dev": { "laravel/pint": "1.2.*", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.3", "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" + "swoole/ide-helper": "4.8.8" }, "type": "library", "autoload": { @@ -1664,9 +1669,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" + "source": "https://github.com/utopia-php/cli/tree/dev-coroutines" }, - "time": "2023-03-01T05:55:14+00:00" + "time": "2024-06-24T13:24:20+00:00" }, { "name": "utopia-php/config", @@ -1721,16 +1726,16 @@ }, { "name": "utopia-php/database", - "version": "0.50.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "ce3eaccb2f3bbd34b2b97419836fec633b26b8f7" + "reference": "5b0d6ba40141dd9d4983d3aac018782f8ecdfc8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/ce3eaccb2f3bbd34b2b97419836fec633b26b8f7", - "reference": "ce3eaccb2f3bbd34b2b97419836fec633b26b8f7", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5b0d6ba40141dd9d4983d3aac018782f8ecdfc8a", + "reference": "5b0d6ba40141dd9d4983d3aac018782f8ecdfc8a", "shasum": "" }, "require": { @@ -1738,7 +1743,7 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.10.*", - "utopia-php/framework": "0.33.*", + "utopia-php/framework": "0.34.*", "utopia-php/mongo": "0.3.*" }, "require-dev": { @@ -1749,7 +1754,7 @@ "phpunit/phpunit": "^9.4", "rregeer/phpunit-coverage-check": "^0.3.1", "swoole/ide-helper": "4.8.0", - "utopia-php/cli": "^0.14.0" + "utopia-php/cli": "0.17.*" }, "type": "library", "autoload": { @@ -1771,27 +1776,88 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.50.0" + "source": "https://github.com/utopia-php/database/tree/feat-framework-v2" }, - "time": "2024-06-21T03:21:42+00:00" + "time": "2024-06-06T00:07:47+00:00" }, { - "name": "utopia-php/domains", - "version": "0.5.0", + "name": "utopia-php/di", + "version": "dev-feat-framework-v2", "source": { "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c" + "url": "https://github.com/utopia-php/di.git", + "reference": "1fb7da5ead16de5d795ef10c1e22e39726eb6943" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/bf07f60326f8389f378ddf6fcde86217e5cfe18c", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c", + "url": "https://api.github.com/repos/utopia-php/di/zipball/1fb7da5ead16de5d795ef10c1e22e39726eb6943", + "reference": "1fb7da5ead16de5d795ef10c1e22e39726eb6943", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/", + "Tests\\E2E\\": "tests/e2e" + } + }, + "scripts": { + "lint": [ + "vendor/bin/pint --test" + ], + "format": [ + "vendor/bin/pint" + ], + "check": [ + "vendor/bin/phpstan analyse -c phpstan.neon" + ], + "test": [ + "vendor/bin/phpunit --configuration phpunit.xml" + ] + }, + "license": [ + "MIT" + ], + "description": "A simple and lite library for managing dependency injections", + "keywords": [ + "framework", + "http", + "php", + "upf" + ], + "support": { + "source": "https://github.com/utopia-php/di/tree/feat-framework-v2", + "issues": "https://github.com/utopia-php/di/issues" + }, + "time": "2024-06-11T16:03:00+00:00" + }, + { + "name": "utopia-php/domains", + "version": "dev-feat-framework-v2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "4e7055f0aaba0c16ae60c972faefb9189fa0db1c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/4e7055f0aaba0c16ae60c972faefb9189fa0db1c", + "reference": "4e7055f0aaba0c16ae60c972faefb9189fa0db1c", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/framework": "0.*.*" + "utopia-php/framework": "0.34.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -1831,9 +1897,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.5.0" + "source": "https://github.com/utopia-php/domains/tree/feat-framework-v2" }, - "time": "2024-01-03T22:04:27+00:00" + "time": "2024-03-08T09:24:35+00:00" }, { "name": "utopia-php/dsn", @@ -1923,26 +1989,30 @@ }, { "name": "utopia-php/framework", - "version": "0.33.6", + "version": "dev-feat-di-upgrade", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "8fe57da0cecd57e3b17cd395b4a666a24f4c07a6" + "reference": "d72069f810521fd90a378791adc7e1cf8fc9a378" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/8fe57da0cecd57e3b17cd395b4a666a24f4c07a6", - "reference": "8fe57da0cecd57e3b17cd395b4a666a24f4c07a6", + "url": "https://api.github.com/repos/utopia-php/http/zipball/d72069f810521fd90a378791adc7e1cf8fc9a378", + "reference": "d72069f810521fd90a378791adc7e1cf8fc9a378", "shasum": "" }, "require": { - "php": ">=8.0" + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/servers": "dev-dev" }, "require-dev": { + "ext-xdebug": "*", "laravel/pint": "^1.2", "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" }, "type": "library", "autoload": { @@ -1954,17 +2024,18 @@ "license": [ "MIT" ], - "description": "A simple, light and advanced PHP framework", + "description": "A simple, light and advanced PHP HTTP framework", "keywords": [ "framework", + "http", "php", "upf" ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.6" + "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-03-21T18:10:57+00:00" + "time": "2024-07-04T15:01:24+00:00" }, { "name": "utopia-php/image", @@ -2279,21 +2350,21 @@ }, { "name": "utopia-php/orchestration", - "version": "0.9.1", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + "reference": "ede2b7a60bd1630ef8bd7fdbbc3cf73275fc9f28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/ede2b7a60bd1630ef8bd7fdbbc3cf73275fc9f28", + "reference": "ede2b7a60bd1630ef8bd7fdbbc3cf73275fc9f28", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*" + "utopia-php/cli": "0.17.*" }, "require-dev": { "laravel/pint": "^1.2", @@ -2323,31 +2394,31 @@ ], "support": { "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + "source": "https://github.com/utopia-php/orchestration/tree/feat-framework-v2" }, - "time": "2023-03-17T15:05:06+00:00" + "time": "2024-03-07T15:56:18+00:00" }, { "name": "utopia-php/platform", - "version": "0.7.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52" + "reference": "f40b23bf84f8fd95bc954ef9297663c01fed5c4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/f40b23bf84f8fd95bc954ef9297663c01fed5c4c", + "reference": "f40b23bf84f8fd95bc954ef9297663c01fed5c4c", "shasum": "" }, "require": { "ext-json": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.33.*", - "utopia-php/queue": "0.7.*" + "utopia-php/cli": "dev-dev-coroutines as 0.17.99", + "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99", + "utopia-php/queue": "dev-feat-coroutine-and-di as 0.7.99" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2373,9 +2444,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.7.0" + "source": "https://github.com/utopia-php/platform/tree/feat-framework-v2" }, - "time": "2024-05-08T17:00:55+00:00" + "time": "2024-06-21T19:25:15+00:00" }, { "name": "utopia-php/pools", @@ -2483,22 +2554,23 @@ }, { "name": "utopia-php/queue", - "version": "0.7.0", + "version": "dev-feat-coroutine-and-di", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "917565256eb94bcab7246f7a746b1a486813761b" + "reference": "41b76eacae43bb3e46824ffc8e9aa14536771b88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/917565256eb94bcab7246f7a746b1a486813761b", - "reference": "917565256eb94bcab7246f7a746b1a486813761b", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/41b76eacae43bb3e46824ffc8e9aa14536771b88", + "reference": "41b76eacae43bb3e46824ffc8e9aa14536771b88", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" + "utopia-php/cli": "0.17.*", + "utopia-php/di": "dev-feat-framework-v2", + "utopia-php/servers": "dev-dev" }, "require-dev": { "laravel/pint": "^0.2.3", @@ -2508,6 +2580,7 @@ "workerman/workerman": "^4.0" }, "suggest": { + "ext-redis": "Needed to support Redis connections", "ext-swoole": "Needed to support Swoole.", "workerman/workerman": "Needed to support Workerman." }, @@ -2538,9 +2611,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.7.0" + "source": "https://github.com/utopia-php/queue/tree/feat-coroutine-and-di" }, - "time": "2024-01-17T19:00:43+00:00" + "time": "2024-07-04T18:02:23+00:00" }, { "name": "utopia-php/registry", @@ -2595,17 +2668,88 @@ "time": "2021-03-10T10:45:22+00:00" }, { - "name": "utopia-php/storage", - "version": "0.18.4", + "name": "utopia-php/servers", + "version": "dev-dev", "source": { "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "94ab8758fabcefee5c5fa723616e45719833f922" + "url": "https://github.com/utopia-php/servers.git", + "reference": "4565c1c111f6da6b18bc0f00f350377a1e691e48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/94ab8758fabcefee5c5fa723616e45719833f922", - "reference": "94ab8758fabcefee5c5fa723616e45719833f922", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/4565c1c111f6da6b18bc0f00f350377a1e691e48", + "reference": "4565c1c111f6da6b18bc0f00f350377a1e691e48", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/di": "dev-feat-framework-v2" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Servers\\": "src/Servers" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\E2E\\": "tests/Servers/Unit" + } + }, + "scripts": { + "test": [ + "phpunit" + ], + "analyse": [ + "vendor/bin/phpstan analyse" + ], + "format": [ + "vendor/bin/pint" + ], + "lint": [ + "vendor/bin/pint --test" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A base library for building Utopia style servers.", + "keywords": [ + "framework", + "php", + "servers", + "upf", + "utopia" + ], + "support": { + "source": "https://github.com/utopia-php/servers/tree/dev", + "issues": "https://github.com/utopia-php/servers/issues" + }, + "time": "2024-06-07T18:49:59+00:00" + }, + { + "name": "utopia-php/storage", + "version": "dev-feat-framework-v2-v2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "80eafa63cb86b33ce35d48c0189bae6ebdc02734" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/80eafa63cb86b33ce35d48c0189bae6ebdc02734", + "reference": "80eafa63cb86b33ce35d48c0189bae6ebdc02734", "shasum": "" }, "require": { @@ -2617,7 +2761,7 @@ "ext-zlib": "*", "ext-zstd": "*", "php": ">=8.0", - "utopia-php/framework": "0.*.*", + "utopia-php/framework": "0.34.*", "utopia-php/system": "0.*.*" }, "require-dev": { @@ -2645,60 +2789,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.4" + "source": "https://github.com/utopia-php/storage/tree/feat-framework-v2-v2" }, - "time": "2024-04-02T08:24:09+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.8.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.33.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "5.0.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.8.2" - }, - "time": "2024-02-01T14:54:12+00:00" + "time": "2024-03-08T09:39:46+00:00" }, { "name": "utopia-php/system", @@ -2805,6 +2898,49 @@ }, "time": "2024-06-26T09:44:52+00:00" }, + { + "name": "utopia-php/view", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/view.git", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/view/zipball/6ee55e83bc014c39ed6b69390f6d399116f65e88", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\View\\": "src/View" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP rendering engine", + "keywords": [ + "php", + "view" + ], + "support": { + "issues": "https://github.com/utopia-php/view/issues", + "source": "https://github.com/utopia-php/view/tree/0.2.0" + }, + "time": "2024-04-01T17:21:29+00:00" + }, { "name": "utopia-php/websocket", "version": "0.1.0", @@ -3041,7 +3177,7 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.3", + "version": "1.1.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", @@ -3068,6 +3204,7 @@ "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -3088,29 +3225,29 @@ }, { "name": "doctrine/instantiator", - "version": "1.5.0", + "version": "1.5.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "reference": "12be2483e1f0e850b353e26869e4e6c038459501" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/12be2483e1f0e850b353e26869e4e6c038459501", + "reference": "12be2483e1f0e850b353e26869e4e6c038459501", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", + "doctrine/coding-standard": "^9 || ^12", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.16 || ^1", "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", @@ -3138,7 +3275,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "source": "https://github.com/doctrine/instantiator/tree/1.5.x" }, "funding": [ { @@ -3154,7 +3291,7 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2023-12-09T14:16:53+00:00" }, { "name": "laravel/pint", @@ -3348,7 +3485,7 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", @@ -3373,6 +3510,7 @@ "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, + "default-branch": true, "type": "library", "autoload": { "files": [ @@ -3466,7 +3604,7 @@ }, { "name": "phar-io/manifest", - "version": "2.0.4", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", @@ -3486,6 +3624,7 @@ "phar-io/version": "^3.0.1", "php": "^7.2 || ^8.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3584,25 +3723,25 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "reference": "a0eeab580cbdf4414fef6978732510a36ed0a9d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/a0eeab580cbdf4414fef6978732510a36ed0a9d6", + "reference": "a0eeab580cbdf4414fef6978732510a36ed0a9d6", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-2.x": "2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -3631,22 +3770,22 @@ ], "support": { "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master" }, - "time": "2020-06-27T09:03:43+00:00" + "time": "2021-06-25T13:47:51+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.4.1", + "version": "5.x-dev", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + "reference": "aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39", + "reference": "aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39", "shasum": "" }, "require": { @@ -3667,6 +3806,7 @@ "phpunit/phpunit": "^9.5", "vimeo/psalm": "^5.13" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3695,29 +3835,29 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.x" }, - "time": "2024-05-21T05:55:05+00:00" + "time": "2024-05-21T06:14:15+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "reference": "eee054a3d40f09920f5b27c9b09a6483f88d9d24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/eee054a3d40f09920f5b27c9b09a6483f88d9d24", + "reference": "eee054a3d40f09920f5b27c9b09a6483f88d9d24", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "phpstan/phpdoc-parser": "^1.18" }, "require-dev": { "ext-tokenizer": "*", @@ -3729,6 +3869,7 @@ "rector/rector": "^0.13.9", "vimeo/psalm": "^4.25" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3753,22 +3894,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.x" }, - "time": "2024-02-23T11:10:43+00:00" + "time": "2024-05-24T14:24:30+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.19.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87" + "reference": "f9e07be0992e7bf1cad210829055b99318df142f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/67a759e7d8746d501c41536ba40cd9c0a07d6a87", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f9e07be0992e7bf1cad210829055b99318df142f", + "reference": "f9e07be0992e7bf1cad210829055b99318df142f", "shasum": "" }, "require": { @@ -3783,6 +3924,7 @@ "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3822,9 +3964,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.19.0" + "source": "https://github.com/phpspec/prophecy/tree/master" }, - "time": "2024-02-29T11:52:51+00:00" + "time": "2024-03-29T09:25:04+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -3874,17 +4016,76 @@ "time": "2024-05-31T08:52:43+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "9.2.31", + "name": "phpstan/phpstan", + "version": "1.8.x-dev", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + "url": "https://github.com/phpstan/phpstan.git", + "reference": "b20042710baa0d9c07636cc66d4c400f03f1477a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b20042710baa0d9c07636cc66d4c400f03f1477a", + "reference": "b20042710baa0d9c07636cc66d4c400f03f1477a", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.8.x" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-10-29T12:56:57+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "328a747f499cca790acff5634a4e55b957f40634" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/328a747f499cca790acff5634a4e55b957f40634", + "reference": "328a747f499cca790acff5634a4e55b957f40634", "shasum": "" }, "require": { @@ -3903,7 +4104,7 @@ "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -3912,7 +4113,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -3941,7 +4142,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2" }, "funding": [ { @@ -3949,20 +4150,20 @@ "type": "github" } ], - "time": "2024-03-02T06:37:42+00:00" + "time": "2024-07-17T05:19:21+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "3.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "38b24367e1b340aa78b96d7cab042942d917bb84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/38b24367e1b340aa78b96d7cab042942d917bb84", + "reference": "38b24367e1b340aa78b96d7cab042942d917bb84", "shasum": "" }, "require": { @@ -4001,7 +4202,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0" }, "funding": [ { @@ -4009,7 +4210,7 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2022-02-11T16:23:04+00:00" }, { "name": "phpunit/php-invoker", @@ -4297,7 +4498,7 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", @@ -4312,6 +4513,7 @@ "require": { "php": ">=8.0.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4347,7 +4549,7 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.2", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", @@ -4514,16 +4716,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "b247957a1c8dc81a671770f74b479c0a78a818f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b247957a1c8dc81a671770f74b479c0a78a818f1", + "reference": "b247957a1c8dc81a671770f74b479c0a78a818f1", "shasum": "" }, "require": { @@ -4576,7 +4778,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0" }, "funding": [ { @@ -4584,11 +4786,11 @@ "type": "github" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2022-09-14T12:46:14+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.3", + "version": "2.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", @@ -4645,7 +4847,7 @@ }, { "name": "sebastian/diff", - "version": "4.0.6", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", @@ -4711,7 +4913,7 @@ }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "5.1.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", @@ -4762,7 +4964,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1" }, "funding": [ { @@ -4774,7 +4976,7 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", @@ -4851,7 +5053,7 @@ }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", @@ -4915,7 +5117,7 @@ }, { "name": "sebastian/lines-of-code", - "version": "1.0.4", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", @@ -5084,7 +5286,7 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", @@ -5147,16 +5349,16 @@ }, { "name": "sebastian/resource-operations", - "version": "3.0.4", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + "reference": "ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25", + "reference": "ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25", "shasum": "" }, "require": { @@ -5165,6 +5367,7 @@ "require-dev": { "phpunit/phpunit": "^9.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -5189,7 +5392,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/main" }, "funding": [ { @@ -5197,11 +5400,11 @@ "type": "github" } ], - "time": "2024-03-14T16:00:52+00:00" + "time": "2024-03-14T18:47:08+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "3.2.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", @@ -5245,7 +5448,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "source": "https://github.com/sebastianbergmann/type/tree/3.2" }, "funding": [ { @@ -5257,7 +5460,7 @@ }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "3.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", @@ -5342,7 +5545,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -5363,6 +5566,7 @@ "suggest": { "ext-ctype": "For best performance" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -5591,9 +5795,88 @@ "time": "2023-11-21T18:54:41+00:00" } ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], + "aliases": [ + { + "package": "utopia-php/abuse", + "version": "dev-feat-framework-v2", + "alias": "0.37.99", + "alias_normalized": "0.37.99.0" + }, + { + "package": "utopia-php/analytics", + "version": "dev-feat-framework-v2", + "alias": "0.10.99", + "alias_normalized": "0.10.99.0" + }, + { + "package": "utopia-php/audit", + "version": "dev-feat-framework-v2", + "alias": "0.39.99", + "alias_normalized": "0.39.99.0" + }, + { + "package": "utopia-php/cli", + "version": "dev-dev-coroutines", + "alias": "0.17.99", + "alias_normalized": "0.17.99.0" + }, + { + "package": "utopia-php/database", + "version": "dev-feat-framework-v2", + "alias": "0.49.99", + "alias_normalized": "0.49.99.0" + }, + { + "package": "utopia-php/domains", + "version": "dev-feat-framework-v2", + "alias": "0.5.99", + "alias_normalized": "0.5.99.0" + }, + { + "package": "utopia-php/framework", + "version": "dev-feat-di-upgrade", + "alias": "0.34.99", + "alias_normalized": "0.34.99.0" + }, + { + "package": "utopia-php/orchestration", + "version": "dev-feat-framework-v2", + "alias": "0.9.99", + "alias_normalized": "0.9.99.0" + }, + { + "package": "utopia-php/platform", + "version": "dev-feat-framework-v2", + "alias": "0.5.99", + "alias_normalized": "0.5.99.0" + }, + { + "package": "utopia-php/queue", + "version": "dev-feat-coroutine-and-di", + "alias": "0.7.99", + "alias_normalized": "0.7.99.0" + }, + { + "package": "utopia-php/storage", + "version": "dev-feat-framework-v2-v2", + "alias": "0.18.99", + "alias_normalized": "0.18.99.0" + } + ], + "minimum-stability": "dev", + "stability-flags": { + "utopia-php/abuse": 20, + "utopia-php/analytics": 20, + "utopia-php/audit": 20, + "utopia-php/cli": 20, + "utopia-php/database": 20, + "utopia-php/domains": 20, + "utopia-php/framework": 20, + "utopia-php/orchestration": 20, + "utopia-php/platform": 20, + "utopia-php/queue": 20, + "utopia-php/storage": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From b245aab9d8da6930af3148115b32d627706f1ad6 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:59:25 -0400 Subject: [PATCH 134/195] fix: Adjusting to coroutine --- app/controllers/api/functions.php | 2 +- app/controllers/shared/api.php | 4 ++-- src/Appwrite/Platform/Tasks/ScheduleExecutions.php | 11 ++++++----- src/Appwrite/Platform/Tasks/ScheduleFunctions.php | 4 +--- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 1278d0572b..dfedcdb910 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1575,7 +1575,7 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId/build') ])); } - $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); $deleteBuild = $executor->deleteRuntime($project->getId(), $deploymentId . "-build"); $queueForEvents diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index adaac618d4..d72b63fec4 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -242,8 +242,8 @@ Http::init() $role = Auth::USER_ROLE_APPS; $scopes = \array_merge($roles[$role]['scopes'], $tokenScopes); - Authorization::setRole(Auth::USER_ROLE_APPS); - Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. + $authorization->addRole(Auth::USER_ROLE_APPS); + $authorization->setDefaultStatus(false); // Cancel security segmentation for API keys. } } elseif($keyType === API_KEY_STANDARD) { // No underline means no prefix. Backwards compatibility. diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index 2fdbd98da3..ab1a8798d7 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -4,7 +4,6 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Func; use Utopia\Database\Database; -use Utopia\Pools\Group; class ScheduleExecutions extends ScheduleBase { @@ -21,10 +20,12 @@ class ScheduleExecutions extends ScheduleBase return 'execution'; } - protected function enqueueResources(Group $pools, Database $dbForConsole): void + protected function enqueueResources(array $pools, Database $dbForConsole): void { - $queue = $pools->get('queue')->pop(); - $connection = $queue->getResource(); + $pool = $pools['pools-queue-main']['pool']; + $connection = $pool->get(); + $this->connections->add($connection, $pool); + $queueForFunctions = new Func($connection); foreach ($this->schedules as $schedule) { @@ -66,6 +67,6 @@ class ScheduleExecutions extends ScheduleBase unset($this->schedules[$schedule['resourceId']]); } - $queue->reclaim(); + $this->connections->reclaim(); } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 1e2f4a5614..050588903b 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -7,7 +7,6 @@ use Cron\CronExpression; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; -use Utopia\Pools\Group; use Utopia\Queue\Connection\Redis; class ScheduleFunctions extends ScheduleBase @@ -70,11 +69,10 @@ class ScheduleFunctions extends ScheduleBase \sleep($delay); // in seconds $pool = $pools['pools-queue-main']['pool']; - $dsn = $pools['pools-queue-main']['dsn']; $connection = $pool->get(); $this->connections->add($connection, $pool); - $queueConnection = new Redis($dsn->getHost(), $dsn->getPort()); + $queueConnection = new Redis($connection); foreach ($scheduleKeys as $scheduleKey) { // Ensure schedule was not deleted From 0bdf75a4a0901e467076af3b197ce4a6d0bffcf4 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 18 Jul 2024 09:11:04 -0400 Subject: [PATCH 135/195] chore: Update response models --- src/Appwrite/Utopia/Response/Models.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Appwrite/Utopia/Response/Models.php b/src/Appwrite/Utopia/Response/Models.php index 5970265005..cdab9d48a7 100644 --- a/src/Appwrite/Utopia/Response/Models.php +++ b/src/Appwrite/Utopia/Response/Models.php @@ -71,6 +71,7 @@ use Appwrite\Utopia\Response\Model\Migration; use Appwrite\Utopia\Response\Model\MigrationFirebaseProject; use Appwrite\Utopia\Response\Model\MigrationReport; use Appwrite\Utopia\Response\Model\Mock; +use Appwrite\Utopia\Response\Model\MockNumber; use Appwrite\Utopia\Response\Model\None; use Appwrite\Utopia\Response\Model\Phone; use Appwrite\Utopia\Response\Model\Platform; @@ -99,6 +100,7 @@ use Appwrite\Utopia\Response\Model\UsageStorage; use Appwrite\Utopia\Response\Model\UsageUsers; use Appwrite\Utopia\Response\Model\User; use Appwrite\Utopia\Response\Model\Variable; +use Appwrite\Utopia\Response\Model\VcsContent; use Appwrite\Utopia\Response\Model\Webhook; use Exception; @@ -154,6 +156,7 @@ class Models self::setModel(new BaseList('Target list', Response::MODEL_TARGET_LIST, 'targets', Response::MODEL_TARGET)); self::setModel(new BaseList('Migrations List', Response::MODEL_MIGRATION_LIST, 'migrations', Response::MODEL_MIGRATION)); self::setModel(new BaseList('Migrations Firebase Projects List', Response::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', Response::MODEL_MIGRATION_FIREBASE_PROJECT)); + self::setModel(new BaseList('VCS Content List', Response::MODEL_VCS_CONTENT_LIST, 'contents', Response::MODEL_VCS_CONTENT)); // Entities self::setModel(new Database()); self::setModel(new Collection()); @@ -196,6 +199,7 @@ class Models self::setModel(new Installation()); self::setModel(new ProviderRepository()); self::setModel(new Detection()); + self::setModel(new VcsContent()); self::setModel(new Branch()); self::setModel(new Runtime()); self::setModel(new Deployment()); @@ -204,6 +208,7 @@ class Models self::setModel(new Project()); self::setModel(new Webhook()); self::setModel(new Key()); + self::setModel(new MockNumber()); self::setModel(new AuthProvider()); self::setModel(new Platform()); self::setModel(new Variable()); From 850c17dde1ef474a3963c1bd6813af24abd7f53b Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 18 Jul 2024 11:51:09 -0400 Subject: [PATCH 136/195] tests: Ensure stability --- app/init2.php | 1 - app/realtime.php | 3 +- composer.json | 2 +- composer.lock | 29 +++++++----------- docker-compose.local.prod.yml | 30 ++++++++++++++++++- src/Appwrite/Platform/Tasks/ScheduleBase.php | 16 +++++----- .../Platform/Tasks/ScheduleExecutions.php | 3 +- src/Appwrite/Utopia/Request.php | 10 +++++++ 8 files changed, 63 insertions(+), 31 deletions(-) diff --git a/app/init2.php b/app/init2.php index d7e27db9ac..55ee5c01ae 100644 --- a/app/init2.php +++ b/app/init2.php @@ -619,7 +619,6 @@ $user 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) { diff --git a/app/realtime.php b/app/realtime.php index 12c26c1e3d..3cc62103fa 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -479,7 +479,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::error('[Error] Message: ' . $response['data']['message']); } } finally { - // TODO NOW $global->get('pools')->reclaim(); + $connections = $container->get('connections'); + $connections->reclaim(); } }); diff --git a/composer.json b/composer.json index d3a92a864b..3206d356c5 100644 --- a/composer.json +++ b/composer.json @@ -72,7 +72,7 @@ "utopia-php/storage": "dev-feat-framework-v2-v2 as 0.18.99", "utopia-php/system": "0.8.*", "utopia-php/vcs": "0.7.*", - "utopia-php/websocket": "0.1.*", + "utopia-php/websocket": "0.2.*", "matomo/device-detector": "6.1.*", "dragonmantank/cron-expression": "3.3.2", "phpmailer/phpmailer": "6.9.1", diff --git a/composer.lock b/composer.lock index d512000fd3..341cdf125b 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": "a96663831d99bf0afd34ac18a3099c1d", + "content-hash": "09274537fc52a7a226561f91542346e3", "packages": [ { "name": "adhocore/jwt", @@ -2943,26 +2943,27 @@ }, { "name": "utopia-php/websocket", - "version": "0.1.0", + "version": "0.2.0", "source": { "type": "git", "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + "reference": "e9d0919b321744a61f12563f5791c47ba9f57810" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/e9d0919b321744a61f12563f5791c47ba9f57810", + "reference": "e9d0919b321744a61f12563f5791c47ba9f57810", "shasum": "" }, "require": { "php": ">=8.0" }, "require-dev": { + "laravel/pint": "^1.15", + "phpstan/phpstan": "^1.8", "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", + "swoole/ide-helper": "5.1.2", "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", "workerman/workerman": "^4.0" }, "type": "library", @@ -2975,16 +2976,6 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], "description": "A simple abstraction for WebSocket servers.", "keywords": [ "framework", @@ -2995,9 +2986,9 @@ ], "support": { "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + "source": "https://github.com/utopia-php/websocket/tree/0.2.0" }, - "time": "2021-12-20T10:50:09+00:00" + "time": "2024-04-09T08:28:11+00:00" }, { "name": "webmozart/assert", diff --git a/docker-compose.local.prod.yml b/docker-compose.local.prod.yml index d4a87226e1..75f3bcbd60 100644 --- a/docker-compose.local.prod.yml +++ b/docker-compose.local.prod.yml @@ -804,6 +804,34 @@ services: - _APP_DB_USER - _APP_DB_PASS + appwrite-task-scheduler-executions: + entrypoint: schedule-executions + <<: *x-logging + container_name: appwrite-task-scheduler-executions + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./vendor:/usr/src/code/vendor + - ./src:/usr/src/code/src + depends_on: + - mariadb + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + appwrite-task-scheduler-messages: entrypoint: schedule-messages <<: *x-logging @@ -848,7 +876,7 @@ services: hostname: exc1 <<: *x-logging stop_signal: SIGINT - image: openruntimes/executor:0.5.6 + image: openruntimes/executor:0.6.1 restart: unless-stopped networks: - appwrite diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index 4fc68ea79e..2fbd26d4fd 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -22,6 +22,7 @@ abstract class ScheduleBase extends Action protected Connections $connections; abstract public static function getName(): string; + abstract public static function getSupportedResource(): string; abstract protected function enqueueResources( @@ -34,12 +35,14 @@ abstract class ScheduleBase extends Action $this->connections = new Connections(); $type = static::getSupportedResource(); - $this - ->desc("Execute {$type}s scheduled in Appwrite") - ->inject('pools') - ->inject('dbForConsole') - ->inject('getProjectDB') - ->callback(fn (array $pools, Database $dbForConsole, callable $getProjectDB) => $this->action($pools, $dbForConsole, $getProjectDB)); + go(function () use ($type) { + $this + ->desc("Execute {$type}s scheduled in Appwrite") + ->inject('pools') + ->inject('dbForConsole') + ->inject('getProjectDB') + ->callback(fn (array $pools, Database $dbForConsole, callable $getProjectDB) => $this->action($pools, $dbForConsole, $getProjectDB)); + }); } /** @@ -191,6 +194,5 @@ abstract class ScheduleBase extends Action ); $this->enqueueResources($pools, $dbForConsole); - } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index ab1a8798d7..ed92dbd61d 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -4,6 +4,7 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Func; use Utopia\Database\Database; +use Utopia\Queue\Connection\Redis; class ScheduleExecutions extends ScheduleBase { @@ -26,7 +27,7 @@ class ScheduleExecutions extends ScheduleBase $connection = $pool->get(); $this->connections->add($connection, $pool); - $queueForFunctions = new Func($connection); + $queueForFunctions = new Func(new Redis($connection)); foreach ($this->schedules as $schedule) { if (!$schedule['active']) { diff --git a/src/Appwrite/Utopia/Request.php b/src/Appwrite/Utopia/Request.php index c974243608..bc8881e121 100644 --- a/src/Appwrite/Utopia/Request.php +++ b/src/Appwrite/Utopia/Request.php @@ -115,6 +115,16 @@ class Request extends HttpRequest return self::$route !== null; } + + public function removeHeader(string $key): static + { + if (isset($this->headers[$key])) { + unset($this->headers[$key]); + } + + return parent::removeHeader($key); + } + /** * Get headers * From 5ad0214a1e0058cb3c30998861d0bcc1b089c832 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 18 Jul 2024 12:16:24 -0400 Subject: [PATCH 137/195] chore: 1.6 -> current --- app/http.php | 22 +++++----------------- docker-compose.local.prod.yml | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/app/http.php b/app/http.php index b167c553c4..853b918f33 100644 --- a/app/http.php +++ b/app/http.php @@ -6,6 +6,7 @@ require_once __DIR__ . '/controllers/general.php'; use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; +use Swoole\Constant; use Utopia\Abuse\Adapters\TimeLimit; use Utopia\Audit\Audit; use Utopia\Cache\Cache; @@ -45,24 +46,11 @@ $server = new Server('0.0.0.0', '80', [ 'send_yield' => true, 'tcp_fastopen' => true, ]); +$http = new Http($server, $container, 'UTC'); +$http->setRequestClass(Request::class); +$http->setResponseClass(Response::class); -$server->on(Constant::EVENT_WORKER_START, function ($server, $workerId) { - Console::success('Worker ' . ++$workerId . ' started successfully'); -}); - -$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) { - Console::success('Starting reload...'); -}); - -$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) { - Console::success('Reload completed...'); -}); - -include __DIR__ . '/controllers/general.php'; - -global $global, $container; - -http::onStart() +Http::onStart() ->inject('authorization') ->inject('cache') ->inject('pools') diff --git a/docker-compose.local.prod.yml b/docker-compose.local.prod.yml index 75f3bcbd60..055c20c862 100644 --- a/docker-compose.local.prod.yml +++ b/docker-compose.local.prod.yml @@ -186,6 +186,27 @@ services: - _APP_CONSOLE_COUNTRIES_DENYLIST - _APP_EXPERIMENT_LOGGING_PROVIDER - _APP_EXPERIMENT_LOGGING_CONFIG + appwrite-console: + <<: *x-logging + container_name: appwrite-console + image: appwrite/console:5.0.0-rc.5 + restart: unless-stopped + networks: + - appwrite + labels: + - "traefik.enable=true" + - "traefik.constraint-label-stack=appwrite" + - "traefik.docker.network=appwrite" + - "traefik.http.services.appwrite_console.loadbalancer.server.port=80" + #ws + - traefik.http.routers.appwrite_console_http.entrypoints=appwrite_web + - traefik.http.routers.appwrite_console_http.rule=PathPrefix(`/console`) + - traefik.http.routers.appwrite_console_http.service=appwrite_console + # wss + - traefik.http.routers.appwrite_console_https.entrypoints=appwrite_websecure + - traefik.http.routers.appwrite_console_https.rule=PathPrefix(`/console`) + - traefik.http.routers.appwrite_console_https.service=appwrite_console + - traefik.http.routers.appwrite_console_https.tls=true appwrite-realtime: entrypoint: realtime From b9596b780b844eec54727b60f6803fd5251a8991 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 18 Jul 2024 12:18:43 -0400 Subject: [PATCH 138/195] chore: lint --- app/http.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/http.php b/app/http.php index 853b918f33..3d873067a8 100644 --- a/app/http.php +++ b/app/http.php @@ -6,7 +6,6 @@ require_once __DIR__ . '/controllers/general.php'; use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Swoole\Constant; use Utopia\Abuse\Adapters\TimeLimit; use Utopia\Audit\Audit; use Utopia\Cache\Cache; From f4e2736475dcc939cc73a0aa03580ba7707e2587 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 18 Jul 2024 13:53:31 -0400 Subject: [PATCH 139/195] fix: Adding restart to the scheduler --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index c1b57ee17f..63d53658a8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -811,6 +811,7 @@ services: entrypoint: schedule-executions <<: *x-logging container_name: appwrite-task-scheduler-executions + restart: unless-stopped image: appwrite-dev networks: - appwrite From 87e0f2539c357d2d4141a81ba119579a0367ee60 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:31:24 -0400 Subject: [PATCH 140/195] fix: tests --- app/controllers/general.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index b5162febc1..57cb4668df 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1001,15 +1001,12 @@ Http::get('/.well-known/acme-challenge/*') $response->text($content); }); -//include_once __DIR__ . '/shared/api.php'; -//include_once __DIR__ . '/shared/api/auth.php'; - -// Http::wildcard() -// ->groups(['api']) -// ->label('scope', 'global') -// ->action(function () { -// throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND); -// }); +Http::wildcard() + ->groups(['api']) + ->label('scope', 'global') + ->action(function () { + throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND); + }); foreach (Config::getParam('services', []) as $service) { //include_once $service['controller']; From cfdf5ff5cb6146f06be1c3edb23922db0823cfa0 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:00:06 -0400 Subject: [PATCH 141/195] chore: Merge --- app/init.php | 35 +++++++++++++++++------------------ app/init/constants.php | 2 +- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/app/init.php b/app/init.php index 9fca6503c9..1f448a756a 100644 --- a/app/init.php +++ b/app/init.php @@ -1,6 +1,5 @@ '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', ''), -// ]); +// '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', ''), -// ]); +// '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' => [ @@ -1143,7 +1142,7 @@ // if ( // isset($node['type']) && // ($node['type'] === Origin::CLIENT_TYPE_WEB || -// $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && +// $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && // !empty($node['hostname']) // ) { // $clients[] = $node['hostname']; @@ -1732,4 +1731,4 @@ //}, ['request']); //App::setResource('plan', function (array $plan = []) { // return []; -//});*/ +//}); diff --git a/app/init/constants.php b/app/init/constants.php index 8c53bd1aa4..993f8b51ac 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -29,7 +29,7 @@ const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 331; -const APP_VERSION_STABLE = '1.5.0'; +const APP_VERSION_STABLE = '1.6.0'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; From 05862e8bc0a52231174bd105ea17dca5c3ebf8b0 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 19 Jul 2024 10:02:32 -0400 Subject: [PATCH 142/195] chore: lint --- app/init.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/init.php b/app/init.php index 1f448a756a..02ccac33b1 100644 --- a/app/init.php +++ b/app/init.php @@ -1,4 +1,5 @@ Date: Fri, 19 Jul 2024 13:49:10 -0400 Subject: [PATCH 143/195] feat: Supporting old project database name --- app/init2.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/app/init2.php b/app/init2.php index 55ee5c01ae..ef4bb7fe25 100644 --- a/app/init2.php +++ b/app/init2.php @@ -99,6 +99,11 @@ if (!Http::isProduction()) { PublicDomain::allow(['request-catcher']); } +function backwardDBName($db): string +{ + return str_replace('database_db_main', 'db_main', $db); +} + function getDevice($root): Device { $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); @@ -203,7 +208,7 @@ $global->set('logger', function () { $configChunks = \explode(";", $providerConfig); $providerConfig = match ($providerName) { - 'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',], + 'sentry' => ['key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',], 'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''], default => ['key' => $providerConfig], }; @@ -758,10 +763,10 @@ $dbForProject } try { - $dsn = new DSN($project->getAttribute('database')); + $dsn = new DSN(backwardDBName($project->getAttribute('database'))); } catch (\InvalidArgumentException) { // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); + $dsn = new DSN('mysql://' . backwardDBName($project->getAttribute('database'))); } $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; @@ -780,10 +785,10 @@ $dbForProject $database = new Database($adapter, $cache); try { - $dsn = new DSN($project->getAttribute('database')); + $dsn = new DSN(backwardDBName($project->getAttribute('database'))); } catch (\InvalidArgumentException) { // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); + $dsn = new DSN('mysql://' . backwardDBName($project->getAttribute('database'))); } if ($dsn->getHost() === DATABASE_SHARED_TABLES) { @@ -1154,10 +1159,10 @@ $getProjectDB } try { - $dsn = new DSN($project->getAttribute('database')); + $dsn = new DSN(backwardDBName($project->getAttribute('database'))); } catch (\InvalidArgumentException) { // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); + $dsn = new DSN('mysql://' . backwardDBName($project->getAttribute('database'))); } if (isset($databases[$dsn->getHost()])) { From 35910a6dbeb56038f1969cd0c6bf49bfeb6a312e Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 19 Jul 2024 13:50:34 -0400 Subject: [PATCH 144/195] chore: updating packages --- composer.json | 8 ++--- composer.lock | 87 ++++++++++++++++++++++++++------------------------- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/composer.json b/composer.json index 3206d356c5..c228f5a279 100644 --- a/composer.json +++ b/composer.json @@ -46,20 +46,20 @@ "ext-sockets": "*", "appwrite/php-runtimes": "0.14.*", "appwrite/php-clamav": "2.0.*", - "utopia-php/abuse": "dev-feat-framework-v2 as 0.37.99", + "utopia-php/abuse": "dev-feat-framework-v2 as 0.39.99", "utopia-php/analytics": "dev-feat-framework-v2 as 0.10.99", - "utopia-php/audit": "dev-feat-framework-v2 as 0.39.99", + "utopia-php/audit": "dev-feat-framework-v2 as 0.40.99", "utopia-php/cache": "0.10.*", "utopia-php/cli": "dev-dev-coroutines as 0.17.99", "utopia-php/config": "0.2.*", - "utopia-php/database": "dev-feat-framework-v2 as 0.49.99", + "utopia-php/database": "dev-feat-framework-v2 as 0.50.99", "utopia-php/domains": "dev-feat-framework-v2 as 0.5.99", "utopia-php/dsn": "0.2.*", "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99", "utopia-php/fetch": "0.2.*", "utopia-php/image": "0.6.*", "utopia-php/locale": "0.4.*", - "utopia-php/logger": "0.5.*", + "utopia-php/logger": "0.6.*", "utopia-php/messaging": "0.12.*", "utopia-php/migration": "0.4.*", "utopia-php/orchestration": "dev-feat-framework-v2 as 0.9.99", diff --git a/composer.lock b/composer.lock index 341cdf125b..1cfdcb9f50 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": "09274537fc52a7a226561f91542346e3", + "content-hash": "741c280f0f13f8618399aafd9c00d7fd", "packages": [ { "name": "adhocore/jwt", @@ -1436,22 +1436,24 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "a2292d71da901ea13129d56f89876626ba92adf0" + "reference": "b3105b361311d601069697c7691517d70f1bd32d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/a2292d71da901ea13129d56f89876626ba92adf0", - "reference": "a2292d71da901ea13129d56f89876626ba92adf0", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b3105b361311d601069697c7691517d70f1bd32d", + "reference": "b3105b361311d601069697c7691517d70f1bd32d", "shasum": "" }, "require": { "ext-curl": "*", "ext-pdo": "*", + "ext-redis": "*", "php": ">=8.0", "utopia-php/database": "dev-feat-framework-v2 as 0.49.99" }, "require-dev": { "laravel/pint": "1.5.*", + "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^9.4" }, @@ -1477,7 +1479,7 @@ "issues": "https://github.com/utopia-php/abuse/issues", "source": "https://github.com/utopia-php/abuse/tree/feat-framework-v2" }, - "time": "2024-04-18T17:04:17+00:00" + "time": "2024-07-19T16:13:52+00:00" }, { "name": "utopia-php/analytics", @@ -1531,17 +1533,17 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "49c2a113277bfa0d7d1774c150de2d2fa07349e7" + "reference": "ab618a689e99e4f278c71f133e5dda49425fb1f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/49c2a113277bfa0d7d1774c150de2d2fa07349e7", - "reference": "49c2a113277bfa0d7d1774c150de2d2fa07349e7", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/ab618a689e99e4f278c71f133e5dda49425fb1f0", + "reference": "ab618a689e99e4f278c71f133e5dda49425fb1f0", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "dev-feat-framework-v2 as 0.49.99" + "utopia-php/database": "dev-feat-framework-v2 as 0.50.99" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1570,7 +1572,7 @@ "issues": "https://github.com/utopia-php/audit/issues", "source": "https://github.com/utopia-php/audit/tree/feat-framework-v2" }, - "time": "2024-04-18T17:02:14+00:00" + "time": "2024-07-19T16:19:18+00:00" }, { "name": "utopia-php/cache", @@ -1730,12 +1732,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "5b0d6ba40141dd9d4983d3aac018782f8ecdfc8a" + "reference": "9ddf91aa4812a0eeb7feb9667b4827b407032df1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/5b0d6ba40141dd9d4983d3aac018782f8ecdfc8a", - "reference": "5b0d6ba40141dd9d4983d3aac018782f8ecdfc8a", + "url": "https://api.github.com/repos/utopia-php/database/zipball/9ddf91aa4812a0eeb7feb9667b4827b407032df1", + "reference": "9ddf91aa4812a0eeb7feb9667b4827b407032df1", "shasum": "" }, "require": { @@ -1778,7 +1780,7 @@ "issues": "https://github.com/utopia-php/database/issues", "source": "https://github.com/utopia-php/database/tree/feat-framework-v2" }, - "time": "2024-06-06T00:07:47+00:00" + "time": "2024-07-19T17:41:23+00:00" }, { "name": "utopia-php/di", @@ -1847,12 +1849,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "4e7055f0aaba0c16ae60c972faefb9189fa0db1c" + "reference": "58c0c8c262329b4f4a7195e63360fc5c4cd05fa9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/4e7055f0aaba0c16ae60c972faefb9189fa0db1c", - "reference": "4e7055f0aaba0c16ae60c972faefb9189fa0db1c", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/58c0c8c262329b4f4a7195e63360fc5c4cd05fa9", + "reference": "58c0c8c262329b4f4a7195e63360fc5c4cd05fa9", "shasum": "" }, "require": { @@ -1861,6 +1863,7 @@ }, "require-dev": { "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.9.x-dev", "phpunit/phpunit": "^9.3" }, "type": "library", @@ -1899,7 +1902,7 @@ "issues": "https://github.com/utopia-php/domains/issues", "source": "https://github.com/utopia-php/domains/tree/feat-framework-v2" }, - "time": "2024-03-08T09:24:35+00:00" + "time": "2024-07-19T16:48:25+00:00" }, { "name": "utopia-php/dsn", @@ -1993,12 +1996,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "d72069f810521fd90a378791adc7e1cf8fc9a378" + "reference": "c27a91d11b6d89a2603ed929a5739c7bb9779d18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/d72069f810521fd90a378791adc7e1cf8fc9a378", - "reference": "d72069f810521fd90a378791adc7e1cf8fc9a378", + "url": "https://api.github.com/repos/utopia-php/http/zipball/c27a91d11b6d89a2603ed929a5739c7bb9779d18", + "reference": "c27a91d11b6d89a2603ed929a5739c7bb9779d18", "shasum": "" }, "require": { @@ -2035,7 +2038,7 @@ "issues": "https://github.com/utopia-php/http/issues", "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-07-04T15:01:24+00:00" + "time": "2024-07-19T16:54:13+00:00" }, { "name": "utopia-php/image", @@ -2138,16 +2141,16 @@ }, { "name": "utopia-php/logger", - "version": "0.5.2", + "version": "0.6.0", "source": { "type": "git", "url": "https://github.com/utopia-php/logger.git", - "reference": "c6dfdb672e41364c309b0c30dc03bc6d45446dba" + "reference": "a2d1daeeb8f61fdec6d851950d9a021a3d05c9f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/c6dfdb672e41364c309b0c30dc03bc6d45446dba", - "reference": "c6dfdb672e41364c309b0c30dc03bc6d45446dba", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/a2d1daeeb8f61fdec6d851950d9a021a3d05c9f9", + "reference": "a2d1daeeb8f61fdec6d851950d9a021a3d05c9f9", "shasum": "" }, "require": { @@ -2186,9 +2189,9 @@ ], "support": { "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.5.2" + "source": "https://github.com/utopia-php/logger/tree/0.6.0" }, - "time": "2024-05-17T09:32:59+00:00" + "time": "2024-05-23T13:37:54+00:00" }, { "name": "utopia-php/messaging", @@ -2558,12 +2561,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "41b76eacae43bb3e46824ffc8e9aa14536771b88" + "reference": "d59c8a3f8ab72abc069c8f0c8f963cdae1f135e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/41b76eacae43bb3e46824ffc8e9aa14536771b88", - "reference": "41b76eacae43bb3e46824ffc8e9aa14536771b88", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/d59c8a3f8ab72abc069c8f0c8f963cdae1f135e6", + "reference": "d59c8a3f8ab72abc069c8f0c8f963cdae1f135e6", "shasum": "" }, "require": { @@ -2613,7 +2616,7 @@ "issues": "https://github.com/utopia-php/queue/issues", "source": "https://github.com/utopia-php/queue/tree/feat-coroutine-and-di" }, - "time": "2024-07-04T18:02:23+00:00" + "time": "2024-07-19T17:00:55+00:00" }, { "name": "utopia-php/registry", @@ -2744,12 +2747,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "80eafa63cb86b33ce35d48c0189bae6ebdc02734" + "reference": "bdf5f9a996e28b20e768829b3f3ecae57fe5cfea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/80eafa63cb86b33ce35d48c0189bae6ebdc02734", - "reference": "80eafa63cb86b33ce35d48c0189bae6ebdc02734", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/bdf5f9a996e28b20e768829b3f3ecae57fe5cfea", + "reference": "bdf5f9a996e28b20e768829b3f3ecae57fe5cfea", "shasum": "" }, "require": { @@ -2791,7 +2794,7 @@ "issues": "https://github.com/utopia-php/storage/issues", "source": "https://github.com/utopia-php/storage/tree/feat-framework-v2-v2" }, - "time": "2024-03-08T09:39:46+00:00" + "time": "2024-07-19T16:59:30+00:00" }, { "name": "utopia-php/system", @@ -5790,8 +5793,8 @@ { "package": "utopia-php/abuse", "version": "dev-feat-framework-v2", - "alias": "0.37.99", - "alias_normalized": "0.37.99.0" + "alias": "0.39.99", + "alias_normalized": "0.39.99.0" }, { "package": "utopia-php/analytics", @@ -5802,8 +5805,8 @@ { "package": "utopia-php/audit", "version": "dev-feat-framework-v2", - "alias": "0.39.99", - "alias_normalized": "0.39.99.0" + "alias": "0.40.99", + "alias_normalized": "0.40.99.0" }, { "package": "utopia-php/cli", @@ -5814,8 +5817,8 @@ { "package": "utopia-php/database", "version": "dev-feat-framework-v2", - "alias": "0.49.99", - "alias_normalized": "0.49.99.0" + "alias": "0.50.99", + "alias_normalized": "0.50.99.0" }, { "package": "utopia-php/domains", From 916f58cd9d43a57994aad3ccf8f6b4b4f3432ac6 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 19 Jul 2024 14:19:30 -0400 Subject: [PATCH 145/195] fixes: Logger and Abuse timelimit --- app/controllers/api/projects.php | 2 +- app/controllers/general.php | 18 ------------------ app/controllers/shared/api.php | 2 +- app/http.php | 3 ++- app/init2.php | 9 +++++---- app/realtime.php | 2 +- composer.lock | 8 ++++---- src/Appwrite/Platform/Workers/Deletes.php | 2 +- 8 files changed, 15 insertions(+), 31 deletions(-) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 009def4f8e..da02fb4514 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -17,7 +17,7 @@ use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use PHPMailer\PHPMailer\PHPMailer; -use Utopia\Abuse\Adapters\TimeLimit; +use Utopia\Abuse\Adapters\Database as TimeLimit; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\Config\Config; diff --git a/app/controllers/general.php b/app/controllers/general.php index 57cb4668df..dba172df67 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -738,24 +738,6 @@ Http::error() $publish = $error->getCode() === 0 || $error->getCode() >= 500; } - if ($error->getCode() >= 400 && $error->getCode() < 500) { - // Register error logger - $providerName = System::getEnv('_APP_EXPERIMENT_LOGGING_PROVIDER', ''); - $providerConfig = System::getEnv('_APP_EXPERIMENT_LOGGING_CONFIG', ''); - - if (!(empty($providerName) || empty($providerConfig))) { - if (!Logger::hasProvider($providerName)) { - throw new Exception("Logging provider not supported. Logging is disabled"); - } - - $classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName); - $adapter = new $classname($providerConfig); - $logger = new Logger($adapter); - $logger->setSample(0.04); - $publish = true; - } - } - if ($publish && $project->getId() !== 'console') { if (!Auth::isPrivilegedUser($authorization->getRoles())) { $fileSize = 0; diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index d72b63fec4..0c76bbc0ff 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -19,7 +19,7 @@ use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Abuse\Abuse; -use Utopia\Abuse\Adapters\TimeLimit; +use Utopia\Abuse\Adapters\Database as TimeLimit; use Utopia\Cache\Adapter\Filesystem; use Utopia\Cache\Cache; use Utopia\Config\Config; diff --git a/app/http.php b/app/http.php index 3d873067a8..284c1a244a 100644 --- a/app/http.php +++ b/app/http.php @@ -6,7 +6,7 @@ require_once __DIR__ . '/controllers/general.php'; use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Utopia\Abuse\Adapters\TimeLimit; +use Utopia\Abuse\Adapters\Database as TimeLimit; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\CLI\Console; @@ -45,6 +45,7 @@ $server = new Server('0.0.0.0', '80', [ 'send_yield' => true, 'tcp_fastopen' => true, ]); + $http = new Http($server, $container, 'UTC'); $http->setRequestClass(Request::class); $http->setResponseClass(Response::class); diff --git a/app/init2.php b/app/init2.php index ef4bb7fe25..7ddd2089b1 100644 --- a/app/init2.php +++ b/app/init2.php @@ -33,7 +33,7 @@ use Swoole\Database\PDOPool; use Swoole\Database\RedisConfig; use Swoole\Database\RedisPool; use Utopia\Abuse\Abuse; -use Utopia\Abuse\Adapters\TimeLimit; +use Utopia\Abuse\Adapters\Database as TimeLimit; use Utopia\Cache\Adapter\Redis as CacheRedis; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; @@ -199,7 +199,7 @@ $global->set('logger', function () { $providerName = $loggingProvider->getScheme(); $providerConfig = match ($providerName) { - 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], + 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://'.$loggingProvider->getHost()], 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], default => ['key' => $loggingProvider->getHost()], }; @@ -229,8 +229,9 @@ $global->set('logger', function () { 'appsignal' => new AppSignal($providerConfig['key']), default => throw new Exception('Provider "' . $providerName . '" not supported.') }; - - return new Logger($adapter); + $logger = new Logger($adapter); + $logger->setSample(0.4); + return $logger; }); $global->set('geodb', function () { diff --git a/app/realtime.php b/app/realtime.php index 3cc62103fa..9c705ba9d5 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -15,7 +15,7 @@ use Swoole\Runtime; use Swoole\Table; use Swoole\Timer; use Utopia\Abuse\Abuse; -use Utopia\Abuse\Adapters\TimeLimit; +use Utopia\Abuse\Adapters\Database as TimeLimit; use Utopia\CLI\Console; use Utopia\Database\DateTime; use Utopia\Database\Document; diff --git a/composer.lock b/composer.lock index 1cfdcb9f50..38f905a96b 100644 --- a/composer.lock +++ b/composer.lock @@ -1996,12 +1996,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "c27a91d11b6d89a2603ed929a5739c7bb9779d18" + "reference": "f355e93f363eabd6ba05fe870632d13ffc224123" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/c27a91d11b6d89a2603ed929a5739c7bb9779d18", - "reference": "c27a91d11b6d89a2603ed929a5739c7bb9779d18", + "url": "https://api.github.com/repos/utopia-php/http/zipball/f355e93f363eabd6ba05fe870632d13ffc224123", + "reference": "f355e93f363eabd6ba05fe870632d13ffc224123", "shasum": "" }, "require": { @@ -2038,7 +2038,7 @@ "issues": "https://github.com/utopia-php/http/issues", "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-07-19T16:54:13+00:00" + "time": "2024-07-19T18:00:28+00:00" }, { "name": "utopia-php/image", diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index d0a5d43f71..7996d37f6a 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -7,7 +7,7 @@ use Appwrite\Extend\Exception; use Executor\Executor; use Throwable; use Utopia\Abuse\Abuse; -use Utopia\Abuse\Adapters\TimeLimit; +use Utopia\Abuse\Adapters\Database as TimeLimit; use Utopia\Audit\Audit; use Utopia\Cache\Adapter\Filesystem; use Utopia\Cache\Cache; From 2947d223925153fb0bfff099e83b62c6062bb021 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:45:39 -0400 Subject: [PATCH 146/195] chore: Updating workers --- .env | 2 +- app/http.php | 4 ++-- app/realtime.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.env b/.env index ba50a84508..9cccf5ee7e 100644 --- a/.env +++ b/.env @@ -1,7 +1,7 @@ _APP_ENV=development _APP_EDITION=self-hosted _APP_LOCALE=en -_APP_WORKER_PER_CORE=2 +_APP_WORKER_PER_CORE=6 _APP_CONSOLE_WHITELIST_ROOT=disabled _APP_CONSOLE_WHITELIST_EMAILS= _APP_CONSOLE_SESSION_ALERTS=enabled diff --git a/app/http.php b/app/http.php index 284c1a244a..00b7fdbec3 100644 --- a/app/http.php +++ b/app/http.php @@ -26,7 +26,7 @@ use Utopia\System\System; global $global, $container; $payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing -$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 2)); +$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); $server = new Server('0.0.0.0', '80', [ 'open_http2_protocol' => true, @@ -36,7 +36,7 @@ $server = new Server('0.0.0.0', '80', [ 'buffer_output_size' => $payloadSize, // Server // 'log_level' => 0, - 'dispatch_mode' => 1, + 'dispatch_mode' => 2, 'worker_num' => $workerNumber, 'reactor_num' => swoole_cpu_num() * 2, 'open_cpu_affinity' => true, diff --git a/app/realtime.php b/app/realtime.php index 9c705ba9d5..695060f772 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -61,7 +61,7 @@ $stats->create(); $containerId = uniqid(); $statsDocument = null; -$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 2)); +$workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); $adapter = new Adapter\Swoole(port: System::getEnv('PORT', 80)); $adapter From 6619870847aecff45db913628ce393370d9b8bd9 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 26 Jul 2024 11:19:54 -0400 Subject: [PATCH 147/195] fix: Load only when needed --- app/init2.php | 4 +++- composer.lock | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/init2.php b/app/init2.php index 7ddd2089b1..d0cb9502a5 100644 --- a/app/init2.php +++ b/app/init2.php @@ -1,6 +1,8 @@ Date: Fri, 26 Jul 2024 12:40:31 -0400 Subject: [PATCH 148/195] fix: Redis with null password --- app/init2.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/init2.php b/app/init2.php index d0cb9502a5..d5ed8e32f5 100644 --- a/app/init2.php +++ b/app/init2.php @@ -201,7 +201,7 @@ $global->set('logger', function () { $providerName = $loggingProvider->getScheme(); $providerConfig = match ($providerName) { - 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://'.$loggingProvider->getHost()], + 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()], 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], default => ['key' => $loggingProvider->getHost()], }; @@ -367,7 +367,7 @@ $global->set( (new RedisConfig()) ->withHost($dsnHost) ->withPort((int)$dsnPort) - ->withAuth($dsnPass), + ->withAuth($dsnPass ?? ''), $poolSize ); break; From b884059ecd45acc8c67556e6dbb3f936927c5821 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 29 Jul 2024 12:29:21 -0400 Subject: [PATCH 149/195] refactor: Making sure database names remains the same as previous versions --- app/http.php | 4 +-- app/init2.php | 26 ++++++++----------- app/realtime.php | 2 +- .../Platform/Tasks/ScheduleExecutions.php | 2 +- .../Platform/Tasks/ScheduleFunctions.php | 2 +- .../Platform/Tasks/ScheduleMessages.php | 4 +-- tests/unit/Event/EventTest.php | 2 +- 7 files changed, 19 insertions(+), 23 deletions(-) diff --git a/app/http.php b/app/http.php index 00b7fdbec3..9ddb62c273 100644 --- a/app/http.php +++ b/app/http.php @@ -65,8 +65,8 @@ Http::onStart() do { try { $attempts++; - $pool = $pools['pools-console-main']['pool']; - $dsn = $pools['pools-console-main']['dsn']; + $pool = $pools['pools-console-console']['pool']; + $dsn = $pools['pools-console-console']['dsn']; $connection = $pool->get(); $connections->add($connection, $pool); diff --git a/app/init2.php b/app/init2.php index d5ed8e32f5..9d03695412 100644 --- a/app/init2.php +++ b/app/init2.php @@ -101,10 +101,6 @@ if (!Http::isProduction()) { PublicDomain::allow(['request-catcher']); } -function backwardDBName($db): string -{ - return str_replace('database_db_main', 'db_main', $db); -} function getDevice($root): Device { @@ -304,14 +300,14 @@ $global->set( foreach ($connections as $key => $connection) { $dsns = $connection['dsns'] ?? ''; - $multipe = $connection['multiple'] ?? false; + $multiple = $connection['multiple'] ?? false; $schemes = $connection['schemes'] ?? []; $dsns = explode(',', $connection['dsns'] ?? ''); $config = []; foreach ($dsns as &$dsn) { $dsn = explode('=', $dsn); - $name = ($multipe) ? $dsn[0] : 'main'; + $name = ($multiple) ? $key . '_' . $dsn[0] : $key; $config[] = $name; $dsn = $dsn[1] ?? ''; @@ -766,10 +762,10 @@ $dbForProject } try { - $dsn = new DSN(backwardDBName($project->getAttribute('database'))); + $dsn = new DSN($project->getAttribute('database')); } catch (\InvalidArgumentException) { // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . backwardDBName($project->getAttribute('database'))); + $dsn = new DSN('mysql://' . $project->getAttribute('database')); } $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; @@ -788,10 +784,10 @@ $dbForProject $database = new Database($adapter, $cache); try { - $dsn = new DSN(backwardDBName($project->getAttribute('database'))); + $dsn = new DSN($project->getAttribute('database')); } catch (\InvalidArgumentException) { // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . backwardDBName($project->getAttribute('database'))); + $dsn = new DSN('mysql://' . $project->getAttribute('database')); } if ($dsn->getHost() === DATABASE_SHARED_TABLES) { @@ -817,8 +813,8 @@ $dbForConsole ->inject('authorization') ->inject('connections') ->setCallback(function (array $pools, Cache $cache, Authorization $authorization, Connections $connections): Database { - $pool = $pools['pools-console-main']['pool']; - $dsn = $pools['pools-console-main']['dsn']; + $pool = $pools['pools-console-console']['pool']; + $dsn = $pools['pools-console-console']['dsn']; $connection = $pool->get(); $connections->add($connection, $pool); @@ -920,7 +916,7 @@ $queue ->inject('pools') ->inject('connections') ->setCallback(function (array $pools, Connections $connections) { - $pool = $pools['pools-queue-main']['pool']; + $pool = $pools['pools-queue-queue']['pool']; $connection = $pool->get(); $connections->add($connection, $pool); @@ -1162,10 +1158,10 @@ $getProjectDB } try { - $dsn = new DSN(backwardDBName($project->getAttribute('database'))); + $dsn = new DSN($project->getAttribute('database')); } catch (\InvalidArgumentException) { // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . backwardDBName($project->getAttribute('database'))); + $dsn = new DSN('mysql://' . $project->getAttribute('database')); } if (isset($databases[$dsn->getHost()])) { diff --git a/app/realtime.php b/app/realtime.php index 695060f772..df044587bd 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -275,7 +275,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats /** @var Connections $connections */ $connections = $container->get('connections'); - $pool = $pools['pools-pubsub-main']['pool']; + $pool = $pools['pools-pubsub-pubsub']['pool']; $connection = $pool->get(); $connections->add($connection, $pool); diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index ed92dbd61d..440055cfc0 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -23,7 +23,7 @@ class ScheduleExecutions extends ScheduleBase protected function enqueueResources(array $pools, Database $dbForConsole): void { - $pool = $pools['pools-queue-main']['pool']; + $pool = $pools['pools-queue-queue']['pool']; $connection = $pool->get(); $this->connections->add($connection, $pool); diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 050588903b..cfa6869662 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -68,7 +68,7 @@ class ScheduleFunctions extends ScheduleBase \go(function () use ($delay, $scheduleKeys, $pools) { \sleep($delay); // in seconds - $pool = $pools['pools-queue-main']['pool']; + $pool = $pools['pools-queue-queue']['pool']; $connection = $pool->get(); $this->connections->add($connection, $pool); diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 58e4218799..fc76d2a543 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -36,8 +36,8 @@ class ScheduleMessages extends ScheduleBase } \go(function () use ($now, $schedule, $pools, $dbForConsole) { - $pool = $pools['pools-queue-main']['pool']; - $dsn = $pools['pools-queue-main']['dsn']; + $pool = $pools['pools-queue-queue']['pool']; + $dsn = $pools['pools-queue-queue']['dsn']; $connection = $pool->get(); $this->connections->add($connection, $pool); diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index e0436b06b2..c6628180c6 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -69,7 +69,7 @@ class EventTest extends TestCase global $global; $pools = $global->get('pools'); - $dsn = $pools['pools-queue-main']['dsn']; + $dsn = $pools['pools-queue-queue']['dsn']; $queue = new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()); $client = new Client($this->object->getQueue(), $queue); From ff1d4219e34ab0b2d0ad3a2269f2c846a4f039e8 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 29 Jul 2024 15:15:57 -0400 Subject: [PATCH 150/195] refactor: Exporting global --- app/init2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/init2.php b/app/init2.php index 9d03695412..80a9deff1e 100644 --- a/app/init2.php +++ b/app/init2.php @@ -91,7 +91,7 @@ ini_set('display_startup_errors', 1); ini_set('default_socket_timeout', -1); error_reporting(E_ALL); -global $http, $container; +global $http, $container, $global; Http::setMode(System::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION)); From 19918843a4d4bca576468a3cc9d2805f259a2570 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 1 Aug 2024 15:38:18 -0400 Subject: [PATCH 151/195] test: --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f580d31f0..a973752026 100644 --- a/README.md +++ b/README.md @@ -235,4 +235,4 @@ Join our growing community around the world! Check out our official [Blog](https ## License -This repository is available under the [BSD 3-Clause License](./LICENSE). +This repository is available under the [BSD 3-Clause License](./LICENSE). From 90acd793453bd6d61a82d3bcf68f134fdcb4b147 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:04:59 -0400 Subject: [PATCH 152/195] chore: Adjusting init to match latest --- app/init.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/init.php b/app/init.php index 02ccac33b1..2af75ebbce 100644 --- a/app/init.php +++ b/app/init.php @@ -1,6 +1,5 @@ set('geodb', function () { -// return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.mmdb'); +// return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-08.mmdb'); //}); //$register->set('passwordsDictionary', function () { // $content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords'); @@ -1243,14 +1243,15 @@ // } // // $jwtUserId = $payload['userId'] ?? ''; -// $jwtSessionId = $payload['sessionId'] ?? ''; -// -// if ($jwtUserId && $jwtSessionId) { +// if (!empty($jwtUserId)) { // $user = $dbForProject->getDocument('users', $jwtUserId); // } // -// if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token -// $user = new Document([]); +// $jwtSessionId = $payload['sessionId'] ?? ''; +// if(!empty($jwtSessionId)) { +// if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token +// $user = new Document([]); +// } // } // } // @@ -1732,4 +1733,4 @@ //}, ['request']); //App::setResource('plan', function (array $plan = []) { // return []; -//}); +//});*/ From 7a1e3ce4d971380b2be6afb0091ce2cde54b795c Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:05:24 -0400 Subject: [PATCH 153/195] chore: merge --- app/init/config.php | 1 + app/init2.php | 13 +++++++------ src/Appwrite/Platform/Services/Tasks.php | 2 ++ src/Appwrite/Utopia/Response/Models.php | 1 + 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/init/config.php b/app/init/config.php index 5ae3c82dbc..80001f13b4 100644 --- a/app/init/config.php +++ b/app/init/config.php @@ -33,3 +33,4 @@ 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('function-templates', __DIR__ . '/../config/function-templates.php'); diff --git a/app/init2.php b/app/init2.php index 80a9deff1e..ec240f8a2d 100644 --- a/app/init2.php +++ b/app/init2.php @@ -236,7 +236,7 @@ $global->set('geodb', function () { /** * @disregard P1009 Undefined type */ - return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.mmdb'); + return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-08.mmdb'); }); $global->set('hooks', function () { @@ -631,14 +631,15 @@ $user } $jwtUserId = $payload['userId'] ?? ''; - $jwtSessionId = $payload['sessionId'] ?? ''; - - if ($jwtUserId && $jwtSessionId) { + if (!empty($jwtUserId)) { $user = $dbForProject->getDocument('users', $jwtUserId); } - if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token - $user = new Document([]); + $jwtSessionId = $payload['sessionId'] ?? ''; + if(!empty($jwtSessionId)) { + if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token + $user = new Document([]); + } } } diff --git a/src/Appwrite/Platform/Services/Tasks.php b/src/Appwrite/Platform/Services/Tasks.php index 7a0d5b60ac..6aba8fd8e4 100644 --- a/src/Appwrite/Platform/Services/Tasks.php +++ b/src/Appwrite/Platform/Services/Tasks.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Services; +use Appwrite\Platform\Tasks\DevGenerateTranslations; use Appwrite\Platform\Tasks\Doctor; use Appwrite\Platform\Tasks\Install; use Appwrite\Platform\Tasks\Maintenance; @@ -25,6 +26,7 @@ class Tasks extends Service { $this->type = Service::TYPE_TASK; $this + ->addAction(DevGenerateTranslations::getName(), new DevGenerateTranslations()) ->addAction(Doctor::getName(), new Doctor()) ->addAction(Install::getName(), new Install()) ->addAction(Maintenance::getName(), new Maintenance()) diff --git a/src/Appwrite/Utopia/Response/Models.php b/src/Appwrite/Utopia/Response/Models.php index cdab9d48a7..778a077e4b 100644 --- a/src/Appwrite/Utopia/Response/Models.php +++ b/src/Appwrite/Utopia/Response/Models.php @@ -127,6 +127,7 @@ class Models self::setModel(new BaseList('Teams List', Response::MODEL_TEAM_LIST, 'teams', Response::MODEL_TEAM)); self::setModel(new BaseList('Memberships List', Response::MODEL_MEMBERSHIP_LIST, 'memberships', Response::MODEL_MEMBERSHIP)); self::setModel(new BaseList('Functions List', Response::MODEL_FUNCTION_LIST, 'functions', Response::MODEL_FUNCTION)); + self::setModel(new BaseList('Function Templates List', Response::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', Response::MODEL_TEMPLATE_FUNCTION)); self::setModel(new BaseList('Installations List', Response::MODEL_INSTALLATION_LIST, 'installations', Response::MODEL_INSTALLATION)); self::setModel(new BaseList('Provider Repositories List', Response::MODEL_PROVIDER_REPOSITORY_LIST, 'providerRepositories', Response::MODEL_PROVIDER_REPOSITORY)); self::setModel(new BaseList('Branches List', Response::MODEL_BRANCH_LIST, 'branches', Response::MODEL_BRANCH)); From 6475f29d457454e0ed4a7e3c2d78eb6b3d8d4f9e Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:12:21 -0400 Subject: [PATCH 154/195] chore: lint --- app/init.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/init.php b/app/init.php index 2af75ebbce..4100d61e83 100644 --- a/app/init.php +++ b/app/init.php @@ -1,4 +1,5 @@ Date: Tue, 6 Aug 2024 15:20:45 -0400 Subject: [PATCH 155/195] chore: stan --- app/controllers/general.php | 1 + composer.lock | 859 +++++++++++++++-------- src/Appwrite/Platform/Services/Tasks.php | 2 - 3 files changed, 569 insertions(+), 293 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 49f408d93a..1bfbb3f640 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1,5 +1,6 @@ =6.0.0", - "yoast/phpunit-polyfills": "^0.1.0" + "phpstan/phpstan-shim": "*", + "phpunit/phpunit": ">=6.0.0 <8" }, "suggest": { "ext-intl": "Needed to allow Assertion::count(), Assertion::isCountable(), Assertion::minCount(), and Assertion::maxCount() to operate on ResourceBundles" @@ -272,9 +271,9 @@ ], "support": { "issues": "https://github.com/beberlei/assert/issues", - "source": "https://github.com/beberlei/assert/tree/v3.3.2" + "source": "https://github.com/beberlei/assert/tree/v3" }, - "time": "2021-12-16T21:41:27+00:00" + "time": "2019-12-19T17:51:41+00:00" }, { "name": "chillerlan/php-qrcode", @@ -567,16 +566,16 @@ }, { "name": "jean85/pretty-package-versions", - "version": "2.0.6", + "version": "2.x-dev", "source": { "type": "git", "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" + "reference": "d2ed36bf14b8b68d7986272734cb6a54c7822554" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/d2ed36bf14b8b68d7986272734cb6a54c7822554", + "reference": "d2ed36bf14b8b68d7986272734cb6a54c7822554", "shasum": "" }, "require": { @@ -587,9 +586,10 @@ "friendsofphp/php-cs-fixer": "^3.2", "jean85/composer-provided-replaced-stub-package": "^1.0", "phpstan/phpstan": "^1.4", - "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "vimeo/psalm": "^4.3 || ^5.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -620,9 +620,9 @@ ], "support": { "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.x" }, - "time": "2024-03-08T09:58:59+00:00" + "time": "2024-08-01T12:14:16+00:00" }, { "name": "league/csv", @@ -907,7 +907,7 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.7.0", + "version": "v2.x-dev", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", @@ -1055,7 +1055,7 @@ }, { "name": "spomky-labs/otphp", - "version": "v10.0.3", + "version": "v10.0.x-dev", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/otphp.git", @@ -1084,6 +1084,7 @@ "phpunit/phpunit": "^8.0", "thecodingmachine/phpstan-safe-rule": "^1.0 || ^2.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -1130,16 +1131,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "8740a072b86292957feb42703edde77fcfca84fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8740a072b86292957feb42703edde77fcfca84fb", + "reference": "8740a072b86292957feb42703edde77fcfca84fb", "shasum": "" }, "require": { @@ -1151,6 +1152,7 @@ "suggest": { "ext-mbstring": "For best performance" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -1190,7 +1192,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/1.x" }, "funding": [ { @@ -1206,11 +1208,11 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-06-20T08:18:00+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -1225,6 +1227,7 @@ "require": { "php": ">=7.1" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -1429,26 +1432,28 @@ }, { "name": "utopia-php/abuse", - "version": "0.38.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "b7be9086c9d9b4561d810cbd42fdda798742f56c" + "reference": "b3105b361311d601069697c7691517d70f1bd32d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b7be9086c9d9b4561d810cbd42fdda798742f56c", - "reference": "b7be9086c9d9b4561d810cbd42fdda798742f56c", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b3105b361311d601069697c7691517d70f1bd32d", + "reference": "b3105b361311d601069697c7691517d70f1bd32d", "shasum": "" }, "require": { "ext-curl": "*", "ext-pdo": "*", + "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "0.50.*" + "utopia-php/database": "dev-feat-framework-v2 as 0.49.99" }, "require-dev": { "laravel/pint": "1.5.*", + "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^9.4" }, @@ -1472,27 +1477,27 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.38.0" + "source": "https://github.com/utopia-php/abuse/tree/feat-framework-v2" }, - "time": "2024-06-24T00:52:02+00:00" + "time": "2024-07-19T16:13:52+00:00" }, { "name": "utopia-php/analytics", - "version": "0.10.2", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + "reference": "5d59c2e50381a25adecbca979ed5a7c81307442f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/5d59c2e50381a25adecbca979ed5a7c81307442f", + "reference": "5d59c2e50381a25adecbca979ed5a7c81307442f", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "^0.15.0" + "utopia-php/cli": "0.17.*" }, "require-dev": { "laravel/pint": "dev-main", @@ -1518,27 +1523,27 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + "source": "https://github.com/utopia-php/analytics/tree/feat-framework-v2" }, - "time": "2023-03-22T12:01:09+00:00" + "time": "2024-03-07T15:54:19+00:00" }, { "name": "utopia-php/audit", - "version": "0.40.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "735ae211ce5fee5b52b736731571b4030b1d7cdc" + "reference": "ab618a689e99e4f278c71f133e5dda49425fb1f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/735ae211ce5fee5b52b736731571b4030b1d7cdc", - "reference": "735ae211ce5fee5b52b736731571b4030b1d7cdc", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/ab618a689e99e4f278c71f133e5dda49425fb1f0", + "reference": "ab618a689e99e4f278c71f133e5dda49425fb1f0", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.50.*" + "utopia-php/database": "dev-feat-framework-v2 as 0.50.99" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1565,9 +1570,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.40.0" + "source": "https://github.com/utopia-php/audit/tree/feat-framework-v2" }, - "time": "2024-06-24T00:52:17+00:00" + "time": "2024-07-19T16:19:18+00:00" }, { "name": "utopia-php/cache", @@ -1621,27 +1626,29 @@ }, { "name": "utopia-php/cli", - "version": "0.15.0", + "version": "dev-dev-coroutines", "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + "reference": "d48b696891dee1e46df2491d192bb91cf4df8f94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/d48b696891dee1e46df2491d192bb91cf4df8f94", + "reference": "d48b696891dee1e46df2491d192bb91cf4df8f94", "shasum": "" }, "require": { "php": ">=7.4", - "utopia-php/framework": "0.*.*" + "utopia-php/di": "dev-feat-framework-v2", + "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99" }, "require-dev": { "laravel/pint": "1.2.*", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.3", "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" + "swoole/ide-helper": "4.8.8" }, "type": "library", "autoload": { @@ -1664,9 +1671,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" + "source": "https://github.com/utopia-php/cli/tree/dev-coroutines" }, - "time": "2023-03-01T05:55:14+00:00" + "time": "2024-06-24T13:24:20+00:00" }, { "name": "utopia-php/config", @@ -1721,16 +1728,16 @@ }, { "name": "utopia-php/database", - "version": "0.50.2", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "c712d1f6c8ec37886a7a1ad4d60a8cd75dec00aa" + "reference": "9ddf91aa4812a0eeb7feb9667b4827b407032df1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/c712d1f6c8ec37886a7a1ad4d60a8cd75dec00aa", - "reference": "c712d1f6c8ec37886a7a1ad4d60a8cd75dec00aa", + "url": "https://api.github.com/repos/utopia-php/database/zipball/9ddf91aa4812a0eeb7feb9667b4827b407032df1", + "reference": "9ddf91aa4812a0eeb7feb9667b4827b407032df1", "shasum": "" }, "require": { @@ -1738,7 +1745,7 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.10.*", - "utopia-php/framework": "0.33.*", + "utopia-php/framework": "0.34.*", "utopia-php/mongo": "0.3.*" }, "require-dev": { @@ -1749,7 +1756,7 @@ "phpunit/phpunit": "^9.4", "rregeer/phpunit-coverage-check": "^0.3.1", "swoole/ide-helper": "4.8.0", - "utopia-php/cli": "^0.14.0" + "utopia-php/cli": "0.17.*" }, "type": "library", "autoload": { @@ -1771,30 +1778,92 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.50.2" + "source": "https://github.com/utopia-php/database/tree/feat-framework-v2" }, - "time": "2024-07-31T10:12:19+00:00" + "time": "2024-07-19T17:41:23+00:00" }, { - "name": "utopia-php/domains", - "version": "0.5.0", + "name": "utopia-php/di", + "version": "dev-feat-framework-v2", "source": { "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c" + "url": "https://github.com/utopia-php/di.git", + "reference": "1fb7da5ead16de5d795ef10c1e22e39726eb6943" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/bf07f60326f8389f378ddf6fcde86217e5cfe18c", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c", + "url": "https://api.github.com/repos/utopia-php/di/zipball/1fb7da5ead16de5d795ef10c1e22e39726eb6943", + "reference": "1fb7da5ead16de5d795ef10c1e22e39726eb6943", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/", + "Tests\\E2E\\": "tests/e2e" + } + }, + "scripts": { + "lint": [ + "vendor/bin/pint --test" + ], + "format": [ + "vendor/bin/pint" + ], + "check": [ + "vendor/bin/phpstan analyse -c phpstan.neon" + ], + "test": [ + "vendor/bin/phpunit --configuration phpunit.xml" + ] + }, + "license": [ + "MIT" + ], + "description": "A simple and lite library for managing dependency injections", + "keywords": [ + "framework", + "http", + "php", + "upf" + ], + "support": { + "source": "https://github.com/utopia-php/di/tree/feat-framework-v2", + "issues": "https://github.com/utopia-php/di/issues" + }, + "time": "2024-06-11T16:03:00+00:00" + }, + { + "name": "utopia-php/domains", + "version": "dev-feat-framework-v2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "58c0c8c262329b4f4a7195e63360fc5c4cd05fa9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/58c0c8c262329b4f4a7195e63360fc5c4cd05fa9", + "reference": "58c0c8c262329b4f4a7195e63360fc5c4cd05fa9", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/framework": "0.*.*" + "utopia-php/framework": "0.34.*" }, "require-dev": { "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.9.x-dev", "phpunit/phpunit": "^9.3" }, "type": "library", @@ -1831,9 +1900,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.5.0" + "source": "https://github.com/utopia-php/domains/tree/feat-framework-v2" }, - "time": "2024-01-03T22:04:27+00:00" + "time": "2024-07-19T16:48:25+00:00" }, { "name": "utopia-php/dsn", @@ -1923,26 +1992,30 @@ }, { "name": "utopia-php/framework", - "version": "0.33.7", + "version": "dev-feat-di-upgrade", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "78d293d99a262bd63ece750bbf989c7e0643b825" + "reference": "f355e93f363eabd6ba05fe870632d13ffc224123" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/78d293d99a262bd63ece750bbf989c7e0643b825", - "reference": "78d293d99a262bd63ece750bbf989c7e0643b825", + "url": "https://api.github.com/repos/utopia-php/http/zipball/f355e93f363eabd6ba05fe870632d13ffc224123", + "reference": "f355e93f363eabd6ba05fe870632d13ffc224123", "shasum": "" }, "require": { - "php": ">=8.0" + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/servers": "dev-dev" }, "require-dev": { + "ext-xdebug": "*", "laravel/pint": "^1.2", "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" }, "type": "library", "autoload": { @@ -1954,17 +2027,18 @@ "license": [ "MIT" ], - "description": "A simple, light and advanced PHP framework", + "description": "A simple, light and advanced PHP HTTP framework", "keywords": [ "framework", + "http", "php", "upf" ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.7" + "source": "https://github.com/utopia-php/http/tree/feat-di-upgrade" }, - "time": "2024-08-01T14:01:04+00:00" + "time": "2024-07-19T18:00:28+00:00" }, { "name": "utopia-php/image", @@ -2067,16 +2141,16 @@ }, { "name": "utopia-php/logger", - "version": "0.5.2", + "version": "0.6.0", "source": { "type": "git", "url": "https://github.com/utopia-php/logger.git", - "reference": "c6dfdb672e41364c309b0c30dc03bc6d45446dba" + "reference": "a2d1daeeb8f61fdec6d851950d9a021a3d05c9f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/c6dfdb672e41364c309b0c30dc03bc6d45446dba", - "reference": "c6dfdb672e41364c309b0c30dc03bc6d45446dba", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/a2d1daeeb8f61fdec6d851950d9a021a3d05c9f9", + "reference": "a2d1daeeb8f61fdec6d851950d9a021a3d05c9f9", "shasum": "" }, "require": { @@ -2115,9 +2189,9 @@ ], "support": { "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.5.2" + "source": "https://github.com/utopia-php/logger/tree/0.6.0" }, - "time": "2024-05-17T09:32:59+00:00" + "time": "2024-05-23T13:37:54+00:00" }, { "name": "utopia-php/messaging", @@ -2279,21 +2353,21 @@ }, { "name": "utopia-php/orchestration", - "version": "0.9.1", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + "reference": "ede2b7a60bd1630ef8bd7fdbbc3cf73275fc9f28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/ede2b7a60bd1630ef8bd7fdbbc3cf73275fc9f28", + "reference": "ede2b7a60bd1630ef8bd7fdbbc3cf73275fc9f28", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*" + "utopia-php/cli": "0.17.*" }, "require-dev": { "laravel/pint": "^1.2", @@ -2323,31 +2397,31 @@ ], "support": { "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + "source": "https://github.com/utopia-php/orchestration/tree/feat-framework-v2" }, - "time": "2023-03-17T15:05:06+00:00" + "time": "2024-03-07T15:56:18+00:00" }, { "name": "utopia-php/platform", - "version": "0.7.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52" + "reference": "f40b23bf84f8fd95bc954ef9297663c01fed5c4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/f40b23bf84f8fd95bc954ef9297663c01fed5c4c", + "reference": "f40b23bf84f8fd95bc954ef9297663c01fed5c4c", "shasum": "" }, "require": { "ext-json": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.33.*", - "utopia-php/queue": "0.7.*" + "utopia-php/cli": "dev-dev-coroutines as 0.17.99", + "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99", + "utopia-php/queue": "dev-feat-coroutine-and-di as 0.7.99" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2373,9 +2447,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.7.0" + "source": "https://github.com/utopia-php/platform/tree/feat-framework-v2" }, - "time": "2024-05-08T17:00:55+00:00" + "time": "2024-06-21T19:25:15+00:00" }, { "name": "utopia-php/pools", @@ -2483,22 +2557,23 @@ }, { "name": "utopia-php/queue", - "version": "0.7.0", + "version": "dev-feat-coroutine-and-di", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "917565256eb94bcab7246f7a746b1a486813761b" + "reference": "d59c8a3f8ab72abc069c8f0c8f963cdae1f135e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/917565256eb94bcab7246f7a746b1a486813761b", - "reference": "917565256eb94bcab7246f7a746b1a486813761b", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/d59c8a3f8ab72abc069c8f0c8f963cdae1f135e6", + "reference": "d59c8a3f8ab72abc069c8f0c8f963cdae1f135e6", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" + "utopia-php/cli": "0.17.*", + "utopia-php/di": "dev-feat-framework-v2", + "utopia-php/servers": "dev-dev" }, "require-dev": { "laravel/pint": "^0.2.3", @@ -2508,6 +2583,7 @@ "workerman/workerman": "^4.0" }, "suggest": { + "ext-redis": "Needed to support Redis connections", "ext-swoole": "Needed to support Swoole.", "workerman/workerman": "Needed to support Workerman." }, @@ -2538,9 +2614,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.7.0" + "source": "https://github.com/utopia-php/queue/tree/feat-coroutine-and-di" }, - "time": "2024-01-17T19:00:43+00:00" + "time": "2024-07-19T17:00:55+00:00" }, { "name": "utopia-php/registry", @@ -2595,17 +2671,88 @@ "time": "2021-03-10T10:45:22+00:00" }, { - "name": "utopia-php/storage", - "version": "0.18.4", + "name": "utopia-php/servers", + "version": "dev-dev", "source": { "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "94ab8758fabcefee5c5fa723616e45719833f922" + "url": "https://github.com/utopia-php/servers.git", + "reference": "4565c1c111f6da6b18bc0f00f350377a1e691e48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/94ab8758fabcefee5c5fa723616e45719833f922", - "reference": "94ab8758fabcefee5c5fa723616e45719833f922", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/4565c1c111f6da6b18bc0f00f350377a1e691e48", + "reference": "4565c1c111f6da6b18bc0f00f350377a1e691e48", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/di": "dev-feat-framework-v2" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Servers\\": "src/Servers" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\E2E\\": "tests/Servers/Unit" + } + }, + "scripts": { + "test": [ + "phpunit" + ], + "analyse": [ + "vendor/bin/phpstan analyse" + ], + "format": [ + "vendor/bin/pint" + ], + "lint": [ + "vendor/bin/pint --test" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A base library for building Utopia style servers.", + "keywords": [ + "framework", + "php", + "servers", + "upf", + "utopia" + ], + "support": { + "source": "https://github.com/utopia-php/servers/tree/dev", + "issues": "https://github.com/utopia-php/servers/issues" + }, + "time": "2024-06-07T18:49:59+00:00" + }, + { + "name": "utopia-php/storage", + "version": "dev-feat-framework-v2-v2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "bdf5f9a996e28b20e768829b3f3ecae57fe5cfea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/bdf5f9a996e28b20e768829b3f3ecae57fe5cfea", + "reference": "bdf5f9a996e28b20e768829b3f3ecae57fe5cfea", "shasum": "" }, "require": { @@ -2617,7 +2764,7 @@ "ext-zlib": "*", "ext-zstd": "*", "php": ">=8.0", - "utopia-php/framework": "0.*.*", + "utopia-php/framework": "0.34.*", "utopia-php/system": "0.*.*" }, "require-dev": { @@ -2645,60 +2792,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.4" + "source": "https://github.com/utopia-php/storage/tree/feat-framework-v2-v2" }, - "time": "2024-04-02T08:24:09+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.8.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.33.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "5.0.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.8.2" - }, - "time": "2024-02-01T14:54:12+00:00" + "time": "2024-07-19T16:59:30+00:00" }, { "name": "utopia-php/system", @@ -2806,27 +2902,71 @@ "time": "2024-06-26T09:44:52+00:00" }, { - "name": "utopia-php/websocket", - "version": "0.1.0", + "name": "utopia-php/view", + "version": "0.2.0", "source": { "type": "git", - "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + "url": "https://github.com/utopia-php/view.git", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "url": "https://api.github.com/repos/utopia-php/view/zipball/6ee55e83bc014c39ed6b69390f6d399116f65e88", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88", "shasum": "" }, "require": { "php": ">=8.0" }, "require-dev": { + "laravel/pint": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\View\\": "src/View" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP rendering engine", + "keywords": [ + "php", + "view" + ], + "support": { + "issues": "https://github.com/utopia-php/view/issues", + "source": "https://github.com/utopia-php/view/tree/0.2.0" + }, + "time": "2024-04-01T17:21:29+00:00" + }, + { + "name": "utopia-php/websocket", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/websocket.git", + "reference": "e9d0919b321744a61f12563f5791c47ba9f57810" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/e9d0919b321744a61f12563f5791c47ba9f57810", + "reference": "e9d0919b321744a61f12563f5791c47ba9f57810", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.15", + "phpstan/phpstan": "^1.8", "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", + "swoole/ide-helper": "5.1.2", "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", "workerman/workerman": "^4.0" }, "type": "library", @@ -2839,16 +2979,6 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], "description": "A simple abstraction for WebSocket servers.", "keywords": [ "framework", @@ -2859,9 +2989,9 @@ ], "support": { "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + "source": "https://github.com/utopia-php/websocket/tree/0.2.0" }, - "time": "2021-12-20T10:50:09+00:00" + "time": "2024-04-09T08:28:11+00:00" }, { "name": "webmozart/assert", @@ -2990,16 +3120,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.39.4", + "version": "0.39.5", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "501b92d73ae55e0f880ed00f57bc64a54d0ce137" + "reference": "40d0f66f2f85be74049ad710b46203aa151f53fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/501b92d73ae55e0f880ed00f57bc64a54d0ce137", - "reference": "501b92d73ae55e0f880ed00f57bc64a54d0ce137", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/40d0f66f2f85be74049ad710b46203aa151f53fd", + "reference": "40d0f66f2f85be74049ad710b46203aa151f53fd", "shasum": "" }, "require": { @@ -3035,13 +3165,13 @@ "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.39.4" + "source": "https://github.com/appwrite/sdk-generator/tree/0.39.5" }, - "time": "2024-07-26T22:34:10+00:00" + "time": "2024-08-06T00:51:40+00:00" }, { "name": "doctrine/deprecations", - "version": "1.1.3", + "version": "1.1.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", @@ -3068,6 +3198,7 @@ "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -3088,29 +3219,29 @@ }, { "name": "doctrine/instantiator", - "version": "1.5.0", + "version": "1.5.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "reference": "12be2483e1f0e850b353e26869e4e6c038459501" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/12be2483e1f0e850b353e26869e4e6c038459501", + "reference": "12be2483e1f0e850b353e26869e4e6c038459501", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", + "doctrine/coding-standard": "^9 || ^12", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.16 || ^1", "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", @@ -3138,7 +3269,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "source": "https://github.com/doctrine/instantiator/tree/1.5.x" }, "funding": [ { @@ -3154,20 +3285,20 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2023-12-09T14:16:53+00:00" }, { "name": "laravel/pint", - "version": "v1.17.1", + "version": "v1.17.2", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "b5b6f716db298671c1dfea5b1082ec2c0ae7064f" + "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/b5b6f716db298671c1dfea5b1082ec2c0ae7064f", - "reference": "b5b6f716db298671c1dfea5b1082ec2c0ae7064f", + "url": "https://api.github.com/repos/laravel/pint/zipball/e8a88130a25e3f9d4d5785e6a1afca98268ab110", + "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110", "shasum": "" }, "require": { @@ -3178,13 +3309,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.59.3", - "illuminate/view": "^10.48.12", - "larastan/larastan": "^2.9.7", + "friendsofphp/php-cs-fixer": "^3.61.1", + "illuminate/view": "^10.48.18", + "larastan/larastan": "^2.9.8", "laravel-zero/framework": "^10.4.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.34.8" + "pestphp/pest": "^2.35.0" }, "bin": [ "builds/pint" @@ -3220,7 +3351,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-08-01T09:06:33+00:00" + "time": "2024-08-06T15:11:54+00:00" }, { "name": "matthiasmullie/minify", @@ -3348,7 +3479,7 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", @@ -3373,6 +3504,7 @@ "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, + "default-branch": true, "type": "library", "autoload": { "files": [ @@ -3466,7 +3598,7 @@ }, { "name": "phar-io/manifest", - "version": "2.0.4", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", @@ -3486,6 +3618,7 @@ "phar-io/version": "^3.0.1", "php": "^7.2 || ^8.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3584,25 +3717,25 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "reference": "a0eeab580cbdf4414fef6978732510a36ed0a9d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/a0eeab580cbdf4414fef6978732510a36ed0a9d6", + "reference": "a0eeab580cbdf4414fef6978732510a36ed0a9d6", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-2.x": "2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -3631,22 +3764,22 @@ ], "support": { "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master" }, - "time": "2020-06-27T09:03:43+00:00" + "time": "2021-06-25T13:47:51+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.4.1", + "version": "5.x-dev", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + "reference": "aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39", + "reference": "aa53f8d4374d1f5bd3fc598548d6272cb5d9bf39", "shasum": "" }, "require": { @@ -3667,6 +3800,7 @@ "phpunit/phpunit": "^9.5", "vimeo/psalm": "^5.13" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3695,29 +3829,29 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.x" }, - "time": "2024-05-21T05:55:05+00:00" + "time": "2024-05-21T06:14:15+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "reference": "eee054a3d40f09920f5b27c9b09a6483f88d9d24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/eee054a3d40f09920f5b27c9b09a6483f88d9d24", + "reference": "eee054a3d40f09920f5b27c9b09a6483f88d9d24", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "phpstan/phpdoc-parser": "^1.18" }, "require-dev": { "ext-tokenizer": "*", @@ -3729,6 +3863,7 @@ "rector/rector": "^0.13.9", "vimeo/psalm": "^4.25" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3753,22 +3888,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.x" }, - "time": "2024-02-23T11:10:43+00:00" + "time": "2024-05-24T14:24:30+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.19.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87" + "reference": "f9e07be0992e7bf1cad210829055b99318df142f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/67a759e7d8746d501c41536ba40cd9c0a07d6a87", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f9e07be0992e7bf1cad210829055b99318df142f", + "reference": "f9e07be0992e7bf1cad210829055b99318df142f", "shasum": "" }, "require": { @@ -3783,6 +3918,7 @@ "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3822,9 +3958,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.19.0" + "source": "https://github.com/phpspec/prophecy/tree/master" }, - "time": "2024-02-29T11:52:51+00:00" + "time": "2024-03-29T09:25:04+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -3874,17 +4010,76 @@ "time": "2024-05-31T08:52:43+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "9.2.31", + "name": "phpstan/phpstan", + "version": "1.8.x-dev", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + "url": "https://github.com/phpstan/phpstan.git", + "reference": "b20042710baa0d9c07636cc66d4c400f03f1477a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b20042710baa0d9c07636cc66d4c400f03f1477a", + "reference": "b20042710baa0d9c07636cc66d4c400f03f1477a", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.8.x" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-10-29T12:56:57+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "328a747f499cca790acff5634a4e55b957f40634" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/328a747f499cca790acff5634a4e55b957f40634", + "reference": "328a747f499cca790acff5634a4e55b957f40634", "shasum": "" }, "require": { @@ -3903,7 +4098,7 @@ "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -3912,7 +4107,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -3941,7 +4136,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2" }, "funding": [ { @@ -3949,20 +4144,20 @@ "type": "github" } ], - "time": "2024-03-02T06:37:42+00:00" + "time": "2024-07-17T05:19:21+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "3.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "38b24367e1b340aa78b96d7cab042942d917bb84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/38b24367e1b340aa78b96d7cab042942d917bb84", + "reference": "38b24367e1b340aa78b96d7cab042942d917bb84", "shasum": "" }, "require": { @@ -4001,7 +4196,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0" }, "funding": [ { @@ -4009,7 +4204,7 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2022-02-11T16:23:04+00:00" }, { "name": "phpunit/php-invoker", @@ -4297,7 +4492,7 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", @@ -4312,6 +4507,7 @@ "require": { "php": ">=8.0.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4347,7 +4543,7 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.2", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", @@ -4514,16 +4710,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "b247957a1c8dc81a671770f74b479c0a78a818f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b247957a1c8dc81a671770f74b479c0a78a818f1", + "reference": "b247957a1c8dc81a671770f74b479c0a78a818f1", "shasum": "" }, "require": { @@ -4576,7 +4772,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0" }, "funding": [ { @@ -4584,11 +4780,11 @@ "type": "github" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2022-09-14T12:46:14+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.3", + "version": "2.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", @@ -4645,7 +4841,7 @@ }, { "name": "sebastian/diff", - "version": "4.0.6", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", @@ -4711,7 +4907,7 @@ }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "5.1.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", @@ -4762,7 +4958,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1" }, "funding": [ { @@ -4774,7 +4970,7 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", @@ -4851,7 +5047,7 @@ }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", @@ -4915,7 +5111,7 @@ }, { "name": "sebastian/lines-of-code", - "version": "1.0.4", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", @@ -5084,7 +5280,7 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", @@ -5147,16 +5343,16 @@ }, { "name": "sebastian/resource-operations", - "version": "3.0.4", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + "reference": "ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25", + "reference": "ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25", "shasum": "" }, "require": { @@ -5165,6 +5361,7 @@ "require-dev": { "phpunit/phpunit": "^9.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -5189,7 +5386,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/main" }, "funding": [ { @@ -5197,11 +5394,11 @@ "type": "github" } ], - "time": "2024-03-14T16:00:52+00:00" + "time": "2024-03-14T18:47:08+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "3.2.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", @@ -5245,7 +5442,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "source": "https://github.com/sebastianbergmann/type/tree/3.2" }, "funding": [ { @@ -5257,7 +5454,7 @@ }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "3.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", @@ -5342,7 +5539,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -5363,6 +5560,7 @@ "suggest": { "ext-ctype": "For best performance" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -5591,9 +5789,88 @@ "time": "2023-11-21T18:54:41+00:00" } ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], + "aliases": [ + { + "package": "utopia-php/abuse", + "version": "dev-feat-framework-v2", + "alias": "0.39.99", + "alias_normalized": "0.39.99.0" + }, + { + "package": "utopia-php/analytics", + "version": "dev-feat-framework-v2", + "alias": "0.10.99", + "alias_normalized": "0.10.99.0" + }, + { + "package": "utopia-php/audit", + "version": "dev-feat-framework-v2", + "alias": "0.40.99", + "alias_normalized": "0.40.99.0" + }, + { + "package": "utopia-php/cli", + "version": "dev-dev-coroutines", + "alias": "0.17.99", + "alias_normalized": "0.17.99.0" + }, + { + "package": "utopia-php/database", + "version": "dev-feat-framework-v2", + "alias": "0.50.99", + "alias_normalized": "0.50.99.0" + }, + { + "package": "utopia-php/domains", + "version": "dev-feat-framework-v2", + "alias": "0.5.99", + "alias_normalized": "0.5.99.0" + }, + { + "package": "utopia-php/framework", + "version": "dev-feat-di-upgrade", + "alias": "0.34.99", + "alias_normalized": "0.34.99.0" + }, + { + "package": "utopia-php/orchestration", + "version": "dev-feat-framework-v2", + "alias": "0.9.99", + "alias_normalized": "0.9.99.0" + }, + { + "package": "utopia-php/platform", + "version": "dev-feat-framework-v2", + "alias": "0.5.99", + "alias_normalized": "0.5.99.0" + }, + { + "package": "utopia-php/queue", + "version": "dev-feat-coroutine-and-di", + "alias": "0.7.99", + "alias_normalized": "0.7.99.0" + }, + { + "package": "utopia-php/storage", + "version": "dev-feat-framework-v2-v2", + "alias": "0.18.99", + "alias_normalized": "0.18.99.0" + } + ], + "minimum-stability": "dev", + "stability-flags": { + "utopia-php/abuse": 20, + "utopia-php/analytics": 20, + "utopia-php/audit": 20, + "utopia-php/cli": 20, + "utopia-php/database": 20, + "utopia-php/domains": 20, + "utopia-php/framework": 20, + "utopia-php/orchestration": 20, + "utopia-php/platform": 20, + "utopia-php/queue": 20, + "utopia-php/storage": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/Appwrite/Platform/Services/Tasks.php b/src/Appwrite/Platform/Services/Tasks.php index 6aba8fd8e4..7a0d5b60ac 100644 --- a/src/Appwrite/Platform/Services/Tasks.php +++ b/src/Appwrite/Platform/Services/Tasks.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Services; -use Appwrite\Platform\Tasks\DevGenerateTranslations; use Appwrite\Platform\Tasks\Doctor; use Appwrite\Platform\Tasks\Install; use Appwrite\Platform\Tasks\Maintenance; @@ -26,7 +25,6 @@ class Tasks extends Service { $this->type = Service::TYPE_TASK; $this - ->addAction(DevGenerateTranslations::getName(), new DevGenerateTranslations()) ->addAction(Doctor::getName(), new Doctor()) ->addAction(Install::getName(), new Install()) ->addAction(Maintenance::getName(), new Maintenance()) From d2b8d3ae7a324aaf6a6e33552ae218b757c1f2a4 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 7 Aug 2024 14:24:52 -0400 Subject: [PATCH 156/195] chore: finishing merge --- app/worker.php | 2 +- src/Appwrite/Auth/Validator/MockNumber.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/worker.php b/app/worker.php index 3ce0281fe7..9fc75b2a61 100644 --- a/app/worker.php +++ b/app/worker.php @@ -115,7 +115,7 @@ if (!isset($args[1])) { $workerName = $args[0]; if (\str_starts_with($workerName, 'databases')) { - $queueName = System::getEnv('_APP_QUEUE_NAME', 'db_main'); + $queueName = System::getEnv('_APP_QUEUE_NAME', 'database_db_main'); } else { $queueName = System::getEnv('_APP_QUEUE_NAME', 'v1-' . strtolower($workerName)); } diff --git a/src/Appwrite/Auth/Validator/MockNumber.php b/src/Appwrite/Auth/Validator/MockNumber.php index 900f1ba215..af976f77f8 100644 --- a/src/Appwrite/Auth/Validator/MockNumber.php +++ b/src/Appwrite/Auth/Validator/MockNumber.php @@ -45,7 +45,7 @@ class MockNumber extends Validator return false; } - $otp = new Validator\Text(6, 6, Text::NUMBERS); + $otp = new Validator\Text(6, 6, Validator\Text::NUMBERS); if (!$otp->isValid($value['otp'])) { $this->message = 'Invalid OTP. Please make sure the OTP is a 6 digit number'; return false; From 579aa5bd1af0f5165ff44ae790ab492cfc2e3cc4 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 7 Aug 2024 14:50:06 -0400 Subject: [PATCH 157/195] fixing: Adding missing models from merge --- src/Appwrite/Utopia/Response/Models.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Appwrite/Utopia/Response/Models.php b/src/Appwrite/Utopia/Response/Models.php index 778a077e4b..6e2bd4d809 100644 --- a/src/Appwrite/Utopia/Response/Models.php +++ b/src/Appwrite/Utopia/Response/Models.php @@ -86,7 +86,10 @@ use Appwrite\Utopia\Response\Model\Subscriber; use Appwrite\Utopia\Response\Model\Target; use Appwrite\Utopia\Response\Model\Team; use Appwrite\Utopia\Response\Model\TemplateEmail; +use Appwrite\Utopia\Response\Model\TemplateFunction; +use Appwrite\Utopia\Response\Model\TemplateRuntime; use Appwrite\Utopia\Response\Model\TemplateSMS; +use Appwrite\Utopia\Response\Model\TemplateVariable; use Appwrite\Utopia\Response\Model\Token; use Appwrite\Utopia\Response\Model\Topic; use Appwrite\Utopia\Response\Model\UsageBuckets; @@ -197,6 +200,9 @@ class Models self::setModel(new Team()); self::setModel(new Membership()); self::setModel(new Func()); + self::setModel(new TemplateFunction()); + self::setModel(new TemplateRuntime()); + self::setModel(new TemplateVariable()); self::setModel(new Installation()); self::setModel(new ProviderRepository()); self::setModel(new Detection()); From 686b5439a256ff5ad4db34c325e0b131c2799435 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:22:53 -0400 Subject: [PATCH 158/195] chore: merge --- app/http.php | 8 +- app/init.php | 3508 +++++++++++------------ app/init/config.php | 1 + app/init/constants.php | 14 +- app/init2.php | 27 +- composer.json | 2 +- src/Appwrite/Utopia/Response.php | 5 +- src/Appwrite/Utopia/Response/Models.php | 12 +- 8 files changed, 1801 insertions(+), 1776 deletions(-) diff --git a/app/http.php b/app/http.php index 307e451421..9ddb62c273 100644 --- a/app/http.php +++ b/app/http.php @@ -6,13 +6,7 @@ require_once __DIR__ . '/controllers/general.php'; use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Swoole\Constant; -use Swoole\Http\Request as SwooleRequest; -use Swoole\Http\Response as SwooleResponse; -use Swoole\Http\Server; -use Swoole\Process; -use Utopia\Abuse\Adapters\Database\TimeLimit; -use Utopia\App; +use Utopia\Abuse\Adapters\Database as TimeLimit; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\CLI\Console; diff --git a/app/init.php b/app/init.php index 08cb09b68b..9a6b608507 100644 --- a/app/init.php +++ b/app/init.php @@ -1,1755 +1,1755 @@ $value], JSON_PRESERVE_ZERO_FRACTION); - }, - function (mixed $value) { - if (is_null($value)) { - return; - } - - return json_decode($value, true)['value']; - } -); - -Database::addFilter( - 'enum', - function (mixed $value, Document $attribute) { - if ($attribute->isSet('elements')) { - $attribute->removeAttribute('elements'); - } - - return $value; - }, - function (mixed $value, Document $attribute) { - $formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true); - if (isset($formatOptions['elements'])) { - $attribute->setAttribute('elements', $formatOptions['elements']); - } - - return $value; - } -); - -Database::addFilter( - 'range', - function (mixed $value, Document $attribute) { - if ($attribute->isSet('min')) { - $attribute->removeAttribute('min'); - } - if ($attribute->isSet('max')) { - $attribute->removeAttribute('max'); - } - - return $value; - }, - function (mixed $value, Document $attribute) { - $formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true); - if (isset($formatOptions['min']) || isset($formatOptions['max'])) { - $attribute - ->setAttribute('min', $formatOptions['min']) - ->setAttribute('max', $formatOptions['max']); - } - - return $value; - } -); - -Database::addFilter( - 'subQueryAttributes', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - $attributes = $database->find('attributes', [ - Query::equal('collectionInternalId', [$document->getInternalId()]), - Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), - Query::limit($database->getLimitForAttributes()), - ]); - - foreach ($attributes as $attribute) { - if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { - $options = $attribute->getAttribute('options'); - foreach ($options as $key => $value) { - $attribute->setAttribute($key, $value); - } - $attribute->removeAttribute('options'); - } - } - - return $attributes; - } -); - -Database::addFilter( - 'subQueryIndexes', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('indexes', [ - Query::equal('collectionInternalId', [$document->getInternalId()]), - Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), - Query::limit($database->getLimitForIndexes()), - ]); - } -); - -Database::addFilter( - 'subQueryPlatforms', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('platforms', [ - Query::equal('projectInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'subQueryKeys', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('keys', [ - Query::equal('projectInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'subQueryWebhooks', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('webhooks', [ - Query::equal('projectInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'subQuerySessions', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database->find('sessions', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryTokens', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('tokens', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryChallenges', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('challenges', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryAuthenticators', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('authenticators', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryMemberships', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('memberships', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY), - ])); - } -); - -Database::addFilter( - 'subQueryVariables', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('variables', [ - Query::equal('resourceInternalId', [$document->getInternalId()]), - Query::equal('resourceType', ['function']), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - } -); - -Database::addFilter( - 'encrypt', - function (mixed $value) { - $key = System::getEnv('_APP_OPENSSL_KEY_V1'); - $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); - $tag = null; - - return json_encode([ - 'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag), - 'method' => OpenSSL::CIPHER_AES_128_GCM, - 'iv' => \bin2hex($iv), - 'tag' => \bin2hex($tag ?? ''), - 'version' => '1', - ]); - }, - function (mixed $value) { - if (is_null($value)) { - return; - } - $value = json_decode($value, true); - $key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']); - - return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag'])); - } -); - -Database::addFilter( - 'subQueryProjectVariables', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return $database - ->find('variables', [ - Query::equal('resourceType', ['project']), - Query::limit(APP_LIMIT_SUBQUERY) - ]); - } -); - -Database::addFilter( - 'userSearch', - function (mixed $value, Document $user) { - $searchValues = [ - $user->getId(), - $user->getAttribute('email', ''), - $user->getAttribute('name', ''), - $user->getAttribute('phone', '') - ]; - - foreach ($user->getAttribute('labels', []) as $label) { - $searchValues[] = 'label:' . $label; - } - - $search = implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'subQueryTargets', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - return Authorization::skip(fn () => $database - ->find('targets', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBQUERY) - ])); - } -); - -Database::addFilter( - 'subQueryTopicTargets', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - $targetIds = Authorization::skip(fn () => \array_map( - fn ($document) => $document->getAttribute('targetInternalId'), - $database->find('subscribers', [ - Query::equal('topicInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY) - ]) - )); - if (\count($targetIds) > 0) { - return $database->skipValidation(fn () => $database->find('targets', [ - Query::equal('$internalId', $targetIds) - ])); - } - return []; - } -); - -Database::addFilter( - 'providerSearch', - function (mixed $value, Document $provider) { - $searchValues = [ - $provider->getId(), - $provider->getAttribute('name', ''), - $provider->getAttribute('provider', ''), - $provider->getAttribute('type', '') - ]; - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'topicSearch', - function (mixed $value, Document $topic) { - $searchValues = [ - $topic->getId(), - $topic->getAttribute('name', ''), - $topic->getAttribute('description', ''), - ]; - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'messageSearch', - function (mixed $value, Document $message) { - $searchValues = [ - $message->getId(), - $message->getAttribute('description', ''), - $message->getAttribute('status', ''), - ]; - - $data = \json_decode($message->getAttribute('data', []), true); - $providerType = $message->getAttribute('providerType', ''); - - if ($providerType === MESSAGE_TYPE_EMAIL) { - $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); - } elseif ($providerType === MESSAGE_TYPE_SMS) { - $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); - } else { - $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); - } - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -/** - * DB Formats - */ -Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () { - return new Email(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () { - return new DatetimeValidator(); -}, Database::VAR_DATETIME); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) { - $elements = $attribute['formatOptions']['elements']; - return new WhiteList($elements, true); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () { - return new IP(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () { - return new URL(); -}, Database::VAR_STRING); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) { - $min = $attribute['formatOptions']['min'] ?? -INF; - $max = $attribute['formatOptions']['max'] ?? INF; - return new Range($min, $max, Range::TYPE_INTEGER); -}, Database::VAR_INTEGER); - -Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) { - $min = $attribute['formatOptions']['min'] ?? -INF; - $max = $attribute['formatOptions']['max'] ?? INF; - return new Range($min, $max, Range::TYPE_FLOAT); -}, Database::VAR_FLOAT); - -/* - * Registry - */ -$register->set('logger', function () { - // Register error logger - $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); - $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); - - try { - $loggingProvider = new DSN($providerConfig ?? ''); - - $providerName = $loggingProvider->getScheme(); - $providerConfig = match ($providerName) { - 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()], - 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], - default => ['key' => $loggingProvider->getHost()], - }; - } catch (Throwable $th) { - // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables - Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage()); - $configChunks = \explode(";", $providerConfig); - - $providerConfig = match ($providerName) { - 'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',], - 'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''], - default => ['key' => $providerConfig], - }; - } - - if (empty($providerName) || empty($providerConfig)) { - return; - } - - if (!Logger::hasProvider($providerName)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); - } - - try { - $adapter = match ($providerName) { - 'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']), - 'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']), - 'raygun' => new Raygun($providerConfig['key']), - 'appsignal' => new AppSignal($providerConfig['key']), - default => null - }; - } catch (Throwable $th) { - $adapter = null; - } - - if($adapter === null) { - Console::error("Logging provider not supported. Logging is disabled"); - return; - } - - return new Logger($adapter); -}); - -$register->set('pools', function () { - $group = new Group(); - - $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ - 'scheme' => 'mariadb', - 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), - 'port' => System::getEnv('_APP_DB_PORT', '3306'), - 'user' => System::getEnv('_APP_DB_USER', ''), - 'pass' => System::getEnv('_APP_DB_PASS', ''), - 'path' => System::getEnv('_APP_DB_SCHEMA', ''), - ]); - $fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([ - 'scheme' => 'redis', - 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => System::getEnv('_APP_REDIS_USER', ''), - 'pass' => System::getEnv('_APP_REDIS_PASS', ''), - ]); - - $connections = [ - 'console' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), - 'multiple' => false, - 'schemes' => ['mariadb', 'mysql'], - ], - 'database' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), - 'multiple' => true, - 'schemes' => ['mariadb', 'mysql'], - ], - 'queue' => [ - 'type' => 'queue', - 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'pubsub' => [ - 'type' => 'pubsub', - 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'cache' => [ - 'type' => 'cache', - 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), - 'multiple' => true, - 'schemes' => ['redis'], - ], - ]; - - $maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151); - $instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14); - - $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; - - if ($multiprocessing) { - $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); - } else { - $workerCount = 1; - } - - if ($workerCount > $instanceConnections) { - throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); - } - - $poolSize = (int)($instanceConnections / $workerCount); - - foreach ($connections as $key => $connection) { - $type = $connection['type'] ?? ''; - $multiple = $connection['multiple'] ?? false; - $schemes = $connection['schemes'] ?? []; - $config = []; - $dsns = explode(',', $connection['dsns'] ?? ''); - foreach ($dsns as &$dsn) { - $dsn = explode('=', $dsn); - $name = ($multiple) ? $key . '_' . $dsn[0] : $key; - $dsn = $dsn[1] ?? ''; - $config[] = $name; - if (empty($dsn)) { - //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); - continue; - } - - $dsn = new DSN($dsn); - $dsnHost = $dsn->getHost(); - $dsnPort = $dsn->getPort(); - $dsnUser = $dsn->getUser(); - $dsnPass = $dsn->getPassword(); - $dsnScheme = $dsn->getScheme(); - $dsnDatabase = $dsn->getPath(); - - if (!in_array($dsnScheme, $schemes)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); - } - - /** - * Get Resource - * - * Creation could be reused across connection types like database, cache, queue, etc. - * - * Resource assignment to an adapter will happen below. - */ - $resource = match ($dsnScheme) { - 'mysql', - 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { - return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( - PDO::ATTR_TIMEOUT => 3, // Seconds - PDO::ATTR_PERSISTENT => true, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - PDO::ATTR_EMULATE_PREPARES => true, - PDO::ATTR_STRINGIFY_FETCHES => true - )); - }); - }, - 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { - $redis = new Redis(); - @$redis->pconnect($dsnHost, (int)$dsnPort); - if ($dsnPass) { - $redis->auth($dsnPass); - } - $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); - - return $redis; - }, - default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'), - }; - - $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { - // Get Adapter - switch ($type) { - case 'database': - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($resource()), - 'mysql' => new MySQL($resource()), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - break; - case 'pubsub': - $adapter = $resource(); - break; - case 'queue': - $adapter = match ($dsn->getScheme()) { - 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), - default => null - }; - break; - case 'cache': - $adapter = match ($dsn->getScheme()) { - 'redis' => new RedisCache($resource()), - default => null - }; - break; - - default: - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); - } - - return $adapter; - }); - - $group->add($pool); - } - - Config::setParam('pools-' . $key, $config); - } - - return $group; -}); - -$register->set('db', function () { - // This is usually for our workers or CLI commands scope - $dbHost = System::getEnv('_APP_DB_HOST', ''); - $dbPort = System::getEnv('_APP_DB_PORT', ''); - $dbUser = System::getEnv('_APP_DB_USER', ''); - $dbPass = System::getEnv('_APP_DB_PASS', ''); - $dbScheme = System::getEnv('_APP_DB_SCHEMA', ''); - - return new PDO( - "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", - $dbUser, - $dbPass, - SQL::getPDOAttributes() - ); -}); - -$register->set('smtp', function () { - $mail = new PHPMailer(true); - - $mail->isSMTP(); - - $username = System::getEnv('_APP_SMTP_USERNAME'); - $password = System::getEnv('_APP_SMTP_PASSWORD'); - - $mail->XMailer = 'Appwrite Mailer'; - $mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp'); - $mail->Port = System::getEnv('_APP_SMTP_PORT', 25); - $mail->SMTPAuth = !empty($username) && !empty($password); - $mail->Username = $username; - $mail->Password = $password; - $mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', ''); - $mail->SMTPAutoTLS = false; - $mail->CharSet = 'UTF-8'; - - $from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); - $email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - - $mail->setFrom($email, $from); - $mail->addReplyTo($email, $from); - - $mail->isHTML(true); - - return $mail; -}); -$register->set('geodb', function () { - return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb'); -}); -$register->set('passwordsDictionary', function () { - $content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords'); - $content = explode("\n", $content); - $content = array_flip($content); - return $content; -}); -$register->set('promiseAdapter', function () { - return new Swoole(); -}); -$register->set('hooks', function () { - return new Hooks(); -}); -/* - * Localization - */ -Locale::$exceptions = false; - -$locales = Config::getParam('locale-codes', []); - -foreach ($locales as $locale) { - $code = $locale['code']; - - $path = __DIR__ . '/config/locale/translations/' . $code . '.json'; - - if (!\file_exists($path)) { - $path = __DIR__ . '/config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar` - if (!\file_exists($path)) { - $path = __DIR__ . '/config/locale/translations/en.json'; // if none translation exists, use default from `en.json` - } - } - - Locale::setLanguageFromJSON($code, $path); -} - -\stream_context_set_default([ // Set global user agent and http settings - 'http' => [ - 'method' => 'GET', - 'user_agent' => \sprintf( - APP_USERAGENT, - System::getEnv('_APP_VERSION', 'UNKNOWN'), - System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)) - ), - 'timeout' => 2, - ], -]); - -// Runtime Execution -App::setResource('log', fn () => new Log()); -App::setResource('logger', function ($register) { - return $register->get('logger'); -}, ['register']); - -App::setResource('hooks', function ($register) { - return $register->get('hooks'); -}, ['register']); - -App::setResource('register', fn () => $register); -App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); - -App::setResource('localeCodes', function () { - return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); -}); - -// Queues -App::setResource('queue', function (Group $pools) { - return $pools->get('queue')->pop()->getResource(); -}, ['pools']); -App::setResource('queueForMessaging', function (Connection $queue) { - return new Messaging($queue); -}, ['queue']); -App::setResource('queueForMails', function (Connection $queue) { - return new Mail($queue); -}, ['queue']); -App::setResource('queueForBuilds', function (Connection $queue) { - return new Build($queue); -}, ['queue']); -App::setResource('queueForDatabase', function (Connection $queue) { - return new EventDatabase($queue); -}, ['queue']); -App::setResource('queueForDeletes', function (Connection $queue) { - return new Delete($queue); -}, ['queue']); -App::setResource('queueForEvents', function (Connection $queue) { - return new Event($queue); -}, ['queue']); -App::setResource('queueForAudits', function (Connection $queue) { - return new Audit($queue); -}, ['queue']); -App::setResource('queueForFunctions', function (Connection $queue) { - return new Func($queue); -}, ['queue']); -App::setResource('queueForUsage', function (Connection $queue) { - return new Usage($queue); -}, ['queue']); -App::setResource('queueForCertificates', function (Connection $queue) { - return new Certificate($queue); -}, ['queue']); -App::setResource('queueForMigrations', function (Connection $queue) { - return new Migration($queue); -}, ['queue']); -App::setResource('clients', function ($request, $console, $project) { - $console->setAttribute('platforms', [ // Always allow current host - '$collection' => ID::custom('platforms'), - 'name' => 'Current Host', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => $request->getHostname(), - ], Document::SET_TYPE_APPEND); - - $hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', '')); - $validator = new Hostname(); - foreach ($hostnames as $hostname) { - $hostname = trim($hostname); - if (!$validator->isValid($hostname)) { - continue; - } - $console->setAttribute('platforms', [ - '$collection' => ID::custom('platforms'), - 'type' => Origin::CLIENT_TYPE_WEB, - 'name' => $hostname, - 'hostname' => $hostname, - ], Document::SET_TYPE_APPEND); - } - - /** - * Get All verified client URLs for both console and current projects - * + Filter for duplicated entries - */ - $clientsConsole = \array_map( - fn ($node) => $node['hostname'], - \array_filter( - $console->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname'])) - ) - ); - - $clients = $clientsConsole; - $platforms = $project->getAttribute('platforms', []); - - foreach ($platforms as $node) { - if ( - isset($node['type']) && - ($node['type'] === Origin::CLIENT_TYPE_WEB || - $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && - !empty($node['hostname']) - ) { - $clients[] = $node['hostname']; - } - } - - return \array_unique($clients); -}, ['request', 'console', 'project']); - -App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $project */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var string $mode */ - - Authorization::setDefaultStatus(true); - - Auth::setCookieName('a_session_' . $project->getId()); - - if (APP_MODE_ADMIN === $mode) { - Auth::setCookieName('a_session_' . $console->getId()); - } - - $session = Auth::decodeSession( - $request->getCookie( - Auth::$cookieName, // Get sessions - $request->getCookie(Auth::$cookieName . '_legacy', '') - ) - ); - - // Get session from header for SSR clients - if (empty($session['id']) && empty($session['secret'])) { - $sessionHeader = $request->getHeader('x-appwrite-session', ''); - - if (!empty($sessionHeader)) { - $session = Auth::decodeSession($sessionHeader); - } - } - - // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies - if ($response) { - $response->addHeader('X-Debug-Fallback', 'false'); - } - - if (empty($session['id']) && empty($session['secret'])) { - if ($response) { - $response->addHeader('X-Debug-Fallback', 'true'); - } - $fallback = $request->getHeader('x-fallback-cookies', ''); - $fallback = \json_decode($fallback, true); - $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); - } - - Auth::$unique = $session['id'] ?? ''; - Auth::$secret = $session['secret'] ?? ''; - - if (APP_MODE_ADMIN !== $mode) { - if ($project->isEmpty()) { - $user = new Document([]); - } else { - if ($project->getId() === 'console') { - $user = $dbForConsole->getDocument('users', Auth::$unique); - } else { - $user = $dbForProject->getDocument('users', Auth::$unique); - } - } - } else { - $user = $dbForConsole->getDocument('users', Auth::$unique); - } - - if ( - $user->isEmpty() // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) - ) { // Validate user has valid login token - $user = new Document([]); - } - - if (APP_MODE_ADMIN === $mode) { - if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { - Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. - } else { - $user = new Document([]); - } - } - - $authJWT = $request->getHeader('x-appwrite-jwt', ''); - - if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication - $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); - - try { - $payload = $jwt->decode($authJWT); - } catch (JWTException $error) { - throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); - } - - $jwtUserId = $payload['userId'] ?? ''; - if (!empty($jwtUserId)) { - $user = $dbForProject->getDocument('users', $jwtUserId); - } - - $jwtSessionId = $payload['sessionId'] ?? ''; - if(!empty($jwtSessionId)) { - if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token - $user = new Document([]); - } - } - } - - $dbForProject->setMetadata('user', $user->getId()); - $dbForConsole->setMetadata('user', $user->getId()); - - return $user; -}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); - -App::setResource('project', function ($dbForConsole, $request, $console) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var Utopia\Database\Document $console */ - - $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); - - if (empty($projectId) || $projectId === 'console') { - return $console; - } - - $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - - return $project; -}, ['dbForConsole', 'request', 'console']); - -App::setResource('session', function (Document $user) { - if ($user->isEmpty()) { - return; - } - - $sessions = $user->getAttribute('sessions', []); - $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); - - if (!$sessionId) { - return; - } - - foreach ($sessions as $session) {/** @var Document $session */ - if ($sessionId === $session->getId()) { - return $session; - } - } - - return; -}, ['user']); - -App::setResource('console', function () { - return new Document([ - '$id' => ID::custom('console'), - '$internalId' => ID::custom('console'), - 'name' => 'Appwrite', - '$collection' => ID::custom('projects'), - 'description' => 'Appwrite core engine', - 'logo' => '', - 'teamId' => -1, - 'webhooks' => [], - 'keys' => [], - 'platforms' => [ - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Localhost', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => 'localhost', - ], // Current host is added on app init - ], - 'legalName' => '', - 'legalCountry' => '', - 'legalState' => '', - 'legalCity' => '', - 'legalAddress' => '', - 'legalTaxId' => '', - 'auths' => [ - 'mockNumbers' => [], - 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', - 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds - 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' - ], - 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], - 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], - 'oAuthProviders' => [ - 'githubEnabled' => true, - 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), - 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') - ], - ]); -}, []); - -App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); - - $database - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; -}, ['pools', 'dbForConsole', 'cache', 'project']); - -App::setResource('dbForConsole', function (Group $pools, Cache $cache) { - $dbAdapter = $pools - ->get('console') - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); - - $database - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - return $database; -}, ['pools', 'cache']); - -App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $configure = (function (Database $database) use ($project, $dsn) { - $database - ->setMetadata('host', \gethostname()) - ->setMetadata('project', $project->getId()) - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - }); - - if (isset($databases[$dsn->getHost()])) { - $database = $databases[$dsn->getHost()]; - $configure($database); - return $database; - } - - $dbAdapter = $pools - ->get($dsn->getHost()) - ->pop() - ->getResource(); - - $database = new Database($dbAdapter, $cache); - $databases[$dsn->getHost()] = $database; - $configure($database); - - return $database; - }; -}, ['pools', 'dbForConsole', 'cache']); - -App::setResource('cache', function (Group $pools) { - $list = Config::getParam('pools-cache', []); - $adapters = []; - - foreach ($list as $value) { - $adapters[] = $pools - ->get($value) - ->pop() - ->getResource() - ; - } - - return new Cache(new Sharding($adapters)); -}, ['pools']); - -App::setResource('deviceForLocal', function () { - return new Local(); -}); - -App::setResource('deviceForFiles', function ($project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); -}, ['project']); - -App::setResource('deviceForFunctions', function ($project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); -}, ['project']); - -App::setResource('deviceForBuilds', function ($project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); -}, ['project']); - -function getDevice($root): Device -{ - $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); - - if (!empty($connection)) { - $acl = 'private'; - $device = Storage::DEVICE_LOCAL; - $accessKey = ''; - $accessSecret = ''; - $bucket = ''; - $region = ''; - - try { - $dsn = new DSN($connection); - $device = $dsn->getScheme(); - $accessKey = $dsn->getUser() ?? ''; - $accessSecret = $dsn->getPassword() ?? ''; - $bucket = $dsn->getPath() ?? ''; - $region = $dsn->getParam('region'); - } catch (\Throwable $e) { - Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); - } - - switch ($device) { - case Storage::DEVICE_S3: - return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case STORAGE::DEVICE_DO_SPACES: - $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); - $device->setHttpVersion(S3::HTTP_VERSION_1_1); - return $device; - case Storage::DEVICE_BACKBLAZE: - return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LINODE: - return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_WASABI: - return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - } - } else { - switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - case Storage::DEVICE_S3: - $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); - $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); - $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); - $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); - $s3Acl = 'private'; - return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); - case Storage::DEVICE_DO_SPACES: - $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); - $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); - $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); - $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); - $doSpacesAcl = 'private'; - $device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); - $device->setHttpVersion(S3::HTTP_VERSION_1_1); - return $device; - case Storage::DEVICE_BACKBLAZE: - $backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); - $backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); - $backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); - $backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); - $backblazeAcl = 'private'; - return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); - case Storage::DEVICE_LINODE: - $linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); - $linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', ''); - $linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', ''); - $linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); - $linodeAcl = 'private'; - return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); - case Storage::DEVICE_WASABI: - $wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); - $wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', ''); - $wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', ''); - $wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); - $wasabiAcl = 'private'; - return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); - } - } -} - -App::setResource('mode', function ($request) { - /** @var Appwrite\Utopia\Request $request */ - - /** - * Defines the mode for the request: - * - 'default' => Requests for Client and Server Side - * - 'admin' => Request from the Console on non-console projects - */ - return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); -}, ['request']); - -App::setResource('geodb', function ($register) { - /** @var Utopia\Registry\Registry $register */ - return $register->get('geodb'); -}, ['register']); - -App::setResource('passwordsDictionary', function ($register) { - /** @var Utopia\Registry\Registry $register */ - return $register->get('passwordsDictionary'); -}, ['register']); - - -App::setResource('servers', function () { - $platforms = Config::getParam('platforms'); - $server = $platforms[APP_PLATFORM_SERVER]; - - $languages = array_map(function ($language) { - return strtolower($language['name']); - }, $server['sdks']); - - return $languages; -}); - -App::setResource('promiseAdapter', function ($register) { - return $register->get('promiseAdapter'); -}, ['register']); - -App::setResource('schema', function ($utopia, $dbForProject) { - - $complexity = function (int $complexity, array $args) { - $queries = Query::parseQueries($args['queries'] ?? []); - $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; - $limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT; - - return $complexity * $limit; - }; - - $attributes = function (int $limit, int $offset) use ($dbForProject) { - $attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [ - Query::limit($limit), - Query::offset($offset), - ])); - - return \array_map(function ($attr) { - return $attr->getArrayCopy(); - }, $attrs); - }; - - $urls = [ - 'list' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents"; - }, - 'create' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents"; - }, - 'read' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; - }, - 'update' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; - }, - 'delete' => function (string $databaseId, string $collectionId, array $args) { - return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; - }, - ]; - - $params = [ - 'list' => function (string $databaseId, string $collectionId, array $args) { - return [ 'queries' => $args['queries']]; - }, - 'create' => function (string $databaseId, string $collectionId, array $args) { - $id = $args['id'] ?? 'unique()'; - $permissions = $args['permissions'] ?? null; - - unset($args['id']); - unset($args['permissions']); - - // Order must be the same as the route params - return [ - 'databaseId' => $databaseId, - 'documentId' => $id, - 'collectionId' => $collectionId, - 'data' => $args, - 'permissions' => $permissions, - ]; - }, - 'update' => function (string $databaseId, string $collectionId, array $args) { - $documentId = $args['id']; - $permissions = $args['permissions'] ?? null; - - unset($args['id']); - unset($args['permissions']); - - // Order must be the same as the route params - return [ - 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'documentId' => $documentId, - 'data' => $args, - 'permissions' => $permissions, - ]; - }, - ]; - - return Schema::build( - $utopia, - $complexity, - $attributes, - $urls, - $params, - ); -}, ['utopia', 'dbForProject']); - -App::setResource('contributors', function () { - $path = 'app/config/contributors.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -App::setResource('employees', function () { - $path = 'app/config/employees.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -App::setResource('heroes', function () { - $path = 'app/config/heroes.json'; - $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; - return $list; -}); - -App::setResource('gitHub', function (Cache $cache) { - return new VcsGitHub($cache); -}, ['cache']); - -App::setResource('requestTimestamp', function ($request) { - //TODO: Move this to the Request class itself - $timestampHeader = $request->getHeader('x-appwrite-timestamp'); - $requestTimestamp = null; - if (!empty($timestampHeader)) { - try { - $requestTimestamp = new \DateTime($timestampHeader); - } catch (\Throwable $e) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value'); - } - } - return $requestTimestamp; -}, ['request']); -App::setResource('plan', function (array $plan = []) { - return []; -}); +// +///** +// * Init +// * +// * Initializes both Appwrite API entry point, queue workers, and CLI tasks. +// * Set configuration, framework resources & app constants +// * +// */ +// +//if (\file_exists(__DIR__ . '/../vendor/autoload.php')) { +// require_once __DIR__ . '/../vendor/autoload.php'; +//} +// +//\ini_set('memory_limit', '512M'); +//\ini_set('display_errors', 1); +//\ini_set('display_startup_errors', 1); +//\ini_set('default_socket_timeout', -1); +//\error_reporting(E_ALL); +// +//use Ahc\Jwt\JWT; +//use Ahc\Jwt\JWTException; +//use Appwrite\Auth\Auth; +//use Appwrite\Event\Audit; +//use Appwrite\Event\Build; +//use Appwrite\Event\Certificate; +//use Appwrite\Event\Database as EventDatabase; +//use Appwrite\Event\Delete; +//use Appwrite\Event\Event; +//use Appwrite\Event\Func; +//use Appwrite\Event\Mail; +//use Appwrite\Event\Messaging; +//use Appwrite\Event\Migration; +//use Appwrite\Event\Usage; +//use Appwrite\Extend\Exception; +//use Appwrite\Functions\Specification; +//use Appwrite\GraphQL\Promises\Adapter\Swoole; +//use Appwrite\GraphQL\Schema; +//use Appwrite\Hooks\Hooks; +//use Appwrite\Network\Validator\Email; +//use Appwrite\Network\Validator\Origin; +//use Appwrite\OpenSSL\OpenSSL; +//use Appwrite\URL\URL as AppwriteURL; +//use MaxMind\Db\Reader; +//use PHPMailer\PHPMailer\PHPMailer; +//use Swoole\Database\PDOProxy; +//use Utopia\App; +//use Utopia\Cache\Adapter\Redis as RedisCache; +//use Utopia\Cache\Adapter\Sharding; +//use Utopia\Cache\Cache; +//use Utopia\CLI\Console; +//use Utopia\Config\Config; +//use Utopia\Database\Adapter\MariaDB; +//use Utopia\Database\Adapter\MySQL; +//use Utopia\Database\Adapter\SQL; +//use Utopia\Database\Database; +//use Utopia\Database\Document; +//use Utopia\Database\Helpers\ID; +//use Utopia\Database\Query; +//use Utopia\Database\Validator\Authorization; +//use Utopia\Database\Validator\Datetime as DatetimeValidator; +//use Utopia\Database\Validator\Structure; +//use Utopia\Domains\Validator\PublicDomain; +//use Utopia\DSN\DSN; +//use Utopia\Locale\Locale; +//use Utopia\Logger\Adapter\AppSignal; +//use Utopia\Logger\Adapter\LogOwl; +//use Utopia\Logger\Adapter\Raygun; +//use Utopia\Logger\Adapter\Sentry; +//use Utopia\Logger\Log; +//use Utopia\Logger\Logger; +//use Utopia\Pools\Group; +//use Utopia\Pools\Pool; +//use Utopia\Queue; +//use Utopia\Queue\Connection; +//use Utopia\Registry\Registry; +//use Utopia\Storage\Device; +//use Utopia\Storage\Device\Backblaze; +//use Utopia\Storage\Device\DOSpaces; +//use Utopia\Storage\Device\Linode; +//use Utopia\Storage\Device\Local; +//use Utopia\Storage\Device\S3; +//use Utopia\Storage\Device\Wasabi; +//use Utopia\Storage\Storage; +//use Utopia\System\System; +//use Utopia\Validator\Hostname; +//use Utopia\Validator\IP; +//use Utopia\Validator\Range; +//use Utopia\Validator\URL; +//use Utopia\Validator\WhiteList; +//use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; +// +//const APP_NAME = 'Appwrite'; +//const APP_DOMAIN = 'appwrite.io'; +//const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address +//const APP_EMAIL_SECURITY = ''; // Default security email address +//const APP_USERAGENT = APP_NAME . '-Server v%s. Please report abuse at %s'; +//const APP_MODE_DEFAULT = 'default'; +//const APP_MODE_ADMIN = 'admin'; +//const APP_PAGING_LIMIT = 12; +//const APP_LIMIT_COUNT = 5000; +//const APP_LIMIT_USERS = 10_000; +//const APP_LIMIT_USER_PASSWORD_HISTORY = 20; +//const APP_LIMIT_USER_SESSIONS_MAX = 100; +//const APP_LIMIT_USER_SESSIONS_DEFAULT = 10; +//const APP_LIMIT_ANTIVIRUS = 20_000_000; //20MB +//const APP_LIMIT_ENCRYPTION = 20_000_000; //20MB +//const APP_LIMIT_COMPRESSION = 20_000_000; //20MB +//const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value +//const APP_LIMIT_ARRAY_LABELS_SIZE = 1000; // Default maximum of how many labels elements can there be in API parameter that expects array value +//const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length. +//const APP_LIMIT_SUBQUERY = 1000; +//const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000; +//const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period +//const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds +//const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls +//const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours +//const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours +//const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours +//const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours +//const APP_CACHE_BUSTER = 4318; +//const APP_VERSION_STABLE = '1.6.0'; +//const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; +//const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; +//const APP_DATABASE_ATTRIBUTE_IP = 'ip'; +//const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime'; +//const APP_DATABASE_ATTRIBUTE_URL = 'url'; +//const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange'; +//const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange'; +//const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char +//const APP_DATABASE_TIMEOUT_MILLISECONDS = 15_000; +//const APP_STORAGE_UPLOADS = '/storage/uploads'; +//const APP_STORAGE_FUNCTIONS = '/storage/functions'; +//const APP_STORAGE_BUILDS = '/storage/builds'; +//const APP_STORAGE_CACHE = '/storage/cache'; +//const APP_STORAGE_CERTIFICATES = '/storage/certificates'; +//const APP_STORAGE_CONFIG = '/storage/config'; +//const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT` +//const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite'; +//const APP_SOCIAL_TWITTER_HANDLE = 'appwrite'; +//const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io'; +//const APP_SOCIAL_LINKEDIN = 'https://www.linkedin.com/company/appwrite'; +//const APP_SOCIAL_INSTAGRAM = 'https://www.instagram.com/appwrite.io'; +//const APP_SOCIAL_GITHUB = 'https://github.com/appwrite'; +//const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord'; +//const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244'; +//const APP_SOCIAL_DEV = 'https://dev.to/appwrite'; +//const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite'; +//const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1'; +//const APP_HOSTNAME_INTERNAL = 'appwrite'; +//const APP_FUNCTION_SPECIFICATION_DEFAULT = Specification::S_05VCPU_512MB; +//const APP_FUNCTION_CPUS_DEFAULT = 0.5; +//const APP_FUNCTION_MEMORY_DEFAULT = 512; +// +//// Database Reconnect +//const DATABASE_RECONNECT_SLEEP = 2; +//const DATABASE_RECONNECT_MAX_ATTEMPTS = 10; +// +//// Database Worker Types +//const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute'; +//const DATABASE_TYPE_CREATE_INDEX = 'createIndex'; +//const DATABASE_TYPE_DELETE_ATTRIBUTE = 'deleteAttribute'; +//const DATABASE_TYPE_DELETE_INDEX = 'deleteIndex'; +//const DATABASE_TYPE_DELETE_COLLECTION = 'deleteCollection'; +//const DATABASE_TYPE_DELETE_DATABASE = 'deleteDatabase'; +// +//// Build Worker Types +//const BUILD_TYPE_DEPLOYMENT = 'deployment'; +//const BUILD_TYPE_RETRY = 'retry'; +// +//// Deletion Types +//const DELETE_TYPE_DATABASES = 'databases'; +//const DELETE_TYPE_DOCUMENT = 'document'; +//const DELETE_TYPE_COLLECTIONS = 'collections'; +//const DELETE_TYPE_PROJECTS = 'projects'; +//const DELETE_TYPE_FUNCTIONS = 'functions'; +//const DELETE_TYPE_DEPLOYMENTS = 'deployments'; +//const DELETE_TYPE_USERS = 'users'; +//const DELETE_TYPE_TEAM_PROJECTS = 'teams_projects'; +//const DELETE_TYPE_EXECUTIONS = 'executions'; +//const DELETE_TYPE_AUDIT = 'audit'; +//const DELETE_TYPE_ABUSE = 'abuse'; +//const DELETE_TYPE_USAGE = 'usage'; +//const DELETE_TYPE_REALTIME = 'realtime'; +//const DELETE_TYPE_BUCKETS = 'buckets'; +//const DELETE_TYPE_INSTALLATIONS = 'installations'; +//const DELETE_TYPE_RULES = 'rules'; +//const DELETE_TYPE_SESSIONS = 'sessions'; +//const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp'; +//const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource'; +//const DELETE_TYPE_SCHEDULES = 'schedules'; +//const DELETE_TYPE_TOPIC = 'topic'; +//const DELETE_TYPE_TARGET = 'target'; +//const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets'; +//const DELETE_TYPE_SESSION_TARGETS = 'session_targets'; +// +//// Message types +//const MESSAGE_SEND_TYPE_INTERNAL = 'internal'; +//const MESSAGE_SEND_TYPE_EXTERNAL = 'external'; +//// Mail Types +//const MAIL_TYPE_VERIFICATION = 'verification'; +//const MAIL_TYPE_MAGIC_SESSION = 'magicSession'; +//const MAIL_TYPE_RECOVERY = 'recovery'; +//const MAIL_TYPE_INVITATION = 'invitation'; +//const MAIL_TYPE_CERTIFICATE = 'certificate'; +//// Auth Types +//const APP_AUTH_TYPE_SESSION = 'Session'; +//const APP_AUTH_TYPE_JWT = 'JWT'; +//const APP_AUTH_TYPE_KEY = 'Key'; +//const APP_AUTH_TYPE_ADMIN = 'Admin'; +//// Response related +//const MAX_OUTPUT_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB +//// Function headers +//const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host']; +//const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length']; +//// Message types +//const MESSAGE_TYPE_EMAIL = 'email'; +//const MESSAGE_TYPE_SMS = 'sms'; +//const MESSAGE_TYPE_PUSH = 'push'; +//// API key types +//const API_KEY_STANDARD = 'standard'; +//const API_KEY_DYNAMIC = 'dynamic'; +//// Usage metrics +//const METRIC_TEAMS = 'teams'; +//const METRIC_USERS = 'users'; +//const METRIC_MESSAGES = 'messages'; +//const METRIC_MESSAGES_COUNTRY_CODE = '{countryCode}.messages'; +//const METRIC_SESSIONS = 'sessions'; +//const METRIC_DATABASES = 'databases'; +//const METRIC_COLLECTIONS = 'collections'; +//const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections'; +//const METRIC_DOCUMENTS = 'documents'; +//const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents'; +//const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents'; +//const METRIC_BUCKETS = 'buckets'; +//const METRIC_FILES = 'files'; +//const METRIC_FILES_STORAGE = 'files.storage'; +//const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files'; +//const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage'; +//const METRIC_FUNCTIONS = 'functions'; +//const METRIC_DEPLOYMENTS = 'deployments'; +//const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage'; +//const METRIC_BUILDS = 'builds'; +//const METRIC_BUILDS_SUCCESS = 'builds.success'; +//const METRIC_BUILDS_FAILED = 'builds.failed'; +//const METRIC_BUILDS_STORAGE = 'builds.storage'; +//const METRIC_BUILDS_COMPUTE = 'builds.compute'; +//const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success'; +//const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed'; +//const METRIC_BUILDS_MB_SECONDS = 'builds.mbSeconds'; +//const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds'; +//const METRIC_FUNCTION_ID_BUILDS_SUCCESS = '{functionInternalId}.builds.success'; +//const METRIC_FUNCTION_ID_BUILDS_FAILED = '{functionInternalId}.builds.failed'; +//const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage'; +//const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute'; +//const METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS = '{functionInternalId}.builds.compute.success'; +//const METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED = '{functionInternalId}.builds.compute.failed'; +//const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments'; +//const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage'; +//const METRIC_FUNCTION_ID_BUILDS_MB_SECONDS = '{functionInternalId}.builds.mbSeconds'; +//const METRIC_EXECUTIONS = 'executions'; +//const METRIC_EXECUTIONS_COMPUTE = 'executions.compute'; +//const METRIC_EXECUTIONS_MB_SECONDS = 'executions.mbSeconds'; +//const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions'; +//const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute'; +//const METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS = '{functionInternalId}.executions.mbSeconds'; +//const METRIC_NETWORK_REQUESTS = 'network.requests'; +//const METRIC_NETWORK_INBOUND = 'network.inbound'; +//const METRIC_NETWORK_OUTBOUND = 'network.outbound'; +// +//$register = new Registry(); +// +//App::setMode(System::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION)); +// +//if (!App::isProduction()) { +// // Allow specific domains to skip public domain validation in dev environment +// // Useful for existing tests involving webhooks +// PublicDomain::allow(['request-catcher']); +//} +// +///* +// * ENV vars +// */ +//Config::load('events', __DIR__ . '/config/events.php'); +//Config::load('auth', __DIR__ . '/config/auth.php'); +//Config::load('apis', __DIR__ . '/config/apis.php'); // List of APIs +//Config::load('errors', __DIR__ . '/config/errors.php'); +//Config::load('oAuthProviders', __DIR__ . '/config/oAuthProviders.php'); +//Config::load('platforms', __DIR__ . '/config/platforms.php'); +//Config::load('collections', __DIR__ . '/config/collections.php'); +//Config::load('runtimes', __DIR__ . '/config/runtimes.php'); +//Config::load('runtimes-v2', __DIR__ . '/config/runtimes-v2.php'); +//Config::load('usage', __DIR__ . '/config/usage.php'); +//Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes +//Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes +//Config::load('services', __DIR__ . '/config/services.php'); // List of services +//Config::load('variables', __DIR__ . '/config/variables.php'); // List of env variables +//Config::load('regions', __DIR__ . '/config/regions.php'); // List of available regions +//Config::load('avatar-browsers', __DIR__ . '/config/avatars/browsers.php'); +//Config::load('avatar-credit-cards', __DIR__ . '/config/avatars/credit-cards.php'); +//Config::load('avatar-flags', __DIR__ . '/config/avatars/flags.php'); +//Config::load('locale-codes', __DIR__ . '/config/locale/codes.php'); +//Config::load('locale-currencies', __DIR__ . '/config/locale/currencies.php'); +//Config::load('locale-eu', __DIR__ . '/config/locale/eu.php'); +//Config::load('locale-languages', __DIR__ . '/config/locale/languages.php'); +//Config::load('locale-phones', __DIR__ . '/config/locale/phones.php'); +//Config::load('locale-countries', __DIR__ . '/config/locale/countries.php'); +//Config::load('locale-continents', __DIR__ . '/config/locale/continents.php'); +//Config::load('locale-templates', __DIR__ . '/config/locale/templates.php'); +//Config::load('storage-logos', __DIR__ . '/config/storage/logos.php'); +//Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php'); +//Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php'); +//Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php'); +//Config::load('runtime-specifications', __DIR__ . '/config/runtimes/specifications.php'); +//Config::load('function-templates', __DIR__ . '/config/function-templates.php'); +// +///** +// * New DB Filters +// */ +//Database::addFilter( +// 'casting', +// function (mixed $value) { +// return json_encode(['value' => $value], JSON_PRESERVE_ZERO_FRACTION); +// }, +// function (mixed $value) { +// if (is_null($value)) { +// return; +// } +// +// return json_decode($value, true)['value']; +// } +//); +// +//Database::addFilter( +// 'enum', +// function (mixed $value, Document $attribute) { +// if ($attribute->isSet('elements')) { +// $attribute->removeAttribute('elements'); +// } +// +// return $value; +// }, +// function (mixed $value, Document $attribute) { +// $formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true); +// if (isset($formatOptions['elements'])) { +// $attribute->setAttribute('elements', $formatOptions['elements']); +// } +// +// return $value; +// } +//); +// +//Database::addFilter( +// 'range', +// function (mixed $value, Document $attribute) { +// if ($attribute->isSet('min')) { +// $attribute->removeAttribute('min'); +// } +// if ($attribute->isSet('max')) { +// $attribute->removeAttribute('max'); +// } +// +// return $value; +// }, +// function (mixed $value, Document $attribute) { +// $formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true); +// if (isset($formatOptions['min']) || isset($formatOptions['max'])) { +// $attribute +// ->setAttribute('min', $formatOptions['min']) +// ->setAttribute('max', $formatOptions['max']); +// } +// +// return $value; +// } +//); +// +//Database::addFilter( +// 'subQueryAttributes', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// $attributes = $database->find('attributes', [ +// Query::equal('collectionInternalId', [$document->getInternalId()]), +// Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), +// Query::limit($database->getLimitForAttributes()), +// ]); +// +// foreach ($attributes as $attribute) { +// if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { +// $options = $attribute->getAttribute('options'); +// foreach ($options as $key => $value) { +// $attribute->setAttribute($key, $value); +// } +// $attribute->removeAttribute('options'); +// } +// } +// +// return $attributes; +// } +//); +// +//Database::addFilter( +// 'subQueryIndexes', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('indexes', [ +// Query::equal('collectionInternalId', [$document->getInternalId()]), +// Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), +// Query::limit($database->getLimitForIndexes()), +// ]); +// } +//); +// +//Database::addFilter( +// 'subQueryPlatforms', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('platforms', [ +// Query::equal('projectInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ]); +// } +//); +// +//Database::addFilter( +// 'subQueryKeys', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('keys', [ +// Query::equal('projectInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ]); +// } +//); +// +//Database::addFilter( +// 'subQueryWebhooks', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('webhooks', [ +// Query::equal('projectInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ]); +// } +//); +// +//Database::addFilter( +// 'subQuerySessions', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database->find('sessions', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryTokens', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('tokens', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryChallenges', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('challenges', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryAuthenticators', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('authenticators', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryMemberships', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('memberships', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY), +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryVariables', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('variables', [ +// Query::equal('resourceInternalId', [$document->getInternalId()]), +// Query::equal('resourceType', ['function']), +// Query::limit(APP_LIMIT_SUBQUERY), +// ]); +// } +//); +// +//Database::addFilter( +// 'encrypt', +// function (mixed $value) { +// $key = System::getEnv('_APP_OPENSSL_KEY_V1'); +// $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); +// $tag = null; +// +// return json_encode([ +// 'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag), +// 'method' => OpenSSL::CIPHER_AES_128_GCM, +// 'iv' => \bin2hex($iv), +// 'tag' => \bin2hex($tag ?? ''), +// 'version' => '1', +// ]); +// }, +// function (mixed $value) { +// if (is_null($value)) { +// return; +// } +// $value = json_decode($value, true); +// $key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']); +// +// return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag'])); +// } +//); +// +//Database::addFilter( +// 'subQueryProjectVariables', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return $database +// ->find('variables', [ +// Query::equal('resourceType', ['project']), +// Query::limit(APP_LIMIT_SUBQUERY) +// ]); +// } +//); +// +//Database::addFilter( +// 'userSearch', +// function (mixed $value, Document $user) { +// $searchValues = [ +// $user->getId(), +// $user->getAttribute('email', ''), +// $user->getAttribute('name', ''), +// $user->getAttribute('phone', '') +// ]; +// +// foreach ($user->getAttribute('labels', []) as $label) { +// $searchValues[] = 'label:' . $label; +// } +// +// $search = implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +//Database::addFilter( +// 'subQueryTargets', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// return Authorization::skip(fn () => $database +// ->find('targets', [ +// Query::equal('userInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBQUERY) +// ])); +// } +//); +// +//Database::addFilter( +// 'subQueryTopicTargets', +// function (mixed $value) { +// return; +// }, +// function (mixed $value, Document $document, Database $database) { +// $targetIds = Authorization::skip(fn () => \array_map( +// fn ($document) => $document->getAttribute('targetInternalId'), +// $database->find('subscribers', [ +// Query::equal('topicInternalId', [$document->getInternalId()]), +// Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY) +// ]) +// )); +// if (\count($targetIds) > 0) { +// return $database->skipValidation(fn () => $database->find('targets', [ +// Query::equal('$internalId', $targetIds) +// ])); +// } +// return []; +// } +//); +// +//Database::addFilter( +// 'providerSearch', +// function (mixed $value, Document $provider) { +// $searchValues = [ +// $provider->getId(), +// $provider->getAttribute('name', ''), +// $provider->getAttribute('provider', ''), +// $provider->getAttribute('type', '') +// ]; +// +// $search = \implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +//Database::addFilter( +// 'topicSearch', +// function (mixed $value, Document $topic) { +// $searchValues = [ +// $topic->getId(), +// $topic->getAttribute('name', ''), +// $topic->getAttribute('description', ''), +// ]; +// +// $search = \implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +//Database::addFilter( +// 'messageSearch', +// function (mixed $value, Document $message) { +// $searchValues = [ +// $message->getId(), +// $message->getAttribute('description', ''), +// $message->getAttribute('status', ''), +// ]; +// +// $data = \json_decode($message->getAttribute('data', []), true); +// $providerType = $message->getAttribute('providerType', ''); +// +// if ($providerType === MESSAGE_TYPE_EMAIL) { +// $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); +// } elseif ($providerType === MESSAGE_TYPE_SMS) { +// $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); +// } else { +// $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); +// } +// +// $search = \implode(' ', \array_filter($searchValues)); +// +// return $search; +// }, +// function (mixed $value) { +// return $value; +// } +//); +// +///** +// * DB Formats +// */ +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () { +// return new Email(); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () { +// return new DatetimeValidator(); +//}, Database::VAR_DATETIME); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) { +// $elements = $attribute['formatOptions']['elements']; +// return new WhiteList($elements, true); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () { +// return new IP(); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () { +// return new URL(); +//}, Database::VAR_STRING); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) { +// $min = $attribute['formatOptions']['min'] ?? -INF; +// $max = $attribute['formatOptions']['max'] ?? INF; +// return new Range($min, $max, Range::TYPE_INTEGER); +//}, Database::VAR_INTEGER); +// +//Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) { +// $min = $attribute['formatOptions']['min'] ?? -INF; +// $max = $attribute['formatOptions']['max'] ?? INF; +// return new Range($min, $max, Range::TYPE_FLOAT); +//}, Database::VAR_FLOAT); +// +///* +// * Registry +// */ +//$register->set('logger', function () { +// // Register error logger +// $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); +// $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); +// +// try { +// $loggingProvider = new DSN($providerConfig ?? ''); +// +// $providerName = $loggingProvider->getScheme(); +// $providerConfig = match ($providerName) { +// 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()], +// 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], +// default => ['key' => $loggingProvider->getHost()], +// }; +// } catch (Throwable $th) { +// // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables +// Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage()); +// $configChunks = \explode(";", $providerConfig); +// +// $providerConfig = match ($providerName) { +// 'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',], +// 'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''], +// default => ['key' => $providerConfig], +// }; +// } +// +// if (empty($providerName) || empty($providerConfig)) { +// return; +// } +// +// if (!Logger::hasProvider($providerName)) { +// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); +// } +// +// try { +// $adapter = match ($providerName) { +// 'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']), +// 'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']), +// 'raygun' => new Raygun($providerConfig['key']), +// 'appsignal' => new AppSignal($providerConfig['key']), +// default => null +// }; +// } catch (Throwable $th) { +// $adapter = null; +// } +// +// if($adapter === null) { +// Console::error("Logging provider not supported. Logging is disabled"); +// return; +// } +// +// return new Logger($adapter); +//}); +// +//$register->set('pools', function () { +// $group = new Group(); +// +// $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ +// 'scheme' => 'mariadb', +// 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), +// 'port' => System::getEnv('_APP_DB_PORT', '3306'), +// 'user' => System::getEnv('_APP_DB_USER', ''), +// 'pass' => System::getEnv('_APP_DB_PASS', ''), +// 'path' => System::getEnv('_APP_DB_SCHEMA', ''), +// ]); +// $fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([ +// 'scheme' => 'redis', +// 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), +// 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), +// 'user' => System::getEnv('_APP_REDIS_USER', ''), +// 'pass' => System::getEnv('_APP_REDIS_PASS', ''), +// ]); +// +// $connections = [ +// 'console' => [ +// 'type' => 'database', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), +// 'multiple' => false, +// 'schemes' => ['mariadb', 'mysql'], +// ], +// 'database' => [ +// 'type' => 'database', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), +// 'multiple' => true, +// 'schemes' => ['mariadb', 'mysql'], +// ], +// 'queue' => [ +// 'type' => 'queue', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), +// 'multiple' => false, +// 'schemes' => ['redis'], +// ], +// 'pubsub' => [ +// 'type' => 'pubsub', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), +// 'multiple' => false, +// 'schemes' => ['redis'], +// ], +// 'cache' => [ +// 'type' => 'cache', +// 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), +// 'multiple' => true, +// 'schemes' => ['redis'], +// ], +// ]; +// +// $maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151); +// $instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14); +// +// $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; +// +// if ($multiprocessing) { +// $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); +// } else { +// $workerCount = 1; +// } +// +// if ($workerCount > $instanceConnections) { +// throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); +// } +// +// $poolSize = (int)($instanceConnections / $workerCount); +// +// foreach ($connections as $key => $connection) { +// $type = $connection['type'] ?? ''; +// $multiple = $connection['multiple'] ?? false; +// $schemes = $connection['schemes'] ?? []; +// $config = []; +// $dsns = explode(',', $connection['dsns'] ?? ''); +// foreach ($dsns as &$dsn) { +// $dsn = explode('=', $dsn); +// $name = ($multiple) ? $key . '_' . $dsn[0] : $key; +// $dsn = $dsn[1] ?? ''; +// $config[] = $name; +// if (empty($dsn)) { +// //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); +// continue; +// } +// +// $dsn = new DSN($dsn); +// $dsnHost = $dsn->getHost(); +// $dsnPort = $dsn->getPort(); +// $dsnUser = $dsn->getUser(); +// $dsnPass = $dsn->getPassword(); +// $dsnScheme = $dsn->getScheme(); +// $dsnDatabase = $dsn->getPath(); +// +// if (!in_array($dsnScheme, $schemes)) { +// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); +// } +// +// /** +// * Get Resource +// * +// * Creation could be reused across connection types like database, cache, queue, etc. +// * +// * Resource assignment to an adapter will happen below. +// */ +// $resource = match ($dsnScheme) { +// 'mysql', +// 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { +// return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { +// return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( +// PDO::ATTR_TIMEOUT => 3, // Seconds +// PDO::ATTR_PERSISTENT => true, +// PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, +// PDO::ATTR_EMULATE_PREPARES => true, +// PDO::ATTR_STRINGIFY_FETCHES => true +// )); +// }); +// }, +// 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { +// $redis = new Redis(); +// @$redis->pconnect($dsnHost, (int)$dsnPort); +// if ($dsnPass) { +// $redis->auth($dsnPass); +// } +// $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); +// +// return $redis; +// }, +// default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'), +// }; +// +// $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { +// // Get Adapter +// switch ($type) { +// case 'database': +// $adapter = match ($dsn->getScheme()) { +// 'mariadb' => new MariaDB($resource()), +// 'mysql' => new MySQL($resource()), +// default => null +// }; +// +// $adapter->setDatabase($dsn->getPath()); +// break; +// case 'pubsub': +// $adapter = $resource(); +// break; +// case 'queue': +// $adapter = match ($dsn->getScheme()) { +// 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), +// default => null +// }; +// break; +// case 'cache': +// $adapter = match ($dsn->getScheme()) { +// 'redis' => new RedisCache($resource()), +// default => null +// }; +// break; +// +// default: +// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); +// } +// +// return $adapter; +// }); +// +// $group->add($pool); +// } +// +// Config::setParam('pools-' . $key, $config); +// } +// +// return $group; +//}); +// +//$register->set('db', function () { +// // This is usually for our workers or CLI commands scope +// $dbHost = System::getEnv('_APP_DB_HOST', ''); +// $dbPort = System::getEnv('_APP_DB_PORT', ''); +// $dbUser = System::getEnv('_APP_DB_USER', ''); +// $dbPass = System::getEnv('_APP_DB_PASS', ''); +// $dbScheme = System::getEnv('_APP_DB_SCHEMA', ''); +// +// return new PDO( +// "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", +// $dbUser, +// $dbPass, +// SQL::getPDOAttributes() +// ); +//}); +// +//$register->set('smtp', function () { +// $mail = new PHPMailer(true); +// +// $mail->isSMTP(); +// +// $username = System::getEnv('_APP_SMTP_USERNAME'); +// $password = System::getEnv('_APP_SMTP_PASSWORD'); +// +// $mail->XMailer = 'Appwrite Mailer'; +// $mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp'); +// $mail->Port = System::getEnv('_APP_SMTP_PORT', 25); +// $mail->SMTPAuth = !empty($username) && !empty($password); +// $mail->Username = $username; +// $mail->Password = $password; +// $mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', ''); +// $mail->SMTPAutoTLS = false; +// $mail->CharSet = 'UTF-8'; +// +// $from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); +// $email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); +// +// $mail->setFrom($email, $from); +// $mail->addReplyTo($email, $from); +// +// $mail->isHTML(true); +// +// return $mail; +//}); +//$register->set('geodb', function () { +// return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb'); +//}); +//$register->set('passwordsDictionary', function () { +// $content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords'); +// $content = explode("\n", $content); +// $content = array_flip($content); +// return $content; +//}); +//$register->set('promiseAdapter', function () { +// return new Swoole(); +//}); +//$register->set('hooks', function () { +// return new Hooks(); +//}); +///* +// * Localization +// */ +//Locale::$exceptions = false; +// +//$locales = Config::getParam('locale-codes', []); +// +//foreach ($locales as $locale) { +// $code = $locale['code']; +// +// $path = __DIR__ . '/config/locale/translations/' . $code . '.json'; +// +// if (!\file_exists($path)) { +// $path = __DIR__ . '/config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar` +// if (!\file_exists($path)) { +// $path = __DIR__ . '/config/locale/translations/en.json'; // if none translation exists, use default from `en.json` +// } +// } +// +// Locale::setLanguageFromJSON($code, $path); +//} +// +//\stream_context_set_default([ // Set global user agent and http settings +// 'http' => [ +// 'method' => 'GET', +// 'user_agent' => \sprintf( +// APP_USERAGENT, +// System::getEnv('_APP_VERSION', 'UNKNOWN'), +// System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)) +// ), +// 'timeout' => 2, +// ], +//]); +// +//// Runtime Execution +//App::setResource('log', fn () => new Log()); +//App::setResource('logger', function ($register) { +// return $register->get('logger'); +//}, ['register']); +// +//App::setResource('hooks', function ($register) { +// return $register->get('hooks'); +//}, ['register']); +// +//App::setResource('register', fn () => $register); +//App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); +// +//App::setResource('localeCodes', function () { +// return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); +//}); +// +//// Queues +//App::setResource('queue', function (Group $pools) { +// return $pools->get('queue')->pop()->getResource(); +//}, ['pools']); +//App::setResource('queueForMessaging', function (Connection $queue) { +// return new Messaging($queue); +//}, ['queue']); +//App::setResource('queueForMails', function (Connection $queue) { +// return new Mail($queue); +//}, ['queue']); +//App::setResource('queueForBuilds', function (Connection $queue) { +// return new Build($queue); +//}, ['queue']); +//App::setResource('queueForDatabase', function (Connection $queue) { +// return new EventDatabase($queue); +//}, ['queue']); +//App::setResource('queueForDeletes', function (Connection $queue) { +// return new Delete($queue); +//}, ['queue']); +//App::setResource('queueForEvents', function (Connection $queue) { +// return new Event($queue); +//}, ['queue']); +//App::setResource('queueForAudits', function (Connection $queue) { +// return new Audit($queue); +//}, ['queue']); +//App::setResource('queueForFunctions', function (Connection $queue) { +// return new Func($queue); +//}, ['queue']); +//App::setResource('queueForUsage', function (Connection $queue) { +// return new Usage($queue); +//}, ['queue']); +//App::setResource('queueForCertificates', function (Connection $queue) { +// return new Certificate($queue); +//}, ['queue']); +//App::setResource('queueForMigrations', function (Connection $queue) { +// return new Migration($queue); +//}, ['queue']); +//App::setResource('clients', function ($request, $console, $project) { +// $console->setAttribute('platforms', [ // Always allow current host +// '$collection' => ID::custom('platforms'), +// 'name' => 'Current Host', +// 'type' => Origin::CLIENT_TYPE_WEB, +// 'hostname' => $request->getHostname(), +// ], Document::SET_TYPE_APPEND); +// +// $hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', '')); +// $validator = new Hostname(); +// foreach ($hostnames as $hostname) { +// $hostname = trim($hostname); +// if (!$validator->isValid($hostname)) { +// continue; +// } +// $console->setAttribute('platforms', [ +// '$collection' => ID::custom('platforms'), +// 'type' => Origin::CLIENT_TYPE_WEB, +// 'name' => $hostname, +// 'hostname' => $hostname, +// ], Document::SET_TYPE_APPEND); +// } +// +// /** +// * Get All verified client URLs for both console and current projects +// * + Filter for duplicated entries +// */ +// $clientsConsole = \array_map( +// fn ($node) => $node['hostname'], +// \array_filter( +// $console->getAttribute('platforms', []), +// fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname'])) +// ) +// ); +// +// $clients = $clientsConsole; +// $platforms = $project->getAttribute('platforms', []); +// +// foreach ($platforms as $node) { +// if ( +// isset($node['type']) && +// ($node['type'] === Origin::CLIENT_TYPE_WEB || +// $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && +// !empty($node['hostname']) +// ) { +// $clients[] = $node['hostname']; +// } +// } +// +// return \array_unique($clients); +//}, ['request', 'console', 'project']); +// +//App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { +// /** @var Appwrite\Utopia\Request $request */ +// /** @var Appwrite\Utopia\Response $response */ +// /** @var Utopia\Database\Document $project */ +// /** @var Utopia\Database\Database $dbForProject */ +// /** @var Utopia\Database\Database $dbForConsole */ +// /** @var string $mode */ +// +// Authorization::setDefaultStatus(true); +// +// Auth::setCookieName('a_session_' . $project->getId()); +// +// if (APP_MODE_ADMIN === $mode) { +// Auth::setCookieName('a_session_' . $console->getId()); +// } +// +// $session = Auth::decodeSession( +// $request->getCookie( +// Auth::$cookieName, // Get sessions +// $request->getCookie(Auth::$cookieName . '_legacy', '') +// ) +// ); +// +// // Get session from header for SSR clients +// if (empty($session['id']) && empty($session['secret'])) { +// $sessionHeader = $request->getHeader('x-appwrite-session', ''); +// +// if (!empty($sessionHeader)) { +// $session = Auth::decodeSession($sessionHeader); +// } +// } +// +// // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies +// if ($response) { +// $response->addHeader('X-Debug-Fallback', 'false'); +// } +// +// if (empty($session['id']) && empty($session['secret'])) { +// if ($response) { +// $response->addHeader('X-Debug-Fallback', 'true'); +// } +// $fallback = $request->getHeader('x-fallback-cookies', ''); +// $fallback = \json_decode($fallback, true); +// $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); +// } +// +// Auth::$unique = $session['id'] ?? ''; +// Auth::$secret = $session['secret'] ?? ''; +// +// if (APP_MODE_ADMIN !== $mode) { +// if ($project->isEmpty()) { +// $user = new Document([]); +// } else { +// if ($project->getId() === 'console') { +// $user = $dbForConsole->getDocument('users', Auth::$unique); +// } else { +// $user = $dbForProject->getDocument('users', Auth::$unique); +// } +// } +// } else { +// $user = $dbForConsole->getDocument('users', Auth::$unique); +// } +// +// if ( +// $user->isEmpty() // Check a document has been found in the DB +// || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) +// ) { // Validate user has valid login token +// $user = new Document([]); +// } +// +// if (APP_MODE_ADMIN === $mode) { +// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { +// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. +// } else { +// $user = new Document([]); +// } +// } +// +// $authJWT = $request->getHeader('x-appwrite-jwt', ''); +// +// if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication +// $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); +// +// try { +// $payload = $jwt->decode($authJWT); +// } catch (JWTException $error) { +// throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); +// } +// +// $jwtUserId = $payload['userId'] ?? ''; +// if (!empty($jwtUserId)) { +// $user = $dbForProject->getDocument('users', $jwtUserId); +// } +// +// $jwtSessionId = $payload['sessionId'] ?? ''; +// if(!empty($jwtSessionId)) { +// if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token +// $user = new Document([]); +// } +// } +// } +// +// $dbForProject->setMetadata('user', $user->getId()); +// $dbForConsole->setMetadata('user', $user->getId()); +// +// return $user; +//}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); +// +//App::setResource('project', function ($dbForConsole, $request, $console) { +// /** @var Appwrite\Utopia\Request $request */ +// /** @var Utopia\Database\Database $dbForConsole */ +// /** @var Utopia\Database\Document $console */ +// +// $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); +// +// if (empty($projectId) || $projectId === 'console') { +// return $console; +// } +// +// $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); +// +// return $project; +//}, ['dbForConsole', 'request', 'console']); +// +//App::setResource('session', function (Document $user) { +// if ($user->isEmpty()) { +// return; +// } +// +// $sessions = $user->getAttribute('sessions', []); +// $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); +// +// if (!$sessionId) { +// return; +// } +// +// foreach ($sessions as $session) {/** @var Document $session */ +// if ($sessionId === $session->getId()) { +// return $session; +// } +// } +// +// return; +//}, ['user']); +// +//App::setResource('console', function () { +// return new Document([ +// '$id' => ID::custom('console'), +// '$internalId' => ID::custom('console'), +// 'name' => 'Appwrite', +// '$collection' => ID::custom('projects'), +// 'description' => 'Appwrite core engine', +// 'logo' => '', +// 'teamId' => -1, +// 'webhooks' => [], +// 'keys' => [], +// 'platforms' => [ +// [ +// '$collection' => ID::custom('platforms'), +// 'name' => 'Localhost', +// 'type' => Origin::CLIENT_TYPE_WEB, +// 'hostname' => 'localhost', +// ], // Current host is added on app init +// ], +// 'legalName' => '', +// 'legalCountry' => '', +// 'legalState' => '', +// 'legalCity' => '', +// 'legalAddress' => '', +// 'legalTaxId' => '', +// 'auths' => [ +// 'mockNumbers' => [], +// 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', +// 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user +// 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds +// 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' +// ], +// 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], +// 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], +// 'oAuthProviders' => [ +// 'githubEnabled' => true, +// 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), +// 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') +// ], +// ]); +//}, []); +// +//App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { +// if ($project->isEmpty() || $project->getId() === 'console') { +// return $dbForConsole; +// } +// +// try { +// $dsn = new DSN($project->getAttribute('database')); +// } catch (\InvalidArgumentException) { +// // TODO: Temporary until all projects are using shared tables +// $dsn = new DSN('mysql://' . $project->getAttribute('database')); +// } +// +// $dbAdapter = $pools +// ->get($dsn->getHost()) +// ->pop() +// ->getResource(); +// +// $database = new Database($dbAdapter, $cache); +// +// $database +// ->setMetadata('host', \gethostname()) +// ->setMetadata('project', $project->getId()) +// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); +// +// try { +// $dsn = new DSN($project->getAttribute('database')); +// } catch (\InvalidArgumentException) { +// // TODO: Temporary until all projects are using shared tables +// $dsn = new DSN('mysql://' . $project->getAttribute('database')); +// } +// +// if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { +// $database +// ->setSharedTables(true) +// ->setTenant($project->getInternalId()) +// ->setNamespace($dsn->getParam('namespace')); +// } else { +// $database +// ->setSharedTables(false) +// ->setTenant(null) +// ->setNamespace('_' . $project->getInternalId()); +// } +// +// return $database; +//}, ['pools', 'dbForConsole', 'cache', 'project']); +// +//App::setResource('dbForConsole', function (Group $pools, Cache $cache) { +// $dbAdapter = $pools +// ->get('console') +// ->pop() +// ->getResource(); +// +// $database = new Database($dbAdapter, $cache); +// +// $database +// ->setNamespace('_console') +// ->setMetadata('host', \gethostname()) +// ->setMetadata('project', 'console') +// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); +// +// return $database; +//}, ['pools', 'cache']); +// +//App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { +// $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools +// +// return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { +// if ($project->isEmpty() || $project->getId() === 'console') { +// return $dbForConsole; +// } +// +// try { +// $dsn = new DSN($project->getAttribute('database')); +// } catch (\InvalidArgumentException) { +// // TODO: Temporary until all projects are using shared tables +// $dsn = new DSN('mysql://' . $project->getAttribute('database')); +// } +// +// $configure = (function (Database $database) use ($project, $dsn) { +// $database +// ->setMetadata('host', \gethostname()) +// ->setMetadata('project', $project->getId()) +// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); +// +// if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { +// $database +// ->setSharedTables(true) +// ->setTenant($project->getInternalId()) +// ->setNamespace($dsn->getParam('namespace')); +// } else { +// $database +// ->setSharedTables(false) +// ->setTenant(null) +// ->setNamespace('_' . $project->getInternalId()); +// } +// }); +// +// if (isset($databases[$dsn->getHost()])) { +// $database = $databases[$dsn->getHost()]; +// $configure($database); +// return $database; +// } +// +// $dbAdapter = $pools +// ->get($dsn->getHost()) +// ->pop() +// ->getResource(); +// +// $database = new Database($dbAdapter, $cache); +// $databases[$dsn->getHost()] = $database; +// $configure($database); +// +// return $database; +// }; +//}, ['pools', 'dbForConsole', 'cache']); +// +//App::setResource('cache', function (Group $pools) { +// $list = Config::getParam('pools-cache', []); +// $adapters = []; +// +// foreach ($list as $value) { +// $adapters[] = $pools +// ->get($value) +// ->pop() +// ->getResource() +// ; +// } +// +// return new Cache(new Sharding($adapters)); +//}, ['pools']); +// +//App::setResource('deviceForLocal', function () { +// return new Local(); +//}); +// +//App::setResource('deviceForFiles', function ($project) { +// return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); +//}, ['project']); +// +//App::setResource('deviceForFunctions', function ($project) { +// return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); +//}, ['project']); +// +//App::setResource('deviceForBuilds', function ($project) { +// return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); +//}, ['project']); +// +//function getDevice($root): Device +//{ +// $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); +// +// if (!empty($connection)) { +// $acl = 'private'; +// $device = Storage::DEVICE_LOCAL; +// $accessKey = ''; +// $accessSecret = ''; +// $bucket = ''; +// $region = ''; +// +// try { +// $dsn = new DSN($connection); +// $device = $dsn->getScheme(); +// $accessKey = $dsn->getUser() ?? ''; +// $accessSecret = $dsn->getPassword() ?? ''; +// $bucket = $dsn->getPath() ?? ''; +// $region = $dsn->getParam('region'); +// } catch (\Throwable $e) { +// Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); +// } +// +// switch ($device) { +// case Storage::DEVICE_S3: +// return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case STORAGE::DEVICE_DO_SPACES: +// $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// $device->setHttpVersion(S3::HTTP_VERSION_1_1); +// return $device; +// case Storage::DEVICE_BACKBLAZE: +// return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case Storage::DEVICE_LINODE: +// return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case Storage::DEVICE_WASABI: +// return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); +// case Storage::DEVICE_LOCAL: +// default: +// return new Local($root); +// } +// } else { +// switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { +// case Storage::DEVICE_LOCAL: +// default: +// return new Local($root); +// case Storage::DEVICE_S3: +// $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); +// $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); +// $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); +// $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); +// $s3Acl = 'private'; +// return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); +// case Storage::DEVICE_DO_SPACES: +// $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); +// $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); +// $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); +// $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); +// $doSpacesAcl = 'private'; +// $device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); +// $device->setHttpVersion(S3::HTTP_VERSION_1_1); +// return $device; +// case Storage::DEVICE_BACKBLAZE: +// $backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); +// $backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); +// $backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); +// $backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); +// $backblazeAcl = 'private'; +// return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); +// case Storage::DEVICE_LINODE: +// $linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); +// $linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', ''); +// $linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', ''); +// $linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); +// $linodeAcl = 'private'; +// return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); +// case Storage::DEVICE_WASABI: +// $wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); +// $wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', ''); +// $wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', ''); +// $wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); +// $wasabiAcl = 'private'; +// return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); +// } +// } +//} +// +//App::setResource('mode', function ($request) { +// /** @var Appwrite\Utopia\Request $request */ +// +// /** +// * Defines the mode for the request: +// * - 'default' => Requests for Client and Server Side +// * - 'admin' => Request from the Console on non-console projects +// */ +// return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); +//}, ['request']); +// +//App::setResource('geodb', function ($register) { +// /** @var Utopia\Registry\Registry $register */ +// return $register->get('geodb'); +//}, ['register']); +// +//App::setResource('passwordsDictionary', function ($register) { +// /** @var Utopia\Registry\Registry $register */ +// return $register->get('passwordsDictionary'); +//}, ['register']); +// +// +//App::setResource('servers', function () { +// $platforms = Config::getParam('platforms'); +// $server = $platforms[APP_PLATFORM_SERVER]; +// +// $languages = array_map(function ($language) { +// return strtolower($language['name']); +// }, $server['sdks']); +// +// return $languages; +//}); +// +//App::setResource('promiseAdapter', function ($register) { +// return $register->get('promiseAdapter'); +//}, ['register']); +// +//App::setResource('schema', function ($utopia, $dbForProject) { +// +// $complexity = function (int $complexity, array $args) { +// $queries = Query::parseQueries($args['queries'] ?? []); +// $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; +// $limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT; +// +// return $complexity * $limit; +// }; +// +// $attributes = function (int $limit, int $offset) use ($dbForProject) { +// $attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [ +// Query::limit($limit), +// Query::offset($offset), +// ])); +// +// return \array_map(function ($attr) { +// return $attr->getArrayCopy(); +// }, $attrs); +// }; +// +// $urls = [ +// 'list' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents"; +// }, +// 'create' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents"; +// }, +// 'read' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; +// }, +// 'update' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; +// }, +// 'delete' => function (string $databaseId, string $collectionId, array $args) { +// return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; +// }, +// ]; +// +// $params = [ +// 'list' => function (string $databaseId, string $collectionId, array $args) { +// return [ 'queries' => $args['queries']]; +// }, +// 'create' => function (string $databaseId, string $collectionId, array $args) { +// $id = $args['id'] ?? 'unique()'; +// $permissions = $args['permissions'] ?? null; +// +// unset($args['id']); +// unset($args['permissions']); +// +// // Order must be the same as the route params +// return [ +// 'databaseId' => $databaseId, +// 'documentId' => $id, +// 'collectionId' => $collectionId, +// 'data' => $args, +// 'permissions' => $permissions, +// ]; +// }, +// 'update' => function (string $databaseId, string $collectionId, array $args) { +// $documentId = $args['id']; +// $permissions = $args['permissions'] ?? null; +// +// unset($args['id']); +// unset($args['permissions']); +// +// // Order must be the same as the route params +// return [ +// 'databaseId' => $databaseId, +// 'collectionId' => $collectionId, +// 'documentId' => $documentId, +// 'data' => $args, +// 'permissions' => $permissions, +// ]; +// }, +// ]; +// +// return Schema::build( +// $utopia, +// $complexity, +// $attributes, +// $urls, +// $params, +// ); +//}, ['utopia', 'dbForProject']); +// +//App::setResource('contributors', function () { +// $path = 'app/config/contributors.json'; +// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; +// return $list; +//}); +// +//App::setResource('employees', function () { +// $path = 'app/config/employees.json'; +// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; +// return $list; +//}); +// +//App::setResource('heroes', function () { +// $path = 'app/config/heroes.json'; +// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; +// return $list; +//}); +// +//App::setResource('gitHub', function (Cache $cache) { +// return new VcsGitHub($cache); +//}, ['cache']); +// +//App::setResource('requestTimestamp', function ($request) { +// //TODO: Move this to the Request class itself +// $timestampHeader = $request->getHeader('x-appwrite-timestamp'); +// $requestTimestamp = null; +// if (!empty($timestampHeader)) { +// try { +// $requestTimestamp = new \DateTime($timestampHeader); +// } catch (\Throwable $e) { +// throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value'); +// } +// } +// return $requestTimestamp; +//}, ['request']); +//App::setResource('plan', function (array $plan = []) { +// return []; +//}); diff --git a/app/init/config.php b/app/init/config.php index 80001f13b4..bc8333b5d9 100644 --- a/app/init/config.php +++ b/app/init/config.php @@ -33,4 +33,5 @@ Config::load('storage-logos', __DIR__ . '/../config/storage/logos.php'); Config::load('storage-mimes', __DIR__ . '/../config/storage/mimes.php'); Config::load('storage-inputs', __DIR__ . '/../config/storage/inputs.php'); Config::load('storage-outputs', __DIR__ . '/../config/storage/outputs.php'); +Config::load('runtime-specifications', __DIR__ . '/../config/runtimes/specifications.php'); Config::load('function-templates', __DIR__ . '/../config/function-templates.php'); diff --git a/app/init/constants.php b/app/init/constants.php index 993f8b51ac..9f7f1c5c7e 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -1,5 +1,7 @@ set('logger', function () { default => ['key' => $loggingProvider->getHost()], }; } catch (Throwable) { + Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage()); // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables $configChunks = \explode(";", $providerConfig); @@ -220,13 +221,23 @@ $global->set('logger', function () { throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); } - $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 => throw new Exception('Provider "' . $providerName . '" not supported.') - }; + 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; + } + $logger = new Logger($adapter); $logger->setSample(0.4); return $logger; @@ -236,7 +247,7 @@ $global->set('geodb', function () { /** * @disregard P1009 Undefined type */ - return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-08.mmdb'); + return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb'); }); $global->set('hooks', function () { diff --git a/composer.json b/composer.json index 40bc26499d..a2d0847b5b 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "test": "vendor/bin/phpunit", "lint": "vendor/bin/pint --test", "format": "vendor/bin/pint", - "bench": "vendor/bin/phpbench run --report=benchmark" + "bench": "vendor/bin/phpbench run --report=benchmark", "check": "./vendor/bin/phpstan analyse -c phpstan.neon --memory-limit 1G app src tests" }, "autoload": { diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index aac41f355c..666e0649e9 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -9,15 +9,14 @@ use JsonException; use Swoole\Http\Response as SwooleHTTPResponse; // Keep last use Utopia\Database\Document; -use Utopia\Http\Adapter\Swoole\Response as HttpResponse; - +use Utopia\Swoole\Response as SwooleResponse; // Keep last /** * @method int getStatusCode() * @method Response setStatusCode(int $code = 200) */ -class Response extends HttpResponse +class Response extends SwooleResponse { // General public const MODEL_NONE = 'none'; diff --git a/src/Appwrite/Utopia/Response/Models.php b/src/Appwrite/Utopia/Response/Models.php index 6e2bd4d809..1c3ef92b8b 100644 --- a/src/Appwrite/Utopia/Response/Models.php +++ b/src/Appwrite/Utopia/Response/Models.php @@ -2,7 +2,9 @@ namespace Appwrite\Utopia\Response; -use Appwrite\Utopia\Response; +use Appwrite\Utopia\Fetch\BodyMultipart; +use Appwrite\Utopia\Response\Filter; +use Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response\Model\Account; use Appwrite\Utopia\Response\Model\AlgoArgon2; use Appwrite\Utopia\Response\Model\AlgoBcrypt; @@ -82,6 +84,7 @@ use Appwrite\Utopia\Response\Model\ProviderRepository; use Appwrite\Utopia\Response\Model\Rule; use Appwrite\Utopia\Response\Model\Runtime; use Appwrite\Utopia\Response\Model\Session; +use Appwrite\Utopia\Response\Model\Specification; use Appwrite\Utopia\Response\Model\Subscriber; use Appwrite\Utopia\Response\Model\Target; use Appwrite\Utopia\Response\Model\Team; @@ -106,6 +109,11 @@ use Appwrite\Utopia\Response\Model\Variable; use Appwrite\Utopia\Response\Model\VcsContent; use Appwrite\Utopia\Response\Model\Webhook; use Exception; +use JsonException; +use Swoole\Http\Response as SwooleHTTPResponse; +// Keep last +use Utopia\Database\Document; +use Utopia\Swoole\Response as SwooleResponse; class Models { @@ -160,6 +168,7 @@ class Models self::setModel(new BaseList('Target list', Response::MODEL_TARGET_LIST, 'targets', Response::MODEL_TARGET)); self::setModel(new BaseList('Migrations List', Response::MODEL_MIGRATION_LIST, 'migrations', Response::MODEL_MIGRATION)); self::setModel(new BaseList('Migrations Firebase Projects List', Response::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', Response::MODEL_MIGRATION_FIREBASE_PROJECT)); + self::setModel(new BaseList('Specifications List', Response::MODEL_SPECIFICATION_LIST, 'specifications', Response::MODEL_SPECIFICATION)); self::setModel(new BaseList('VCS Content List', Response::MODEL_VCS_CONTENT_LIST, 'contents', Response::MODEL_VCS_CONTENT)); // Entities self::setModel(new Database()); @@ -242,6 +251,7 @@ class Models self::setModel(new UsageFunction()); self::setModel(new UsageProject()); self::setModel(new Headers()); + self::setModel(new Specification()); self::setModel(new Rule()); self::setModel(new TemplateSMS()); self::setModel(new TemplateEmail()); From cedf3cf28d62e519c8bad4cd805bfec74cd67735 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:32:30 -0400 Subject: [PATCH 159/195] chore: format --- app/cli.php | 2 +- app/controllers/api/functions.php | 9 +------- app/controllers/general.php | 1 - app/init.php | 1 + app/init2.php | 2 +- app/realtime.php | 2 -- src/Appwrite/Migration/Migration.php | 28 ++++++++++++------------- src/Appwrite/Platform/Tasks/Migrate.php | 16 ++++++++++---- src/Appwrite/Utopia/Response.php | 2 +- src/Appwrite/Utopia/Response/Models.php | 8 +------ 10 files changed, 32 insertions(+), 39 deletions(-) diff --git a/app/cli.php b/app/cli.php index 404c3cdf8e..6074185461 100644 --- a/app/cli.php +++ b/app/cli.php @@ -88,7 +88,7 @@ $logError $log->addExtra('file', $error->getFile()); $log->addExtra('line', $error->getLine()); $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('trace', $error->getTraceAsString()); + $log->addExtra('trace', $error->getTraceAsString()); $log->setAction($action); diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index a974e7cd68..04c560b71f 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -54,16 +54,9 @@ use Utopia\Storage\Validator\File; use Utopia\Storage\Validator\FileExt; use Utopia\Storage\Validator\FileSize; use Utopia\Storage\Validator\Upload; -use Utopia\Swoole\Request; use Utopia\System\System; use Utopia\Validator\AnyOf; -use Utopia\Validator\ArrayList; -use Utopia\Validator\Assoc; -use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; -use Utopia\Validator\Range; -use Utopia\Validator\Text; -use Utopia\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub; use Utopia\VCS\Exception\RepositoryNotFound; @@ -193,7 +186,7 @@ Http::post('/v1/functions') ->inject('dbForConsole') ->inject('gitHub') ->inject('authorization') - ->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 $templateBranch, string $specification Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $authorization) 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 $templateBranch, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $authorization) use ($redeployVcs) { $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId; $allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', ''))); diff --git a/app/controllers/general.php b/app/controllers/general.php index 34e139516c..5b79709176 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -33,7 +33,6 @@ use Utopia\Http\Route; use Utopia\Http\Validator\Hostname; use Utopia\Http\Validator\Text; use Utopia\Locale\Locale; -use Utopia\Logger\Adapter\Sentry; use Utopia\Logger\Log; use Utopia\Logger\Log\User; use Utopia\Logger\Logger; diff --git a/app/init.php b/app/init.php index 9a6b608507..b3fd0a833f 100644 --- a/app/init.php +++ b/app/init.php @@ -1,4 +1,5 @@ set('logger', function () { throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); } - try{ + try { $adapter = match ($providerName) { 'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']), 'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']), diff --git a/app/realtime.php b/app/realtime.php index 2415051940..1020ca0cc5 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -16,8 +16,6 @@ use Swoole\Table; use Swoole\Timer; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\Database\TimeLimit; -use Utopia\Cache\Adapter\Sharding; -use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Database\DateTime; use Utopia\Database\Document; diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index fc4c88aebf..bded4397a9 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -175,23 +175,23 @@ abstract class Migration Console::log('Migrating Collection ' . $collection['$id'] . ':'); foreach ($this->documentsIterator($collection['$id']) as $document) { - if (empty($document->getId()) || empty($document->getCollection())) { - return; - } + if (empty($document->getId()) || empty($document->getCollection())) { + return; + } - $old = $document->getArrayCopy(); - $new = call_user_func($callback, $document); + $old = $document->getArrayCopy(); + $new = call_user_func($callback, $document); - if (is_null($new) || $new->getArrayCopy() == $old) { - return; - } + if (is_null($new) || $new->getArrayCopy() == $old) { + return; + } - try { - $this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document); - } catch (\Throwable $th) { - Console::error('Failed to update document: ' . $th->getMessage()); - return; - } + try { + $this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document); + } catch (\Throwable $th) { + Console::error('Failed to update document: ' . $th->getMessage()); + return; + } } } } diff --git a/src/Appwrite/Platform/Tasks/Migrate.php b/src/Appwrite/Platform/Tasks/Migrate.php index bcabbc8595..55be0b81c2 100644 --- a/src/Appwrite/Platform/Tasks/Migrate.php +++ b/src/Appwrite/Platform/Tasks/Migrate.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Migration\Migration; use Redis; use Utopia\App; -use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; @@ -38,8 +37,8 @@ class Migrate extends Action ->inject('authorization') ->inject('console') ->callback(function ($version, $dbForConsole, $getProjectDB, Registry $register, Authorization $authorization, Document $console) { - \Co\run(function () use ($version, $dbForConsole, $getProjectDB, $register, Authorization $authorization, Document $console) { - $this->action($version, $dbForConsole, $getProjectDB, $register, Authorization $authorization, Document $console); + \Co\run(function () use ($version, $dbForConsole, $getProjectDB, $register, $authorization, $console) { + $this->action($version, $dbForConsole, $getProjectDB, $register, $authorization, $console); }); }); } @@ -47,7 +46,16 @@ class Migrate extends Action private function clearProjectsCache(Document $project) { try { - $cache->purge("cache-_{$project->getInternalId()}:*"); + $iterator = null; + do { + $pattern = "default-cache-_{$project->getInternalId()}:*"; + $keys = $this->redis->scan($iterator, $pattern, 1000); + if ($keys !== false) { + foreach ($keys as $key) { + $this->redis->del($key); + } + } + } while ($iterator > 0); } catch (\Throwable $th) { Console::error('Failed to clear project ("' . $project->getId() . '") cache with error: ' . $th->getMessage()); } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 666e0649e9..a1f87ecbcd 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -6,10 +6,10 @@ use Appwrite\Utopia\Fetch\BodyMultipart; use Appwrite\Utopia\Response\Filter; use Exception; use JsonException; -use Swoole\Http\Response as SwooleHTTPResponse; // Keep last use Utopia\Database\Document; use Utopia\Swoole\Response as SwooleResponse; + // Keep last /** diff --git a/src/Appwrite/Utopia/Response/Models.php b/src/Appwrite/Utopia/Response/Models.php index 1c3ef92b8b..8f26035778 100644 --- a/src/Appwrite/Utopia/Response/Models.php +++ b/src/Appwrite/Utopia/Response/Models.php @@ -2,9 +2,6 @@ namespace Appwrite\Utopia\Response; -use Appwrite\Utopia\Fetch\BodyMultipart; -use Appwrite\Utopia\Response\Filter; -use Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response\Model\Account; use Appwrite\Utopia\Response\Model\AlgoArgon2; use Appwrite\Utopia\Response\Model\AlgoBcrypt; @@ -109,11 +106,8 @@ use Appwrite\Utopia\Response\Model\Variable; use Appwrite\Utopia\Response\Model\VcsContent; use Appwrite\Utopia\Response\Model\Webhook; use Exception; -use JsonException; -use Swoole\Http\Response as SwooleHTTPResponse; + // Keep last -use Utopia\Database\Document; -use Utopia\Swoole\Response as SwooleResponse; class Models { From feb775d2c4500e2a53385a3699c8edcd2f60581c Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 4 Sep 2024 14:52:01 -0400 Subject: [PATCH 160/195] chore: merge, lint, stan --- app/controllers/api/account.php | 2 +- app/controllers/api/functions.php | 28 +- app/controllers/api/project.php | 2 - app/controllers/api/users.php | 6 +- app/controllers/api/vcs.php | 2 +- app/controllers/shared/api.php | 6 +- app/controllers/shared/api/auth.php | 2 +- app/http.php | 2 +- app/init.php | 10 +- app/init/database/filters.php | 1 - app/init2.php | 8 +- composer.json | 10 +- composer.lock | 995 +++++++++++------- src/Appwrite/Extend/Exception.php | 2 +- src/Appwrite/Functions/Validator/Headers.php | 6 +- src/Appwrite/Functions/Validator/Payload.php | 2 +- .../Validator/RuntimeSpecification.php | 2 +- src/Appwrite/Platform/Workers/Builds.php | 10 +- src/Appwrite/Platform/Workers/Messaging.php | 2 +- .../Specification/Format/OpenAPI3.php | 4 +- .../Specification/Format/Swagger2.php | 6 +- src/Appwrite/Utopia/Request.php | 2 +- src/Appwrite/Utopia/Response.php | 4 +- src/Appwrite/Utopia/Response/Filters/V16.php | 4 +- src/Appwrite/Utopia/Response/Filters/V18.php | 4 +- src/Appwrite/Utopia/Response/Models.php | 1 + src/Executor/Executor.php | 2 +- .../e2e/Services/Functions/FunctionsBase.php | 2 +- .../Functions/FunctionsCustomServerTest.php | 6 +- tests/e2e/Services/Webhooks/WebhooksBase.php | 2 +- .../unit/Functions/Validator/HeadersBench.php | 8 +- .../unit/Functions/Validator/HeadersTest.php | 2 +- 32 files changed, 705 insertions(+), 440 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index d6be88c9de..33c044bfaa 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -393,7 +393,7 @@ Http::post('/v1/account') $existingTarget = $dbForProject->findOne('targets', [ Query::equal('identifier', [$email]), ]); - if($existingTarget) { + if ($existingTarget) { $user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND); } } diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 04c560b71f..14eb005542 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -43,9 +43,11 @@ use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Roles; use Utopia\Database\Validator\UID; use Utopia\Http\Http; +use Utopia\Http\Validator\AnyOf; use Utopia\Http\Validator\ArrayList; use Utopia\Http\Validator\Assoc; use Utopia\Http\Validator\Boolean; +use Utopia\Http\Validator\Nullable; use Utopia\Http\Validator\Range; use Utopia\Http\Validator\Text; use Utopia\Http\Validator\WhiteList; @@ -55,8 +57,6 @@ use Utopia\Storage\Validator\FileExt; use Utopia\Storage\Validator\FileSize; use Utopia\Storage\Validator\Upload; use Utopia\System\System; -use Utopia\Validator\AnyOf; -use Utopia\Validator\Nullable; use Utopia\VCS\Adapter\Git\GitHub; use Utopia\VCS\Exception\RepositoryNotFound; @@ -173,8 +173,8 @@ Http::post('/v1/functions') ->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification( $plan, Config::getParam('runtime-specifications', []), - App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT), - App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT) + System::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT), + System::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT) ), 'Runtime specification for the function and builds.', true, ['plan']) ->inject('request') ->inject('response') @@ -299,7 +299,7 @@ Http::post('/v1/functions') if (!empty($providerRepositoryId)) { // Deploy VCS $redeployVcs($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github); - } elseif(!$template->isEmpty()) { + } elseif (!$template->isEmpty()) { // Deploy non-VCS from template $deploymentId = ID::unique(); $deployment = $dbForProject->createDocument('deployments', new Document([ @@ -785,8 +785,8 @@ Http::put('/v1/functions/:functionId') ->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification( $plan, Config::getParam('runtime-specifications', []), - App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT), - App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT) + System::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT), + System::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT) ), 'Runtime specification for the function and builds.', true, ['plan']) ->inject('request') ->inject('response') @@ -900,7 +900,7 @@ Http::put('/v1/functions/:functionId') // Enforce Cold Start if spec limits change. if ($function->getAttribute('specification') !== $specification && !empty($function->getAttribute('deployment'))) { - $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); try { $executor->deleteRuntime($project->getId(), $function->getAttribute('deployment')); } catch (\Throwable $th) { @@ -1590,7 +1590,7 @@ Http::post('/v1/functions/:functionId/deployments/:deploymentId/build') } $path = $deployment->getAttribute('path'); - if(empty($path) || !$deviceForFunctions->exists($path)) { + if (empty($path) || !$deviceForFunctions->exists($path)) { throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } @@ -1697,7 +1697,7 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId/build') $dbForProject->purgeCachedDocument('deployments', $deployment->getId()); try { - $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); + $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); $executor->deleteRuntime($project->getId(), $deploymentId . "-build"); } catch (\Throwable $th) { // Don't throw if the deployment doesn't exist @@ -1747,7 +1747,7 @@ Http::post('/v1/functions/:functionId/executions') ->inject('authentication') ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, Authorization $authorization, Authentication $authentication) { - if(!$async && !is_null($scheduledAt)) { + if (!$async && !is_null($scheduledAt)) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Scheduled executions must run asynchronously. Set scheduledAt to a future date, or set async to true.'); } @@ -1878,7 +1878,7 @@ Http::post('/v1/functions/:functionId/executions') $status = $async ? 'waiting' : 'processing'; - if(!is_null($scheduledAt)) { + if (!is_null($scheduledAt)) { $status = 'scheduled'; } @@ -1909,7 +1909,7 @@ Http::post('/v1/functions/:functionId/executions') if ($async) { - if(is_null($scheduledAt)) { + if (is_null($scheduledAt)) { $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); $queueForFunctions ->setType('http') @@ -2087,7 +2087,7 @@ Http::post('/v1/functions/:functionId/executions') $acceptTypes = \explode(', ', $request->getHeader('accept')); foreach ($acceptTypes as $acceptType) { - if(\str_starts_with($acceptType, 'application/json') || \str_starts_with($acceptType, 'application/*')) { + if (\str_starts_with($acceptType, 'application/json') || \str_starts_with($acceptType, 'application/*')) { $response->setContentType(Response::CONTENT_TYPE_JSON); break; } elseif (\str_starts_with($acceptType, 'multipart/form-data') || \str_starts_with($acceptType, 'multipart/*')) { diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 0e1485ef44..03feaf5ff1 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -275,8 +275,6 @@ Http::get('/v1/project/usage') 'filesStorageTotal' => $total[METRIC_FILES_STORAGE], 'functionsStorageTotal' => $total[METRIC_DEPLOYMENTS_STORAGE] + $total[METRIC_BUILDS_STORAGE], 'executionsBreakdown' => $executionsBreakdown, - 'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown, - 'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown, 'bucketsBreakdown' => $bucketsBreakdown, 'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown, 'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown, diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index cf91f88801..d3ac1b75e5 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -140,7 +140,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e $existingTarget = $dbForProject->findOne('targets', [ Query::equal('identifier', [$email]), ]); - if($existingTarget) { + if ($existingTarget) { $user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND); } } @@ -164,7 +164,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e $existingTarget = $dbForProject->findOne('targets', [ Query::equal('identifier', [$phone]), ]); - if($existingTarget) { + if ($existingTarget) { $user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND); } } @@ -2125,7 +2125,7 @@ Http::post('/v1/users/:userId/jwts') $sessions = $user->getAttribute('sessions', []); $session = new Document(); - if($sessionId === 'recent') { + if ($sessionId === 'recent') { // Get most recent $session = \count($sessions) > 0 ? $sessions[\count($sessions) - 1] : new Document(); } else { diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index a7c4ef496c..987d84bb7e 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -508,7 +508,7 @@ Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pr $vcsContents = []; foreach ($contents as $content) { $isDirectory = false; - if($content['type'] === GitHub::CONTENTS_DIRECTORY) { + if ($content['type'] === GitHub::CONTENTS_DIRECTORY) { $isDirectory = true; } diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index fba2ac1005..5e97d7292a 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -209,14 +209,14 @@ Http::init() } // Remove after migration - if(!\str_contains($apiKey, '_')) { + if (!\str_contains($apiKey, '_')) { $keyType = API_KEY_STANDARD; $authKey = $apiKey; } else { [ $keyType, $authKey ] = \explode('_', $apiKey, 2); } - if($keyType === API_KEY_DYNAMIC) { + if ($keyType === API_KEY_DYNAMIC) { // Dynamic key $jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); @@ -246,7 +246,7 @@ Http::init() $authorization->addRole(Auth::USER_ROLE_APPS); $authorization->setDefaultStatus(false); // Cancel security segmentation for API keys. } - } elseif($keyType === API_KEY_STANDARD) { + } elseif ($keyType === API_KEY_STANDARD) { // No underline means no prefix. Backwards compatibility. // Regular key diff --git a/app/controllers/shared/api/auth.php b/app/controllers/shared/api/auth.php index b5ccc36d28..a2bda7cff7 100644 --- a/app/controllers/shared/api/auth.php +++ b/app/controllers/shared/api/auth.php @@ -55,7 +55,7 @@ Http::init() return; } - if(str_contains($route->getPath(), '/v1/graphql')) { // Skip for graphQL recursive call + if (str_contains($route->getPath(), '/v1/graphql')) { // Skip for graphQL recursive call return; } diff --git a/app/http.php b/app/http.php index 9ddb62c273..2f58d8b693 100644 --- a/app/http.php +++ b/app/http.php @@ -6,7 +6,7 @@ require_once __DIR__ . '/controllers/general.php'; use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Utopia\Abuse\Adapters\Database as TimeLimit; +use Utopia\Abuse\Adapters\Database\TimeLimit; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\CLI\Console; diff --git a/app/init.php b/app/init.php index b3fd0a833f..2d3e2a5f02 100644 --- a/app/init.php +++ b/app/init.php @@ -84,11 +84,11 @@ //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\Http\Validator\Hostname; +//use Utopia\Http\Validator\IP; +//use Utopia\Http\Validator\Range; +//use Utopia\Http\Validator\URL; +//use Utopia\Http\Validator\WhiteList; //use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub; // //const APP_NAME = 'Appwrite'; diff --git a/app/init/database/filters.php b/app/init/database/filters.php index 1564d25294..8031f5f75d 100644 --- a/app/init/database/filters.php +++ b/app/init/database/filters.php @@ -4,7 +4,6 @@ use Appwrite\OpenSSL\OpenSSL; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; - use Utopia\System\System; Database::addFilter( diff --git a/app/init2.php b/app/init2.php index b3332405b1..faf7b8cc89 100644 --- a/app/init2.php +++ b/app/init2.php @@ -35,7 +35,7 @@ use Swoole\Database\PDOPool; use Swoole\Database\RedisConfig; use Swoole\Database\RedisPool; use Utopia\Abuse\Abuse; -use Utopia\Abuse\Adapters\Database as TimeLimit; +use Utopia\Abuse\Adapters\Database\TimeLimit; use Utopia\Cache\Adapter\Redis as CacheRedis; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; @@ -201,7 +201,7 @@ $global->set('logger', function () { 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], default => ['key' => $loggingProvider->getHost()], }; - } catch (Throwable) { + } catch (Throwable $th) { Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage()); // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables $configChunks = \explode(";", $providerConfig); @@ -233,7 +233,7 @@ $global->set('logger', function () { $adapter = null; } - if($adapter === null) { + if ($adapter === null) { Console::error("Logging provider not supported. Logging is disabled"); return; } @@ -647,7 +647,7 @@ $user } $jwtSessionId = $payload['sessionId'] ?? ''; - if(!empty($jwtSessionId)) { + if (!empty($jwtSessionId)) { if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token $user = new Document([]); } diff --git a/composer.json b/composer.json index a2d0847b5b..e0b0abce46 100644 --- a/composer.json +++ b/composer.json @@ -51,12 +51,12 @@ "utopia-php/analytics": "dev-feat-framework-v2 as 0.10.99", "utopia-php/audit": "dev-feat-framework-v2 as 0.40.99", "utopia-php/cache": "0.10.*", - "utopia-php/cli": "dev-dev-coroutines as 0.17.99", + "utopia-php/cli": "1.0.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "dev-feat-framework-v2 as 0.50.99", + "utopia-php/database": "1.0.*", "utopia-php/domains": "dev-feat-framework-v2 as 0.5.99", "utopia-php/dsn": "0.2.*", - "utopia-php/framework": "dev-feat-di-upgrade as 0.34.99", + "utopia-php/framework": "1.0.0-RC2", "utopia-php/fetch": "0.2.*", "utopia-php/image": "0.6.*", "utopia-php/locale": "0.4.*", @@ -72,8 +72,8 @@ "utopia-php/registry": "0.5.*", "utopia-php/storage": "dev-feat-framework-v2-v2 as 0.18.99", "utopia-php/system": "0.8.*", - "utopia-php/vcs": "0.8.*", - "utopia-php/websocket": "0.1.*", + "utopia-php/vcs": "dev-feat-di", + "utopia-php/websocket": "0.2.*", "matomo/device-detector": "6.1.*", "dragonmantank/cron-expression": "3.3.2", "phpmailer/phpmailer": "6.9.1", diff --git a/composer.lock b/composer.lock index 78e256efee..b1061cfa46 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": "eba741eab8bb748ed684c32711d472df", + "content-hash": "ce1482681baac6626ac7c23f29a8afaa", "packages": [ { "name": "adhocore/jwt", @@ -211,16 +211,16 @@ }, { "name": "beberlei/assert", - "version": "v3.3.2", + "version": "v3.x-dev", "source": { "type": "git", "url": "https://github.com/beberlei/assert.git", - "reference": "cb70015c04be1baee6f5f5c953703347c0ac1655" + "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/beberlei/assert/zipball/cb70015c04be1baee6f5f5c953703347c0ac1655", - "reference": "cb70015c04be1baee6f5f5c953703347c0ac1655", + "url": "https://api.github.com/repos/beberlei/assert/zipball/d63a6943fc4fd1a2aedb65994e3548715105abcf", + "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf", "shasum": "" }, "require": { @@ -228,13 +228,12 @@ "ext-json": "*", "ext-mbstring": "*", "ext-simplexml": "*", - "php": "^7.0 || ^8.0" + "php": "^7" }, "require-dev": { "friendsofphp/php-cs-fixer": "*", - "phpstan/phpstan": "*", - "phpunit/phpunit": ">=6.0.0", - "yoast/phpunit-polyfills": "^0.1.0" + "phpstan/phpstan-shim": "*", + "phpunit/phpunit": ">=6.0.0 <8" }, "suggest": { "ext-intl": "Needed to allow Assertion::count(), Assertion::isCountable(), Assertion::minCount(), and Assertion::maxCount() to operate on ResourceBundles" @@ -272,9 +271,9 @@ ], "support": { "issues": "https://github.com/beberlei/assert/issues", - "source": "https://github.com/beberlei/assert/tree/v3.3.2" + "source": "https://github.com/beberlei/assert/tree/v3" }, - "time": "2021-12-16T21:41:27+00:00" + "time": "2019-12-19T17:51:41+00:00" }, { "name": "chillerlan/php-qrcode", @@ -567,16 +566,16 @@ }, { "name": "jean85/pretty-package-versions", - "version": "2.0.6", + "version": "2.x-dev", "source": { "type": "git", "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" + "reference": "d2ed36bf14b8b68d7986272734cb6a54c7822554" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/d2ed36bf14b8b68d7986272734cb6a54c7822554", + "reference": "d2ed36bf14b8b68d7986272734cb6a54c7822554", "shasum": "" }, "require": { @@ -587,9 +586,10 @@ "friendsofphp/php-cs-fixer": "^3.2", "jean85/composer-provided-replaced-stub-package": "^1.0", "phpstan/phpstan": "^1.4", - "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "vimeo/psalm": "^4.3 || ^5.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -620,9 +620,9 @@ ], "support": { "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.x" }, - "time": "2024-03-08T09:58:59+00:00" + "time": "2024-08-01T12:14:16+00:00" }, { "name": "league/csv", @@ -907,7 +907,7 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.7.0", + "version": "v2.x-dev", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", @@ -1055,7 +1055,7 @@ }, { "name": "spomky-labs/otphp", - "version": "v10.0.3", + "version": "v10.0.x-dev", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/otphp.git", @@ -1084,6 +1084,7 @@ "phpunit/phpunit": "^8.0", "thecodingmachine/phpstan-safe-rule": "^1.0 || ^2.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -1130,16 +1131,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "8740a072b86292957feb42703edde77fcfca84fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8740a072b86292957feb42703edde77fcfca84fb", + "reference": "8740a072b86292957feb42703edde77fcfca84fb", "shasum": "" }, "require": { @@ -1151,6 +1152,7 @@ "suggest": { "ext-mbstring": "For best performance" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -1190,7 +1192,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/1.x" }, "funding": [ { @@ -1206,11 +1208,11 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-06-20T08:18:00+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -1225,6 +1227,7 @@ "require": { "php": ">=7.1" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -1429,16 +1432,16 @@ }, { "name": "utopia-php/abuse", - "version": "0.42.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "08cf17e7f4fd213966c8d8702e406f2269244f0f" + "reference": "0059ae5daec97edd85276bb5c7eb501484b79ad5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/08cf17e7f4fd213966c8d8702e406f2269244f0f", - "reference": "08cf17e7f4fd213966c8d8702e406f2269244f0f", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/0059ae5daec97edd85276bb5c7eb501484b79ad5", + "reference": "0059ae5daec97edd85276bb5c7eb501484b79ad5", "shasum": "" }, "require": { @@ -1446,7 +1449,7 @@ "ext-pdo": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "0.52.*" + "utopia-php/database": "1.0.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1474,27 +1477,27 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.42.0" + "source": "https://github.com/utopia-php/abuse/tree/feat-framework-v2" }, - "time": "2024-08-21T08:24:01+00:00" + "time": "2024-09-04T18:14:29+00:00" }, { "name": "utopia-php/analytics", - "version": "0.10.2", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + "reference": "d2d98487581cdfa27095811eb95f4dadfed47bec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/d2d98487581cdfa27095811eb95f4dadfed47bec", + "reference": "d2d98487581cdfa27095811eb95f4dadfed47bec", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "^0.15.0" + "utopia-php/cli": "1.0.*" }, "require-dev": { "laravel/pint": "dev-main", @@ -1520,27 +1523,27 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + "source": "https://github.com/utopia-php/analytics/tree/feat-framework-v2" }, - "time": "2023-03-22T12:01:09+00:00" + "time": "2024-08-09T20:52:05+00:00" }, { "name": "utopia-php/audit", - "version": "0.42.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "9dc168470625bcf11ff8cd9ab5660db09129f618" + "reference": "b61bcec3f72027cfe128c20a70b200c1630d2ad3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/9dc168470625bcf11ff8cd9ab5660db09129f618", - "reference": "9dc168470625bcf11ff8cd9ab5660db09129f618", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/b61bcec3f72027cfe128c20a70b200c1630d2ad3", + "reference": "b61bcec3f72027cfe128c20a70b200c1630d2ad3", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.52.*" + "utopia-php/database": "1.0.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1567,9 +1570,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.42.0" + "source": "https://github.com/utopia-php/audit/tree/feat-framework-v2" }, - "time": "2024-08-21T08:24:08+00:00" + "time": "2024-08-09T20:52:52+00:00" }, { "name": "utopia-php/cache", @@ -1623,27 +1626,29 @@ }, { "name": "utopia-php/cli", - "version": "0.15.0", + "version": "1.0.0-RC1", "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + "reference": "7664161dcdb9b76a3ece0ae2f36a9aca1e548e80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/7664161dcdb9b76a3ece0ae2f36a9aca1e548e80", + "reference": "7664161dcdb9b76a3ece0ae2f36a9aca1e548e80", "shasum": "" }, "require": { "php": ">=7.4", - "utopia-php/framework": "0.*.*" + "utopia-php/di": "0.1.*", + "utopia-php/framework": "1.0.*" }, "require-dev": { "laravel/pint": "1.2.*", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.3", "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" + "swoole/ide-helper": "4.8.8" }, "type": "library", "autoload": { @@ -1666,9 +1671,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" + "source": "https://github.com/utopia-php/cli/tree/1.0.0-RC1" }, - "time": "2023-03-01T05:55:14+00:00" + "time": "2024-08-09T17:35:04+00:00" }, { "name": "utopia-php/config", @@ -1723,16 +1728,16 @@ }, { "name": "utopia-php/database", - "version": "0.52.2", + "version": "1.0.0-RC2", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "24b29bcac7eb7a8b81698a80bb75fc5909f4975e" + "reference": "d70eb76c7cf5ee596752209a4d57e4d08fe8a986" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/24b29bcac7eb7a8b81698a80bb75fc5909f4975e", - "reference": "24b29bcac7eb7a8b81698a80bb75fc5909f4975e", + "url": "https://api.github.com/repos/utopia-php/database/zipball/d70eb76c7cf5ee596752209a4d57e4d08fe8a986", + "reference": "d70eb76c7cf5ee596752209a4d57e4d08fe8a986", "shasum": "" }, "require": { @@ -1740,7 +1745,7 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.10.*", - "utopia-php/framework": "0.33.*", + "utopia-php/framework": "1.0.*", "utopia-php/mongo": "0.3.*" }, "require-dev": { @@ -1751,7 +1756,7 @@ "phpunit/phpunit": "9.6.*", "rregeer/phpunit-coverage-check": "0.3.*", "swoole/ide-helper": "5.1.3", - "utopia-php/cli": "0.14.*" + "utopia-php/cli": "1.0.*" }, "type": "library", "autoload": { @@ -1773,30 +1778,92 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.52.2" + "source": "https://github.com/utopia-php/database/tree/1.0.0-RC2" }, - "time": "2024-09-02T06:28:50+00:00" + "time": "2024-09-04T17:58:16+00:00" }, { - "name": "utopia-php/domains", - "version": "0.5.0", + "name": "utopia-php/di", + "version": "0.1.0", "source": { "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c" + "url": "https://github.com/utopia-php/di.git", + "reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/bf07f60326f8389f378ddf6fcde86217e5cfe18c", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c", + "url": "https://api.github.com/repos/utopia-php/di/zipball/22490c95f7ac3898ed1c33f1b1b5dd577305ee31", + "reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/", + "Tests\\E2E\\": "tests/e2e" + } + }, + "scripts": { + "lint": [ + "vendor/bin/pint --test" + ], + "format": [ + "vendor/bin/pint" + ], + "check": [ + "vendor/bin/phpstan analyse -c phpstan.neon" + ], + "test": [ + "vendor/bin/phpunit --configuration phpunit.xml" + ] + }, + "license": [ + "MIT" + ], + "description": "A simple and lite library for managing dependency injections", + "keywords": [ + "framework", + "http", + "php", + "upf" + ], + "support": { + "source": "https://github.com/utopia-php/di/tree/0.1.0", + "issues": "https://github.com/utopia-php/di/issues" + }, + "time": "2024-08-08T14:35:19+00:00" + }, + { + "name": "utopia-php/domains", + "version": "dev-feat-framework-v2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "a387c966ae2ab39878f77f94f0d01557a888bad9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/a387c966ae2ab39878f77f94f0d01557a888bad9", + "reference": "a387c966ae2ab39878f77f94f0d01557a888bad9", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/framework": "0.*.*" + "utopia-php/framework": "1.0.*" }, "require-dev": { "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.9.x-dev", "phpunit/phpunit": "^9.3" }, "type": "library", @@ -1833,9 +1900,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.5.0" + "source": "https://github.com/utopia-php/domains/tree/feat-framework-v2" }, - "time": "2024-01-03T22:04:27+00:00" + "time": "2024-08-09T20:54:00+00:00" }, { "name": "utopia-php/dsn", @@ -1925,26 +1992,30 @@ }, { "name": "utopia-php/framework", - "version": "0.33.8", + "version": "1.0.0-RC2", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5" + "reference": "d1e9674dbf33bed03fa53854ec1f2c6e074cf4d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/a7f577540a25cb90896fef2b64767bf8d700f3c5", - "reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5", + "url": "https://api.github.com/repos/utopia-php/http/zipball/d1e9674dbf33bed03fa53854ec1f2c6e074cf4d7", + "reference": "d1e9674dbf33bed03fa53854ec1f2c6e074cf4d7", "shasum": "" }, "require": { - "php": ">=8.0" + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/servers": "0.1.* " }, "require-dev": { + "ext-xdebug": "*", "laravel/pint": "^1.2", "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" }, "type": "library", "autoload": { @@ -1956,17 +2027,18 @@ "license": [ "MIT" ], - "description": "A simple, light and advanced PHP framework", + "description": "A simple, light and advanced PHP HTTP framework", "keywords": [ "framework", + "http", "php", "upf" ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.8" + "source": "https://github.com/utopia-php/http/tree/1.0.0-RC2" }, - "time": "2024-08-15T14:10:09+00:00" + "time": "2024-08-08T14:46:41+00:00" }, { "name": "utopia-php/image", @@ -2174,16 +2246,16 @@ }, { "name": "utopia-php/migration", - "version": "0.5.2", + "version": "0.4.4", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "f18d44d4459f78c292dac9edde856fd156fe497a" + "reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/f18d44d4459f78c292dac9edde856fd156fe497a", - "reference": "f18d44d4459f78c292dac9edde856fd156fe497a", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/a8a5d392bebf082faf289f4dfe09d9fd76844c33", + "reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33", "shasum": "" }, "require": { @@ -2193,7 +2265,6 @@ "require-dev": { "laravel/pint": "1.*", "phpunit/phpunit": "9.*", - "utopia-php/cli": "^0.18.0", "vlucas/phpdotenv": "5.*" }, "type": "library", @@ -2216,9 +2287,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.5.2" + "source": "https://github.com/utopia-php/migration/tree/0.4.4" }, - "time": "2024-07-22T09:27:07+00:00" + "time": "2024-05-17T05:25:31+00:00" }, { "name": "utopia-php/mongo", @@ -2282,26 +2353,26 @@ }, { "name": "utopia-php/orchestration", - "version": "0.9.1", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + "reference": "df65de2132d61b0d862d5812cfab515aca4739cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/df65de2132d61b0d862d5812cfab515aca4739cd", + "reference": "df65de2132d61b0d862d5812cfab515aca4739cd", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*" + "utopia-php/cli": "1.0.*" }, "require-dev": { "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.3" }, "type": "library", "autoload": { @@ -2326,31 +2397,31 @@ ], "support": { "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + "source": "https://github.com/utopia-php/orchestration/tree/feat-framework-v2" }, - "time": "2023-03-17T15:05:06+00:00" + "time": "2024-08-09T20:55:49+00:00" }, { "name": "utopia-php/platform", - "version": "0.7.0", + "version": "dev-feat-framework-v2", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52" + "reference": "c64b8efb3448b4ed1868c8e80bdb1c5a8407f7e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/c64b8efb3448b4ed1868c8e80bdb1c5a8407f7e5", + "reference": "c64b8efb3448b4ed1868c8e80bdb1c5a8407f7e5", "shasum": "" }, "require": { "ext-json": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.33.*", - "utopia-php/queue": "0.7.*" + "utopia-php/cli": "1.0.*", + "utopia-php/framework": "1.0.*", + "utopia-php/queue": "dev-feat-coroutine-and-di" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2376,9 +2447,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.7.0" + "source": "https://github.com/utopia-php/platform/tree/feat-framework-v2" }, - "time": "2024-05-08T17:00:55+00:00" + "time": "2024-09-04T18:20:34+00:00" }, { "name": "utopia-php/pools", @@ -2486,22 +2557,23 @@ }, { "name": "utopia-php/queue", - "version": "0.7.0", + "version": "dev-feat-coroutine-and-di", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "917565256eb94bcab7246f7a746b1a486813761b" + "reference": "ed85fd26200f07d9b93d18d2fa7f5bbef2984902" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/917565256eb94bcab7246f7a746b1a486813761b", - "reference": "917565256eb94bcab7246f7a746b1a486813761b", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/ed85fd26200f07d9b93d18d2fa7f5bbef2984902", + "reference": "ed85fd26200f07d9b93d18d2fa7f5bbef2984902", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" + "utopia-php/cli": "1.0.*", + "utopia-php/di": "0.1.*", + "utopia-php/servers": "0.1.*" }, "require-dev": { "laravel/pint": "^0.2.3", @@ -2511,6 +2583,7 @@ "workerman/workerman": "^4.0" }, "suggest": { + "ext-redis": "Needed to support Redis connections", "ext-swoole": "Needed to support Swoole.", "workerman/workerman": "Needed to support Workerman." }, @@ -2541,9 +2614,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.7.0" + "source": "https://github.com/utopia-php/queue/tree/feat-coroutine-and-di" }, - "time": "2024-01-17T19:00:43+00:00" + "time": "2024-09-04T18:19:23+00:00" }, { "name": "utopia-php/registry", @@ -2598,17 +2671,88 @@ "time": "2021-03-10T10:45:22+00:00" }, { - "name": "utopia-php/storage", - "version": "0.18.4", + "name": "utopia-php/servers", + "version": "0.1.0", "source": { "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "94ab8758fabcefee5c5fa723616e45719833f922" + "url": "https://github.com/utopia-php/servers.git", + "reference": "7d9e4f364fb1ab1889fb89ca96eb9946467cb09c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/94ab8758fabcefee5c5fa723616e45719833f922", - "reference": "94ab8758fabcefee5c5fa723616e45719833f922", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/7d9e4f364fb1ab1889fb89ca96eb9946467cb09c", + "reference": "7d9e4f364fb1ab1889fb89ca96eb9946467cb09c", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/di": "0.1.*" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Servers\\": "src/Servers" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\E2E\\": "tests/Servers/Unit" + } + }, + "scripts": { + "test": [ + "phpunit" + ], + "analyse": [ + "vendor/bin/phpstan analyse" + ], + "format": [ + "vendor/bin/pint" + ], + "lint": [ + "vendor/bin/pint --test" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A base library for building Utopia style servers.", + "keywords": [ + "framework", + "php", + "servers", + "upf", + "utopia" + ], + "support": { + "source": "https://github.com/utopia-php/servers/tree/0.1.0", + "issues": "https://github.com/utopia-php/servers/issues" + }, + "time": "2024-08-08T14:31:39+00:00" + }, + { + "name": "utopia-php/storage", + "version": "dev-feat-framework-v2-v2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "dffcbe0be08c8114750e6af4f682ae965e3791ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/dffcbe0be08c8114750e6af4f682ae965e3791ea", + "reference": "dffcbe0be08c8114750e6af4f682ae965e3791ea", "shasum": "" }, "require": { @@ -2620,7 +2764,7 @@ "ext-zlib": "*", "ext-zstd": "*", "php": ">=8.0", - "utopia-php/framework": "0.*.*", + "utopia-php/framework": "1.0.*", "utopia-php/system": "0.*.*" }, "require-dev": { @@ -2648,60 +2792,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.4" + "source": "https://github.com/utopia-php/storage/tree/feat-framework-v2-v2" }, - "time": "2024-04-02T08:24:09+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.8.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.33.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "5.0.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.8.2" - }, - "time": "2024-02-01T14:54:12+00:00" + "time": "2024-08-09T21:00:39+00:00" }, { "name": "utopia-php/system", @@ -2761,23 +2854,23 @@ }, { "name": "utopia-php/vcs", - "version": "0.8.2", + "version": "dev-feat-di", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18" + "reference": "5995ae714550d3a386b4585e4d4fde1b3637919c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18", - "reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/5995ae714550d3a386b4585e4d4fde1b3637919c", + "reference": "5995ae714550d3a386b4585e4d4fde1b3637919c", "shasum": "" }, "require": { "adhocore/jwt": "^1.1", "php": ">=8.0", - "utopia-php/cache": "^0.10.0", - "utopia-php/framework": "0.*.*" + "utopia-php/cache": "0.10.*", + "utopia-php/framework": "1.0.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2804,32 +2897,76 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.8.2" + "source": "https://github.com/utopia-php/vcs/tree/feat-di" }, - "time": "2024-08-13T14:36:30+00:00" + "time": "2024-09-04T18:24:08+00:00" }, { - "name": "utopia-php/websocket", - "version": "0.1.0", + "name": "utopia-php/view", + "version": "0.2.0", "source": { "type": "git", - "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + "url": "https://github.com/utopia-php/view.git", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "url": "https://api.github.com/repos/utopia-php/view/zipball/6ee55e83bc014c39ed6b69390f6d399116f65e88", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88", "shasum": "" }, "require": { "php": ">=8.0" }, "require-dev": { + "laravel/pint": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\View\\": "src/View" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP rendering engine", + "keywords": [ + "php", + "view" + ], + "support": { + "issues": "https://github.com/utopia-php/view/issues", + "source": "https://github.com/utopia-php/view/tree/0.2.0" + }, + "time": "2024-04-01T17:21:29+00:00" + }, + { + "name": "utopia-php/websocket", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/websocket.git", + "reference": "e9d0919b321744a61f12563f5791c47ba9f57810" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/e9d0919b321744a61f12563f5791c47ba9f57810", + "reference": "e9d0919b321744a61f12563f5791c47ba9f57810", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.15", + "phpstan/phpstan": "^1.8", "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", + "swoole/ide-helper": "5.1.2", "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", "workerman/workerman": "^4.0" }, "type": "library", @@ -2842,16 +2979,6 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], "description": "A simple abstraction for WebSocket servers.", "keywords": [ "framework", @@ -2862,9 +2989,9 @@ ], "support": { "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + "source": "https://github.com/utopia-php/websocket/tree/0.2.0" }, - "time": "2021-12-20T10:50:09+00:00" + "time": "2024-04-09T08:28:11+00:00" }, { "name": "webmozart/assert", @@ -3044,16 +3171,16 @@ }, { "name": "doctrine/annotations", - "version": "2.0.1", + "version": "2.0.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" + "reference": "94f40ad7ecbc6931958faa8a57c48dce5da2b468" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/94f40ad7ecbc6931958faa8a57c48dce5da2b468", + "reference": "94f40ad7ecbc6931958faa8a57c48dce5da2b468", "shasum": "" }, "require": { @@ -3073,6 +3200,7 @@ "suggest": { "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -3114,13 +3242,13 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.1" + "source": "https://github.com/doctrine/annotations/tree/2.0.x" }, - "time": "2023-02-02T22:02:53+00:00" + "time": "2023-08-23T17:36:07+00:00" }, { "name": "doctrine/deprecations", - "version": "1.1.3", + "version": "1.1.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", @@ -3147,6 +3275,7 @@ "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -3167,29 +3296,29 @@ }, { "name": "doctrine/instantiator", - "version": "1.5.0", + "version": "1.5.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "reference": "12be2483e1f0e850b353e26869e4e6c038459501" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/12be2483e1f0e850b353e26869e4e6c038459501", + "reference": "12be2483e1f0e850b353e26869e4e6c038459501", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", + "doctrine/coding-standard": "^9 || ^12", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.16 || ^1", "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", @@ -3217,7 +3346,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "source": "https://github.com/doctrine/instantiator/tree/1.5.x" }, "funding": [ { @@ -3233,20 +3362,20 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2023-12-09T14:16:53+00:00" }, { "name": "doctrine/lexer", - "version": "3.0.1", + "version": "3.1.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + "reference": "042e47e28c5e03f1cf6772fdf3dd4e0785433e05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/042e47e28c5e03f1cf6772fdf3dd4e0785433e05", + "reference": "042e47e28c5e03f1cf6772fdf3dd4e0785433e05", "shasum": "" }, "require": { @@ -3294,7 +3423,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.0.1" + "source": "https://github.com/doctrine/lexer/tree/3.1.x" }, "funding": [ { @@ -3310,20 +3439,20 @@ "type": "tidelift" } ], - "time": "2024-02-05T11:56:58+00:00" + "time": "2024-07-29T08:29:21+00:00" }, { "name": "laravel/pint", - "version": "v1.17.2", + "version": "v1.17.3", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110" + "reference": "9d77be916e145864f10788bb94531d03e1f7b482" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/e8a88130a25e3f9d4d5785e6a1afca98268ab110", - "reference": "e8a88130a25e3f9d4d5785e6a1afca98268ab110", + "url": "https://api.github.com/repos/laravel/pint/zipball/9d77be916e145864f10788bb94531d03e1f7b482", + "reference": "9d77be916e145864f10788bb94531d03e1f7b482", "shasum": "" }, "require": { @@ -3334,13 +3463,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.61.1", - "illuminate/view": "^10.48.18", + "friendsofphp/php-cs-fixer": "^3.64.0", + "illuminate/view": "^10.48.20", "larastan/larastan": "^2.9.8", "laravel-zero/framework": "^10.4.0", "mockery/mockery": "^1.6.12", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.35.0" + "pestphp/pest": "^2.35.1" }, "bin": [ "builds/pint" @@ -3376,7 +3505,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-08-06T15:11:54+00:00" + "time": "2024-09-03T15:00:28+00:00" }, { "name": "matthiasmullie/minify", @@ -3504,7 +3633,7 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", @@ -3529,6 +3658,7 @@ "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, + "default-branch": true, "type": "library", "autoload": { "files": [ @@ -3622,7 +3752,7 @@ }, { "name": "phar-io/manifest", - "version": "2.0.4", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", @@ -3642,6 +3772,7 @@ "phar-io/version": "^3.0.1", "php": "^7.2 || ^8.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3740,7 +3871,7 @@ }, { "name": "phpbench/container", - "version": "2.2.2", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpbench/container.git", @@ -3761,6 +3892,7 @@ "phpstan/phpstan": "^0.12.52", "phpunit/phpunit": "^8" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3941,25 +4073,25 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "reference": "a0eeab580cbdf4414fef6978732510a36ed0a9d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/a0eeab580cbdf4414fef6978732510a36ed0a9d6", + "reference": "a0eeab580cbdf4414fef6978732510a36ed0a9d6", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-2.x": "2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -3988,22 +4120,22 @@ ], "support": { "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master" }, - "time": "2020-06-27T09:03:43+00:00" + "time": "2021-06-25T13:47:51+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.4.1", + "version": "5.x-dev", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + "reference": "60741fe3871f40e44ca10a28ce85d08b7ed841cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/60741fe3871f40e44ca10a28ce85d08b7ed841cd", + "reference": "60741fe3871f40e44ca10a28ce85d08b7ed841cd", "shasum": "" }, "require": { @@ -4024,6 +4156,7 @@ "phpunit/phpunit": "^9.5", "vimeo/psalm": "^5.13" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4052,29 +4185,29 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.x" }, - "time": "2024-05-21T05:55:05+00:00" + "time": "2024-08-14T20:00:37+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "reference": "eee054a3d40f09920f5b27c9b09a6483f88d9d24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/eee054a3d40f09920f5b27c9b09a6483f88d9d24", + "reference": "eee054a3d40f09920f5b27c9b09a6483f88d9d24", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "phpstan/phpdoc-parser": "^1.18" }, "require-dev": { "ext-tokenizer": "*", @@ -4086,6 +4219,7 @@ "rector/rector": "^0.13.9", "vimeo/psalm": "^4.25" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4110,22 +4244,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.x" }, - "time": "2024-02-23T11:10:43+00:00" + "time": "2024-05-24T14:24:30+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.19.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87" + "reference": "e3810e5e638cd91f6f6d82059c60ad85f2b3ffb1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/67a759e7d8746d501c41536ba40cd9c0a07d6a87", - "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e3810e5e638cd91f6f6d82059c60ad85f2b3ffb1", + "reference": "e3810e5e638cd91f6f6d82059c60ad85f2b3ffb1", "shasum": "" }, "require": { @@ -4136,10 +4270,12 @@ "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^3.40", "phpspec/phpspec": "^6.0 || ^7.0", "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4179,22 +4315,22 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.19.0" + "source": "https://github.com/phpspec/prophecy/tree/master" }, - "time": "2024-02-29T11:52:51+00:00" + "time": "2024-08-27T07:52:54+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.29.1", + "version": "1.30.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" + "reference": "5ceb0e384997db59f38774bf79c2a6134252c08f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/5ceb0e384997db59f38774bf79c2a6134252c08f", + "reference": "5ceb0e384997db59f38774bf79c2a6134252c08f", "shasum": "" }, "require": { @@ -4226,13 +4362,72 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.30.0" }, - "time": "2024-05-31T08:52:43+00:00" + "time": "2024-08-29T09:54:52+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.8.x-dev", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "b20042710baa0d9c07636cc66d4c400f03f1477a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b20042710baa0d9c07636cc66d4c400f03f1477a", + "reference": "b20042710baa0d9c07636cc66d4c400f03f1477a", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.8.x" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-10-29T12:56:57+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.32", + "version": "9.2.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", @@ -4310,16 +4505,16 @@ }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "3.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "38b24367e1b340aa78b96d7cab042942d917bb84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/38b24367e1b340aa78b96d7cab042942d917bb84", + "reference": "38b24367e1b340aa78b96d7cab042942d917bb84", "shasum": "" }, "require": { @@ -4358,7 +4553,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0" }, "funding": [ { @@ -4366,7 +4561,7 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2022-02-11T16:23:04+00:00" }, { "name": "phpunit/php-invoker", @@ -4654,25 +4849,29 @@ }, { "name": "psr/cache", - "version": "3.0.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + "reference": "0a7c67d0d1c8167b342eb74339d6f961663826ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "url": "https://api.github.com/repos/php-fig/cache/zipball/0a7c67d0d1c8167b342eb74339d6f961663826ce", + "reference": "0a7c67d0d1c8167b342eb74339d6f961663826ce", "shasum": "" }, "require": { "php": ">=8.0.0" }, + "suggest": { + "fig/cache-util": "Provides some useful PSR-6 utilities" + }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -4697,27 +4896,28 @@ "psr-6" ], "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" + "source": "https://github.com/php-fig/cache/tree/master" }, - "time": "2021-02-03T23:26:27+00:00" + "time": "2021-02-24T03:25:37+00:00" }, { "name": "psr/container", - "version": "2.0.2", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + "reference": "707984727bd5b2b670e59559d3ed2500240cf875" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://api.github.com/repos/php-fig/container/zipball/707984727bd5b2b670e59559d3ed2500240cf875", + "reference": "707984727bd5b2b670e59559d3ed2500240cf875", "shasum": "" }, "require": { "php": ">=7.4.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4750,13 +4950,13 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" + "source": "https://github.com/php-fig/container" }, - "time": "2021-11-05T16:47:00+00:00" + "time": "2023-09-22T11:11:30+00:00" }, { "name": "psr/log", - "version": "3.0.1", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", @@ -4771,6 +4971,7 @@ "require": { "php": ">=8.0.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4806,7 +5007,7 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.2", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", @@ -4973,16 +5174,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "b247957a1c8dc81a671770f74b479c0a78a818f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b247957a1c8dc81a671770f74b479c0a78a818f1", + "reference": "b247957a1c8dc81a671770f74b479c0a78a818f1", "shasum": "" }, "require": { @@ -5035,7 +5236,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0" }, "funding": [ { @@ -5043,11 +5244,11 @@ "type": "github" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2022-09-14T12:46:14+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.3", + "version": "2.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", @@ -5104,7 +5305,7 @@ }, { "name": "sebastian/diff", - "version": "4.0.6", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", @@ -5170,7 +5371,7 @@ }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "5.1.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", @@ -5221,7 +5422,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1" }, "funding": [ { @@ -5233,7 +5434,7 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", @@ -5310,7 +5511,7 @@ }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", @@ -5374,7 +5575,7 @@ }, { "name": "sebastian/lines-of-code", - "version": "1.0.4", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", @@ -5543,7 +5744,7 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", @@ -5606,16 +5807,16 @@ }, { "name": "sebastian/resource-operations", - "version": "3.0.4", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + "reference": "ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", - "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25", + "reference": "ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25", "shasum": "" }, "require": { @@ -5624,6 +5825,7 @@ "require-dev": { "phpunit/phpunit": "^9.0" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -5648,7 +5850,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/main" }, "funding": [ { @@ -5656,11 +5858,11 @@ "type": "github" } ], - "time": "2024-03-14T16:00:52+00:00" + "time": "2024-03-14T18:47:08+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "3.2.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", @@ -5704,7 +5906,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "source": "https://github.com/sebastianbergmann/type/tree/3.2" }, "funding": [ { @@ -5716,7 +5918,7 @@ }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "3.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", @@ -5865,16 +6067,16 @@ }, { "name": "symfony/console", - "version": "v7.1.3", + "version": "7.2.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9" + "reference": "ee7ee22b1dabcff731b7d79d3633c96251ad8404" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", - "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", + "url": "https://api.github.com/repos/symfony/console/zipball/ee7ee22b1dabcff731b7d79d3633c96251ad8404", + "reference": "ee7ee22b1dabcff731b7d79d3633c96251ad8404", "shasum": "" }, "require": { @@ -5938,7 +6140,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.3" + "source": "https://github.com/symfony/console/tree/7.2" }, "funding": [ { @@ -5954,11 +6156,11 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:41:01+00:00" + "time": "2024-08-30T15:40:32+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.0", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", @@ -5973,6 +6175,7 @@ "require": { "php": ">=8.1" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -6025,16 +6228,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.1.2", + "version": "7.2.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "92a91985250c251de9b947a14bb2c9390b1a562c" + "reference": "c46c178f375c2dfddc7b6a32731077c778e14264" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/92a91985250c251de9b947a14bb2c9390b1a562c", - "reference": "92a91985250c251de9b947a14bb2c9390b1a562c", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c46c178f375c2dfddc7b6a32731077c778e14264", + "reference": "c46c178f375c2dfddc7b6a32731077c778e14264", "shasum": "" }, "require": { @@ -6071,7 +6274,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.1.2" + "source": "https://github.com/symfony/filesystem/tree/7.2" }, "funding": [ { @@ -6087,20 +6290,20 @@ "type": "tidelift" } ], - "time": "2024-06-28T10:03:55+00:00" + "time": "2024-07-23T10:47:31+00:00" }, { "name": "symfony/finder", - "version": "v7.1.3", + "version": "7.2.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "717c6329886f32dc65e27461f80f2a465412fdca" + "reference": "afa87bce0d224a744963ecc8db07b1f0f96a3518" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/717c6329886f32dc65e27461f80f2a465412fdca", - "reference": "717c6329886f32dc65e27461f80f2a465412fdca", + "url": "https://api.github.com/repos/symfony/finder/zipball/afa87bce0d224a744963ecc8db07b1f0f96a3518", + "reference": "afa87bce0d224a744963ecc8db07b1f0f96a3518", "shasum": "" }, "require": { @@ -6135,7 +6338,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.3" + "source": "https://github.com/symfony/finder/tree/7.2" }, "funding": [ { @@ -6151,20 +6354,20 @@ "type": "tidelift" } ], - "time": "2024-07-24T07:08:44+00:00" + "time": "2024-09-03T13:22:29+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.1.1", + "version": "7.2.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55" + "reference": "28b7840cd3a01a74bc86fa77587f02b351ad3ade" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55", - "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/28b7840cd3a01a74bc86fa77587f02b351ad3ade", + "reference": "28b7840cd3a01a74bc86fa77587f02b351ad3ade", "shasum": "" }, "require": { @@ -6202,7 +6405,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.1.1" + "source": "https://github.com/symfony/options-resolver/tree/7.2" }, "funding": [ { @@ -6218,11 +6421,11 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-07-06T07:57:47+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -6243,6 +6446,7 @@ "suggest": { "ext-ctype": "For best performance" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -6301,7 +6505,7 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", @@ -6319,6 +6523,7 @@ "suggest": { "ext-intl": "For best performance" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -6379,7 +6584,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.30.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -6397,6 +6602,7 @@ "suggest": { "ext-intl": "For best performance" }, + "default-branch": true, "type": "library", "extra": { "thanks": { @@ -6460,16 +6666,16 @@ }, { "name": "symfony/process", - "version": "v7.1.3", + "version": "7.2.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca" + "reference": "bb0a8b7772610211c2cd7d6e4e36acfcbadcb613" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/7f2f542c668ad6c313dc4a5e9c3321f733197eca", - "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca", + "url": "https://api.github.com/repos/symfony/process/zipball/bb0a8b7772610211c2cd7d6e4e36acfcbadcb613", + "reference": "bb0a8b7772610211c2cd7d6e4e36acfcbadcb613", "shasum": "" }, "require": { @@ -6501,7 +6707,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.3" + "source": "https://github.com/symfony/process/tree/7.2" }, "funding": [ { @@ -6517,11 +6723,11 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:44:47+00:00" + "time": "2024-07-29T06:33:22+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.5.0", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", @@ -6541,6 +6747,7 @@ "conflict": { "ext-psr": "<1.1|>=2" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -6604,16 +6811,16 @@ }, { "name": "symfony/string", - "version": "v7.1.3", + "version": "7.2.x-dev", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ea272a882be7f20cad58d5d78c215001617b7f07" + "reference": "c38cbd6dcf2a45bcfbd79fb89d6bd08f7afdc7dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ea272a882be7f20cad58d5d78c215001617b7f07", - "reference": "ea272a882be7f20cad58d5d78c215001617b7f07", + "url": "https://api.github.com/repos/symfony/string/zipball/c38cbd6dcf2a45bcfbd79fb89d6bd08f7afdc7dc", + "reference": "c38cbd6dcf2a45bcfbd79fb89d6bd08f7afdc7dc", "shasum": "" }, "require": { @@ -6671,7 +6878,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.3" + "source": "https://github.com/symfony/string/tree/7.2" }, "funding": [ { @@ -6687,7 +6894,7 @@ "type": "tidelift" } ], - "time": "2024-07-22T10:25:37+00:00" + "time": "2024-08-12T10:26:02+00:00" }, { "name": "textalk/websocket", @@ -6862,16 +7069,16 @@ }, { "name": "webmozart/glob", - "version": "4.7.0", + "version": "4.8.x-dev", "source": { "type": "git", "url": "https://github.com/webmozarts/glob.git", - "reference": "8a2842112d6916e61e0e15e316465b611f3abc17" + "reference": "6712c9c4a8b0f6f629303bd1b26b9f88339d901e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/glob/zipball/8a2842112d6916e61e0e15e316465b611f3abc17", - "reference": "8a2842112d6916e61e0e15e316465b611f3abc17", + "url": "https://api.github.com/repos/webmozarts/glob/zipball/6712c9c4a8b0f6f629303bd1b26b9f88339d901e", + "reference": "6712c9c4a8b0f6f629303bd1b26b9f88339d901e", "shasum": "" }, "require": { @@ -6881,6 +7088,7 @@ "phpunit/phpunit": "^9.5", "symfony/filesystem": "^5.3" }, + "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -6905,14 +7113,73 @@ "description": "A PHP implementation of Ant's glob.", "support": { "issues": "https://github.com/webmozarts/glob/issues", - "source": "https://github.com/webmozarts/glob/tree/4.7.0" + "source": "https://github.com/webmozarts/glob/tree/4.8.x" }, - "time": "2024-03-07T20:33:40+00:00" + "time": "2024-08-06T15:56:59+00:00" } ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], + "aliases": [ + { + "package": "utopia-php/abuse", + "version": "dev-feat-framework-v2", + "alias": "0.39.99", + "alias_normalized": "0.39.99.0" + }, + { + "package": "utopia-php/analytics", + "version": "dev-feat-framework-v2", + "alias": "0.10.99", + "alias_normalized": "0.10.99.0" + }, + { + "package": "utopia-php/audit", + "version": "dev-feat-framework-v2", + "alias": "0.40.99", + "alias_normalized": "0.40.99.0" + }, + { + "package": "utopia-php/domains", + "version": "dev-feat-framework-v2", + "alias": "0.5.99", + "alias_normalized": "0.5.99.0" + }, + { + "package": "utopia-php/orchestration", + "version": "dev-feat-framework-v2", + "alias": "0.9.99", + "alias_normalized": "0.9.99.0" + }, + { + "package": "utopia-php/platform", + "version": "dev-feat-framework-v2", + "alias": "0.5.99", + "alias_normalized": "0.5.99.0" + }, + { + "package": "utopia-php/queue", + "version": "dev-feat-coroutine-and-di", + "alias": "0.7.99", + "alias_normalized": "0.7.99.0" + }, + { + "package": "utopia-php/storage", + "version": "dev-feat-framework-v2-v2", + "alias": "0.18.99", + "alias_normalized": "0.18.99.0" + } + ], + "minimum-stability": "dev", + "stability-flags": { + "utopia-php/abuse": 20, + "utopia-php/analytics": 20, + "utopia-php/audit": 20, + "utopia-php/domains": 20, + "utopia-php/orchestration": 20, + "utopia-php/platform": 20, + "utopia-php/queue": 20, + "utopia-php/storage": 20, + "utopia-php/vcs": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 884296ff67..e0cf8058c9 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -313,7 +313,7 @@ class Exception extends \Exception $this->code = $code ?? $this->errors[$type]['code']; // Mark string errors like HY001 from PDO as 500 errors - if(\is_string($this->code)) { + if (\is_string($this->code)) { if (\is_numeric($this->code)) { $this->code = (int) $this->code; } else { diff --git a/src/Appwrite/Functions/Validator/Headers.php b/src/Appwrite/Functions/Validator/Headers.php index 6d5b2da5df..4c30a98045 100644 --- a/src/Appwrite/Functions/Validator/Headers.php +++ b/src/Appwrite/Functions/Validator/Headers.php @@ -2,7 +2,7 @@ namespace Appwrite\Functions\Validator; -use Utopia\Validator; +use Utopia\Http\Validator; /** * Headers. @@ -44,7 +44,7 @@ class Headers extends Validator return false; } - if(\count($value) > $this->maxKeys) { + if (\count($value) > $this->maxKeys) { return false; } @@ -57,7 +57,7 @@ class Headers extends Validator } $size += $length + \strlen($val); - if($size >= $this->maxSize) { + if ($size >= $this->maxSize) { return false; } diff --git a/src/Appwrite/Functions/Validator/Payload.php b/src/Appwrite/Functions/Validator/Payload.php index acb461eabd..3b2ff4b918 100644 --- a/src/Appwrite/Functions/Validator/Payload.php +++ b/src/Appwrite/Functions/Validator/Payload.php @@ -2,7 +2,7 @@ namespace Appwrite\Functions\Validator; -use Utopia\Validator\Text; +use Utopia\Http\Validator\Text; class Payload extends Text { diff --git a/src/Appwrite/Functions/Validator/RuntimeSpecification.php b/src/Appwrite/Functions/Validator/RuntimeSpecification.php index 22311838f6..fa6efe90a4 100644 --- a/src/Appwrite/Functions/Validator/RuntimeSpecification.php +++ b/src/Appwrite/Functions/Validator/RuntimeSpecification.php @@ -2,7 +2,7 @@ namespace Appwrite\Functions\Validator; -use Utopia\Validator; +use Utopia\Http\Validator; class RuntimeSpecification extends Validator { diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 911ffe3f92..7bbc14daa4 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -211,7 +211,7 @@ class Builds extends Action } try { - if($isNewBuild && !$isVcsEnabled) { + if ($isNewBuild && !$isVcsEnabled) { // Non-vcs+Template $templateRepositoryName = $template->getAttribute('repositoryName', ''); @@ -231,7 +231,7 @@ class Builds extends Action $exit = Console::execute($gitCloneCommandForTemplate, '', $output); if ($exit !== 0) { - throw new \Exception('Unable to clone code repository: ' . $stderr); + throw new \Exception('Unable to clone code repository: ' . $output); } // Ensure directories @@ -281,7 +281,7 @@ class Builds extends Action $cloneVersion = $branchName; $cloneType = GitHub::CLONE_TYPE_BRANCH; - if(!empty($commitHash)) { + if (!empty($commitHash)) { $cloneVersion = $commitHash; $cloneType = GitHub::CLONE_TYPE_COMMIT; } @@ -307,7 +307,7 @@ class Builds extends Action $exit = Console::execute('mv "' . \escapeshellarg($from) . '" "' . \escapeshellarg($to) . '"', '', $output); if ($exit !== 0) { - throw new \Exception('Unable to move function with spaces' . $stderr); + throw new \Exception('Unable to move function with spaces' . $output); } $rootDirectory = $rootDirectoryWithoutSpaces; } @@ -541,7 +541,7 @@ class Builds extends Action deploymentId: $deployment->getId(), projectId: $project->getId(), callback: function ($logs) use (&$response, &$err, &$build, $dbForProject, $allEvents, $project, &$isCanceled) { - if($isCanceled) { + if ($isCanceled) { return; } diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 6f642fabb7..0f0d298e3b 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -669,7 +669,7 @@ class Messaging extends Action private function getLocalDevice($project): Local { - if($this->localDevice === null) { + if ($this->localDevice === null) { $this->localDevice = new Local(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); } diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index a2cbd0a139..b107dc3ab4 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -240,7 +240,7 @@ class OpenAPI3 extends Format if ($route->getLabel('sdk.response.code', 500) === 204) { $labelCode = (string)$route->getLabel('sdk.response.code', '500'); $temp['responses'][$labelCode]['description'] = 'No content'; - if(isset($temp['responses'][$labelCode]['schema'])) { + if (isset($temp['responses'][$labelCode]['schema'])) { unset($temp['responses'][$labelCode]['schema']); } } @@ -274,7 +274,7 @@ class OpenAPI3 extends Format foreach ($route->getParams() as $name => $param) { // Set params $injections = []; - if(isset($param['injections'])) { + if (isset($param['injections'])) { $injections = array_map(fn ($injection) => $this->http->getContainer()->get($injection), $param['injections']); } diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index c0eb30cb17..5773501c7e 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -272,7 +272,7 @@ class Swagger2 extends Format foreach ($parameters as $name => $param) { // Set params $injections = []; - if(isset($param['injections'])) { + if (isset($param['injections'])) { $injections = array_map(fn ($injection) => $this->http->getContainer()->get($injection), $param['injections']); } @@ -293,13 +293,13 @@ class Swagger2 extends Format } $validatorClass = (!empty($validator)) ? \get_class($validator) : ''; - if($validatorClass === 'Utopia\Validator\AnyOf') { + if ($validatorClass === 'Utopia\Http\Validator\AnyOf') { $validator = $param['validator']->getValidators()[0]; $validatorClass = \get_class($validator); } switch ($validatorClass) { - case 'Utopia\Validator\Text': + case 'Utopia\Http\Validator\Text': case 'Utopia\Database\Validator\UID': $node['type'] = $validator->getType(); $node['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; diff --git a/src/Appwrite/Utopia/Request.php b/src/Appwrite/Utopia/Request.php index bc8881e121..6be9701baa 100644 --- a/src/Appwrite/Utopia/Request.php +++ b/src/Appwrite/Utopia/Request.php @@ -134,7 +134,7 @@ class Request extends HttpRequest */ public function getHeaders(): array { - if($this->headers !== null) { + if ($this->headers !== null) { return $this->headers; } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index a1f87ecbcd..7a3ab850a3 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -8,7 +8,7 @@ use Exception; use JsonException; // Keep last use Utopia\Database\Document; -use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Http\Adapter\Swoole\Response as HttpResponse; // Keep last @@ -16,7 +16,7 @@ use Utopia\Swoole\Response as SwooleResponse; * @method int getStatusCode() * @method Response setStatusCode(int $code = 200) */ -class Response extends SwooleResponse +class Response extends HttpResponse { // General public const MODEL_NONE = 'none'; diff --git a/src/Appwrite/Utopia/Response/Filters/V16.php b/src/Appwrite/Utopia/Response/Filters/V16.php index 2a27715d5e..7eb3ec6eb3 100644 --- a/src/Appwrite/Utopia/Response/Filters/V16.php +++ b/src/Appwrite/Utopia/Response/Filters/V16.php @@ -33,13 +33,13 @@ class V16 extends Filter protected function parseDeployment(array $content) { - if(isset($content['buildLogs'])) { + if (isset($content['buildLogs'])) { $content['buildStderr'] = ''; $content['buildStdout'] = $content['buildLogs']; unset($content['buildLogs']); } - if(isset($content['buildSize'])) { + if (isset($content['buildSize'])) { $content['size'] += + $content['buildSize'] ?? 0; unset($content['buildSize']); } diff --git a/src/Appwrite/Utopia/Response/Filters/V18.php b/src/Appwrite/Utopia/Response/Filters/V18.php index 0a74a2afed..fc1624a289 100644 --- a/src/Appwrite/Utopia/Response/Filters/V18.php +++ b/src/Appwrite/Utopia/Response/Filters/V18.php @@ -25,8 +25,8 @@ class V18 extends Filter protected function parseExecution(array $content) { - if(!empty($content['status']) && !empty($content['statusCode'])) { - if($content['status'] === 'completed' && $content['statusCode'] >= 400 && $content['statusCode'] < 500) { + if (!empty($content['status']) && !empty($content['statusCode'])) { + if ($content['status'] === 'completed' && $content['statusCode'] >= 400 && $content['statusCode'] < 500) { $content['status'] = 'failed'; } } diff --git a/src/Appwrite/Utopia/Response/Models.php b/src/Appwrite/Utopia/Response/Models.php index 8f26035778..2a0393321c 100644 --- a/src/Appwrite/Utopia/Response/Models.php +++ b/src/Appwrite/Utopia/Response/Models.php @@ -2,6 +2,7 @@ namespace Appwrite\Utopia\Response; +use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model\Account; use Appwrite\Utopia\Response\Model\AlgoArgon2; use Appwrite\Utopia\Response\Model\AlgoBcrypt; diff --git a/src/Executor/Executor.php b/src/Executor/Executor.php index 7cd239623c..c230cfb664 100644 --- a/src/Executor/Executor.php +++ b/src/Executor/Executor.php @@ -217,7 +217,7 @@ class Executor 'restartPolicy' => 'always' // Once utopia/orchestration has it, use DockerAPI::ALWAYS (0.13+) ]; - if(!empty($body)) { + if (!empty($body)) { $params['body'] = $body; } diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 29bf40d326..f9898757f7 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -33,7 +33,7 @@ trait FunctionsBase \sleep(1); } - if($checkForSuccess) { + if ($checkForSuccess) { $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals('ready', $deployment['body']['status'], \json_encode($deployment['body'])); } diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 0607f40924..feb5693c1d 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -10,12 +10,12 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; -use Utopia\App; use Utopia\Database\Document; 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 { @@ -1072,7 +1072,7 @@ class FunctionsCustomServerTest extends Scope $found = false; foreach ($response['body']['deployments'] as $deployment) { - if($deployment['$id'] === $deploymentId) { + if ($deployment['$id'] === $deploymentId) { $found = true; $this->assertEquals($deploymentSize, $deployment['size']); break; @@ -2434,7 +2434,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($cookie, $response['body']); // Await Aggregation - sleep(App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', 30)); + sleep(System::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', 30)); $tries = 0; while (true) { diff --git a/tests/e2e/Services/Webhooks/WebhooksBase.php b/tests/e2e/Services/Webhooks/WebhooksBase.php index 6f6b36c520..6be3e16c1f 100644 --- a/tests/e2e/Services/Webhooks/WebhooksBase.php +++ b/tests/e2e/Services/Webhooks/WebhooksBase.php @@ -31,7 +31,7 @@ trait WebhooksBase \sleep(1); } - if($checkForSuccess) { + if ($checkForSuccess) { $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals('ready', $deployment['body']['status'], \json_encode($deployment['body'])); } diff --git a/tests/unit/Functions/Validator/HeadersBench.php b/tests/unit/Functions/Validator/HeadersBench.php index e1f9fcc5ff..f95fa65f9b 100644 --- a/tests/unit/Functions/Validator/HeadersBench.php +++ b/tests/unit/Functions/Validator/HeadersBench.php @@ -27,19 +27,19 @@ final class HeadersBench yield 'empty' => [ 'value' => [] ]; $value = []; - for($i = 0; $i < 10; $i++) { + for ($i = 0; $i < 10; $i++) { $value[bin2hex(random_bytes(8))] = bin2hex(random_bytes(8)); } yield 'items_10-size_320' => [ 'value' => $value ]; $value = []; - for($i = 0; $i < 100; $i++) { + for ($i = 0; $i < 100; $i++) { $value[bin2hex(random_bytes(8))] = bin2hex(random_bytes(8)); } yield 'items_100-size_3200' => [ 'value' => $value ]; $value = []; - for($i = 0; $i < 100; $i++) { + for ($i = 0; $i < 100; $i++) { $value[bin2hex(random_bytes(32))] = bin2hex(random_bytes(32)); } yield 'items_100-size_12800' => [ 'value' => $value ]; @@ -53,7 +53,7 @@ final class HeadersBench public function benchHeadersValidator(array $data): void { $assertion = $this->validator->isValid($data['value']); - if(!$assertion) { + if (!$assertion) { exit(1); } } diff --git a/tests/unit/Functions/Validator/HeadersTest.php b/tests/unit/Functions/Validator/HeadersTest.php index c9373c5991..4a45f57427 100644 --- a/tests/unit/Functions/Validator/HeadersTest.php +++ b/tests/unit/Functions/Validator/HeadersTest.php @@ -109,7 +109,7 @@ class HeadersTest extends TestCase $this->assertTrue($this->object->isValid($headers)); $headers = []; - for($i = 0; $i < 100; $i++) { + for ($i = 0; $i < 100; $i++) { $headers['key-' . $i] = 'value_' . $i; } $this->assertTrue($this->object->isValid($headers)); From ec4af20d09fe277be93286dcde0d1e4ef4981c2c Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 4 Sep 2024 15:13:49 -0400 Subject: [PATCH 161/195] chore: packages update --- composer.lock | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/composer.lock b/composer.lock index b1061cfa46..7b594c830a 100644 --- a/composer.lock +++ b/composer.lock @@ -2858,19 +2858,20 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "5995ae714550d3a386b4585e4d4fde1b3637919c" + "reference": "8739b09cf9cfcbf761a22e8474ecc00422614e79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/5995ae714550d3a386b4585e4d4fde1b3637919c", - "reference": "5995ae714550d3a386b4585e4d4fde1b3637919c", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/8739b09cf9cfcbf761a22e8474ecc00422614e79", + "reference": "8739b09cf9cfcbf761a22e8474ecc00422614e79", "shasum": "" }, "require": { "adhocore/jwt": "^1.1", "php": ">=8.0", "utopia-php/cache": "0.10.*", - "utopia-php/framework": "1.0.*" + "utopia-php/framework": "1.0.*", + "utopia-php/system": "0.8.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2899,7 +2900,7 @@ "issues": "https://github.com/utopia-php/vcs/issues", "source": "https://github.com/utopia-php/vcs/tree/feat-di" }, - "time": "2024-09-04T18:24:08+00:00" + "time": "2024-09-04T18:59:56+00:00" }, { "name": "utopia-php/view", From 74a8c7da12f7e20aa2d3060a233bdd9e1c18d233 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:41:45 -0400 Subject: [PATCH 162/195] fix: fixes --- app/controllers/api/functions.php | 7 +++-- composer.json | 2 +- composer.lock | 29 ++++++++++--------- .../Functions/FunctionsCustomServerTest.php | 2 -- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 14eb005542..1ad3995e24 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -186,7 +186,7 @@ Http::post('/v1/functions') ->inject('dbForConsole') ->inject('gitHub') ->inject('authorization') - ->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 $templateBranch, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $authorization) 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, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $authorization) use ($redeployVcs) { $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId; $allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', ''))); @@ -2597,9 +2597,10 @@ Http::get('/v1/functions/templates/:templateId') ->action(function (string $templateId, Response $response) { $templates = Config::getParam('function-templates', []); - $template = array_shift(\array_filter($templates, function ($template) use ($templateId) { + $array = \array_filter($templates, function ($template) use ($templateId) { return $template['id'] === $templateId; - })); + }); + $template = array_shift($array); if (empty($template)) { throw new Exception(Exception::FUNCTION_TEMPLATE_NOT_FOUND); diff --git a/composer.json b/composer.json index e0b0abce46..6219643e21 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ "utopia-php/database": "1.0.*", "utopia-php/domains": "dev-feat-framework-v2 as 0.5.99", "utopia-php/dsn": "0.2.*", - "utopia-php/framework": "1.0.0-RC2", + "utopia-php/framework": "1.0.*", "utopia-php/fetch": "0.2.*", "utopia-php/image": "0.6.*", "utopia-php/locale": "0.4.*", diff --git a/composer.lock b/composer.lock index 7b594c830a..31ca57d17d 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": "ce1482681baac6626ac7c23f29a8afaa", + "content-hash": "4c170232ed9a57c4b5d5e98f8955e452", "packages": [ { "name": "adhocore/jwt", @@ -1533,12 +1533,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "b61bcec3f72027cfe128c20a70b200c1630d2ad3" + "reference": "adc098f3a188755c487b2409e5f2897bb60ee6f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/b61bcec3f72027cfe128c20a70b200c1630d2ad3", - "reference": "b61bcec3f72027cfe128c20a70b200c1630d2ad3", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/adc098f3a188755c487b2409e5f2897bb60ee6f3", + "reference": "adc098f3a188755c487b2409e5f2897bb60ee6f3", "shasum": "" }, "require": { @@ -1572,7 +1572,7 @@ "issues": "https://github.com/utopia-php/audit/issues", "source": "https://github.com/utopia-php/audit/tree/feat-framework-v2" }, - "time": "2024-08-09T20:52:52+00:00" + "time": "2024-09-04T19:29:05+00:00" }, { "name": "utopia-php/cache", @@ -2357,12 +2357,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", - "reference": "df65de2132d61b0d862d5812cfab515aca4739cd" + "reference": "430d83aa3df5c2fca285245b29ed0d470421ada2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/df65de2132d61b0d862d5812cfab515aca4739cd", - "reference": "df65de2132d61b0d862d5812cfab515aca4739cd", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/430d83aa3df5c2fca285245b29ed0d470421ada2", + "reference": "430d83aa3df5c2fca285245b29ed0d470421ada2", "shasum": "" }, "require": { @@ -2399,7 +2399,7 @@ "issues": "https://github.com/utopia-php/orchestration/issues", "source": "https://github.com/utopia-php/orchestration/tree/feat-framework-v2" }, - "time": "2024-08-09T20:55:49+00:00" + "time": "2024-09-04T19:33:56+00:00" }, { "name": "utopia-php/platform", @@ -2407,12 +2407,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "c64b8efb3448b4ed1868c8e80bdb1c5a8407f7e5" + "reference": "b09988fcbfd6378a44afa3e27468e17023eb359c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/c64b8efb3448b4ed1868c8e80bdb1c5a8407f7e5", - "reference": "c64b8efb3448b4ed1868c8e80bdb1c5a8407f7e5", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/b09988fcbfd6378a44afa3e27468e17023eb359c", + "reference": "b09988fcbfd6378a44afa3e27468e17023eb359c", "shasum": "" }, "require": { @@ -2421,7 +2421,8 @@ "php": ">=8.0", "utopia-php/cli": "1.0.*", "utopia-php/framework": "1.0.*", - "utopia-php/queue": "dev-feat-coroutine-and-di" + "utopia-php/queue": "dev-feat-coroutine-and-di", + "utopia-php/servers": "0.1.0" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2449,7 +2450,7 @@ "issues": "https://github.com/utopia-php/platform/issues", "source": "https://github.com/utopia-php/platform/tree/feat-framework-v2" }, - "time": "2024-09-04T18:20:34+00:00" + "time": "2024-09-04T20:19:09+00:00" }, { "name": "utopia-php/pools", diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index feb5693c1d..5a142272d0 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -476,7 +476,6 @@ class FunctionsCustomServerTest extends Scope * and ensure variable works as expected in execution. */ $this->assertEmpty($template['body']['variables']); - // Create function using settings from template. // Deployment is automatically created from template inside endpoint $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ @@ -504,7 +503,6 @@ class FunctionsCustomServerTest extends Scope $this->assertNotEmpty($function['body']['$id']); $functionId = $function['body']['$id']; - // List deployments so we can await deployment build $deployments = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments', [ 'content-type' => 'application/json', From f3ab0d2dcacf61d2a0c7a771794ea136f474d414 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:30:28 -0400 Subject: [PATCH 163/195] fix: latest vcs --- composer.json | 12 +----------- composer.lock | 8 ++++---- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/composer.json b/composer.json index 6219643e21..19b4c5c2dd 100644 --- a/composer.json +++ b/composer.json @@ -100,15 +100,5 @@ "platform": { "php": "8.3" } - }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/utopia-php/di" - }, - { - "type": "vcs", - "url": "https://github.com/utopia-php/servers" - } - ] + } } diff --git a/composer.lock b/composer.lock index 31ca57d17d..8362ebe859 100644 --- a/composer.lock +++ b/composer.lock @@ -2859,12 +2859,12 @@ "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "8739b09cf9cfcbf761a22e8474ecc00422614e79" + "reference": "3efa907981745056b6a3481bdb0372885571c442" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/8739b09cf9cfcbf761a22e8474ecc00422614e79", - "reference": "8739b09cf9cfcbf761a22e8474ecc00422614e79", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/3efa907981745056b6a3481bdb0372885571c442", + "reference": "3efa907981745056b6a3481bdb0372885571c442", "shasum": "" }, "require": { @@ -2901,7 +2901,7 @@ "issues": "https://github.com/utopia-php/vcs/issues", "source": "https://github.com/utopia-php/vcs/tree/feat-di" }, - "time": "2024-09-04T18:59:56+00:00" + "time": "2024-09-04T21:22:50+00:00" }, { "name": "utopia-php/view", From 5ae124c54cd5d12e3891c2fbe0f3e215f835c061 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:33:19 -0400 Subject: [PATCH 164/195] chore: update --- composer.lock | 45 +++++++-------------------------------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/composer.lock b/composer.lock index 8362ebe859..47cb129e11 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": "4c170232ed9a57c4b5d5e98f8955e452", + "content-hash": "b6b09570c47171378f66e61a71b943c6", "packages": [ { "name": "adhocore/jwt", @@ -1813,20 +1813,7 @@ "Tests\\E2E\\": "tests/e2e" } }, - "scripts": { - "lint": [ - "vendor/bin/pint --test" - ], - "format": [ - "vendor/bin/pint" - ], - "check": [ - "vendor/bin/phpstan analyse -c phpstan.neon" - ], - "test": [ - "vendor/bin/phpunit --configuration phpunit.xml" - ] - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1838,8 +1825,8 @@ "upf" ], "support": { - "source": "https://github.com/utopia-php/di/tree/0.1.0", - "issues": "https://github.com/utopia-php/di/issues" + "issues": "https://github.com/utopia-php/di/issues", + "source": "https://github.com/utopia-php/di/tree/0.1.0" }, "time": "2024-08-08T14:35:19+00:00" }, @@ -2700,25 +2687,7 @@ "Utopia\\Servers\\": "src/Servers" } }, - "autoload-dev": { - "psr-4": { - "Tests\\E2E\\": "tests/Servers/Unit" - } - }, - "scripts": { - "test": [ - "phpunit" - ], - "analyse": [ - "vendor/bin/phpstan analyse" - ], - "format": [ - "vendor/bin/pint" - ], - "lint": [ - "vendor/bin/pint --test" - ] - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2737,8 +2706,8 @@ "utopia" ], "support": { - "source": "https://github.com/utopia-php/servers/tree/0.1.0", - "issues": "https://github.com/utopia-php/servers/issues" + "issues": "https://github.com/utopia-php/servers/issues", + "source": "https://github.com/utopia-php/servers/tree/0.1.0" }, "time": "2024-08-08T14:31:39+00:00" }, From 946bf20dd7c5292797a7e0d76904b1bd139ece6f Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Wed, 4 Sep 2024 20:48:10 -0400 Subject: [PATCH 165/195] chore: update packages to `RC` --- composer.json | 20 +- composer.lock | 513 +++++++++++++++++++++----------------------------- 2 files changed, 225 insertions(+), 308 deletions(-) diff --git a/composer.json b/composer.json index 19b4c5c2dd..4a5adc48ef 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "description": "End to end backend server for frontend and mobile apps.", "type": "project", "license": "BSD-3-Clause", - "minimum-stability": "dev", + "minimum-stability": "RC", "authors": [ { "name": "Eldad Fux", @@ -47,14 +47,14 @@ "ext-sockets": "*", "appwrite/php-runtimes": "0.15.*", "appwrite/php-clamav": "2.0.*", - "utopia-php/abuse": "dev-feat-framework-v2 as 0.39.99", - "utopia-php/analytics": "dev-feat-framework-v2 as 0.10.99", - "utopia-php/audit": "dev-feat-framework-v2 as 0.40.99", + "utopia-php/abuse": "0.44.*", + "utopia-php/analytics": "0.13.*", + "utopia-php/audit": "0.44.*", "utopia-php/cache": "0.10.*", "utopia-php/cli": "1.0.*", "utopia-php/config": "0.2.*", "utopia-php/database": "1.0.*", - "utopia-php/domains": "dev-feat-framework-v2 as 0.5.99", + "utopia-php/domains": "0.6.*", "utopia-php/dsn": "0.2.*", "utopia-php/framework": "1.0.*", "utopia-php/fetch": "0.2.*", @@ -63,16 +63,16 @@ "utopia-php/logger": "0.6.*", "utopia-php/messaging": "0.12.*", "utopia-php/migration": "0.4.*", - "utopia-php/orchestration": "dev-feat-framework-v2 as 0.9.99", - "utopia-php/platform": "dev-feat-framework-v2 as 0.5.99", + "utopia-php/orchestration": "0.15.*", + "utopia-php/platform": "0.6.*", "utopia-php/view": "0.2.*", "utopia-php/pools": "0.5.*", "utopia-php/preloader": "0.2.*", - "utopia-php/queue": "dev-feat-coroutine-and-di as 0.7.99", + "utopia-php/queue": "0.8.*", "utopia-php/registry": "0.5.*", - "utopia-php/storage": "dev-feat-framework-v2-v2 as 0.18.99", + "utopia-php/storage": "0.19.*", "utopia-php/system": "0.8.*", - "utopia-php/vcs": "dev-feat-di", + "utopia-php/vcs": "0.9.*", "utopia-php/websocket": "0.2.*", "matomo/device-detector": "6.1.*", "dragonmantank/cron-expression": "3.3.2", diff --git a/composer.lock b/composer.lock index 47cb129e11..905e1f58be 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": "b6b09570c47171378f66e61a71b943c6", + "content-hash": "3cb2a27888b7c646da7977c41383eff4", "packages": [ { "name": "adhocore/jwt", @@ -211,16 +211,16 @@ }, { "name": "beberlei/assert", - "version": "v3.x-dev", + "version": "v3.3.2", "source": { "type": "git", "url": "https://github.com/beberlei/assert.git", - "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf" + "reference": "cb70015c04be1baee6f5f5c953703347c0ac1655" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/beberlei/assert/zipball/d63a6943fc4fd1a2aedb65994e3548715105abcf", - "reference": "d63a6943fc4fd1a2aedb65994e3548715105abcf", + "url": "https://api.github.com/repos/beberlei/assert/zipball/cb70015c04be1baee6f5f5c953703347c0ac1655", + "reference": "cb70015c04be1baee6f5f5c953703347c0ac1655", "shasum": "" }, "require": { @@ -228,12 +228,13 @@ "ext-json": "*", "ext-mbstring": "*", "ext-simplexml": "*", - "php": "^7" + "php": "^7.0 || ^8.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "*", - "phpstan/phpstan-shim": "*", - "phpunit/phpunit": ">=6.0.0 <8" + "phpstan/phpstan": "*", + "phpunit/phpunit": ">=6.0.0", + "yoast/phpunit-polyfills": "^0.1.0" }, "suggest": { "ext-intl": "Needed to allow Assertion::count(), Assertion::isCountable(), Assertion::minCount(), and Assertion::maxCount() to operate on ResourceBundles" @@ -271,9 +272,9 @@ ], "support": { "issues": "https://github.com/beberlei/assert/issues", - "source": "https://github.com/beberlei/assert/tree/v3" + "source": "https://github.com/beberlei/assert/tree/v3.3.2" }, - "time": "2019-12-19T17:51:41+00:00" + "time": "2021-12-16T21:41:27+00:00" }, { "name": "chillerlan/php-qrcode", @@ -566,16 +567,16 @@ }, { "name": "jean85/pretty-package-versions", - "version": "2.x-dev", + "version": "2.0.6", "source": { "type": "git", "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "d2ed36bf14b8b68d7986272734cb6a54c7822554" + "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/d2ed36bf14b8b68d7986272734cb6a54c7822554", - "reference": "d2ed36bf14b8b68d7986272734cb6a54c7822554", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", + "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", "shasum": "" }, "require": { @@ -586,10 +587,9 @@ "friendsofphp/php-cs-fixer": "^3.2", "jean85/composer-provided-replaced-stub-package": "^1.0", "phpstan/phpstan": "^1.4", - "phpunit/phpunit": "^7.5|^8.5|^9.6", - "vimeo/psalm": "^4.3 || ^5.0" + "phpunit/phpunit": "^7.5|^8.5|^9.4", + "vimeo/psalm": "^4.3" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -620,9 +620,9 @@ ], "support": { "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.x" + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" }, - "time": "2024-08-01T12:14:16+00:00" + "time": "2024-03-08T09:58:59+00:00" }, { "name": "league/csv", @@ -907,7 +907,7 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.x-dev", + "version": "v2.7.0", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", @@ -1055,7 +1055,7 @@ }, { "name": "spomky-labs/otphp", - "version": "v10.0.x-dev", + "version": "v10.0.3", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/otphp.git", @@ -1084,7 +1084,6 @@ "phpunit/phpunit": "^8.0", "thecodingmachine/phpstan-safe-rule": "^1.0 || ^2.0" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -1131,16 +1130,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "1.x-dev", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8740a072b86292957feb42703edde77fcfca84fb" + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8740a072b86292957feb42703edde77fcfca84fb", - "reference": "8740a072b86292957feb42703edde77fcfca84fb", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", "shasum": "" }, "require": { @@ -1152,7 +1151,6 @@ "suggest": { "ext-mbstring": "For best performance" }, - "default-branch": true, "type": "library", "extra": { "thanks": { @@ -1192,7 +1190,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/1.x" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" }, "funding": [ { @@ -1208,11 +1206,11 @@ "type": "tidelift" } ], - "time": "2024-06-20T08:18:00+00:00" + "time": "2024-06-19T12:30:46+00:00" }, { "name": "symfony/polyfill-php80", - "version": "1.x-dev", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -1227,7 +1225,6 @@ "require": { "php": ">=7.1" }, - "default-branch": true, "type": "library", "extra": { "thanks": { @@ -1432,7 +1429,7 @@ }, { "name": "utopia-php/abuse", - "version": "dev-feat-framework-v2", + "version": "0.44.0-RC1", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", @@ -1477,27 +1474,28 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/feat-framework-v2" + "source": "https://github.com/utopia-php/abuse/tree/0.44.0-RC1" }, "time": "2024-09-04T18:14:29+00:00" }, { "name": "utopia-php/analytics", - "version": "dev-feat-framework-v2", + "version": "0.13.0-RC2", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "d2d98487581cdfa27095811eb95f4dadfed47bec" + "reference": "b6a61e8834f169fd5e1b3678d48c7f0029aaa8c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/d2d98487581cdfa27095811eb95f4dadfed47bec", - "reference": "d2d98487581cdfa27095811eb95f4dadfed47bec", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/b6a61e8834f169fd5e1b3678d48c7f0029aaa8c2", + "reference": "b6a61e8834f169fd5e1b3678d48c7f0029aaa8c2", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "1.0.*" + "utopia-php/cli": "1.0.*", + "utopia-php/system": "0.8.*" }, "require-dev": { "laravel/pint": "dev-main", @@ -1523,13 +1521,13 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/feat-framework-v2" + "source": "https://github.com/utopia-php/analytics/tree/0.13.0-RC2" }, - "time": "2024-08-09T20:52:05+00:00" + "time": "2024-09-05T00:39:55+00:00" }, { "name": "utopia-php/audit", - "version": "dev-feat-framework-v2", + "version": "0.44.0-RC1", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", @@ -1570,7 +1568,7 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/feat-framework-v2" + "source": "https://github.com/utopia-php/audit/tree/0.44.0-RC1" }, "time": "2024-09-04T19:29:05+00:00" }, @@ -1832,7 +1830,7 @@ }, { "name": "utopia-php/domains", - "version": "dev-feat-framework-v2", + "version": "0.6.0-RC1", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", @@ -1887,7 +1885,7 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/feat-framework-v2" + "source": "https://github.com/utopia-php/domains/tree/0.6.0-RC1" }, "time": "2024-08-09T20:54:00+00:00" }, @@ -1979,16 +1977,16 @@ }, { "name": "utopia-php/framework", - "version": "1.0.0-RC2", + "version": "1.0.0-RC3", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "d1e9674dbf33bed03fa53854ec1f2c6e074cf4d7" + "reference": "31306629e6d7df2bee36b4402ff84f19fb012889" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/d1e9674dbf33bed03fa53854ec1f2c6e074cf4d7", - "reference": "d1e9674dbf33bed03fa53854ec1f2c6e074cf4d7", + "url": "https://api.github.com/repos/utopia-php/http/zipball/31306629e6d7df2bee36b4402ff84f19fb012889", + "reference": "31306629e6d7df2bee36b4402ff84f19fb012889", "shasum": "" }, "require": { @@ -2023,9 +2021,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/1.0.0-RC2" + "source": "https://github.com/utopia-php/http/tree/1.0.0-RC3" }, - "time": "2024-08-08T14:46:41+00:00" + "time": "2024-09-05T00:43:38+00:00" }, { "name": "utopia-php/image", @@ -2340,7 +2338,7 @@ }, { "name": "utopia-php/orchestration", - "version": "dev-feat-framework-v2", + "version": "0.15.0-RC1", "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", @@ -2384,22 +2382,22 @@ ], "support": { "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/feat-framework-v2" + "source": "https://github.com/utopia-php/orchestration/tree/0.15.0-RC1" }, "time": "2024-09-04T19:33:56+00:00" }, { "name": "utopia-php/platform", - "version": "dev-feat-framework-v2", + "version": "0.6.0-RC1", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "b09988fcbfd6378a44afa3e27468e17023eb359c" + "reference": "32b778f8acbbfc5852f6815086b78babe2a4396e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/b09988fcbfd6378a44afa3e27468e17023eb359c", - "reference": "b09988fcbfd6378a44afa3e27468e17023eb359c", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/32b778f8acbbfc5852f6815086b78babe2a4396e", + "reference": "32b778f8acbbfc5852f6815086b78babe2a4396e", "shasum": "" }, "require": { @@ -2408,7 +2406,7 @@ "php": ">=8.0", "utopia-php/cli": "1.0.*", "utopia-php/framework": "1.0.*", - "utopia-php/queue": "dev-feat-coroutine-and-di", + "utopia-php/queue": "0.8.*", "utopia-php/servers": "0.1.0" }, "require-dev": { @@ -2435,9 +2433,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/feat-framework-v2" + "source": "https://github.com/utopia-php/platform/tree/0.6.0-RC1" }, - "time": "2024-09-04T20:19:09+00:00" + "time": "2024-09-05T00:23:57+00:00" }, { "name": "utopia-php/pools", @@ -2545,7 +2543,7 @@ }, { "name": "utopia-php/queue", - "version": "dev-feat-coroutine-and-di", + "version": "0.8.0-RC1", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", @@ -2602,7 +2600,7 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/feat-coroutine-and-di" + "source": "https://github.com/utopia-php/queue/tree/0.8.0-RC1" }, "time": "2024-09-04T18:19:23+00:00" }, @@ -2713,7 +2711,7 @@ }, { "name": "utopia-php/storage", - "version": "dev-feat-framework-v2-v2", + "version": "0.19.0-RC1", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", @@ -2762,7 +2760,7 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/feat-framework-v2-v2" + "source": "https://github.com/utopia-php/storage/tree/0.19.0-RC1" }, "time": "2024-08-09T21:00:39+00:00" }, @@ -2824,7 +2822,7 @@ }, { "name": "utopia-php/vcs", - "version": "dev-feat-di", + "version": "0.9.0-RC1", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", @@ -2868,7 +2866,7 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/feat-di" + "source": "https://github.com/utopia-php/vcs/tree/0.9.0-RC1" }, "time": "2024-09-04T21:22:50+00:00" }, @@ -3142,16 +3140,16 @@ }, { "name": "doctrine/annotations", - "version": "2.0.x-dev", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "94f40ad7ecbc6931958faa8a57c48dce5da2b468" + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/94f40ad7ecbc6931958faa8a57c48dce5da2b468", - "reference": "94f40ad7ecbc6931958faa8a57c48dce5da2b468", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", "shasum": "" }, "require": { @@ -3171,7 +3169,6 @@ "suggest": { "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" }, - "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -3213,13 +3210,13 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.x" + "source": "https://github.com/doctrine/annotations/tree/2.0.1" }, - "time": "2023-08-23T17:36:07+00:00" + "time": "2023-02-02T22:02:53+00:00" }, { "name": "doctrine/deprecations", - "version": "1.1.x-dev", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", @@ -3246,7 +3243,6 @@ "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, - "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -3267,29 +3263,29 @@ }, { "name": "doctrine/instantiator", - "version": "1.5.x-dev", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "12be2483e1f0e850b353e26869e4e6c038459501" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/12be2483e1f0e850b353e26869e4e6c038459501", - "reference": "12be2483e1f0e850b353e26869e4e6c038459501", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^0.16 || ^1", "phpstan/phpstan": "^1.4", "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", @@ -3317,7 +3313,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.x" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { @@ -3333,20 +3329,20 @@ "type": "tidelift" } ], - "time": "2023-12-09T14:16:53+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { "name": "doctrine/lexer", - "version": "3.1.x-dev", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "042e47e28c5e03f1cf6772fdf3dd4e0785433e05" + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/042e47e28c5e03f1cf6772fdf3dd4e0785433e05", - "reference": "042e47e28c5e03f1cf6772fdf3dd4e0785433e05", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { @@ -3394,7 +3390,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.1.x" + "source": "https://github.com/doctrine/lexer/tree/3.0.1" }, "funding": [ { @@ -3410,7 +3406,7 @@ "type": "tidelift" } ], - "time": "2024-07-29T08:29:21+00:00" + "time": "2024-02-05T11:56:58+00:00" }, { "name": "laravel/pint", @@ -3604,7 +3600,7 @@ }, { "name": "myclabs/deep-copy", - "version": "1.x-dev", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", @@ -3629,7 +3625,6 @@ "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, - "default-branch": true, "type": "library", "autoload": { "files": [ @@ -3723,7 +3718,7 @@ }, { "name": "phar-io/manifest", - "version": "dev-master", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", @@ -3743,7 +3738,6 @@ "phar-io/version": "^3.0.1", "php": "^7.2 || ^8.0" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -3842,7 +3836,7 @@ }, { "name": "phpbench/container", - "version": "dev-master", + "version": "2.2.2", "source": { "type": "git", "url": "https://github.com/phpbench/container.git", @@ -3863,7 +3857,6 @@ "phpstan/phpstan": "^0.12.52", "phpunit/phpunit": "^8" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4044,25 +4037,25 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "dev-master", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "a0eeab580cbdf4414fef6978732510a36ed0a9d6" + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/a0eeab580cbdf4414fef6978732510a36ed0a9d6", - "reference": "a0eeab580cbdf4414fef6978732510a36ed0a9d6", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=7.1" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { @@ -4091,22 +4084,22 @@ ], "support": { "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master" + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" }, - "time": "2021-06-25T13:47:51+00:00" + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.x-dev", + "version": "5.4.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "60741fe3871f40e44ca10a28ce85d08b7ed841cd" + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/60741fe3871f40e44ca10a28ce85d08b7ed841cd", - "reference": "60741fe3871f40e44ca10a28ce85d08b7ed841cd", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", "shasum": "" }, "require": { @@ -4127,7 +4120,6 @@ "phpunit/phpunit": "^9.5", "vimeo/psalm": "^5.13" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4156,29 +4148,29 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.x" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" }, - "time": "2024-08-14T20:00:37+00:00" + "time": "2024-05-21T05:55:05+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.x-dev", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "eee054a3d40f09920f5b27c9b09a6483f88d9d24" + "reference": "153ae662783729388a584b4361f2545e4d841e3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/eee054a3d40f09920f5b27c9b09a6483f88d9d24", - "reference": "eee054a3d40f09920f5b27c9b09a6483f88d9d24", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.18" + "phpstan/phpdoc-parser": "^1.13" }, "require-dev": { "ext-tokenizer": "*", @@ -4190,7 +4182,6 @@ "rector/rector": "^0.13.9", "vimeo/psalm": "^4.25" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4215,22 +4206,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.x" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" }, - "time": "2024-05-24T14:24:30+00:00" + "time": "2024-02-23T11:10:43+00:00" }, { "name": "phpspec/prophecy", - "version": "dev-master", + "version": "v1.19.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "e3810e5e638cd91f6f6d82059c60ad85f2b3ffb1" + "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e3810e5e638cd91f6f6d82059c60ad85f2b3ffb1", - "reference": "e3810e5e638cd91f6f6d82059c60ad85f2b3ffb1", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/67a759e7d8746d501c41536ba40cd9c0a07d6a87", + "reference": "67a759e7d8746d501c41536ba40cd9c0a07d6a87", "shasum": "" }, "require": { @@ -4241,12 +4232,10 @@ "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.40", "phpspec/phpspec": "^6.0 || ^7.0", "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4286,9 +4275,9 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/master" + "source": "https://github.com/phpspec/prophecy/tree/v1.19.0" }, - "time": "2024-08-27T07:52:54+00:00" + "time": "2024-02-29T11:52:51+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -4339,16 +4328,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.8.x-dev", + "version": "1.8.11", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b20042710baa0d9c07636cc66d4c400f03f1477a" + "reference": "46e223dd68a620da18855c23046ddb00940b4014" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b20042710baa0d9c07636cc66d4c400f03f1477a", - "reference": "b20042710baa0d9c07636cc66d4c400f03f1477a", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014", + "reference": "46e223dd68a620da18855c23046ddb00940b4014", "shasum": "" }, "require": { @@ -4378,7 +4367,7 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.8.x" + "source": "https://github.com/phpstan/phpstan/tree/1.8.11" }, "funding": [ { @@ -4394,11 +4383,11 @@ "type": "tidelift" } ], - "time": "2022-10-29T12:56:57+00:00" + "time": "2022-10-24T15:45:13+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.x-dev", + "version": "9.2.32", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", @@ -4476,16 +4465,16 @@ }, { "name": "phpunit/php-file-iterator", - "version": "3.0.x-dev", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "38b24367e1b340aa78b96d7cab042942d917bb84" + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/38b24367e1b340aa78b96d7cab042942d917bb84", - "reference": "38b24367e1b340aa78b96d7cab042942d917bb84", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { @@ -4524,7 +4513,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" }, "funding": [ { @@ -4532,7 +4521,7 @@ "type": "github" } ], - "time": "2022-02-11T16:23:04+00:00" + "time": "2021-12-02T12:48:52+00:00" }, { "name": "phpunit/php-invoker", @@ -4820,29 +4809,25 @@ }, { "name": "psr/cache", - "version": "dev-master", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/cache.git", - "reference": "0a7c67d0d1c8167b342eb74339d6f961663826ce" + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/0a7c67d0d1c8167b342eb74339d6f961663826ce", - "reference": "0a7c67d0d1c8167b342eb74339d6f961663826ce", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "shasum": "" }, "require": { "php": ">=8.0.0" }, - "suggest": { - "fig/cache-util": "Provides some useful PSR-6 utilities" - }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -4867,28 +4852,27 @@ "psr-6" ], "support": { - "source": "https://github.com/php-fig/cache/tree/master" + "source": "https://github.com/php-fig/cache/tree/3.0.0" }, - "time": "2021-02-24T03:25:37+00:00" + "time": "2021-02-03T23:26:27+00:00" }, { "name": "psr/container", - "version": "dev-master", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "707984727bd5b2b670e59559d3ed2500240cf875" + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/707984727bd5b2b670e59559d3ed2500240cf875", - "reference": "707984727bd5b2b670e59559d3ed2500240cf875", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { "php": ">=7.4.0" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4921,13 +4905,13 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container" + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2023-09-22T11:11:30+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { "name": "psr/log", - "version": "dev-master", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", @@ -4942,7 +4926,6 @@ "require": { "php": ">=8.0.0" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -4978,7 +4961,7 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.x-dev", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", @@ -5145,16 +5128,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.x-dev", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "b247957a1c8dc81a671770f74b479c0a78a818f1" + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b247957a1c8dc81a671770f74b479c0a78a818f1", - "reference": "b247957a1c8dc81a671770f74b479c0a78a818f1", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { @@ -5207,7 +5190,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { @@ -5215,11 +5198,11 @@ "type": "github" } ], - "time": "2022-09-14T12:46:14+00:00" + "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.x-dev", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", @@ -5276,7 +5259,7 @@ }, { "name": "sebastian/diff", - "version": "4.0.x-dev", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", @@ -5342,7 +5325,7 @@ }, { "name": "sebastian/environment", - "version": "5.1.x-dev", + "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", @@ -5393,7 +5376,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { @@ -5405,7 +5388,7 @@ }, { "name": "sebastian/exporter", - "version": "4.0.x-dev", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", @@ -5482,7 +5465,7 @@ }, { "name": "sebastian/global-state", - "version": "5.0.x-dev", + "version": "5.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", @@ -5546,7 +5529,7 @@ }, { "name": "sebastian/lines-of-code", - "version": "1.0.x-dev", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", @@ -5715,7 +5698,7 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.x-dev", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", @@ -5778,16 +5761,16 @@ }, { "name": "sebastian/resource-operations", - "version": "dev-main", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25" + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25", - "reference": "ff553e7482dcee39fa4acc2b175d6ddeb0f7bc25", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", "shasum": "" }, "require": { @@ -5796,7 +5779,6 @@ "require-dev": { "phpunit/phpunit": "^9.0" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -5821,7 +5803,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "source": "https://github.com/sebastianbergmann/resource-operations/tree/main" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" }, "funding": [ { @@ -5829,11 +5811,11 @@ "type": "github" } ], - "time": "2024-03-14T18:47:08+00:00" + "time": "2024-03-14T16:00:52+00:00" }, { "name": "sebastian/type", - "version": "3.2.x-dev", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", @@ -5877,7 +5859,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2" + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { @@ -5889,7 +5871,7 @@ }, { "name": "sebastian/version", - "version": "3.0.x-dev", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", @@ -6038,16 +6020,16 @@ }, { "name": "symfony/console", - "version": "7.2.x-dev", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "ee7ee22b1dabcff731b7d79d3633c96251ad8404" + "reference": "1eed7af6961d763e7832e874d7f9b21c3ea9c111" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/ee7ee22b1dabcff731b7d79d3633c96251ad8404", - "reference": "ee7ee22b1dabcff731b7d79d3633c96251ad8404", + "url": "https://api.github.com/repos/symfony/console/zipball/1eed7af6961d763e7832e874d7f9b21c3ea9c111", + "reference": "1eed7af6961d763e7832e874d7f9b21c3ea9c111", "shasum": "" }, "require": { @@ -6111,7 +6093,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/7.2" + "source": "https://github.com/symfony/console/tree/v7.1.4" }, "funding": [ { @@ -6127,11 +6109,11 @@ "type": "tidelift" } ], - "time": "2024-08-30T15:40:32+00:00" + "time": "2024-08-15T22:48:53+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "dev-main", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", @@ -6146,7 +6128,6 @@ "require": { "php": ">=8.1" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -6199,16 +6180,16 @@ }, { "name": "symfony/filesystem", - "version": "7.2.x-dev", + "version": "v7.1.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "c46c178f375c2dfddc7b6a32731077c778e14264" + "reference": "92a91985250c251de9b947a14bb2c9390b1a562c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/c46c178f375c2dfddc7b6a32731077c778e14264", - "reference": "c46c178f375c2dfddc7b6a32731077c778e14264", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/92a91985250c251de9b947a14bb2c9390b1a562c", + "reference": "92a91985250c251de9b947a14bb2c9390b1a562c", "shasum": "" }, "require": { @@ -6245,7 +6226,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/7.2" + "source": "https://github.com/symfony/filesystem/tree/v7.1.2" }, "funding": [ { @@ -6261,20 +6242,20 @@ "type": "tidelift" } ], - "time": "2024-07-23T10:47:31+00:00" + "time": "2024-06-28T10:03:55+00:00" }, { "name": "symfony/finder", - "version": "7.2.x-dev", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "afa87bce0d224a744963ecc8db07b1f0f96a3518" + "reference": "d95bbf319f7d052082fb7af147e0f835a695e823" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/afa87bce0d224a744963ecc8db07b1f0f96a3518", - "reference": "afa87bce0d224a744963ecc8db07b1f0f96a3518", + "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823", + "reference": "d95bbf319f7d052082fb7af147e0f835a695e823", "shasum": "" }, "require": { @@ -6309,7 +6290,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/7.2" + "source": "https://github.com/symfony/finder/tree/v7.1.4" }, "funding": [ { @@ -6325,20 +6306,20 @@ "type": "tidelift" } ], - "time": "2024-09-03T13:22:29+00:00" + "time": "2024-08-13T14:28:19+00:00" }, { "name": "symfony/options-resolver", - "version": "7.2.x-dev", + "version": "v7.1.1", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "28b7840cd3a01a74bc86fa77587f02b351ad3ade" + "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/28b7840cd3a01a74bc86fa77587f02b351ad3ade", - "reference": "28b7840cd3a01a74bc86fa77587f02b351ad3ade", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55", + "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55", "shasum": "" }, "require": { @@ -6376,7 +6357,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/7.2" + "source": "https://github.com/symfony/options-resolver/tree/v7.1.1" }, "funding": [ { @@ -6392,11 +6373,11 @@ "type": "tidelift" } ], - "time": "2024-07-06T07:57:47+00:00" + "time": "2024-05-31T14:57:53+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "1.x-dev", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -6417,7 +6398,6 @@ "suggest": { "ext-ctype": "For best performance" }, - "default-branch": true, "type": "library", "extra": { "thanks": { @@ -6476,7 +6456,7 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "1.x-dev", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", @@ -6494,7 +6474,6 @@ "suggest": { "ext-intl": "For best performance" }, - "default-branch": true, "type": "library", "extra": { "thanks": { @@ -6555,7 +6534,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "1.x-dev", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -6573,7 +6552,6 @@ "suggest": { "ext-intl": "For best performance" }, - "default-branch": true, "type": "library", "extra": { "thanks": { @@ -6637,16 +6615,16 @@ }, { "name": "symfony/process", - "version": "7.2.x-dev", + "version": "v7.1.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "bb0a8b7772610211c2cd7d6e4e36acfcbadcb613" + "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/bb0a8b7772610211c2cd7d6e4e36acfcbadcb613", - "reference": "bb0a8b7772610211c2cd7d6e4e36acfcbadcb613", + "url": "https://api.github.com/repos/symfony/process/zipball/7f2f542c668ad6c313dc4a5e9c3321f733197eca", + "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca", "shasum": "" }, "require": { @@ -6678,7 +6656,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/7.2" + "source": "https://github.com/symfony/process/tree/v7.1.3" }, "funding": [ { @@ -6694,11 +6672,11 @@ "type": "tidelift" } ], - "time": "2024-07-29T06:33:22+00:00" + "time": "2024-07-26T12:44:47+00:00" }, { "name": "symfony/service-contracts", - "version": "dev-main", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", @@ -6718,7 +6696,6 @@ "conflict": { "ext-psr": "<1.1|>=2" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -6782,16 +6759,16 @@ }, { "name": "symfony/string", - "version": "7.2.x-dev", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "c38cbd6dcf2a45bcfbd79fb89d6bd08f7afdc7dc" + "reference": "6cd670a6d968eaeb1c77c2e76091c45c56bc367b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/c38cbd6dcf2a45bcfbd79fb89d6bd08f7afdc7dc", - "reference": "c38cbd6dcf2a45bcfbd79fb89d6bd08f7afdc7dc", + "url": "https://api.github.com/repos/symfony/string/zipball/6cd670a6d968eaeb1c77c2e76091c45c56bc367b", + "reference": "6cd670a6d968eaeb1c77c2e76091c45c56bc367b", "shasum": "" }, "require": { @@ -6849,7 +6826,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/7.2" + "source": "https://github.com/symfony/string/tree/v7.1.4" }, "funding": [ { @@ -6865,7 +6842,7 @@ "type": "tidelift" } ], - "time": "2024-08-12T10:26:02+00:00" + "time": "2024-08-12T09:59:40+00:00" }, { "name": "textalk/websocket", @@ -7040,16 +7017,16 @@ }, { "name": "webmozart/glob", - "version": "4.8.x-dev", + "version": "4.7.0", "source": { "type": "git", "url": "https://github.com/webmozarts/glob.git", - "reference": "6712c9c4a8b0f6f629303bd1b26b9f88339d901e" + "reference": "8a2842112d6916e61e0e15e316465b611f3abc17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/glob/zipball/6712c9c4a8b0f6f629303bd1b26b9f88339d901e", - "reference": "6712c9c4a8b0f6f629303bd1b26b9f88339d901e", + "url": "https://api.github.com/repos/webmozarts/glob/zipball/8a2842112d6916e61e0e15e316465b611f3abc17", + "reference": "8a2842112d6916e61e0e15e316465b611f3abc17", "shasum": "" }, "require": { @@ -7059,7 +7036,6 @@ "phpunit/phpunit": "^9.5", "symfony/filesystem": "^5.3" }, - "default-branch": true, "type": "library", "extra": { "branch-alias": { @@ -7084,73 +7060,14 @@ "description": "A PHP implementation of Ant's glob.", "support": { "issues": "https://github.com/webmozarts/glob/issues", - "source": "https://github.com/webmozarts/glob/tree/4.8.x" + "source": "https://github.com/webmozarts/glob/tree/4.7.0" }, - "time": "2024-08-06T15:56:59+00:00" + "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [ - { - "package": "utopia-php/abuse", - "version": "dev-feat-framework-v2", - "alias": "0.39.99", - "alias_normalized": "0.39.99.0" - }, - { - "package": "utopia-php/analytics", - "version": "dev-feat-framework-v2", - "alias": "0.10.99", - "alias_normalized": "0.10.99.0" - }, - { - "package": "utopia-php/audit", - "version": "dev-feat-framework-v2", - "alias": "0.40.99", - "alias_normalized": "0.40.99.0" - }, - { - "package": "utopia-php/domains", - "version": "dev-feat-framework-v2", - "alias": "0.5.99", - "alias_normalized": "0.5.99.0" - }, - { - "package": "utopia-php/orchestration", - "version": "dev-feat-framework-v2", - "alias": "0.9.99", - "alias_normalized": "0.9.99.0" - }, - { - "package": "utopia-php/platform", - "version": "dev-feat-framework-v2", - "alias": "0.5.99", - "alias_normalized": "0.5.99.0" - }, - { - "package": "utopia-php/queue", - "version": "dev-feat-coroutine-and-di", - "alias": "0.7.99", - "alias_normalized": "0.7.99.0" - }, - { - "package": "utopia-php/storage", - "version": "dev-feat-framework-v2-v2", - "alias": "0.18.99", - "alias_normalized": "0.18.99.0" - } - ], - "minimum-stability": "dev", - "stability-flags": { - "utopia-php/abuse": 20, - "utopia-php/analytics": 20, - "utopia-php/audit": 20, - "utopia-php/domains": 20, - "utopia-php/orchestration": 20, - "utopia-php/platform": 20, - "utopia-php/queue": 20, - "utopia-php/storage": 20, - "utopia-php/vcs": 20 - }, + "aliases": [], + "minimum-stability": "RC", + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { From 3af2ea0d4c9d6074c46403a0cd05166c441841fa Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:04:42 -0400 Subject: [PATCH 166/195] chore: updating packages --- composer.json | 8 +-- composer.lock | 184 +++++++++++++++++++++++++------------------------- 2 files changed, 96 insertions(+), 96 deletions(-) diff --git a/composer.json b/composer.json index 4a5adc48ef..e23806adc9 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "description": "End to end backend server for frontend and mobile apps.", "type": "project", "license": "BSD-3-Clause", - "minimum-stability": "RC", + "minimum-stability": "stable", "authors": [ { "name": "Eldad Fux", @@ -51,9 +51,9 @@ "utopia-php/analytics": "0.13.*", "utopia-php/audit": "0.44.*", "utopia-php/cache": "0.10.*", - "utopia-php/cli": "1.0.*", + "utopia-php/cli": "0.19.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "1.0.*", + "utopia-php/database": "0.54.*", "utopia-php/domains": "0.6.*", "utopia-php/dsn": "0.2.*", "utopia-php/framework": "1.0.*", @@ -64,7 +64,7 @@ "utopia-php/messaging": "0.12.*", "utopia-php/migration": "0.4.*", "utopia-php/orchestration": "0.15.*", - "utopia-php/platform": "0.6.*", + "utopia-php/platform": "0.8.*", "utopia-php/view": "0.2.*", "utopia-php/pools": "0.5.*", "utopia-php/preloader": "0.2.*", diff --git a/composer.lock b/composer.lock index 905e1f58be..7a4729007b 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": "3cb2a27888b7c646da7977c41383eff4", + "content-hash": "6017f815da50b7d4dabad66386e013e3", "packages": [ { "name": "adhocore/jwt", @@ -1429,16 +1429,16 @@ }, { "name": "utopia-php/abuse", - "version": "0.44.0-RC1", + "version": "0.44.0", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "0059ae5daec97edd85276bb5c7eb501484b79ad5" + "reference": "cd37bba23778f3a0dc54b4ba6ffb36d4d91ed8f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/0059ae5daec97edd85276bb5c7eb501484b79ad5", - "reference": "0059ae5daec97edd85276bb5c7eb501484b79ad5", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/cd37bba23778f3a0dc54b4ba6ffb36d4d91ed8f1", + "reference": "cd37bba23778f3a0dc54b4ba6ffb36d4d91ed8f1", "shasum": "" }, "require": { @@ -1446,7 +1446,7 @@ "ext-pdo": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "1.0.*" + "utopia-php/database": "0.54.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1474,27 +1474,27 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.44.0-RC1" + "source": "https://github.com/utopia-php/abuse/tree/0.44.0" }, - "time": "2024-09-04T18:14:29+00:00" + "time": "2024-09-05T16:09:32+00:00" }, { "name": "utopia-php/analytics", - "version": "0.13.0-RC2", + "version": "0.13.0", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "b6a61e8834f169fd5e1b3678d48c7f0029aaa8c2" + "reference": "3dace02af5d4190623f88fb6e02f5559a99f14c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/b6a61e8834f169fd5e1b3678d48c7f0029aaa8c2", - "reference": "b6a61e8834f169fd5e1b3678d48c7f0029aaa8c2", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/3dace02af5d4190623f88fb6e02f5559a99f14c4", + "reference": "3dace02af5d4190623f88fb6e02f5559a99f14c4", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "1.0.*", + "utopia-php/cli": "0.19.*", "utopia-php/system": "0.8.*" }, "require-dev": { @@ -1521,27 +1521,27 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.13.0-RC2" + "source": "https://github.com/utopia-php/analytics/tree/0.13.0" }, - "time": "2024-09-05T00:39:55+00:00" + "time": "2024-09-05T16:19:26+00:00" }, { "name": "utopia-php/audit", - "version": "0.44.0-RC1", + "version": "0.44.0", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "adc098f3a188755c487b2409e5f2897bb60ee6f3" + "reference": "69eee24e4d6cb8fdae31235d80b9a46b18092139" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/adc098f3a188755c487b2409e5f2897bb60ee6f3", - "reference": "adc098f3a188755c487b2409e5f2897bb60ee6f3", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/69eee24e4d6cb8fdae31235d80b9a46b18092139", + "reference": "69eee24e4d6cb8fdae31235d80b9a46b18092139", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "1.0.*" + "utopia-php/database": "0.54.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1568,9 +1568,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.44.0-RC1" + "source": "https://github.com/utopia-php/audit/tree/0.44.0" }, - "time": "2024-09-04T19:29:05+00:00" + "time": "2024-09-05T16:12:41+00:00" }, { "name": "utopia-php/cache", @@ -1624,16 +1624,16 @@ }, { "name": "utopia-php/cli", - "version": "1.0.0-RC1", + "version": "0.19.0", "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "7664161dcdb9b76a3ece0ae2f36a9aca1e548e80" + "reference": "f8af1d6087f498bc1f0191750a118d357ded9948" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/7664161dcdb9b76a3ece0ae2f36a9aca1e548e80", - "reference": "7664161dcdb9b76a3ece0ae2f36a9aca1e548e80", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/f8af1d6087f498bc1f0191750a118d357ded9948", + "reference": "f8af1d6087f498bc1f0191750a118d357ded9948", "shasum": "" }, "require": { @@ -1669,9 +1669,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/1.0.0-RC1" + "source": "https://github.com/utopia-php/cli/tree/0.19.0" }, - "time": "2024-08-09T17:35:04+00:00" + "time": "2024-09-05T15:46:56+00:00" }, { "name": "utopia-php/config", @@ -1726,16 +1726,16 @@ }, { "name": "utopia-php/database", - "version": "1.0.0-RC2", + "version": "0.54.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "d70eb76c7cf5ee596752209a4d57e4d08fe8a986" + "reference": "1e97fc8b212a8daf9b9a68244677ed34c9db143e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/d70eb76c7cf5ee596752209a4d57e4d08fe8a986", - "reference": "d70eb76c7cf5ee596752209a4d57e4d08fe8a986", + "url": "https://api.github.com/repos/utopia-php/database/zipball/1e97fc8b212a8daf9b9a68244677ed34c9db143e", + "reference": "1e97fc8b212a8daf9b9a68244677ed34c9db143e", "shasum": "" }, "require": { @@ -1754,7 +1754,7 @@ "phpunit/phpunit": "9.6.*", "rregeer/phpunit-coverage-check": "0.3.*", "swoole/ide-helper": "5.1.3", - "utopia-php/cli": "1.0.*" + "utopia-php/cli": "0.19.*" }, "type": "library", "autoload": { @@ -1776,9 +1776,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/1.0.0-RC2" + "source": "https://github.com/utopia-php/database/tree/0.54.0" }, - "time": "2024-09-04T17:58:16+00:00" + "time": "2024-09-05T16:00:42+00:00" }, { "name": "utopia-php/di", @@ -1830,16 +1830,16 @@ }, { "name": "utopia-php/domains", - "version": "0.6.0-RC1", + "version": "0.6.0", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "a387c966ae2ab39878f77f94f0d01557a888bad9" + "reference": "5c70b0f524deeb1fccc3962ad1da650ae2c94cda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/a387c966ae2ab39878f77f94f0d01557a888bad9", - "reference": "a387c966ae2ab39878f77f94f0d01557a888bad9", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/5c70b0f524deeb1fccc3962ad1da650ae2c94cda", + "reference": "5c70b0f524deeb1fccc3962ad1da650ae2c94cda", "shasum": "" }, "require": { @@ -1885,9 +1885,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.6.0-RC1" + "source": "https://github.com/utopia-php/domains/tree/0.6.0" }, - "time": "2024-08-09T20:54:00+00:00" + "time": "2024-09-05T16:21:11+00:00" }, { "name": "utopia-php/dsn", @@ -1977,22 +1977,22 @@ }, { "name": "utopia-php/framework", - "version": "1.0.0-RC3", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "31306629e6d7df2bee36b4402ff84f19fb012889" + "reference": "cc880ec41f7f163d4f9956fec26cc6be51b412cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/31306629e6d7df2bee36b4402ff84f19fb012889", - "reference": "31306629e6d7df2bee36b4402ff84f19fb012889", + "url": "https://api.github.com/repos/utopia-php/http/zipball/cc880ec41f7f163d4f9956fec26cc6be51b412cf", + "reference": "cc880ec41f7f163d4f9956fec26cc6be51b412cf", "shasum": "" }, "require": { "ext-swoole": "*", "php": ">=8.0", - "utopia-php/servers": "0.1.* " + "utopia-php/servers": "0.1.*" }, "require-dev": { "ext-xdebug": "*", @@ -2021,9 +2021,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/1.0.0-RC3" + "source": "https://github.com/utopia-php/http/tree/1.0.0" }, - "time": "2024-09-05T00:43:38+00:00" + "time": "2024-09-05T15:38:08+00:00" }, { "name": "utopia-php/image", @@ -2338,21 +2338,21 @@ }, { "name": "utopia-php/orchestration", - "version": "0.15.0-RC1", + "version": "0.15.0", "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", - "reference": "430d83aa3df5c2fca285245b29ed0d470421ada2" + "reference": "cd55650ba5f13118c3580048e6dd86b604f9a5b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/430d83aa3df5c2fca285245b29ed0d470421ada2", - "reference": "430d83aa3df5c2fca285245b29ed0d470421ada2", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/cd55650ba5f13118c3580048e6dd86b604f9a5b3", + "reference": "cd55650ba5f13118c3580048e6dd86b604f9a5b3", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "1.0.*" + "utopia-php/cli": "0.19.*" }, "require-dev": { "laravel/pint": "^1.2", @@ -2382,29 +2382,29 @@ ], "support": { "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.15.0-RC1" + "source": "https://github.com/utopia-php/orchestration/tree/0.15.0" }, - "time": "2024-09-04T19:33:56+00:00" + "time": "2024-09-05T16:28:02+00:00" }, { "name": "utopia-php/platform", - "version": "0.6.0-RC1", + "version": "0.8.0", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "32b778f8acbbfc5852f6815086b78babe2a4396e" + "reference": "186236124e2b3a2c6190568e3e227d3a48074d0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/32b778f8acbbfc5852f6815086b78babe2a4396e", - "reference": "32b778f8acbbfc5852f6815086b78babe2a4396e", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/186236124e2b3a2c6190568e3e227d3a48074d0f", + "reference": "186236124e2b3a2c6190568e3e227d3a48074d0f", "shasum": "" }, "require": { "ext-json": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/cli": "1.0.*", + "utopia-php/cli": "0.19.*", "utopia-php/framework": "1.0.*", "utopia-php/queue": "0.8.*", "utopia-php/servers": "0.1.0" @@ -2433,9 +2433,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.6.0-RC1" + "source": "https://github.com/utopia-php/platform/tree/0.8.0" }, - "time": "2024-09-05T00:23:57+00:00" + "time": "2024-09-05T16:36:36+00:00" }, { "name": "utopia-php/pools", @@ -2543,21 +2543,21 @@ }, { "name": "utopia-php/queue", - "version": "0.8.0-RC1", + "version": "0.8.0", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "ed85fd26200f07d9b93d18d2fa7f5bbef2984902" + "reference": "a518b271f8c158d6e66e36972f767189111033c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/ed85fd26200f07d9b93d18d2fa7f5bbef2984902", - "reference": "ed85fd26200f07d9b93d18d2fa7f5bbef2984902", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/a518b271f8c158d6e66e36972f767189111033c2", + "reference": "a518b271f8c158d6e66e36972f767189111033c2", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "1.0.*", + "utopia-php/cli": "0.19.*", "utopia-php/di": "0.1.*", "utopia-php/servers": "0.1.*" }, @@ -2600,9 +2600,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.8.0-RC1" + "source": "https://github.com/utopia-php/queue/tree/0.8.0" }, - "time": "2024-09-04T18:19:23+00:00" + "time": "2024-09-05T16:33:01+00:00" }, { "name": "utopia-php/registry", @@ -2711,16 +2711,16 @@ }, { "name": "utopia-php/storage", - "version": "0.19.0-RC1", + "version": "0.19.0", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "dffcbe0be08c8114750e6af4f682ae965e3791ea" + "reference": "5013b894a776874d6010753fc9349df2225c69af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/dffcbe0be08c8114750e6af4f682ae965e3791ea", - "reference": "dffcbe0be08c8114750e6af4f682ae965e3791ea", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/5013b894a776874d6010753fc9349df2225c69af", + "reference": "5013b894a776874d6010753fc9349df2225c69af", "shasum": "" }, "require": { @@ -2733,7 +2733,7 @@ "ext-zstd": "*", "php": ">=8.0", "utopia-php/framework": "1.0.*", - "utopia-php/system": "0.*.*" + "utopia-php/system": "0.8.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2760,9 +2760,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.19.0-RC1" + "source": "https://github.com/utopia-php/storage/tree/0.19.0" }, - "time": "2024-08-09T21:00:39+00:00" + "time": "2024-09-05T17:00:24+00:00" }, { "name": "utopia-php/system", @@ -2822,16 +2822,16 @@ }, { "name": "utopia-php/vcs", - "version": "0.9.0-RC1", + "version": "0.9.0", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "3efa907981745056b6a3481bdb0372885571c442" + "reference": "673abe2fef0750a841a4fa8fa6f99d4a602c68e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/3efa907981745056b6a3481bdb0372885571c442", - "reference": "3efa907981745056b6a3481bdb0372885571c442", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/673abe2fef0750a841a4fa8fa6f99d4a602c68e7", + "reference": "673abe2fef0750a841a4fa8fa6f99d4a602c68e7", "shasum": "" }, "require": { @@ -2866,9 +2866,9 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.9.0-RC1" + "source": "https://github.com/utopia-php/vcs/tree/0.9.0" }, - "time": "2024-09-04T21:22:50+00:00" + "time": "2024-09-05T16:44:48+00:00" }, { "name": "utopia-php/view", @@ -3140,16 +3140,16 @@ }, { "name": "doctrine/annotations", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/901c2ee5d26eb64ff43c47976e114bf00843acf7", + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7", "shasum": "" }, "require": { @@ -3161,10 +3161,10 @@ "require-dev": { "doctrine/cache": "^2.0", "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.8.0", + "phpstan/phpstan": "^1.10.28", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^5.4 || ^6", - "vimeo/psalm": "^4.10" + "symfony/cache": "^5.4 || ^6.4 || ^7", + "vimeo/psalm": "^4.30 || ^5.14" }, "suggest": { "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" @@ -3210,9 +3210,9 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.1" + "source": "https://github.com/doctrine/annotations/tree/2.0.2" }, - "time": "2023-02-02T22:02:53+00:00" + "time": "2024-09-05T10:17:24+00:00" }, { "name": "doctrine/deprecations", @@ -7066,7 +7066,7 @@ } ], "aliases": [], - "minimum-stability": "RC", + "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, From 5d70884e6cf4aa65e4da38f1bff870d4725964f0 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:11:39 -0400 Subject: [PATCH 167/195] chore: cleaning --- CHANGES.md | 2 +- README.md | 2 +- app/cli.php | 2 +- app/http.php | 2 +- app/init.php | 3133 +++++++++------------ app/init2.php | 1381 --------- app/realtime.php | 2 +- app/views/install/compose.phtml | 2 +- app/worker.php | 2 +- docker-compose.local.prod.yml | 1108 -------- docker-compose.yml | 2 +- phpunit.xml | 2 +- tests/resources/docker/docker-compose.yml | 2 +- tests/unit/Event/EventTest.php | 2 +- 14 files changed, 1390 insertions(+), 4254 deletions(-) delete mode 100644 app/init2.php delete mode 100644 docker-compose.local.prod.yml diff --git a/CHANGES.md b/CHANGES.md index 73c0292b03..e6649d795e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -635,7 +635,7 @@ * Bump console to version 3.2.7 [#7148](https://github.com/appwrite/appwrite/pull/7148) * Chore update database to 0.45.2 [#7138](https://github.com/appwrite/appwrite/pull/7138) * Implement queue thresholds for the health API [#7123](https://github.com/appwrite/appwrite/pull/7123) -* Add $auth->skip to the usage worker [#7124](https://github.com/appwrite/appwrite/pull/7124) +* Add Authorization::skip to the usage worker [#7124](https://github.com/appwrite/appwrite/pull/7124) ## Bug fixes * fix: use queueForDeletes in git installation delete endpoint [#7140](https://github.com/appwrite/appwrite/pull/7140) diff --git a/README.md b/README.md index 4418b8f040..8305d78ef0 100644 --- a/README.md +++ b/README.md @@ -235,4 +235,4 @@ Join our growing community around the world! Check out our official [Blog](https ## License -This repository is available under the [BSD 3-Clause License](./LICENSE). +This repository is available under the [BSD 3-Clause License](./LICENSE). diff --git a/app/cli.php b/app/cli.php index 6074185461..a0f55e82cc 100644 --- a/app/cli.php +++ b/app/cli.php @@ -1,6 +1,6 @@ $value], JSON_PRESERVE_ZERO_FRACTION); -// }, -// function (mixed $value) { -// if (is_null($value)) { -// return; -// } -// -// return json_decode($value, true)['value']; -// } -//); -// -//Database::addFilter( -// 'enum', -// function (mixed $value, Document $attribute) { -// if ($attribute->isSet('elements')) { -// $attribute->removeAttribute('elements'); -// } -// -// return $value; -// }, -// function (mixed $value, Document $attribute) { -// $formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true); -// if (isset($formatOptions['elements'])) { -// $attribute->setAttribute('elements', $formatOptions['elements']); -// } -// -// return $value; -// } -//); -// -//Database::addFilter( -// 'range', -// function (mixed $value, Document $attribute) { -// if ($attribute->isSet('min')) { -// $attribute->removeAttribute('min'); -// } -// if ($attribute->isSet('max')) { -// $attribute->removeAttribute('max'); -// } -// -// return $value; -// }, -// function (mixed $value, Document $attribute) { -// $formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true); -// if (isset($formatOptions['min']) || isset($formatOptions['max'])) { -// $attribute -// ->setAttribute('min', $formatOptions['min']) -// ->setAttribute('max', $formatOptions['max']); -// } -// -// return $value; -// } -//); -// -//Database::addFilter( -// 'subQueryAttributes', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// $attributes = $database->find('attributes', [ -// Query::equal('collectionInternalId', [$document->getInternalId()]), -// Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), -// Query::limit($database->getLimitForAttributes()), -// ]); -// -// foreach ($attributes as $attribute) { -// if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { -// $options = $attribute->getAttribute('options'); -// foreach ($options as $key => $value) { -// $attribute->setAttribute($key, $value); -// } -// $attribute->removeAttribute('options'); -// } -// } -// -// return $attributes; -// } -//); -// -//Database::addFilter( -// 'subQueryIndexes', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// return $database -// ->find('indexes', [ -// Query::equal('collectionInternalId', [$document->getInternalId()]), -// Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), -// Query::limit($database->getLimitForIndexes()), -// ]); -// } -//); -// -//Database::addFilter( -// 'subQueryPlatforms', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// return $database -// ->find('platforms', [ -// Query::equal('projectInternalId', [$document->getInternalId()]), -// Query::limit(APP_LIMIT_SUBQUERY), -// ]); -// } -//); -// -//Database::addFilter( -// 'subQueryKeys', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// return $database -// ->find('keys', [ -// Query::equal('projectInternalId', [$document->getInternalId()]), -// Query::limit(APP_LIMIT_SUBQUERY), -// ]); -// } -//); -// -//Database::addFilter( -// 'subQueryWebhooks', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// return $database -// ->find('webhooks', [ -// Query::equal('projectInternalId', [$document->getInternalId()]), -// Query::limit(APP_LIMIT_SUBQUERY), -// ]); -// } -//); -// -//Database::addFilter( -// 'subQuerySessions', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// return Authorization::skip(fn () => $database->find('sessions', [ -// Query::equal('userInternalId', [$document->getInternalId()]), -// Query::limit(APP_LIMIT_SUBQUERY), -// ])); -// } -//); -// -//Database::addFilter( -// 'subQueryTokens', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// return Authorization::skip(fn () => $database -// ->find('tokens', [ -// Query::equal('userInternalId', [$document->getInternalId()]), -// Query::limit(APP_LIMIT_SUBQUERY), -// ])); -// } -//); -// -//Database::addFilter( -// 'subQueryChallenges', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// return Authorization::skip(fn () => $database -// ->find('challenges', [ -// Query::equal('userInternalId', [$document->getInternalId()]), -// Query::limit(APP_LIMIT_SUBQUERY), -// ])); -// } -//); -// -//Database::addFilter( -// 'subQueryAuthenticators', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// return Authorization::skip(fn () => $database -// ->find('authenticators', [ -// Query::equal('userInternalId', [$document->getInternalId()]), -// Query::limit(APP_LIMIT_SUBQUERY), -// ])); -// } -//); -// -//Database::addFilter( -// 'subQueryMemberships', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// return Authorization::skip(fn () => $database -// ->find('memberships', [ -// Query::equal('userInternalId', [$document->getInternalId()]), -// Query::limit(APP_LIMIT_SUBQUERY), -// ])); -// } -//); -// -//Database::addFilter( -// 'subQueryVariables', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// return $database -// ->find('variables', [ -// Query::equal('resourceInternalId', [$document->getInternalId()]), -// Query::equal('resourceType', ['function']), -// Query::limit(APP_LIMIT_SUBQUERY), -// ]); -// } -//); -// -//Database::addFilter( -// 'encrypt', -// function (mixed $value) { -// $key = System::getEnv('_APP_OPENSSL_KEY_V1'); -// $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); -// $tag = null; -// -// return json_encode([ -// 'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag), -// 'method' => OpenSSL::CIPHER_AES_128_GCM, -// 'iv' => \bin2hex($iv), -// 'tag' => \bin2hex($tag ?? ''), -// 'version' => '1', -// ]); -// }, -// function (mixed $value) { -// if (is_null($value)) { -// return; -// } -// $value = json_decode($value, true); -// $key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']); -// -// return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag'])); -// } -//); -// -//Database::addFilter( -// 'subQueryProjectVariables', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// return $database -// ->find('variables', [ -// Query::equal('resourceType', ['project']), -// Query::limit(APP_LIMIT_SUBQUERY) -// ]); -// } -//); -// -//Database::addFilter( -// 'userSearch', -// function (mixed $value, Document $user) { -// $searchValues = [ -// $user->getId(), -// $user->getAttribute('email', ''), -// $user->getAttribute('name', ''), -// $user->getAttribute('phone', '') -// ]; -// -// foreach ($user->getAttribute('labels', []) as $label) { -// $searchValues[] = 'label:' . $label; -// } -// -// $search = implode(' ', \array_filter($searchValues)); -// -// return $search; -// }, -// function (mixed $value) { -// return $value; -// } -//); -// -//Database::addFilter( -// 'subQueryTargets', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// return Authorization::skip(fn () => $database -// ->find('targets', [ -// Query::equal('userInternalId', [$document->getInternalId()]), -// Query::limit(APP_LIMIT_SUBQUERY) -// ])); -// } -//); -// -//Database::addFilter( -// 'subQueryTopicTargets', -// function (mixed $value) { -// return; -// }, -// function (mixed $value, Document $document, Database $database) { -// $targetIds = Authorization::skip(fn () => \array_map( -// fn ($document) => $document->getAttribute('targetInternalId'), -// $database->find('subscribers', [ -// Query::equal('topicInternalId', [$document->getInternalId()]), -// Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY) -// ]) -// )); -// if (\count($targetIds) > 0) { -// return $database->skipValidation(fn () => $database->find('targets', [ -// Query::equal('$internalId', $targetIds) -// ])); -// } -// return []; -// } -//); -// -//Database::addFilter( -// 'providerSearch', -// function (mixed $value, Document $provider) { -// $searchValues = [ -// $provider->getId(), -// $provider->getAttribute('name', ''), -// $provider->getAttribute('provider', ''), -// $provider->getAttribute('type', '') -// ]; -// -// $search = \implode(' ', \array_filter($searchValues)); -// -// return $search; -// }, -// function (mixed $value) { -// return $value; -// } -//); -// -//Database::addFilter( -// 'topicSearch', -// function (mixed $value, Document $topic) { -// $searchValues = [ -// $topic->getId(), -// $topic->getAttribute('name', ''), -// $topic->getAttribute('description', ''), -// ]; -// -// $search = \implode(' ', \array_filter($searchValues)); -// -// return $search; -// }, -// function (mixed $value) { -// return $value; -// } -//); -// -//Database::addFilter( -// 'messageSearch', -// function (mixed $value, Document $message) { -// $searchValues = [ -// $message->getId(), -// $message->getAttribute('description', ''), -// $message->getAttribute('status', ''), -// ]; -// -// $data = \json_decode($message->getAttribute('data', []), true); -// $providerType = $message->getAttribute('providerType', ''); -// -// if ($providerType === MESSAGE_TYPE_EMAIL) { -// $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); -// } elseif ($providerType === MESSAGE_TYPE_SMS) { -// $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); -// } else { -// $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); -// } -// -// $search = \implode(' ', \array_filter($searchValues)); -// -// return $search; -// }, -// function (mixed $value) { -// return $value; -// } -//); -// -///** -// * DB Formats -// */ -//Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () { -// return new Email(); -//}, Database::VAR_STRING); -// -//Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () { -// return new DatetimeValidator(); -//}, Database::VAR_DATETIME); -// -//Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) { -// $elements = $attribute['formatOptions']['elements']; -// return new WhiteList($elements, true); -//}, Database::VAR_STRING); -// -//Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () { -// return new IP(); -//}, Database::VAR_STRING); -// -//Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () { -// return new URL(); -//}, Database::VAR_STRING); -// -//Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) { -// $min = $attribute['formatOptions']['min'] ?? -INF; -// $max = $attribute['formatOptions']['max'] ?? INF; -// return new Range($min, $max, Range::TYPE_INTEGER); -//}, Database::VAR_INTEGER); -// -//Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) { -// $min = $attribute['formatOptions']['min'] ?? -INF; -// $max = $attribute['formatOptions']['max'] ?? INF; -// return new Range($min, $max, Range::TYPE_FLOAT); -//}, Database::VAR_FLOAT); -// -///* -// * Registry -// */ -//$register->set('logger', function () { -// // Register error logger -// $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); -// $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); -// -// try { -// $loggingProvider = new DSN($providerConfig ?? ''); -// -// $providerName = $loggingProvider->getScheme(); -// $providerConfig = match ($providerName) { -// 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()], -// 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], -// default => ['key' => $loggingProvider->getHost()], -// }; -// } catch (Throwable $th) { -// // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables -// Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage()); -// $configChunks = \explode(";", $providerConfig); -// -// $providerConfig = match ($providerName) { -// 'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',], -// 'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''], -// default => ['key' => $providerConfig], -// }; -// } -// -// if (empty($providerName) || empty($providerConfig)) { -// return; -// } -// -// if (!Logger::hasProvider($providerName)) { -// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); -// } -// -// try { -// $adapter = match ($providerName) { -// 'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']), -// 'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']), -// 'raygun' => new Raygun($providerConfig['key']), -// 'appsignal' => new AppSignal($providerConfig['key']), -// default => null -// }; -// } catch (Throwable $th) { -// $adapter = null; -// } -// -// if($adapter === null) { -// Console::error("Logging provider not supported. Logging is disabled"); -// return; -// } -// -// return new Logger($adapter); -//}); -// -//$register->set('pools', function () { -// $group = new Group(); -// -// $fallbackForDB = 'db_main=' . AppwriteURL::unparse([ -// 'scheme' => 'mariadb', -// 'host' => System::getEnv('_APP_DB_HOST', 'mariadb'), -// 'port' => System::getEnv('_APP_DB_PORT', '3306'), -// 'user' => System::getEnv('_APP_DB_USER', ''), -// 'pass' => System::getEnv('_APP_DB_PASS', ''), -// 'path' => System::getEnv('_APP_DB_SCHEMA', ''), -// ]); -// $fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([ -// 'scheme' => 'redis', -// 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), -// 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), -// 'user' => System::getEnv('_APP_REDIS_USER', ''), -// 'pass' => System::getEnv('_APP_REDIS_PASS', ''), -// ]); -// -// $connections = [ -// 'console' => [ -// 'type' => 'database', -// 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), -// 'multiple' => false, -// 'schemes' => ['mariadb', 'mysql'], -// ], -// 'database' => [ -// 'type' => 'database', -// 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), -// 'multiple' => true, -// 'schemes' => ['mariadb', 'mysql'], -// ], -// 'queue' => [ -// 'type' => 'queue', -// 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), -// 'multiple' => false, -// 'schemes' => ['redis'], -// ], -// 'pubsub' => [ -// 'type' => 'pubsub', -// 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), -// 'multiple' => false, -// 'schemes' => ['redis'], -// ], -// 'cache' => [ -// 'type' => 'cache', -// 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), -// 'multiple' => true, -// 'schemes' => ['redis'], -// ], -// ]; -// -// $maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151); -// $instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14); -// -// $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; -// -// if ($multiprocessing) { -// $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); -// } else { -// $workerCount = 1; -// } -// -// if ($workerCount > $instanceConnections) { -// throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500); -// } -// -// $poolSize = (int)($instanceConnections / $workerCount); -// -// foreach ($connections as $key => $connection) { -// $type = $connection['type'] ?? ''; -// $multiple = $connection['multiple'] ?? false; -// $schemes = $connection['schemes'] ?? []; -// $config = []; -// $dsns = explode(',', $connection['dsns'] ?? ''); -// foreach ($dsns as &$dsn) { -// $dsn = explode('=', $dsn); -// $name = ($multiple) ? $key . '_' . $dsn[0] : $key; -// $dsn = $dsn[1] ?? ''; -// $config[] = $name; -// if (empty($dsn)) { -// //throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); -// continue; -// } -// -// $dsn = new DSN($dsn); -// $dsnHost = $dsn->getHost(); -// $dsnPort = $dsn->getPort(); -// $dsnUser = $dsn->getUser(); -// $dsnPass = $dsn->getPassword(); -// $dsnScheme = $dsn->getScheme(); -// $dsnDatabase = $dsn->getPath(); -// -// if (!in_array($dsnScheme, $schemes)) { -// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); -// } -// -// /** -// * Get Resource -// * -// * Creation could be reused across connection types like database, cache, queue, etc. -// * -// * Resource assignment to an adapter will happen below. -// */ -// $resource = match ($dsnScheme) { -// 'mysql', -// 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { -// return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { -// return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( -// PDO::ATTR_TIMEOUT => 3, // Seconds -// PDO::ATTR_PERSISTENT => true, -// PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, -// PDO::ATTR_EMULATE_PREPARES => true, -// PDO::ATTR_STRINGIFY_FETCHES => true -// )); -// }); -// }, -// 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { -// $redis = new Redis(); -// @$redis->pconnect($dsnHost, (int)$dsnPort); -// if ($dsnPass) { -// $redis->auth($dsnPass); -// } -// $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); -// -// return $redis; -// }, -// default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'), -// }; -// -// $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { -// // Get Adapter -// switch ($type) { -// case 'database': -// $adapter = match ($dsn->getScheme()) { -// 'mariadb' => new MariaDB($resource()), -// 'mysql' => new MySQL($resource()), -// default => null -// }; -// -// $adapter->setDatabase($dsn->getPath()); -// break; -// case 'pubsub': -// $adapter = $resource(); -// break; -// case 'queue': -// $adapter = match ($dsn->getScheme()) { -// 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), -// default => null -// }; -// break; -// case 'cache': -// $adapter = match ($dsn->getScheme()) { -// 'redis' => new RedisCache($resource()), -// default => null -// }; -// break; -// -// default: -// throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); -// } -// -// return $adapter; -// }); -// -// $group->add($pool); -// } -// -// Config::setParam('pools-' . $key, $config); -// } -// -// return $group; -//}); -// -//$register->set('db', function () { -// // This is usually for our workers or CLI commands scope -// $dbHost = System::getEnv('_APP_DB_HOST', ''); -// $dbPort = System::getEnv('_APP_DB_PORT', ''); -// $dbUser = System::getEnv('_APP_DB_USER', ''); -// $dbPass = System::getEnv('_APP_DB_PASS', ''); -// $dbScheme = System::getEnv('_APP_DB_SCHEMA', ''); -// -// return new PDO( -// "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", -// $dbUser, -// $dbPass, -// SQL::getPDOAttributes() -// ); -//}); -// -//$register->set('smtp', function () { -// $mail = new PHPMailer(true); -// -// $mail->isSMTP(); -// -// $username = System::getEnv('_APP_SMTP_USERNAME'); -// $password = System::getEnv('_APP_SMTP_PASSWORD'); -// -// $mail->XMailer = 'Appwrite Mailer'; -// $mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp'); -// $mail->Port = System::getEnv('_APP_SMTP_PORT', 25); -// $mail->SMTPAuth = !empty($username) && !empty($password); -// $mail->Username = $username; -// $mail->Password = $password; -// $mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', ''); -// $mail->SMTPAutoTLS = false; -// $mail->CharSet = 'UTF-8'; -// -// $from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server')); -// $email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); -// -// $mail->setFrom($email, $from); -// $mail->addReplyTo($email, $from); -// -// $mail->isHTML(true); -// -// return $mail; -//}); -//$register->set('geodb', function () { -// return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb'); -//}); -//$register->set('passwordsDictionary', function () { -// $content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords'); -// $content = explode("\n", $content); -// $content = array_flip($content); -// return $content; -//}); -//$register->set('promiseAdapter', function () { -// return new Swoole(); -//}); -//$register->set('hooks', function () { -// return new Hooks(); -//}); -///* -// * Localization -// */ -//Locale::$exceptions = false; -// -//$locales = Config::getParam('locale-codes', []); -// -//foreach ($locales as $locale) { -// $code = $locale['code']; -// -// $path = __DIR__ . '/config/locale/translations/' . $code . '.json'; -// -// if (!\file_exists($path)) { -// $path = __DIR__ . '/config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar` -// if (!\file_exists($path)) { -// $path = __DIR__ . '/config/locale/translations/en.json'; // if none translation exists, use default from `en.json` -// } -// } -// -// Locale::setLanguageFromJSON($code, $path); -//} -// -//\stream_context_set_default([ // Set global user agent and http settings -// 'http' => [ -// 'method' => 'GET', -// 'user_agent' => \sprintf( -// APP_USERAGENT, -// System::getEnv('_APP_VERSION', 'UNKNOWN'), -// System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)) -// ), -// 'timeout' => 2, -// ], -//]); -// -//// Runtime Execution -//App::setResource('log', fn () => new Log()); -//App::setResource('logger', function ($register) { -// return $register->get('logger'); -//}, ['register']); -// -//App::setResource('hooks', function ($register) { -// return $register->get('hooks'); -//}, ['register']); -// -//App::setResource('register', fn () => $register); -//App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); -// -//App::setResource('localeCodes', function () { -// return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', [])); -//}); -// -//// Queues -//App::setResource('queue', function (Group $pools) { -// return $pools->get('queue')->pop()->getResource(); -//}, ['pools']); -//App::setResource('queueForMessaging', function (Connection $queue) { -// return new Messaging($queue); -//}, ['queue']); -//App::setResource('queueForMails', function (Connection $queue) { -// return new Mail($queue); -//}, ['queue']); -//App::setResource('queueForBuilds', function (Connection $queue) { -// return new Build($queue); -//}, ['queue']); -//App::setResource('queueForDatabase', function (Connection $queue) { -// return new EventDatabase($queue); -//}, ['queue']); -//App::setResource('queueForDeletes', function (Connection $queue) { -// return new Delete($queue); -//}, ['queue']); -//App::setResource('queueForEvents', function (Connection $queue) { -// return new Event($queue); -//}, ['queue']); -//App::setResource('queueForAudits', function (Connection $queue) { -// return new Audit($queue); -//}, ['queue']); -//App::setResource('queueForFunctions', function (Connection $queue) { -// return new Func($queue); -//}, ['queue']); -//App::setResource('queueForUsage', function (Connection $queue) { -// return new Usage($queue); -//}, ['queue']); -//App::setResource('queueForCertificates', function (Connection $queue) { -// return new Certificate($queue); -//}, ['queue']); -//App::setResource('queueForMigrations', function (Connection $queue) { -// return new Migration($queue); -//}, ['queue']); -//App::setResource('clients', function ($request, $console, $project) { -// $console->setAttribute('platforms', [ // Always allow current host -// '$collection' => ID::custom('platforms'), -// 'name' => 'Current Host', -// 'type' => Origin::CLIENT_TYPE_WEB, -// 'hostname' => $request->getHostname(), -// ], Document::SET_TYPE_APPEND); -// -// $hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', '')); -// $validator = new Hostname(); -// foreach ($hostnames as $hostname) { -// $hostname = trim($hostname); -// if (!$validator->isValid($hostname)) { -// continue; -// } -// $console->setAttribute('platforms', [ -// '$collection' => ID::custom('platforms'), -// 'type' => Origin::CLIENT_TYPE_WEB, -// 'name' => $hostname, -// 'hostname' => $hostname, -// ], Document::SET_TYPE_APPEND); -// } -// -// /** -// * Get All verified client URLs for both console and current projects -// * + Filter for duplicated entries -// */ -// $clientsConsole = \array_map( -// fn ($node) => $node['hostname'], -// \array_filter( -// $console->getAttribute('platforms', []), -// fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname'])) -// ) -// ); -// -// $clients = $clientsConsole; -// $platforms = $project->getAttribute('platforms', []); -// -// foreach ($platforms as $node) { -// if ( -// isset($node['type']) && -// ($node['type'] === Origin::CLIENT_TYPE_WEB || -// $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && -// !empty($node['hostname']) -// ) { -// $clients[] = $node['hostname']; -// } -// } -// -// return \array_unique($clients); -//}, ['request', 'console', 'project']); -// -//App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { -// /** @var Appwrite\Utopia\Request $request */ -// /** @var Appwrite\Utopia\Response $response */ -// /** @var Utopia\Database\Document $project */ -// /** @var Utopia\Database\Database $dbForProject */ -// /** @var Utopia\Database\Database $dbForConsole */ -// /** @var string $mode */ -// -// Authorization::setDefaultStatus(true); -// -// Auth::setCookieName('a_session_' . $project->getId()); -// -// if (APP_MODE_ADMIN === $mode) { -// Auth::setCookieName('a_session_' . $console->getId()); -// } -// -// $session = Auth::decodeSession( -// $request->getCookie( -// Auth::$cookieName, // Get sessions -// $request->getCookie(Auth::$cookieName . '_legacy', '') -// ) -// ); -// -// // Get session from header for SSR clients -// if (empty($session['id']) && empty($session['secret'])) { -// $sessionHeader = $request->getHeader('x-appwrite-session', ''); -// -// if (!empty($sessionHeader)) { -// $session = Auth::decodeSession($sessionHeader); -// } -// } -// -// // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies -// if ($response) { -// $response->addHeader('X-Debug-Fallback', 'false'); -// } -// -// if (empty($session['id']) && empty($session['secret'])) { -// if ($response) { -// $response->addHeader('X-Debug-Fallback', 'true'); -// } -// $fallback = $request->getHeader('x-fallback-cookies', ''); -// $fallback = \json_decode($fallback, true); -// $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); -// } -// -// Auth::$unique = $session['id'] ?? ''; -// Auth::$secret = $session['secret'] ?? ''; -// -// if (APP_MODE_ADMIN !== $mode) { -// if ($project->isEmpty()) { -// $user = new Document([]); -// } else { -// if ($project->getId() === 'console') { -// $user = $dbForConsole->getDocument('users', Auth::$unique); -// } else { -// $user = $dbForProject->getDocument('users', Auth::$unique); -// } -// } -// } else { -// $user = $dbForConsole->getDocument('users', Auth::$unique); -// } -// -// if ( -// $user->isEmpty() // Check a document has been found in the DB -// || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) -// ) { // Validate user has valid login token -// $user = new Document([]); -// } -// -// if (APP_MODE_ADMIN === $mode) { -// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { -// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. -// } else { -// $user = new Document([]); -// } -// } -// -// $authJWT = $request->getHeader('x-appwrite-jwt', ''); -// -// if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication -// $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); -// -// try { -// $payload = $jwt->decode($authJWT); -// } catch (JWTException $error) { -// throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); -// } -// -// $jwtUserId = $payload['userId'] ?? ''; -// if (!empty($jwtUserId)) { -// $user = $dbForProject->getDocument('users', $jwtUserId); -// } -// -// $jwtSessionId = $payload['sessionId'] ?? ''; -// if(!empty($jwtSessionId)) { -// if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token -// $user = new Document([]); -// } -// } -// } -// -// $dbForProject->setMetadata('user', $user->getId()); -// $dbForConsole->setMetadata('user', $user->getId()); -// -// return $user; -//}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); -// -//App::setResource('project', function ($dbForConsole, $request, $console) { -// /** @var Appwrite\Utopia\Request $request */ -// /** @var Utopia\Database\Database $dbForConsole */ -// /** @var Utopia\Database\Document $console */ -// -// $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); -// -// if (empty($projectId) || $projectId === 'console') { -// return $console; -// } -// -// $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); -// -// return $project; -//}, ['dbForConsole', 'request', 'console']); -// -//App::setResource('session', function (Document $user) { -// if ($user->isEmpty()) { -// return; -// } -// -// $sessions = $user->getAttribute('sessions', []); -// $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); -// -// if (!$sessionId) { -// return; -// } -// -// foreach ($sessions as $session) {/** @var Document $session */ -// if ($sessionId === $session->getId()) { -// return $session; -// } -// } -// -// return; -//}, ['user']); -// -//App::setResource('console', function () { -// return new Document([ -// '$id' => ID::custom('console'), -// '$internalId' => ID::custom('console'), -// 'name' => 'Appwrite', -// '$collection' => ID::custom('projects'), -// 'description' => 'Appwrite core engine', -// 'logo' => '', -// 'teamId' => -1, -// 'webhooks' => [], -// 'keys' => [], -// 'platforms' => [ -// [ -// '$collection' => ID::custom('platforms'), -// 'name' => 'Localhost', -// 'type' => Origin::CLIENT_TYPE_WEB, -// 'hostname' => 'localhost', -// ], // Current host is added on app init -// ], -// 'legalName' => '', -// 'legalCountry' => '', -// 'legalState' => '', -// 'legalCity' => '', -// 'legalAddress' => '', -// 'legalTaxId' => '', -// 'auths' => [ -// 'mockNumbers' => [], -// 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', -// 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user -// 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds -// 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' -// ], -// 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], -// 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], -// 'oAuthProviders' => [ -// 'githubEnabled' => true, -// 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), -// 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') -// ], -// ]); -//}, []); -// -//App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { -// if ($project->isEmpty() || $project->getId() === 'console') { -// return $dbForConsole; -// } -// -// try { -// $dsn = new DSN($project->getAttribute('database')); -// } catch (\InvalidArgumentException) { -// // TODO: Temporary until all projects are using shared tables -// $dsn = new DSN('mysql://' . $project->getAttribute('database')); -// } -// -// $dbAdapter = $pools -// ->get($dsn->getHost()) -// ->pop() -// ->getResource(); -// -// $database = new Database($dbAdapter, $cache); -// -// $database -// ->setMetadata('host', \gethostname()) -// ->setMetadata('project', $project->getId()) -// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); -// -// try { -// $dsn = new DSN($project->getAttribute('database')); -// } catch (\InvalidArgumentException) { -// // TODO: Temporary until all projects are using shared tables -// $dsn = new DSN('mysql://' . $project->getAttribute('database')); -// } -// -// if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { -// $database -// ->setSharedTables(true) -// ->setTenant($project->getInternalId()) -// ->setNamespace($dsn->getParam('namespace')); -// } else { -// $database -// ->setSharedTables(false) -// ->setTenant(null) -// ->setNamespace('_' . $project->getInternalId()); -// } -// -// return $database; -//}, ['pools', 'dbForConsole', 'cache', 'project']); -// -//App::setResource('dbForConsole', function (Group $pools, Cache $cache) { -// $dbAdapter = $pools -// ->get('console') -// ->pop() -// ->getResource(); -// -// $database = new Database($dbAdapter, $cache); -// -// $database -// ->setNamespace('_console') -// ->setMetadata('host', \gethostname()) -// ->setMetadata('project', 'console') -// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); -// -// return $database; -//}, ['pools', 'cache']); -// -//App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { -// $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools -// -// return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { -// if ($project->isEmpty() || $project->getId() === 'console') { -// return $dbForConsole; -// } -// -// try { -// $dsn = new DSN($project->getAttribute('database')); -// } catch (\InvalidArgumentException) { -// // TODO: Temporary until all projects are using shared tables -// $dsn = new DSN('mysql://' . $project->getAttribute('database')); -// } -// -// $configure = (function (Database $database) use ($project, $dsn) { -// $database -// ->setMetadata('host', \gethostname()) -// ->setMetadata('project', $project->getId()) -// ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); -// -// if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { -// $database -// ->setSharedTables(true) -// ->setTenant($project->getInternalId()) -// ->setNamespace($dsn->getParam('namespace')); -// } else { -// $database -// ->setSharedTables(false) -// ->setTenant(null) -// ->setNamespace('_' . $project->getInternalId()); -// } -// }); -// -// if (isset($databases[$dsn->getHost()])) { -// $database = $databases[$dsn->getHost()]; -// $configure($database); -// return $database; -// } -// -// $dbAdapter = $pools -// ->get($dsn->getHost()) -// ->pop() -// ->getResource(); -// -// $database = new Database($dbAdapter, $cache); -// $databases[$dsn->getHost()] = $database; -// $configure($database); -// -// return $database; -// }; -//}, ['pools', 'dbForConsole', 'cache']); -// -//App::setResource('cache', function (Group $pools) { -// $list = Config::getParam('pools-cache', []); -// $adapters = []; -// -// foreach ($list as $value) { -// $adapters[] = $pools -// ->get($value) -// ->pop() -// ->getResource() -// ; -// } -// -// return new Cache(new Sharding($adapters)); -//}, ['pools']); -// -//App::setResource('deviceForLocal', function () { -// return new Local(); -//}); -// -//App::setResource('deviceForFiles', function ($project) { -// return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); -//}, ['project']); -// -//App::setResource('deviceForFunctions', function ($project) { -// return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); -//}, ['project']); -// -//App::setResource('deviceForBuilds', function ($project) { -// return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); -//}, ['project']); -// -//function getDevice($root): Device -//{ -// $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); -// -// if (!empty($connection)) { -// $acl = 'private'; -// $device = Storage::DEVICE_LOCAL; -// $accessKey = ''; -// $accessSecret = ''; -// $bucket = ''; -// $region = ''; -// -// try { -// $dsn = new DSN($connection); -// $device = $dsn->getScheme(); -// $accessKey = $dsn->getUser() ?? ''; -// $accessSecret = $dsn->getPassword() ?? ''; -// $bucket = $dsn->getPath() ?? ''; -// $region = $dsn->getParam('region'); -// } catch (\Throwable $e) { -// Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); -// } -// -// switch ($device) { -// case Storage::DEVICE_S3: -// return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); -// case STORAGE::DEVICE_DO_SPACES: -// $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); -// $device->setHttpVersion(S3::HTTP_VERSION_1_1); -// return $device; -// case Storage::DEVICE_BACKBLAZE: -// return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); -// case Storage::DEVICE_LINODE: -// return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); -// case Storage::DEVICE_WASABI: -// return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); -// case Storage::DEVICE_LOCAL: -// default: -// return new Local($root); -// } -// } else { -// switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { -// case Storage::DEVICE_LOCAL: -// default: -// return new Local($root); -// case Storage::DEVICE_S3: -// $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); -// $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); -// $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); -// $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); -// $s3Acl = 'private'; -// return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); -// case Storage::DEVICE_DO_SPACES: -// $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); -// $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); -// $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); -// $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); -// $doSpacesAcl = 'private'; -// $device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); -// $device->setHttpVersion(S3::HTTP_VERSION_1_1); -// return $device; -// case Storage::DEVICE_BACKBLAZE: -// $backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); -// $backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); -// $backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); -// $backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); -// $backblazeAcl = 'private'; -// return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); -// case Storage::DEVICE_LINODE: -// $linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); -// $linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', ''); -// $linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', ''); -// $linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); -// $linodeAcl = 'private'; -// return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); -// case Storage::DEVICE_WASABI: -// $wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); -// $wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', ''); -// $wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', ''); -// $wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); -// $wasabiAcl = 'private'; -// return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); -// } -// } -//} -// -//App::setResource('mode', function ($request) { -// /** @var Appwrite\Utopia\Request $request */ -// -// /** -// * Defines the mode for the request: -// * - 'default' => Requests for Client and Server Side -// * - 'admin' => Request from the Console on non-console projects -// */ -// return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); -//}, ['request']); -// -//App::setResource('geodb', function ($register) { -// /** @var Utopia\Registry\Registry $register */ -// return $register->get('geodb'); -//}, ['register']); -// -//App::setResource('passwordsDictionary', function ($register) { -// /** @var Utopia\Registry\Registry $register */ -// return $register->get('passwordsDictionary'); -//}, ['register']); -// -// -//App::setResource('servers', function () { -// $platforms = Config::getParam('platforms'); -// $server = $platforms[APP_PLATFORM_SERVER]; -// -// $languages = array_map(function ($language) { -// return strtolower($language['name']); -// }, $server['sdks']); -// -// return $languages; -//}); -// -//App::setResource('promiseAdapter', function ($register) { -// return $register->get('promiseAdapter'); -//}, ['register']); -// -//App::setResource('schema', function ($utopia, $dbForProject) { -// -// $complexity = function (int $complexity, array $args) { -// $queries = Query::parseQueries($args['queries'] ?? []); -// $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; -// $limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT; -// -// return $complexity * $limit; -// }; -// -// $attributes = function (int $limit, int $offset) use ($dbForProject) { -// $attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [ -// Query::limit($limit), -// Query::offset($offset), -// ])); -// -// return \array_map(function ($attr) { -// return $attr->getArrayCopy(); -// }, $attrs); -// }; -// -// $urls = [ -// 'list' => function (string $databaseId, string $collectionId, array $args) { -// return "/v1/databases/$databaseId/collections/$collectionId/documents"; -// }, -// 'create' => function (string $databaseId, string $collectionId, array $args) { -// return "/v1/databases/$databaseId/collections/$collectionId/documents"; -// }, -// 'read' => function (string $databaseId, string $collectionId, array $args) { -// return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; -// }, -// 'update' => function (string $databaseId, string $collectionId, array $args) { -// return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; -// }, -// 'delete' => function (string $databaseId, string $collectionId, array $args) { -// return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; -// }, -// ]; -// -// $params = [ -// 'list' => function (string $databaseId, string $collectionId, array $args) { -// return [ 'queries' => $args['queries']]; -// }, -// 'create' => function (string $databaseId, string $collectionId, array $args) { -// $id = $args['id'] ?? 'unique()'; -// $permissions = $args['permissions'] ?? null; -// -// unset($args['id']); -// unset($args['permissions']); -// -// // Order must be the same as the route params -// return [ -// 'databaseId' => $databaseId, -// 'documentId' => $id, -// 'collectionId' => $collectionId, -// 'data' => $args, -// 'permissions' => $permissions, -// ]; -// }, -// 'update' => function (string $databaseId, string $collectionId, array $args) { -// $documentId = $args['id']; -// $permissions = $args['permissions'] ?? null; -// -// unset($args['id']); -// unset($args['permissions']); -// -// // Order must be the same as the route params -// return [ -// 'databaseId' => $databaseId, -// 'collectionId' => $collectionId, -// 'documentId' => $documentId, -// 'data' => $args, -// 'permissions' => $permissions, -// ]; -// }, -// ]; -// -// return Schema::build( -// $utopia, -// $complexity, -// $attributes, -// $urls, -// $params, -// ); -//}, ['utopia', 'dbForProject']); -// -//App::setResource('contributors', function () { -// $path = 'app/config/contributors.json'; -// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; -// return $list; -//}); -// -//App::setResource('employees', function () { -// $path = 'app/config/employees.json'; -// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; -// return $list; -//}); -// -//App::setResource('heroes', function () { -// $path = 'app/config/heroes.json'; -// $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; -// return $list; -//}); -// -//App::setResource('gitHub', function (Cache $cache) { -// return new VcsGitHub($cache); -//}, ['cache']); -// -//App::setResource('requestTimestamp', function ($request) { -// //TODO: Move this to the Request class itself -// $timestampHeader = $request->getHeader('x-appwrite-timestamp'); -// $requestTimestamp = null; -// if (!empty($timestampHeader)) { -// try { -// $requestTimestamp = new \DateTime($timestampHeader); -// } catch (\Throwable $e) { -// throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value'); -// } -// } -// return $requestTimestamp; -//}, ['request']); -//App::setResource('plan', function (array $plan = []) { -// return []; -//}); +if (\file_exists(__DIR__ . '/../vendor/autoload.php')) { + require_once __DIR__ . '/../vendor/autoload.php'; +} + +use Ahc\Jwt\JWT; +use Ahc\Jwt\JWTException; +use Appwrite\Auth\Auth; +use Appwrite\Auth\Authentication; +use Appwrite\Auth\MFA\Type\TOTP; +use Appwrite\Event\Audit; +use Appwrite\Event\Build; +use Appwrite\Event\Certificate; +use Appwrite\Event\Database as EventDatabase; +use Appwrite\Event\Delete; +use Appwrite\Event\Event; +use Appwrite\Event\Func; +use Appwrite\Event\Mail; +use Appwrite\Event\Messaging; +use Appwrite\Event\Migration; +use Appwrite\Event\Usage; +use Appwrite\Extend\Exception; +use Appwrite\GraphQL\Promises\Adapter\Swoole; +use Appwrite\GraphQL\Schema; +use Appwrite\Hooks\Hooks; +use Appwrite\Network\Validator\Origin; +use Appwrite\URL\URL; +use Appwrite\Utopia\Queue\Connections; +use Appwrite\Utopia\Response\Models; +use MaxMind\Db\Reader; +use PHPMailer\PHPMailer\PHPMailer; +use Swoole\Database\PDOConfig; +use Swoole\Database\PDOPool; +use Swoole\Database\RedisConfig; +use Swoole\Database\RedisPool; +use Utopia\Abuse\Abuse; +use Utopia\Abuse\Adapters\Database\TimeLimit; +use Utopia\Cache\Adapter\Redis as CacheRedis; +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\DI\Container; +use Utopia\DI\Dependency; +use Utopia\Domains\Domain; +use Utopia\Domains\Validator\PublicDomain; +use Utopia\DSN\DSN; +use Utopia\Http\Http; +use Utopia\Http\Request; +use Utopia\Http\Response; +use Utopia\Http\Validator\Hostname; +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\Queue; +use Utopia\Queue\Connection; +use Utopia\Registry\Registry; +use Utopia\Storage\Device; +use Utopia\Storage\Device\Backblaze; +use Utopia\Storage\Device\DOSpaces; +use Utopia\Storage\Device\Linode; +use Utopia\Storage\Device\Local; +use Utopia\Storage\Device\S3; +use Utopia\Storage\Device\Wasabi; +use Utopia\Storage\Storage; +use Utopia\System\System; +use Utopia\VCS\Adapter\Git\GitHub; + +require_once __DIR__ . '/init/constants.php'; +require_once __DIR__ . '/init/config.php'; +require_once __DIR__ . '/init/locale.php'; +require_once __DIR__ . '/init/database/filters.php'; +require_once __DIR__ . '/init/database/formats.php'; + +ini_set('memory_limit', '-1'); +ini_set('display_errors', 1); +ini_set('display_startup_errors', 1); +ini_set('default_socket_timeout', -1); +error_reporting(E_ALL); + +global $http, $container, $global; + +Http::setMode(System::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION)); + +if (!Http::isProduction()) { + // Allow specific domains to skip public domain validation in dev environment + // Useful for existing tests involving webhooks + PublicDomain::allow(['request-catcher']); +} + + +function getDevice($root): Device +{ + $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); + + if (!empty($connection)) { + $acl = 'private'; + $device = Storage::DEVICE_LOCAL; + $accessKey = ''; + $accessSecret = ''; + $bucket = ''; + $region = ''; + + try { + $dsn = new DSN($connection); + $device = $dsn->getScheme(); + $accessKey = $dsn->getUser() ?? ''; + $accessSecret = $dsn->getPassword() ?? ''; + $bucket = $dsn->getPath() ?? ''; + $region = $dsn->getParam('region'); + } catch (\Throwable $e) { + Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); + } + + switch ($device) { + case Storage::DEVICE_S3: + return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case STORAGE::DEVICE_DO_SPACES: + return new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_BACKBLAZE: + return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_LINODE: + return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_WASABI: + return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_LOCAL: + default: + return new Local($root); + } + } else { + switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { + case Storage::DEVICE_LOCAL: + default: + return new Local($root); + case Storage::DEVICE_S3: + $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); + $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); + $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); + $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); + $s3Acl = 'private'; + return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); + case Storage::DEVICE_DO_SPACES: + $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); + $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); + $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); + $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); + $doSpacesAcl = 'private'; + return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); + 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); + } + } +} + +$container = new Container(); +$global = new Registry(); + +$global->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) { + Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage()); + // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables + $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; + } + + $logger = new Logger($adapter); + $logger->setSample(0.4); + return $logger; +}); + +$global->set('geodb', function () { + /** + * @disregard P1009 Undefined type + */ + return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb'); +}); + +$global->set('hooks', function () { + return new Hooks(); +}); + +$global->set( + 'pools', + (function () { + $fallbackForDB = 'db_main=' . URL::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=' . URL::unparse([ + 'scheme' => 'redis', + 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), + 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), + 'user' => System::getEnv('_APP_REDIS_USER', ''), + 'pass' => System::getEnv('_APP_REDIS_PASS', ''), + ]); + + $connections = [ + 'console' => [ + 'type' => 'database', + 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), + 'multiple' => false, + 'schemes' => ['mariadb', 'mysql'], + ], + 'database' => [ + 'type' => 'database', + 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), + 'multiple' => true, + 'schemes' => ['mariadb', 'mysql'], + ], + 'queue' => [ + 'type' => 'queue', + 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'pubsub' => [ + 'type' => 'pubsub', + 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'cache' => [ + 'type' => 'cache', + 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), + 'multiple' => true, + 'schemes' => ['redis'], + ], + ]; + + $pools = []; + $poolSize = (int)System::getEnv('_APP_POOL_CLIENTS', 64); + + foreach ($connections as $key => $connection) { + $dsns = $connection['dsns'] ?? ''; + $multiple = $connection['multiple'] ?? false; + $schemes = $connection['schemes'] ?? []; + $dsns = explode(',', $connection['dsns'] ?? ''); + $config = []; + + foreach ($dsns as &$dsn) { + $dsn = explode('=', $dsn); + $name = ($multiple) ? $key . '_' . $dsn[0] : $key; + $config[] = $name; + $dsn = $dsn[1] ?? ''; + + if (empty($dsn)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); + } + + $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 accross connection types like database, cache, queue, etc. + * + * Resource assignment to an adapter will happen below. + */ + switch ($dsnScheme) { + case 'mysql': + case 'mariadb': + $pool = new PDOPool( + (new PDOConfig()) + ->withHost($dsnHost) + ->withPort($dsnPort) + ->withDbName($dsnDatabase) + ->withCharset('utf8mb4') + ->withUsername($dsnUser) + ->withPassword($dsnPass) + ->withOptions([ + // No need to set PDO::ATTR_ERRMODE it is overwitten in PDOProxy + // 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, + PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, + + ]), + $poolSize + ); + break; + case 'redis': + $pool = new RedisPool( + (new RedisConfig()) + ->withHost($dsnHost) + ->withPort((int)$dsnPort) + ->withAuth($dsnPass ?? ''), + $poolSize + ); + break; + + default: + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid scheme"); + } + + $pools['pools-' . $key . '-' . $name] = [ + 'pool' => $pool, + 'dsn' => $dsn, + ]; + } + + Config::setParam('pools-' . $key, $config); + } + + return function () use ($pools): array { + return $pools; + }; + })() +); + +$global->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; +}); + +$global->set('promiseAdapter', function () { + return new Swoole(); +}); + +$global->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() + ); +}); + +// Autoload +class_exists(JWT::class, true); +class_exists(DSN::class, true); +class_exists(Log::class, true); +class_exists(TOTP::class, true); +class_exists(Mail::class, true); +class_exists(Func::class, true); +class_exists(Cache::class, true); +class_exists(Abuse::class, true); +class_exists(MySQL::class, true); +class_exists(Event::class, true); +class_exists(Audit::class, true); +class_exists(Usage::class, true); +class_exists(Local::class, true); +class_exists(Build::class, true); +class_exists(Locale::class, true); +class_exists(Delete::class, true); +class_exists(GitHub::class, true); +class_exists(Schema::class, true); +class_exists(Domain::class, true); +class_exists(Console::class, true); +class_exists(Request::class, true); +class_exists(MariaDB::class, true); +class_exists(Document::class, true); +class_exists(Sharding::class, true); +class_exists(Database::class, true); +class_exists(Hostname::class, true); +class_exists(TimeLimit::class, true); +class_exists(Migration::class, true); +class_exists(Messaging::class, true); +class_exists(CacheRedis::class, true); +class_exists(Connections::class, true); +class_exists(Certificate::class, true); +class_exists(EventDatabase::class, true); +class_exists(Authorization::class, true); +class_exists(Authentication::class, true); +class_exists(Queue\Connection\Redis::class, true); + +$log = new Dependency(); +$mode = new Dependency(); +$user = new Dependency(); +$plan = new Dependency(); +$pools = new Dependency(); +$geodb = new Dependency(); +$cache = new Dependency(); +$pools = new Dependency(); +$queue = new Dependency(); +$hooks = new Dependency(); +$logger = new Dependency(); +$locale = new Dependency(); +$schema = new Dependency(); +$github = new Dependency(); +$session = new Dependency(); +$console = new Dependency(); +$project = new Dependency(); +$clients = new Dependency(); +$servers = new Dependency(); +$registry = new Dependency(); +$connections = new Dependency(); +$localeCodes = new Dependency(); +$getProjectDB = new Dependency(); +$dbForProject = new Dependency(); +$dbForConsole = new Dependency(); +$queueForUsage = new Dependency(); +$queueForMails = new Dependency(); +$authorization = new Dependency(); +$authentication = new Dependency(); +$queueForBuilds = new Dependency(); +$deviceForLocal = new Dependency(); +$deviceForFiles = new Dependency(); +$queueForEvents = new Dependency(); +$queueForAudits = new Dependency(); +$promiseAdapter = new Dependency(); +$schemaVariable = new Dependency(); +$deviceForBuilds = new Dependency(); +$queueForDeletes = new Dependency(); +$requestTimestamp = new Dependency(); +$queueForDatabase = new Dependency(); +$queueForMessaging = new Dependency(); +$queueForFunctions = new Dependency(); +$queueForMigrations = new Dependency(); +$deviceForFunctions = new Dependency(); +$passwordsDictionary = new Dependency(); +$queueForCertificates = new Dependency(); + + +$plan + ->setName('plan') + ->setCallback(fn () => []); + +$mode + ->setName('mode') + ->inject('request') + ->setCallback(function (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)); + }); + +$user + ->setName('user') + ->inject('mode') + ->inject('project') + ->inject('console') + ->inject('request') + ->inject('response') + ->inject('dbForProject') + ->inject('dbForConsole') + ->inject('authorization') + ->inject('authentication') + ->setCallback(function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization, Authentication $authentication) { + $authorization->setDefaultStatus(true); + $authentication->setCookieName('a_session_' . $project->getId()); + + if (APP_MODE_ADMIN === $mode) { + $authentication->setCookieName('a_session_' . $console->getId()); + } + + $session = Auth::decodeSession( + $request->getCookie( + $authentication->getCookieName(), // Get sessions + $request->getCookie($authentication->getCookieName() . '_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[$authentication->getCookieName()])) ? $fallback[$authentication->getCookieName()] : '')); + } + + $authentication->setUnique($session['id'] ?? ''); + $authentication->setSecret($session['secret'] ?? ''); + + if (APP_MODE_ADMIN !== $mode) { + if ($project->isEmpty()) { + $user = new Document([]); + } else { + if ($project->getId() === 'console') { + $user = $dbForConsole->getDocument('users', $authentication->getUnique()); + } else { + $user = $dbForProject->getDocument('users', $authentication->getUnique()); + } + } + } else { + $user = $dbForConsole->getDocument('users', $authentication->getUnique()); + } + + if ( + $user->isEmpty() // Check a document has been found in the DB + || !Auth::sessionVerify($user->getAttribute('sessions', []), $authentication->getSecret()) + ) { // Validate user has valid login token + $user = new Document([]); + } + + if (APP_MODE_ADMIN === $mode) { + if ($user->find('teamId', $project->getAttribute('teamId'), '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) { + $request->removeHeader('x-appwrite-jwt'); + 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([]); + } + } + } + + // Adds logs to database queries + $dbForProject->setMetadata('user', $user->getId()); + $dbForConsole->setMetadata('user', $user->getId()); + + return $user; + }); + + +$session + ->setName('session') + ->inject('user') + ->inject('project') + ->inject('authorization') + ->inject('authentication') + ->setCallback(function (Document $user, Document $project, Authorization $authorization, Authentication $authentication) { + if ($user->isEmpty()) { + return; + } + + $sessions = $user->getAttribute('sessions', []); + $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret(), $authDuration); + + if (!$sessionId) { + return; + } + + foreach ($sessions as $session) { + if ($sessionId === $session->getId()) { + return $session; + } + } + + return; + }); + +$console + ->setName('console') + ->setCallback(function () { + return new Document([ + '$id' => ID::custom('console'), + '$internalId' => ID::custom('console'), + 'name' => 'Appwrite', + '$collection' => ID::custom('projects'), + 'description' => 'Appwrite core engine', + 'logo' => '', + 'teamId' => -1, + 'webhooks' => [], + 'keys' => [], + 'platforms' => [ + [ + '$collection' => ID::custom('platforms'), + 'name' => 'Localhost', + 'type' => Origin::CLIENT_TYPE_WEB, + 'hostname' => 'localhost', + ], // Current host is added on app init + ], + 'legalName' => '', + 'legalCountry' => '', + 'legalState' => '', + 'legalCity' => '', + 'legalAddress' => '', + 'legalTaxId' => '', + 'auths' => [ + 'mockNumbers' => [], + 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', + 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user + 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds + 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' + ], + 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], + 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], + 'oAuthProviders' => [ + 'githubEnabled' => true, + 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), + 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') + ], + ]); + }); + + +$project + ->setName('project') + ->inject('dbForConsole') + ->inject('request') + ->inject('console') + ->inject('authorization') + ->setCallback(function (Database $dbForConsole, Request $request, Document $console, Authorization $authorization) { + $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); + + if (empty($projectId) || $projectId === 'console') { + return $console; + } + + $project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + + return $project; + }); + +$pools + ->setName('pools') + ->inject('registry') + ->setCallback(function (Registry $registry) { + return $registry->get('pools'); + }); + +$dbForProject + ->setName('dbForProject') + ->inject('cache') + ->inject('pools') + ->inject('project') + ->inject('dbForConsole') + ->inject('authorization') + ->inject('connections') + ->setCallback(function (Cache $cache, array $pools, Document $project, Database $dbForConsole, Authorization $authorization, Connections $connections) { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; + $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($connectionDsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($connectionDsn->getPath()); + + $database = new Database($adapter, $cache); + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + if ($dsn->getHost() === DATABASE_SHARED_TABLES) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + $database->setAuthorization($authorization); + return $database; + }); + +$dbForConsole + ->setName('dbForConsole') + ->inject('pools') + ->inject('cache') + ->inject('authorization') + ->inject('connections') + ->setCallback(function (array $pools, Cache $cache, Authorization $authorization, Connections $connections): Database { + $pool = $pools['pools-console-console']['pool']; + $dsn = $pools['pools-console-console']['dsn']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); + + $database = new Database($adapter, $cache); + $database->setAuthorization($authorization); + + $database + ->setNamespace('_console') + ->setMetadata('host', \gethostname()) + ->setMetadata('project', 'console') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + return $database; + }); + +$cache + ->setName('cache') + ->inject('pools') + ->inject('connections') + ->setCallback(function (array $pools, Connections $connections) { + $adapters = []; + $databases = Config::getParam('pools-cache'); + + foreach ($databases as $database) { + $pool = $pools['pools-cache-' . $database]['pool']; + $dsn = $pools['pools-cache-' . $database]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + + $adapters[] = new CacheRedis($connection); + } + + return new Cache(new Sharding($adapters)); + }); + +$authorization + ->setName('authorization') + ->setCallback(function (): Authorization { + return new Authorization(); + }); + +$authentication + ->setName('authentication') + ->setCallback(function (): Authentication { + return new Authentication(); + }); + +$registry + ->setName('registry') + ->setCallback(function () use (&$global): Registry { + return $global; + }); + +$pools + ->setName('pools') + ->inject('registry') + ->setCallback(function (Registry $registry) { + return $registry->get('pools'); + }); + +$logger + ->setName('logger') + ->inject('registry') + ->setCallback(function (Registry $registry) { + return $registry->get('logger'); + }); + +$log + ->setName('log') + ->setCallback(function () { + return new Log(); + }); + +$connections + ->setName('connections') + ->setCallback(function () { + return new Connections(); + }); + +$locale + ->setName('locale') + ->setCallback(fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); + +$localeCodes + ->setName('localeCodes') + ->setCallback(fn () => array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []))); + +$queue + ->setName('queue') + ->inject('pools') + ->inject('connections') + ->setCallback(function (array $pools, Connections $connections) { + $pool = $pools['pools-queue-queue']['pool']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + return new Queue\Connection\Redis($connection); + }); + +$queueForMessaging + ->setName('queueForMessaging') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Messaging($queue); + }); + +$queueForMails + ->setName('queueForMails') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Mail($queue); + }); + +$queueForBuilds + ->setName('queueForBuilds') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Build($queue); + }); + +$queueForDatabase + ->setName('queueForDatabase') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new EventDatabase($queue); + }); + +$queueForDeletes + ->setName('queueForDeletes') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Delete($queue); + }); + +$queueForEvents + ->setName('queueForEvents') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Event($queue); + }); + +$queueForAudits + ->setName('queueForAudits') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Audit($queue); + }); + +$queueForFunctions + ->setName('queueForFunctions') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Func($queue); + }); + +$queueForUsage + ->setName('queueForUsage') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Usage($queue); + }); + +$queueForCertificates + ->setName('queueForCertificates') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Certificate($queue); + }); + +$queueForMigrations + ->setName('queueForMigrations') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Migration($queue); + }); + +$deviceForLocal + ->setName('deviceForLocal') + ->setCallback(function () { + return new Local(); + }); + +$deviceForFiles + ->setName('deviceForFiles') + ->inject('project') + ->setCallback(function ($project) { + return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); + }); + +$deviceForFunctions + ->setName('deviceForFunctions') + ->inject('project') + ->setCallback(function ($project) { + return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); + }); + +$deviceForBuilds + ->setName('deviceForBuilds') + ->inject('project') + ->setCallback(function ($project) { + return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); + }); + +$clients + ->setName('clients') + ->inject('request') + ->inject('console') + ->inject('project') + ->setCallback(function (Request $request, Document $console, Document $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) && isset($node['hostname']) && !empty($node['hostname'])) + ) + ); + + $clients = \array_unique( + \array_merge( + $clientsConsole, + \array_map( + fn ($node) => $node['hostname'], + \array_filter( + $project->getAttribute('platforms', []), + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) + ) + ) + ) + ); + + return $clients; + }); + +$servers + ->setName('servers') + ->setCallback(function () { + $platforms = Config::getParam('platforms'); + $server = $platforms[APP_PLATFORM_SERVER]; + + $languages = array_map(function ($language) { + return strtolower($language['name']); + }, $server['sdks']); + + return $languages; + }); + +$geodb + ->setName('geodb') + ->inject('registry') + ->setCallback(function (Registry $register) { + return $register->get('geodb'); + }); + +$passwordsDictionary + ->setName('passwordsDictionary') + ->setCallback(function () { + $content = file_get_contents(__DIR__ . '/assets/security/10k-common-passwords'); + $content = explode("\n", $content); + $content = array_flip($content); + return $content; + }); + +$hooks + ->setName('hooks') + ->inject('registry') + ->setCallback(function (Registry $registry) { + return $registry->get('hooks'); + }); + +$github + ->setName('gitHub') + ->inject('cache') + ->setCallback(function (Cache $cache) { + return new GitHub($cache); + }); + +$requestTimestamp + ->setName('requestTimestamp') + ->inject('request') + ->setCallback(function ($request) { + $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; + }); + +$getProjectDB + ->setName('getProjectDB') + ->inject('pools') + ->inject('dbForConsole') + ->inject('cache') + ->inject('authorization') + ->inject('connections') + ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $authorization, Connections $connections) { + $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools + + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $authorization, $connections): Database { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + if (isset($databases[$dsn->getHost()])) { + $database = $databases[$dsn->getHost()]; + + if ($dsn->getHost() === DATABASE_SHARED_TABLES) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + return $database; + } + + $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; + $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($connectionDsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + $adapter->setDatabase($connectionDsn->getPath()); + + $database = new Database($adapter, $cache); + $database->setAuthorization($authorization); + + $databases[$dsn->getHost()] = $database; + + if ($dsn->getHost() === DATABASE_SHARED_TABLES) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + return $database; + }; + }); + +$promiseAdapter + ->setName('promiseAdapter') + ->setCallback(function () use ($global) { + return $global->get('promiseAdapter'); + }); + +$schemaVariable + ->setName('schemaVariable') + ->setCallback(fn () => new Schema()); + +$schema + ->setName('schema') + ->inject('http') + ->inject('context') + ->inject('request') + ->inject('response') + ->inject('dbForProject') + ->inject('authorization') + ->inject('schemaVariable') + ->setCallback(function (Http $http, Container $context, Request $request, Response $response, Database $dbForProject, Authorization $authorization, $schemaVariable) { + $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, $authorization) { + $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']); + + 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']); + + return [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'documentId' => $documentId, + 'data' => $args, + 'permissions' => $permissions, + ]; + }, + ]; + + return $schemaVariable->build( + $http, + $request, + $response, + $context, + $complexity, + $attributes, + $urls, + $params, + ); + }); + +$container->set($log); +$container->set($mode); +$container->set($user); +$container->set($plan); +$container->set($cache); +$container->set($pools); +$container->set($queue); +$container->set($geodb); +$container->set($hooks); +$container->set($locale); +$container->set($schema); +$container->set($github); +$container->set($logger); +$container->set($session); +$container->set($console); +$container->set($project); +$container->set($clients); +$container->set($servers); +$container->set($registry); +$container->set($connections); +$container->set($localeCodes); +$container->set($dbForProject); +$container->set($dbForConsole); +$container->set($getProjectDB); +$container->set($queueForUsage); +$container->set($queueForMails); +$container->set($authorization); +$container->set($authentication); +$container->set($schemaVariable); +$container->set($queueForBuilds); +$container->set($queueForEvents); +$container->set($queueForAudits); +$container->set($deviceForLocal); +$container->set($deviceForFiles); +$container->set($promiseAdapter); +$container->set($queueForDeletes); +$container->set($deviceForBuilds); +$container->set($queueForDatabase); +$container->set($requestTimestamp); +$container->set($queueForMessaging); +$container->set($queueForFunctions); +$container->set($queueForMigrations); +$container->set($deviceForFunctions); +$container->set($passwordsDictionary); +$container->set($queueForCertificates); + +Models::init(); diff --git a/app/init2.php b/app/init2.php deleted file mode 100644 index faf7b8cc89..0000000000 --- a/app/init2.php +++ /dev/null @@ -1,1381 +0,0 @@ -getScheme(); - $accessKey = $dsn->getUser() ?? ''; - $accessSecret = $dsn->getPassword() ?? ''; - $bucket = $dsn->getPath() ?? ''; - $region = $dsn->getParam('region'); - } catch (\Throwable $e) { - Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); - } - - switch ($device) { - case Storage::DEVICE_S3: - return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case STORAGE::DEVICE_DO_SPACES: - return new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_BACKBLAZE: - return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LINODE: - return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_WASABI: - return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - } - } else { - switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - case Storage::DEVICE_S3: - $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); - $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); - $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); - $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); - $s3Acl = 'private'; - return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); - case Storage::DEVICE_DO_SPACES: - $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); - $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); - $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); - $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); - $doSpacesAcl = 'private'; - return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); - 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); - } - } -} - -$container = new Container(); -$global = new Registry(); - -$global->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) { - Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage()); - // Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables - $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; - } - - $logger = new Logger($adapter); - $logger->setSample(0.4); - return $logger; -}); - -$global->set('geodb', function () { - /** - * @disregard P1009 Undefined type - */ - return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb'); -}); - -$global->set('hooks', function () { - return new Hooks(); -}); - -$global->set( - 'pools', - (function () { - $fallbackForDB = 'db_main=' . URL::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=' . URL::unparse([ - 'scheme' => 'redis', - 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => System::getEnv('_APP_REDIS_USER', ''), - 'pass' => System::getEnv('_APP_REDIS_PASS', ''), - ]); - - $connections = [ - 'console' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), - 'multiple' => false, - 'schemes' => ['mariadb', 'mysql'], - ], - 'database' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), - 'multiple' => true, - 'schemes' => ['mariadb', 'mysql'], - ], - 'queue' => [ - 'type' => 'queue', - 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'pubsub' => [ - 'type' => 'pubsub', - 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'cache' => [ - 'type' => 'cache', - 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), - 'multiple' => true, - 'schemes' => ['redis'], - ], - ]; - - $pools = []; - $poolSize = (int)System::getEnv('_APP_POOL_CLIENTS', 64); - - foreach ($connections as $key => $connection) { - $dsns = $connection['dsns'] ?? ''; - $multiple = $connection['multiple'] ?? false; - $schemes = $connection['schemes'] ?? []; - $dsns = explode(',', $connection['dsns'] ?? ''); - $config = []; - - foreach ($dsns as &$dsn) { - $dsn = explode('=', $dsn); - $name = ($multiple) ? $key . '_' . $dsn[0] : $key; - $config[] = $name; - $dsn = $dsn[1] ?? ''; - - if (empty($dsn)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); - } - - $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 accross connection types like database, cache, queue, etc. - * - * Resource assignment to an adapter will happen below. - */ - switch ($dsnScheme) { - case 'mysql': - case 'mariadb': - $pool = new PDOPool( - (new PDOConfig()) - ->withHost($dsnHost) - ->withPort($dsnPort) - ->withDbName($dsnDatabase) - ->withCharset('utf8mb4') - ->withUsername($dsnUser) - ->withPassword($dsnPass) - ->withOptions([ - // No need to set PDO::ATTR_ERRMODE it is overwitten in PDOProxy - // 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, - PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, - - ]), - $poolSize - ); - break; - case 'redis': - $pool = new RedisPool( - (new RedisConfig()) - ->withHost($dsnHost) - ->withPort((int)$dsnPort) - ->withAuth($dsnPass ?? ''), - $poolSize - ); - break; - - default: - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid scheme"); - } - - $pools['pools-' . $key . '-' . $name] = [ - 'pool' => $pool, - 'dsn' => $dsn, - ]; - } - - Config::setParam('pools-' . $key, $config); - } - - return function () use ($pools): array { - return $pools; - }; - })() -); - -$global->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; -}); - -$global->set('promiseAdapter', function () { - return new Swoole(); -}); - -$global->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() - ); -}); - -// Autoload -class_exists(JWT::class, true); -class_exists(DSN::class, true); -class_exists(Log::class, true); -class_exists(TOTP::class, true); -class_exists(Mail::class, true); -class_exists(Func::class, true); -class_exists(Cache::class, true); -class_exists(Abuse::class, true); -class_exists(MySQL::class, true); -class_exists(Event::class, true); -class_exists(Audit::class, true); -class_exists(Usage::class, true); -class_exists(Local::class, true); -class_exists(Build::class, true); -class_exists(Locale::class, true); -class_exists(Delete::class, true); -class_exists(GitHub::class, true); -class_exists(Schema::class, true); -class_exists(Domain::class, true); -class_exists(Console::class, true); -class_exists(Request::class, true); -class_exists(MariaDB::class, true); -class_exists(Document::class, true); -class_exists(Sharding::class, true); -class_exists(Database::class, true); -class_exists(Hostname::class, true); -class_exists(TimeLimit::class, true); -class_exists(Migration::class, true); -class_exists(Messaging::class, true); -class_exists(CacheRedis::class, true); -class_exists(Connections::class, true); -class_exists(Certificate::class, true); -class_exists(EventDatabase::class, true); -class_exists(Authorization::class, true); -class_exists(Authentication::class, true); -class_exists(Queue\Connection\Redis::class, true); - -$log = new Dependency(); -$mode = new Dependency(); -$user = new Dependency(); -$plan = new Dependency(); -$pools = new Dependency(); -$geodb = new Dependency(); -$cache = new Dependency(); -$pools = new Dependency(); -$queue = new Dependency(); -$hooks = new Dependency(); -$logger = new Dependency(); -$locale = new Dependency(); -$schema = new Dependency(); -$github = new Dependency(); -$session = new Dependency(); -$console = new Dependency(); -$project = new Dependency(); -$clients = new Dependency(); -$servers = new Dependency(); -$registry = new Dependency(); -$connections = new Dependency(); -$localeCodes = new Dependency(); -$getProjectDB = new Dependency(); -$dbForProject = new Dependency(); -$dbForConsole = new Dependency(); -$queueForUsage = new Dependency(); -$queueForMails = new Dependency(); -$authorization = new Dependency(); -$authentication = new Dependency(); -$queueForBuilds = new Dependency(); -$deviceForLocal = new Dependency(); -$deviceForFiles = new Dependency(); -$queueForEvents = new Dependency(); -$queueForAudits = new Dependency(); -$promiseAdapter = new Dependency(); -$schemaVariable = new Dependency(); -$deviceForBuilds = new Dependency(); -$queueForDeletes = new Dependency(); -$requestTimestamp = new Dependency(); -$queueForDatabase = new Dependency(); -$queueForMessaging = new Dependency(); -$queueForFunctions = new Dependency(); -$queueForMigrations = new Dependency(); -$deviceForFunctions = new Dependency(); -$passwordsDictionary = new Dependency(); -$queueForCertificates = new Dependency(); - - -$plan - ->setName('plan') - ->setCallback(fn () => []); - -$mode - ->setName('mode') - ->inject('request') - ->setCallback(function (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)); - }); - -$user - ->setName('user') - ->inject('mode') - ->inject('project') - ->inject('console') - ->inject('request') - ->inject('response') - ->inject('dbForProject') - ->inject('dbForConsole') - ->inject('authorization') - ->inject('authentication') - ->setCallback(function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization, Authentication $authentication) { - $authorization->setDefaultStatus(true); - $authentication->setCookieName('a_session_' . $project->getId()); - - if (APP_MODE_ADMIN === $mode) { - $authentication->setCookieName('a_session_' . $console->getId()); - } - - $session = Auth::decodeSession( - $request->getCookie( - $authentication->getCookieName(), // Get sessions - $request->getCookie($authentication->getCookieName() . '_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[$authentication->getCookieName()])) ? $fallback[$authentication->getCookieName()] : '')); - } - - $authentication->setUnique($session['id'] ?? ''); - $authentication->setSecret($session['secret'] ?? ''); - - if (APP_MODE_ADMIN !== $mode) { - if ($project->isEmpty()) { - $user = new Document([]); - } else { - if ($project->getId() === 'console') { - $user = $dbForConsole->getDocument('users', $authentication->getUnique()); - } else { - $user = $dbForProject->getDocument('users', $authentication->getUnique()); - } - } - } else { - $user = $dbForConsole->getDocument('users', $authentication->getUnique()); - } - - if ( - $user->isEmpty() // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), $authentication->getSecret()) - ) { // Validate user has valid login token - $user = new Document([]); - } - - if (APP_MODE_ADMIN === $mode) { - if ($user->find('teamId', $project->getAttribute('teamId'), '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) { - $request->removeHeader('x-appwrite-jwt'); - 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([]); - } - } - } - - // Adds logs to database queries - $dbForProject->setMetadata('user', $user->getId()); - $dbForConsole->setMetadata('user', $user->getId()); - - return $user; - }); - - -$session - ->setName('session') - ->inject('user') - ->inject('project') - ->inject('authorization') - ->inject('authentication') - ->setCallback(function (Document $user, Document $project, Authorization $authorization, Authentication $authentication) { - if ($user->isEmpty()) { - return; - } - - $sessions = $user->getAttribute('sessions', []); - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; - $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret(), $authDuration); - - if (!$sessionId) { - return; - } - - foreach ($sessions as $session) { - if ($sessionId === $session->getId()) { - return $session; - } - } - - return; - }); - -$console - ->setName('console') - ->setCallback(function () { - return new Document([ - '$id' => ID::custom('console'), - '$internalId' => ID::custom('console'), - 'name' => 'Appwrite', - '$collection' => ID::custom('projects'), - 'description' => 'Appwrite core engine', - 'logo' => '', - 'teamId' => -1, - 'webhooks' => [], - 'keys' => [], - 'platforms' => [ - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Localhost', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => 'localhost', - ], // Current host is added on app init - ], - 'legalName' => '', - 'legalCountry' => '', - 'legalState' => '', - 'legalCity' => '', - 'legalAddress' => '', - 'legalTaxId' => '', - 'auths' => [ - 'mockNumbers' => [], - 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', - 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds - 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' - ], - 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], - 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], - 'oAuthProviders' => [ - 'githubEnabled' => true, - 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), - 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') - ], - ]); - }); - - -$project - ->setName('project') - ->inject('dbForConsole') - ->inject('request') - ->inject('console') - ->inject('authorization') - ->setCallback(function (Database $dbForConsole, Request $request, Document $console, Authorization $authorization) { - $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); - - if (empty($projectId) || $projectId === 'console') { - return $console; - } - - $project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - - return $project; - }); - -$pools - ->setName('pools') - ->inject('registry') - ->setCallback(function (Registry $registry) { - return $registry->get('pools'); - }); - -$dbForProject - ->setName('dbForProject') - ->inject('cache') - ->inject('pools') - ->inject('project') - ->inject('dbForConsole') - ->inject('authorization') - ->inject('connections') - ->setCallback(function (Cache $cache, array $pools, Document $project, Database $dbForConsole, Authorization $authorization, Connections $connections) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; - $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($connectionDsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($connectionDsn->getPath()); - - $database = new Database($adapter, $cache); - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - $database->setAuthorization($authorization); - return $database; - }); - -$dbForConsole - ->setName('dbForConsole') - ->inject('pools') - ->inject('cache') - ->inject('authorization') - ->inject('connections') - ->setCallback(function (array $pools, Cache $cache, Authorization $authorization, Connections $connections): Database { - $pool = $pools['pools-console-console']['pool']; - $dsn = $pools['pools-console-console']['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - - $database = new Database($adapter, $cache); - $database->setAuthorization($authorization); - - $database - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - return $database; - }); - -$cache - ->setName('cache') - ->inject('pools') - ->inject('connections') - ->setCallback(function (array $pools, Connections $connections) { - $adapters = []; - $databases = Config::getParam('pools-cache'); - - foreach ($databases as $database) { - $pool = $pools['pools-cache-' . $database]['pool']; - $dsn = $pools['pools-cache-' . $database]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapters[] = new CacheRedis($connection); - } - - return new Cache(new Sharding($adapters)); - }); - -$authorization - ->setName('authorization') - ->setCallback(function (): Authorization { - return new Authorization(); - }); - -$authentication - ->setName('authentication') - ->setCallback(function (): Authentication { - return new Authentication(); - }); - -$registry - ->setName('registry') - ->setCallback(function () use (&$global): Registry { - return $global; - }); - -$pools - ->setName('pools') - ->inject('registry') - ->setCallback(function (Registry $registry) { - return $registry->get('pools'); - }); - -$logger - ->setName('logger') - ->inject('registry') - ->setCallback(function (Registry $registry) { - return $registry->get('logger'); - }); - -$log - ->setName('log') - ->setCallback(function () { - return new Log(); - }); - -$connections - ->setName('connections') - ->setCallback(function () { - return new Connections(); - }); - -$locale - ->setName('locale') - ->setCallback(fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); - -$localeCodes - ->setName('localeCodes') - ->setCallback(fn () => array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []))); - -$queue - ->setName('queue') - ->inject('pools') - ->inject('connections') - ->setCallback(function (array $pools, Connections $connections) { - $pool = $pools['pools-queue-queue']['pool']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - return new Queue\Connection\Redis($connection); - }); - -$queueForMessaging - ->setName('queueForMessaging') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Messaging($queue); - }); - -$queueForMails - ->setName('queueForMails') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Mail($queue); - }); - -$queueForBuilds - ->setName('queueForBuilds') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Build($queue); - }); - -$queueForDatabase - ->setName('queueForDatabase') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new EventDatabase($queue); - }); - -$queueForDeletes - ->setName('queueForDeletes') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Delete($queue); - }); - -$queueForEvents - ->setName('queueForEvents') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Event($queue); - }); - -$queueForAudits - ->setName('queueForAudits') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Audit($queue); - }); - -$queueForFunctions - ->setName('queueForFunctions') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Func($queue); - }); - -$queueForUsage - ->setName('queueForUsage') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Usage($queue); - }); - -$queueForCertificates - ->setName('queueForCertificates') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Certificate($queue); - }); - -$queueForMigrations - ->setName('queueForMigrations') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Migration($queue); - }); - -$deviceForLocal - ->setName('deviceForLocal') - ->setCallback(function () { - return new Local(); - }); - -$deviceForFiles - ->setName('deviceForFiles') - ->inject('project') - ->setCallback(function ($project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); - }); - -$deviceForFunctions - ->setName('deviceForFunctions') - ->inject('project') - ->setCallback(function ($project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); - }); - -$deviceForBuilds - ->setName('deviceForBuilds') - ->inject('project') - ->setCallback(function ($project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); - }); - -$clients - ->setName('clients') - ->inject('request') - ->inject('console') - ->inject('project') - ->setCallback(function (Request $request, Document $console, Document $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) && isset($node['hostname']) && !empty($node['hostname'])) - ) - ); - - $clients = \array_unique( - \array_merge( - $clientsConsole, - \array_map( - fn ($node) => $node['hostname'], - \array_filter( - $project->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) - ) - ) - ) - ); - - return $clients; - }); - -$servers - ->setName('servers') - ->setCallback(function () { - $platforms = Config::getParam('platforms'); - $server = $platforms[APP_PLATFORM_SERVER]; - - $languages = array_map(function ($language) { - return strtolower($language['name']); - }, $server['sdks']); - - return $languages; - }); - -$geodb - ->setName('geodb') - ->inject('registry') - ->setCallback(function (Registry $register) { - return $register->get('geodb'); - }); - -$passwordsDictionary - ->setName('passwordsDictionary') - ->setCallback(function () { - $content = file_get_contents(__DIR__ . '/assets/security/10k-common-passwords'); - $content = explode("\n", $content); - $content = array_flip($content); - return $content; - }); - -$hooks - ->setName('hooks') - ->inject('registry') - ->setCallback(function (Registry $registry) { - return $registry->get('hooks'); - }); - -$github - ->setName('gitHub') - ->inject('cache') - ->setCallback(function (Cache $cache) { - return new GitHub($cache); - }); - -$requestTimestamp - ->setName('requestTimestamp') - ->inject('request') - ->setCallback(function ($request) { - $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; - }); - -$getProjectDB - ->setName('getProjectDB') - ->inject('pools') - ->inject('dbForConsole') - ->inject('cache') - ->inject('authorization') - ->inject('connections') - ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $authorization, Connections $connections) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $authorization, $connections): Database { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if (isset($databases[$dsn->getHost()])) { - $database = $databases[$dsn->getHost()]; - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; - } - - $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; - $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($connectionDsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - $adapter->setDatabase($connectionDsn->getPath()); - - $database = new Database($adapter, $cache); - $database->setAuthorization($authorization); - - $databases[$dsn->getHost()] = $database; - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; - }; - }); - -$promiseAdapter - ->setName('promiseAdapter') - ->setCallback(function () use ($global) { - return $global->get('promiseAdapter'); - }); - -$schemaVariable - ->setName('schemaVariable') - ->setCallback(fn () => new Schema()); - -$schema - ->setName('schema') - ->inject('http') - ->inject('context') - ->inject('request') - ->inject('response') - ->inject('dbForProject') - ->inject('authorization') - ->inject('schemaVariable') - ->setCallback(function (Http $http, Container $context, Request $request, Response $response, Database $dbForProject, Authorization $authorization, $schemaVariable) { - $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, $authorization) { - $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']); - - 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']); - - return [ - 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'documentId' => $documentId, - 'data' => $args, - 'permissions' => $permissions, - ]; - }, - ]; - - return $schemaVariable->build( - $http, - $request, - $response, - $context, - $complexity, - $attributes, - $urls, - $params, - ); - }); - -$container->set($log); -$container->set($mode); -$container->set($user); -$container->set($plan); -$container->set($cache); -$container->set($pools); -$container->set($queue); -$container->set($geodb); -$container->set($hooks); -$container->set($locale); -$container->set($schema); -$container->set($github); -$container->set($logger); -$container->set($session); -$container->set($console); -$container->set($project); -$container->set($clients); -$container->set($servers); -$container->set($registry); -$container->set($connections); -$container->set($localeCodes); -$container->set($dbForProject); -$container->set($dbForConsole); -$container->set($getProjectDB); -$container->set($queueForUsage); -$container->set($queueForMails); -$container->set($authorization); -$container->set($authentication); -$container->set($schemaVariable); -$container->set($queueForBuilds); -$container->set($queueForEvents); -$container->set($queueForAudits); -$container->set($deviceForLocal); -$container->set($deviceForFiles); -$container->set($promiseAdapter); -$container->set($queueForDeletes); -$container->set($deviceForBuilds); -$container->set($queueForDatabase); -$container->set($requestTimestamp); -$container->set($queueForMessaging); -$container->set($queueForFunctions); -$container->set($queueForMigrations); -$container->set($deviceForFunctions); -$container->set($passwordsDictionary); -$container->set($queueForCertificates); - -Models::init(); diff --git a/app/realtime.php b/app/realtime.php index 1020ca0cc5..0556dd093f 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -42,7 +42,7 @@ use Utopia\WebSocket\Server; global $global, $container; -require_once __DIR__ . '/init2.php'; +require_once __DIR__ . '/init.php'; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index cbdb421e8f..e03dbc790f 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -848,7 +848,7 @@ services: - MYSQL_USER=${_APP_DB_USER} - MYSQL_PASSWORD=${_APP_DB_PASS} - MARIADB_AUTO_UPGRADE=1 - command: 'mysqld --innodb-flush-method=fsync' + command: 'mysqld --innodb-flush-method=fsync --max_connections=5000' redis: image: redis:7.2.4-alpine diff --git a/app/worker.php b/app/worker.php index 4d34d6c2ab..618867b6a8 100644 --- a/app/worker.php +++ b/app/worker.php @@ -1,6 +1,6 @@ - redis-server - --maxmemory 512mb - --maxmemory-policy allkeys-lru - --maxmemory-samples 5 - ports: - - "6379:6379" - networks: - - appwrite - volumes: - - appwrite-redis:/data:rw - - # clamav: - # image: appwrite/clamav:1.2.0 - # container_name: appwrite-clamav - # networks: - # - appwrite - # volumes: - # - appwrite-uploads:/storage/uploads - - # Dev Tools Start ------------------------------------------------------------------------------------------ - # - # The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack - # - # Here is a description of the different tools and why are we using them: - # - # MailCatcher - An SMTP server. Catches all system emails and displays them in a nice UI. - # RequestCatcher - An HTTP server. Catches all system https calls and displays them using a simple HTTP API. Used to debug & tests webhooks and HTTP tasks - # Redis Insight - A nice UI for exploring Redis data - # Adminer - A nice UI for exploring MariaDB data - # GraphQl Explorer - A nice UI for exploring GraphQL API - - maildev: # used mainly for dev tests - image: appwrite/mailcatcher:1.0.0 - container_name: appwrite-mailcatcher - <<: *x-logging - ports: - - "9503:1080" - networks: - - appwrite - - request-catcher: # used mainly for dev tests - image: appwrite/requestcatcher:1.0.0 - container_name: appwrite-requestcatcher - <<: *x-logging - ports: - - "9504:5000" - networks: - - appwrite - - adminer: - image: adminer - container_name: appwrite-adminer - <<: *x-logging - restart: always - ports: - - 9506:8080 - networks: - - appwrite - - redis-insight: - image: redis/redisinsight:latest - restart: unless-stopped - networks: - - appwrite - environment: - - PHP_IDE_CONFIG=serverName=Appwrite - - REDIS_HOSTS=redis - ports: - - "8081:5540" - - graphql-explorer: - container_name: appwrite-graphql-explorer - image: appwrite/altair:0.3.0 - restart: unless-stopped - networks: - - appwrite - ports: - - "9509:3000" - environment: - - PHP_IDE_CONFIG=serverName=Appwrite - - SERVER_URL=http://localhost/v1/graphql - - # Dev Tools End ------------------------------------------------------------------------------------------ - -networks: - gateway: - name: gateway - appwrite: - name: appwrite - runtimes: - name: runtimes - -volumes: - appwrite-mariadb: - appwrite-redis: - appwrite-cache: - appwrite-uploads: - appwrite-certificates: - appwrite-functions: - appwrite-builds: - appwrite-config: diff --git a/docker-compose.yml b/docker-compose.yml index f2bbb895e5..bc2c02b62e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -960,7 +960,7 @@ services: - MYSQL_USER=${_APP_DB_USER} - MYSQL_PASSWORD=${_APP_DB_PASS} - MARIADB_AUTO_UPGRADE=1 - command: "mysqld --innodb-flush-method=fsync" + command: 'mysqld --innodb-flush-method=fsync --max_connections=5000' redis: image: redis:7.2.4-alpine diff --git a/phpunit.xml b/phpunit.xml index d2bc4610cf..90ebd4225f 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,6 +1,6 @@ Date: Thu, 5 Sep 2024 13:20:44 -0400 Subject: [PATCH 168/195] refactor: global to registry --- app/cli.php | 8 ++++---- app/http.php | 2 +- app/init.php | 26 +++++++++++++------------- app/realtime.php | 20 ++++++++++---------- app/worker.php | 4 ++-- tests/unit/Event/EventTest.php | 4 ++-- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/app/cli.php b/app/cli.php index a0f55e82cc..7c79250f4d 100644 --- a/app/cli.php +++ b/app/cli.php @@ -18,12 +18,12 @@ use Utopia\Queue\Connection; use Utopia\Registry\Registry; use Utopia\System\System; -global $global, $container; +global $registry, $container; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); /** - * @var Registry $global + * @var Registry $registry * @var Container $container */ $context = new Dependency(); @@ -39,8 +39,8 @@ $context $register ->setName('register') - ->setCallback(function () use (&$global): Registry { - return $global; + ->setCallback(function () use (&$registry): Registry { + return $registry; }); $queueForFunctions diff --git a/app/http.php b/app/http.php index 45929cf23c..320621ad33 100644 --- a/app/http.php +++ b/app/http.php @@ -23,7 +23,7 @@ use Utopia\Http\Adapter\Swoole\Server; use Utopia\Http\Http; use Utopia\System\System; -global $global, $container; +global $registry, $container; $payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing $workerNumber = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); diff --git a/app/init.php b/app/init.php index faf7b8cc89..68bba360e8 100644 --- a/app/init.php +++ b/app/init.php @@ -91,7 +91,7 @@ ini_set('display_startup_errors', 1); ini_set('default_socket_timeout', -1); error_reporting(E_ALL); -global $http, $container, $global; +global $http, $container, $registry; Http::setMode(System::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION)); @@ -185,9 +185,9 @@ function getDevice($root): Device } $container = new Container(); -$global = new Registry(); +$registry = new Registry(); -$global->set('logger', function () { +$registry->set('logger', function () { // Register error logger $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); @@ -243,18 +243,18 @@ $global->set('logger', function () { return $logger; }); -$global->set('geodb', function () { +$registry->set('geodb', function () { /** * @disregard P1009 Undefined type */ return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb'); }); -$global->set('hooks', function () { +$registry->set('hooks', function () { return new Hooks(); }); -$global->set( +$registry->set( 'pools', (function () { $fallbackForDB = 'db_main=' . URL::unparse([ @@ -398,7 +398,7 @@ $global->set( })() ); -$global->set('smtp', function () { +$registry->set('smtp', function () { $mail = new PHPMailer(true); $mail->isSMTP(); @@ -427,11 +427,11 @@ $global->set('smtp', function () { return $mail; }); -$global->set('promiseAdapter', function () { +$registry->set('promiseAdapter', function () { return new Swoole(); }); -$global->set('db', function () { +$registry->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', ''); @@ -885,8 +885,8 @@ $authentication $registry ->setName('registry') - ->setCallback(function () use (&$global): Registry { - return $global; + ->setCallback(function () use (&$registry): Registry { + return $registry; }); $pools @@ -1229,8 +1229,8 @@ $getProjectDB $promiseAdapter ->setName('promiseAdapter') - ->setCallback(function () use ($global) { - return $global->get('promiseAdapter'); + ->setCallback(function () use ($registry) { + return $registry->get('promiseAdapter'); }); $schemaVariable diff --git a/app/realtime.php b/app/realtime.php index 0556dd093f..17f5974791 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -36,10 +36,10 @@ use Utopia\WebSocket\Adapter; use Utopia\WebSocket\Server; /** - * @var Registry $global + * @var Registry $registry * @var Container $container */ -global $global, $container; +global $registry, $container; require_once __DIR__ . '/init.php'; @@ -70,8 +70,8 @@ $adapter $server = new Server($adapter); -$logError = function (Throwable $error, string $action) use ($global) { - $logger = $global->get('logger'); +$logError = function (Throwable $error, string $action) use ($registry) { + $logger = $registry->get('logger'); if ($logger && !$error instanceof Exception) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); @@ -138,7 +138,7 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum sleep(DATABASE_RECONNECT_SLEEP); } } while (true); - // TODO NOW $global->get('pools')->reclaim(); + // TODO NOW $registry->get('pools')->reclaim(); }); /** @@ -166,7 +166,7 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum } catch (Throwable $th) { call_user_func($logError, $th, "updateWorkerDocument"); } finally { - // TODO NOW $global->get('pools')->reclaim(); + // TODO NOW $registry->get('pools')->reclaim(); } }); } @@ -231,7 +231,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats 'data' => $event['data'] ])); } - // TODO NOW $global->get('pools')->reclaim(); + // TODO NOW $registry->get('pools')->reclaim(); } } /** @@ -311,7 +311,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats $realtime->unsubscribe($connection); $realtime->subscribe($projectId, $connection, $roles, $channels); - //TODO NOW $global->get('pools')->reclaim(); + //TODO NOW $registry->get('pools')->reclaim(); } } @@ -343,7 +343,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats sleep(DATABASE_RECONNECT_SLEEP); continue; } finally { - //$global->get('pools')->reclaim(); + //$registry->get('pools')->reclaim(); // TODO eldad add connections reclaim } } @@ -586,7 +586,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $co $server->close($connection, $th->getCode()); } } finally { - // TODO NOW $global->get('pools')->reclaim(); + // TODO NOW $registry->get('pools')->reclaim(); } }); diff --git a/app/worker.php b/app/worker.php index 618867b6a8..57343ce38d 100644 --- a/app/worker.php +++ b/app/worker.php @@ -21,7 +21,7 @@ use Utopia\Queue\Worker; use Utopia\Storage\Device\Local; use Utopia\System\System; -global $global, $container; +global $registry, $container; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); @@ -37,7 +37,7 @@ $deviceForLocalFiles = new Dependency(); $register ->setName('register') - ->setCallback(fn () => $global); + ->setCallback(fn () => $registry); $project ->setName('project') diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index 0aebd4c209..38534b9b16 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -67,8 +67,8 @@ class EventTest extends TestCase $this->assertEquals('eventValue2', $this->object->getParam('eventKey2')); $this->assertEquals(null, $this->object->getParam('eventKey3')); - global $global; - $pools = $global->get('pools'); + global $registry; + $pools = $registry->get('pools'); $dsn = $pools['pools-queue-queue']['dsn']; $queue = new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()); From 41bffa016564bb90663fec5a384e5f18f390e185 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:24:54 -0400 Subject: [PATCH 169/195] refactor: small change --- src/Appwrite/Specification/Format/OpenAPI3.php | 6 +----- src/Appwrite/Specification/Format/Swagger2.php | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index b107dc3ab4..c9186ee0ac 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -272,11 +272,7 @@ class OpenAPI3 extends Format $bodyRequired = []; foreach ($route->getParams() as $name => $param) { // Set params - $injections = []; - - if (isset($param['injections'])) { - $injections = array_map(fn ($injection) => $this->http->getContainer()->get($injection), $param['injections']); - } + $injections = array_map(fn ($injection) => $this->http->getContainer()->get($injection), $param['injections'] ?? []); /** @var Validator $validator */ $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $injections) : $param['validator']; diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 5773501c7e..81e0f2c41d 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -270,11 +270,7 @@ class Swagger2 extends Format ); foreach ($parameters as $name => $param) { // Set params - $injections = []; - - if (isset($param['injections'])) { - $injections = array_map(fn ($injection) => $this->http->getContainer()->get($injection), $param['injections']); - } + $injections = array_map(fn ($injection) => $this->http->getContainer()->get($injection), $param['injections'] ?? []); /** @var Validator $validator */ $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $injections) : $param['validator']; From 0ecc61fe9efabc05d3b7fe674417e39cea8bf409 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:26:26 -0400 Subject: [PATCH 170/195] refactor: import instead of FQDN --- src/Appwrite/GraphQL/Schema.php | 4 ++-- src/Appwrite/Platform/Tasks/Specs.php | 3 ++- src/Appwrite/Utopia/Response.php | 7 ++++--- tests/unit/GraphQL/BuilderTest.php | 7 ++++--- tests/unit/Utopia/ResponseTest.php | 7 ++++--- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Appwrite/GraphQL/Schema.php b/src/Appwrite/GraphQL/Schema.php index 1ffd6edf91..2b05f08aee 100644 --- a/src/Appwrite/GraphQL/Schema.php +++ b/src/Appwrite/GraphQL/Schema.php @@ -3,7 +3,7 @@ namespace Appwrite\GraphQL; use Appwrite\GraphQL\Types\Mapper; -use Appwrite\Utopia\Response; +use Appwrite\Utopia\Response\Models; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; use GraphQL\Type\Schema as GQLSchema; @@ -98,7 +98,7 @@ class Schema */ protected function api(Http $http, Request $request, UtopiaHttpResponse $response, Container $container, callable $complexity): array { - Mapper::init(Response\Models::getModels()); + Mapper::init(Models::getModels()); $mapper = new Mapper(); diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 7dfbb25786..77ae26ec95 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -6,6 +6,7 @@ use Appwrite\Specification\Format\OpenAPI3; use Appwrite\Specification\Format\Swagger2; use Appwrite\Specification\Specification; use Appwrite\Utopia\Response; +use Appwrite\Utopia\Response\Models; use Exception; use Swoole\Http\Request as SwooleHttpRequest; use Swoole\Http\Response as SwooleHttpResponse; @@ -256,7 +257,7 @@ class Specs extends Action ]; } - $models = Response\Models::getModels(); + $models = Models::getModels(); foreach ($models as $key => $value) { if ($platform !== APP_PLATFORM_CONSOLE && !$value->isPublic()) { diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 7a3ab850a3..3808b1e9f8 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -4,6 +4,7 @@ namespace Appwrite\Utopia; use Appwrite\Utopia\Fetch\BodyMultipart; use Appwrite\Utopia\Response\Filter; +use Appwrite\Utopia\Response\Models; use Exception; use JsonException; // Keep last @@ -306,7 +307,7 @@ class Response extends HttpResponse public function output(Document $document, string $model): array { $data = clone $document; - $model = Response\Models::getModel($model); + $model = Models::getModel($model); $output = []; $data = $model->filter($data); @@ -336,7 +337,7 @@ class Response extends HttpResponse if (\is_array($rule['type'])) { foreach ($rule['type'] as $type) { $condition = false; - foreach (Response\Models::getModel($type)->conditions as $attribute => $val) { + foreach (Models::getModel($type)->conditions as $attribute => $val) { $condition = $item->getAttribute($attribute) === $val; if (!$condition) { break; @@ -351,7 +352,7 @@ class Response extends HttpResponse $ruleType = $rule['type']; } - if (!array_key_exists($ruleType, Response\Models::getModels())) { + if (!array_key_exists($ruleType, Models::getModels())) { throw new Exception('Missing model for rule: ' . $ruleType); } diff --git a/tests/unit/GraphQL/BuilderTest.php b/tests/unit/GraphQL/BuilderTest.php index 348b7ac4a4..f11045f318 100644 --- a/tests/unit/GraphQL/BuilderTest.php +++ b/tests/unit/GraphQL/BuilderTest.php @@ -4,6 +4,7 @@ namespace Tests\Unit\GraphQL; use Appwrite\GraphQL\Types\Mapper; use Appwrite\Utopia\Response; +use Appwrite\Utopia\Response\Models; use PHPUnit\Framework\TestCase; use Swoole\Http\Response as SwooleResponse; use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse; @@ -14,9 +15,9 @@ class BuilderTest extends TestCase public function setUp(): void { - Response\Models::init(); + Models::init(); $this->response = new Response(new UtopiaSwooleResponse(new SwooleResponse())); - Mapper::init(Response\Models::getModels()); + Mapper::init(Models::getModels()); } /** @@ -24,7 +25,7 @@ class BuilderTest extends TestCase */ public function testCreateTypeMapping() { - $model = Response\Models::getModel(Response::MODEL_COLLECTION); + $model = Models::getModel(Response::MODEL_COLLECTION); $type = Mapper::model(\ucfirst($model->getType())); $this->assertEquals('Collection', $type->name); } diff --git a/tests/unit/Utopia/ResponseTest.php b/tests/unit/Utopia/ResponseTest.php index 9ab61cb4f0..1cd02beb2c 100644 --- a/tests/unit/Utopia/ResponseTest.php +++ b/tests/unit/Utopia/ResponseTest.php @@ -3,6 +3,7 @@ namespace Tests\Unit\Utopia; use Appwrite\Utopia\Response; +use Appwrite\Utopia\Response\Models; use Exception; use PHPUnit\Framework\TestCase; use Swoole\Http\Response as SwooleResponse; @@ -18,9 +19,9 @@ class ResponseTest extends TestCase public function setUp(): void { $this->response = new Response(new UtopiaSwooleResponse(new SwooleResponse())); - Response\Models::setModel(new Single()); - Response\Models::setModel(new Lists()); - Response\Models::setModel(new Nested()); + Models::setModel(new Single()); + Models::setModel(new Lists()); + Models::setModel(new Nested()); } public function testFilters(): void From 8e145a951914ad46a759d660849cf6288fc84738 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:03:59 -0400 Subject: [PATCH 171/195] refactor: global to registry --- app/init.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/init.php b/app/init.php index 68bba360e8..2d458330f9 100644 --- a/app/init.php +++ b/app/init.php @@ -504,7 +504,7 @@ $console = new Dependency(); $project = new Dependency(); $clients = new Dependency(); $servers = new Dependency(); -$registry = new Dependency(); +$register = new Dependency(); $connections = new Dependency(); $localeCodes = new Dependency(); $getProjectDB = new Dependency(); @@ -883,7 +883,7 @@ $authentication return new Authentication(); }); -$registry +$register ->setName('registry') ->setCallback(function () use (&$registry): Registry { return $registry; @@ -1350,7 +1350,7 @@ $container->set($console); $container->set($project); $container->set($clients); $container->set($servers); -$container->set($registry); +$container->set($register); $container->set($connections); $container->set($localeCodes); $container->set($dbForProject); From 439e42e1ab76b1915e983ddf77bc47364cdb57fd Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:49:37 -0400 Subject: [PATCH 172/195] review: addressing --- app/controllers/general.php | 8 ++------ app/init.php | 2 +- mariadb-config.cnf | 2 -- src/Appwrite/Auth/Validator/MockNumber.php | 3 ++- tests/e2e/Services/Databases/DatabasesBase.php | 9 +-------- 5 files changed, 6 insertions(+), 18 deletions(-) delete mode 100644 mariadb-config.cnf diff --git a/app/controllers/general.php b/app/controllers/general.php index 5b79709176..d37b4b6ef4 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -731,9 +731,8 @@ Http::error() Console::error('[Error] File: ' . $file); Console::error('[Error] Line: ' . $line); } - switch ($class) { - case 'Utopia\Servers\Exception': + case 'Utopia\Http\Exception': $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); switch ($code) { case 400: @@ -958,7 +957,7 @@ Http::get('/humans.txt') ->inject('geodb') ->inject('route') ->inject('authorization') - ->action(function (Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, ?Route $route, Authorization $authorization) { + ->action(function (Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Route $route, Authorization $authorization) { $host = $request->getHostname() ?? ''; $mainDomain = System::getEnv('_APP_DOMAIN', ''); @@ -966,9 +965,6 @@ Http::get('/humans.txt') $template = new View(__DIR__ . '/../views/general/humans.phtml'); $response->text($template->render(false)); } else { - if (is_null($route)) { - $route = new Route($request->getMethod(), $request->getURI()); - } router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization); } }); diff --git a/app/init.php b/app/init.php index 2d458330f9..49cb7e8920 100644 --- a/app/init.php +++ b/app/init.php @@ -307,7 +307,7 @@ $registry->set( ]; $pools = []; - $poolSize = (int)System::getEnv('_APP_POOL_CLIENTS', 64); + $poolSize = (int)System::getEnv('_APP_POOL_SIZE', 64); foreach ($connections as $key => $connection) { $dsns = $connection['dsns'] ?? ''; diff --git a/mariadb-config.cnf b/mariadb-config.cnf deleted file mode 100644 index 601dccc285..0000000000 --- a/mariadb-config.cnf +++ /dev/null @@ -1,2 +0,0 @@ -[mysqld] -max_connections=1024 \ No newline at end of file diff --git a/src/Appwrite/Auth/Validator/MockNumber.php b/src/Appwrite/Auth/Validator/MockNumber.php index af976f77f8..b92bb3b6bf 100644 --- a/src/Appwrite/Auth/Validator/MockNumber.php +++ b/src/Appwrite/Auth/Validator/MockNumber.php @@ -3,6 +3,7 @@ namespace Appwrite\Auth\Validator; use Utopia\Http\Validator; +use Utopia\Http\Validator\Text; /** * MockNumber. @@ -45,7 +46,7 @@ class MockNumber extends Validator return false; } - $otp = new Validator\Text(6, 6, Validator\Text::NUMBERS); + $otp = new Text(6, 6, Validator\Text::NUMBERS); if (!$otp->isValid($value['otp'])) { $this->message = 'Invalid OTP. Please make sure the OTP is a 6 digit number'; return false; diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index d92e814bdc..61932fc92a 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -2868,14 +2868,7 @@ trait DatabasesBase $this->assertEquals('Invalid document structure: Attribute "floatRange" has invalid format. Value must be a valid range between 1 and 1', $badFloatRange['body']['message']); $this->assertEquals('Invalid document structure: Attribute "probability" has invalid format. Value must be a valid range between 0 and 1', $badProbability['body']['message']); $this->assertEquals('Invalid document structure: Attribute "upperBound" has invalid format. Value must be a valid range between -9,223,372,036,854,775,808 and 10', $tooHigh['body']['message']); - - $max = '9,223,372,036,854,775,807'; - - if (PHP_VERSION_ID < 80300) { - $max = '9,223,372,036,854,775,808'; - } - - $this->assertEquals('Invalid document structure: Attribute "lowerBound" has invalid format. Value must be a valid range between 5 and '.$max, $tooLow['body']['message']); + $this->assertEquals('Invalid document structure: Attribute "lowerBound" has invalid format. Value must be a valid range between 5 and '.\number_format(PHP_INT_MAX), $tooLow['body']['message']); } /** From 557eefe2c4e5938ecc908eeee462715a93fae3a9 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 5 Sep 2024 18:06:41 -0400 Subject: [PATCH 173/195] review: addressing --- app/controllers/general.php | 4 ---- app/controllers/shared/api/auth.php | 2 +- app/realtime.php | 17 +++++++++-------- app/worker.php | 2 +- src/Appwrite/Auth/Validator/MockNumber.php | 2 +- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index d37b4b6ef4..4445353a04 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1026,10 +1026,6 @@ Http::wildcard() throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND); }); -foreach (Config::getParam('services', []) as $service) { - //include_once $service['controller']; -} - include_once 'mock.php'; include_once 'shared/api.php'; include_once 'shared/api/auth.php'; diff --git a/app/controllers/shared/api/auth.php b/app/controllers/shared/api/auth.php index a2bda7cff7..bea9c9752b 100644 --- a/app/controllers/shared/api/auth.php +++ b/app/controllers/shared/api/auth.php @@ -55,7 +55,7 @@ Http::init() return; } - if (str_contains($route->getPath(), '/v1/graphql')) { // Skip for graphQL recursive call + if ($route->getLabel('sdk.namespace','') === 'graphql') { // Skip for graphQL recursive call return; } diff --git a/app/realtime.php b/app/realtime.php index 17f5974791..4d2cceae9a 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -138,7 +138,7 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum sleep(DATABASE_RECONNECT_SLEEP); } } while (true); - // TODO NOW $registry->get('pools')->reclaim(); + ($container->get('connections'))->reclaim(); }); /** @@ -166,7 +166,8 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum } catch (Throwable $th) { call_user_func($logError, $th, "updateWorkerDocument"); } finally { - // TODO NOW $registry->get('pools')->reclaim(); + ($container->get('connections'))->reclaim(); + $container->refresh('dbForConsole'); } }); } @@ -231,7 +232,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats 'data' => $event['data'] ])); } - // TODO NOW $registry->get('pools')->reclaim(); + ($container->get('connections'))->reclaim(); + $container->refresh('dbForConsole'); } } /** @@ -311,7 +313,6 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats $realtime->unsubscribe($connection); $realtime->subscribe($projectId, $connection, $roles, $channels); - //TODO NOW $registry->get('pools')->reclaim(); } } @@ -343,8 +344,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats sleep(DATABASE_RECONNECT_SLEEP); continue; } finally { - //$registry->get('pools')->reclaim(); - // TODO eldad add connections reclaim + ($container->get('connections'))->reclaim(); + $container->refresh('dbForConsole'); } } @@ -369,7 +370,6 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, Console::info("Connection open (user: {$connection})"); try { - $dbForConsole = $container->get('dbForConsole'); /** @var Document $project */ $project = $container->refresh('project')->get('project'); @@ -586,7 +586,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $co $server->close($connection, $th->getCode()); } } finally { - // TODO NOW $registry->get('pools')->reclaim(); + ($container->get('connections'))->reclaim(); + $container->refresh('dbForConsole'); } }); diff --git a/app/worker.php b/app/worker.php index 57343ce38d..80d027fbc2 100644 --- a/app/worker.php +++ b/app/worker.php @@ -104,7 +104,7 @@ $container->set($executionRetention); $container->set($deviceForLocalFiles); $platform = new Appwrite(); -$args = $_SERVER['argv']; +$args = $platform->getEnv('argv'); if (!isset($args[1])) { Console::error('Missing worker name'); diff --git a/src/Appwrite/Auth/Validator/MockNumber.php b/src/Appwrite/Auth/Validator/MockNumber.php index b92bb3b6bf..141a469724 100644 --- a/src/Appwrite/Auth/Validator/MockNumber.php +++ b/src/Appwrite/Auth/Validator/MockNumber.php @@ -46,7 +46,7 @@ class MockNumber extends Validator return false; } - $otp = new Text(6, 6, Validator\Text::NUMBERS); + $otp = new Text(6, 6, Text::NUMBERS); if (!$otp->isValid($value['otp'])) { $this->message = 'Invalid OTP. Please make sure the OTP is a 6 digit number'; return false; From 48264673da1e56c10a0d0aaf43303a3f0eb66775 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 5 Sep 2024 18:14:06 -0400 Subject: [PATCH 174/195] fix: adding experimental logger --- app/controllers/general.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/app/controllers/general.php b/app/controllers/general.php index e6fa6b2129..646f190597 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -780,6 +780,31 @@ Http::error() } else { $publish = $error->getCode() === 0 || $error->getCode() >= 500; } + if ($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(); + + if (!empty($providerName) && $providerName === 'sentry') { + $key = $loggingProvider->getPassword(); + $projectId = $loggingProvider->getUser() ?? ''; + $host = 'https://' . $loggingProvider->getHost(); + + $adapter = new Sentry($projectId, $key, $host); + $logger = new Logger($adapter); + $logger->setSample(0.04); + $publish = true; + } else { + throw new \Exception('Invalid experimental logging provider'); + } + } catch (\Throwable $th) { + Console::warning('Failed to initialize logging provider: ' . $th->getMessage()); + } + } if ($publish && $project->getId() !== 'console') { if (!Auth::isPrivilegedUser($authorization->getRoles())) { From 0d41b084ea1ff915e9e8028286b1b353f3b9fe52 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 5 Sep 2024 18:17:16 -0400 Subject: [PATCH 175/195] chore: formatting and packages updates --- app/controllers/shared/api/auth.php | 2 +- app/init.php | 10 +- composer.lock | 529 ++++++++++++++++++---------- 3 files changed, 348 insertions(+), 193 deletions(-) diff --git a/app/controllers/shared/api/auth.php b/app/controllers/shared/api/auth.php index bea9c9752b..224dcb3392 100644 --- a/app/controllers/shared/api/auth.php +++ b/app/controllers/shared/api/auth.php @@ -55,7 +55,7 @@ Http::init() return; } - if ($route->getLabel('sdk.namespace','') === 'graphql') { // Skip for graphQL recursive call + if ($route->getLabel('sdk.namespace', '') === 'graphql') { // Skip for graphQL recursive call return; } diff --git a/app/init.php b/app/init.php index 1e56c25bfb..49cb7e8920 100644 --- a/app/init.php +++ b/app/init.php @@ -646,13 +646,13 @@ $user $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([]); + $jwtSessionId = $payload['sessionId'] ?? ''; + if (!empty($jwtSessionId)) { + if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token + $user = new Document([]); + } } } - } // Adds logs to database queries $dbForProject->setMetadata('user', $user->getId()); diff --git a/composer.lock b/composer.lock index 469eb531c1..7a4729007b 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": "eba741eab8bb748ed684c32711d472df", + "content-hash": "6017f815da50b7d4dabad66386e013e3", "packages": [ { "name": "adhocore/jwt", @@ -1429,16 +1429,16 @@ }, { "name": "utopia-php/abuse", - "version": "0.42.0", + "version": "0.44.0", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "08cf17e7f4fd213966c8d8702e406f2269244f0f" + "reference": "cd37bba23778f3a0dc54b4ba6ffb36d4d91ed8f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/08cf17e7f4fd213966c8d8702e406f2269244f0f", - "reference": "08cf17e7f4fd213966c8d8702e406f2269244f0f", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/cd37bba23778f3a0dc54b4ba6ffb36d4d91ed8f1", + "reference": "cd37bba23778f3a0dc54b4ba6ffb36d4d91ed8f1", "shasum": "" }, "require": { @@ -1446,7 +1446,7 @@ "ext-pdo": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "0.52.*" + "utopia-php/database": "0.54.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1474,27 +1474,28 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.42.0" + "source": "https://github.com/utopia-php/abuse/tree/0.44.0" }, - "time": "2024-08-21T08:24:01+00:00" + "time": "2024-09-05T16:09:32+00:00" }, { "name": "utopia-php/analytics", - "version": "0.10.2", + "version": "0.13.0", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + "reference": "3dace02af5d4190623f88fb6e02f5559a99f14c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/3dace02af5d4190623f88fb6e02f5559a99f14c4", + "reference": "3dace02af5d4190623f88fb6e02f5559a99f14c4", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "^0.15.0" + "utopia-php/cli": "0.19.*", + "utopia-php/system": "0.8.*" }, "require-dev": { "laravel/pint": "dev-main", @@ -1520,27 +1521,27 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + "source": "https://github.com/utopia-php/analytics/tree/0.13.0" }, - "time": "2023-03-22T12:01:09+00:00" + "time": "2024-09-05T16:19:26+00:00" }, { "name": "utopia-php/audit", - "version": "0.42.0", + "version": "0.44.0", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "9dc168470625bcf11ff8cd9ab5660db09129f618" + "reference": "69eee24e4d6cb8fdae31235d80b9a46b18092139" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/9dc168470625bcf11ff8cd9ab5660db09129f618", - "reference": "9dc168470625bcf11ff8cd9ab5660db09129f618", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/69eee24e4d6cb8fdae31235d80b9a46b18092139", + "reference": "69eee24e4d6cb8fdae31235d80b9a46b18092139", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.52.*" + "utopia-php/database": "0.54.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1567,9 +1568,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.42.0" + "source": "https://github.com/utopia-php/audit/tree/0.44.0" }, - "time": "2024-08-21T08:24:08+00:00" + "time": "2024-09-05T16:12:41+00:00" }, { "name": "utopia-php/cache", @@ -1623,27 +1624,29 @@ }, { "name": "utopia-php/cli", - "version": "0.15.0", + "version": "0.19.0", "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + "reference": "f8af1d6087f498bc1f0191750a118d357ded9948" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/f8af1d6087f498bc1f0191750a118d357ded9948", + "reference": "f8af1d6087f498bc1f0191750a118d357ded9948", "shasum": "" }, "require": { "php": ">=7.4", - "utopia-php/framework": "0.*.*" + "utopia-php/di": "0.1.*", + "utopia-php/framework": "1.0.*" }, "require-dev": { "laravel/pint": "1.2.*", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.3", "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" + "swoole/ide-helper": "4.8.8" }, "type": "library", "autoload": { @@ -1666,9 +1669,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" + "source": "https://github.com/utopia-php/cli/tree/0.19.0" }, - "time": "2023-03-01T05:55:14+00:00" + "time": "2024-09-05T15:46:56+00:00" }, { "name": "utopia-php/config", @@ -1723,16 +1726,16 @@ }, { "name": "utopia-php/database", - "version": "0.52.2", + "version": "0.54.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "24b29bcac7eb7a8b81698a80bb75fc5909f4975e" + "reference": "1e97fc8b212a8daf9b9a68244677ed34c9db143e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/24b29bcac7eb7a8b81698a80bb75fc5909f4975e", - "reference": "24b29bcac7eb7a8b81698a80bb75fc5909f4975e", + "url": "https://api.github.com/repos/utopia-php/database/zipball/1e97fc8b212a8daf9b9a68244677ed34c9db143e", + "reference": "1e97fc8b212a8daf9b9a68244677ed34c9db143e", "shasum": "" }, "require": { @@ -1740,7 +1743,7 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.10.*", - "utopia-php/framework": "0.33.*", + "utopia-php/framework": "1.0.*", "utopia-php/mongo": "0.3.*" }, "require-dev": { @@ -1751,7 +1754,7 @@ "phpunit/phpunit": "9.6.*", "rregeer/phpunit-coverage-check": "0.3.*", "swoole/ide-helper": "5.1.3", - "utopia-php/cli": "0.14.*" + "utopia-php/cli": "0.19.*" }, "type": "library", "autoload": { @@ -1773,30 +1776,79 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.52.2" + "source": "https://github.com/utopia-php/database/tree/0.54.0" }, - "time": "2024-09-02T06:28:50+00:00" + "time": "2024-09-05T16:00:42+00:00" }, { - "name": "utopia-php/domains", - "version": "0.5.0", + "name": "utopia-php/di", + "version": "0.1.0", "source": { "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c" + "url": "https://github.com/utopia-php/di.git", + "reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/bf07f60326f8389f378ddf6fcde86217e5cfe18c", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c", + "url": "https://api.github.com/repos/utopia-php/di/zipball/22490c95f7ac3898ed1c33f1b1b5dd577305ee31", + "reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/", + "Tests\\E2E\\": "tests/e2e" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple and lite library for managing dependency injections", + "keywords": [ + "framework", + "http", + "php", + "upf" + ], + "support": { + "issues": "https://github.com/utopia-php/di/issues", + "source": "https://github.com/utopia-php/di/tree/0.1.0" + }, + "time": "2024-08-08T14:35:19+00:00" + }, + { + "name": "utopia-php/domains", + "version": "0.6.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "5c70b0f524deeb1fccc3962ad1da650ae2c94cda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/5c70b0f524deeb1fccc3962ad1da650ae2c94cda", + "reference": "5c70b0f524deeb1fccc3962ad1da650ae2c94cda", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/framework": "0.*.*" + "utopia-php/framework": "1.0.*" }, "require-dev": { "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.9.x-dev", "phpunit/phpunit": "^9.3" }, "type": "library", @@ -1833,9 +1885,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.5.0" + "source": "https://github.com/utopia-php/domains/tree/0.6.0" }, - "time": "2024-01-03T22:04:27+00:00" + "time": "2024-09-05T16:21:11+00:00" }, { "name": "utopia-php/dsn", @@ -1925,26 +1977,30 @@ }, { "name": "utopia-php/framework", - "version": "0.33.8", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5" + "reference": "cc880ec41f7f163d4f9956fec26cc6be51b412cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/a7f577540a25cb90896fef2b64767bf8d700f3c5", - "reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5", + "url": "https://api.github.com/repos/utopia-php/http/zipball/cc880ec41f7f163d4f9956fec26cc6be51b412cf", + "reference": "cc880ec41f7f163d4f9956fec26cc6be51b412cf", "shasum": "" }, "require": { - "php": ">=8.0" + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/servers": "0.1.*" }, "require-dev": { + "ext-xdebug": "*", "laravel/pint": "^1.2", "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" }, "type": "library", "autoload": { @@ -1956,17 +2012,18 @@ "license": [ "MIT" ], - "description": "A simple, light and advanced PHP framework", + "description": "A simple, light and advanced PHP HTTP framework", "keywords": [ "framework", + "http", "php", "upf" ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.8" + "source": "https://github.com/utopia-php/http/tree/1.0.0" }, - "time": "2024-08-15T14:10:09+00:00" + "time": "2024-09-05T15:38:08+00:00" }, { "name": "utopia-php/image", @@ -2174,16 +2231,16 @@ }, { "name": "utopia-php/migration", - "version": "0.5.2", + "version": "0.4.4", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "f18d44d4459f78c292dac9edde856fd156fe497a" + "reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/f18d44d4459f78c292dac9edde856fd156fe497a", - "reference": "f18d44d4459f78c292dac9edde856fd156fe497a", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/a8a5d392bebf082faf289f4dfe09d9fd76844c33", + "reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33", "shasum": "" }, "require": { @@ -2193,7 +2250,6 @@ "require-dev": { "laravel/pint": "1.*", "phpunit/phpunit": "9.*", - "utopia-php/cli": "^0.18.0", "vlucas/phpdotenv": "5.*" }, "type": "library", @@ -2216,9 +2272,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.5.2" + "source": "https://github.com/utopia-php/migration/tree/0.4.4" }, - "time": "2024-07-22T09:27:07+00:00" + "time": "2024-05-17T05:25:31+00:00" }, { "name": "utopia-php/mongo", @@ -2282,26 +2338,26 @@ }, { "name": "utopia-php/orchestration", - "version": "0.9.1", + "version": "0.15.0", "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + "reference": "cd55650ba5f13118c3580048e6dd86b604f9a5b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/cd55650ba5f13118c3580048e6dd86b604f9a5b3", + "reference": "cd55650ba5f13118c3580048e6dd86b604f9a5b3", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*" + "utopia-php/cli": "0.19.*" }, "require-dev": { "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.3" }, "type": "library", "autoload": { @@ -2326,31 +2382,32 @@ ], "support": { "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + "source": "https://github.com/utopia-php/orchestration/tree/0.15.0" }, - "time": "2023-03-17T15:05:06+00:00" + "time": "2024-09-05T16:28:02+00:00" }, { "name": "utopia-php/platform", - "version": "0.7.0", + "version": "0.8.0", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52" + "reference": "186236124e2b3a2c6190568e3e227d3a48074d0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/186236124e2b3a2c6190568e3e227d3a48074d0f", + "reference": "186236124e2b3a2c6190568e3e227d3a48074d0f", "shasum": "" }, "require": { "ext-json": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.33.*", - "utopia-php/queue": "0.7.*" + "utopia-php/cli": "0.19.*", + "utopia-php/framework": "1.0.*", + "utopia-php/queue": "0.8.*", + "utopia-php/servers": "0.1.0" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2376,9 +2433,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.7.0" + "source": "https://github.com/utopia-php/platform/tree/0.8.0" }, - "time": "2024-05-08T17:00:55+00:00" + "time": "2024-09-05T16:36:36+00:00" }, { "name": "utopia-php/pools", @@ -2486,22 +2543,23 @@ }, { "name": "utopia-php/queue", - "version": "0.7.0", + "version": "0.8.0", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "917565256eb94bcab7246f7a746b1a486813761b" + "reference": "a518b271f8c158d6e66e36972f767189111033c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/917565256eb94bcab7246f7a746b1a486813761b", - "reference": "917565256eb94bcab7246f7a746b1a486813761b", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/a518b271f8c158d6e66e36972f767189111033c2", + "reference": "a518b271f8c158d6e66e36972f767189111033c2", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" + "utopia-php/cli": "0.19.*", + "utopia-php/di": "0.1.*", + "utopia-php/servers": "0.1.*" }, "require-dev": { "laravel/pint": "^0.2.3", @@ -2511,6 +2569,7 @@ "workerman/workerman": "^4.0" }, "suggest": { + "ext-redis": "Needed to support Redis connections", "ext-swoole": "Needed to support Swoole.", "workerman/workerman": "Needed to support Workerman." }, @@ -2541,9 +2600,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.7.0" + "source": "https://github.com/utopia-php/queue/tree/0.8.0" }, - "time": "2024-01-17T19:00:43+00:00" + "time": "2024-09-05T16:33:01+00:00" }, { "name": "utopia-php/registry", @@ -2598,17 +2657,70 @@ "time": "2021-03-10T10:45:22+00:00" }, { - "name": "utopia-php/storage", - "version": "0.18.5", + "name": "utopia-php/servers", + "version": "0.1.0", "source": { "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "7d355c5e3ccc8ecebc0266f8ddd30088a43be919" + "url": "https://github.com/utopia-php/servers.git", + "reference": "7d9e4f364fb1ab1889fb89ca96eb9946467cb09c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/7d355c5e3ccc8ecebc0266f8ddd30088a43be919", - "reference": "7d355c5e3ccc8ecebc0266f8ddd30088a43be919", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/7d9e4f364fb1ab1889fb89ca96eb9946467cb09c", + "reference": "7d9e4f364fb1ab1889fb89ca96eb9946467cb09c", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/di": "0.1.*" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Servers\\": "src/Servers" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A base library for building Utopia style servers.", + "keywords": [ + "framework", + "php", + "servers", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/servers/issues", + "source": "https://github.com/utopia-php/servers/tree/0.1.0" + }, + "time": "2024-08-08T14:31:39+00:00" + }, + { + "name": "utopia-php/storage", + "version": "0.19.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "5013b894a776874d6010753fc9349df2225c69af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/5013b894a776874d6010753fc9349df2225c69af", + "reference": "5013b894a776874d6010753fc9349df2225c69af", "shasum": "" }, "require": { @@ -2620,8 +2732,8 @@ "ext-zlib": "*", "ext-zstd": "*", "php": ">=8.0", - "utopia-php/framework": "0.*.*", - "utopia-php/system": "0.*.*" + "utopia-php/framework": "1.0.*", + "utopia-php/system": "0.8.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2648,60 +2760,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.5" + "source": "https://github.com/utopia-php/storage/tree/0.19.0" }, - "time": "2024-09-04T08:57:27+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.8.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.33.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "5.0.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.8.2" - }, - "time": "2024-02-01T14:54:12+00:00" + "time": "2024-09-05T17:00:24+00:00" }, { "name": "utopia-php/system", @@ -2761,23 +2822,24 @@ }, { "name": "utopia-php/vcs", - "version": "0.8.2", + "version": "0.9.0", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18" + "reference": "673abe2fef0750a841a4fa8fa6f99d4a602c68e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18", - "reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/673abe2fef0750a841a4fa8fa6f99d4a602c68e7", + "reference": "673abe2fef0750a841a4fa8fa6f99d4a602c68e7", "shasum": "" }, "require": { "adhocore/jwt": "^1.1", "php": ">=8.0", - "utopia-php/cache": "^0.10.0", - "utopia-php/framework": "0.*.*" + "utopia-php/cache": "0.10.*", + "utopia-php/framework": "1.0.*", + "utopia-php/system": "0.8.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2804,32 +2866,76 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.8.2" + "source": "https://github.com/utopia-php/vcs/tree/0.9.0" }, - "time": "2024-08-13T14:36:30+00:00" + "time": "2024-09-05T16:44:48+00:00" }, { - "name": "utopia-php/websocket", - "version": "0.1.0", + "name": "utopia-php/view", + "version": "0.2.0", "source": { "type": "git", - "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + "url": "https://github.com/utopia-php/view.git", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "url": "https://api.github.com/repos/utopia-php/view/zipball/6ee55e83bc014c39ed6b69390f6d399116f65e88", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88", "shasum": "" }, "require": { "php": ">=8.0" }, "require-dev": { + "laravel/pint": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\View\\": "src/View" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP rendering engine", + "keywords": [ + "php", + "view" + ], + "support": { + "issues": "https://github.com/utopia-php/view/issues", + "source": "https://github.com/utopia-php/view/tree/0.2.0" + }, + "time": "2024-04-01T17:21:29+00:00" + }, + { + "name": "utopia-php/websocket", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/websocket.git", + "reference": "e9d0919b321744a61f12563f5791c47ba9f57810" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/e9d0919b321744a61f12563f5791c47ba9f57810", + "reference": "e9d0919b321744a61f12563f5791c47ba9f57810", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.15", + "phpstan/phpstan": "^1.8", "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", + "swoole/ide-helper": "5.1.2", "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", "workerman/workerman": "^4.0" }, "type": "library", @@ -2842,16 +2948,6 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], "description": "A simple abstraction for WebSocket servers.", "keywords": [ "framework", @@ -2862,9 +2958,9 @@ ], "support": { "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + "source": "https://github.com/utopia-php/websocket/tree/0.2.0" }, - "time": "2021-12-20T10:50:09+00:00" + "time": "2024-04-09T08:28:11+00:00" }, { "name": "webmozart/assert", @@ -3044,16 +3140,16 @@ }, { "name": "doctrine/annotations", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/901c2ee5d26eb64ff43c47976e114bf00843acf7", + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7", "shasum": "" }, "require": { @@ -3065,10 +3161,10 @@ "require-dev": { "doctrine/cache": "^2.0", "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.8.0", + "phpstan/phpstan": "^1.10.28", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^5.4 || ^6", - "vimeo/psalm": "^4.10" + "symfony/cache": "^5.4 || ^6.4 || ^7", + "vimeo/psalm": "^4.30 || ^5.14" }, "suggest": { "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" @@ -3114,9 +3210,9 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.1" + "source": "https://github.com/doctrine/annotations/tree/2.0.2" }, - "time": "2023-02-02T22:02:53+00:00" + "time": "2024-09-05T10:17:24+00:00" }, { "name": "doctrine/deprecations", @@ -4230,6 +4326,65 @@ }, "time": "2024-08-29T09:54:52+00:00" }, + { + "name": "phpstan/phpstan", + "version": "1.8.11", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "46e223dd68a620da18855c23046ddb00940b4014" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014", + "reference": "46e223dd68a620da18855c23046ddb00940b4014", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.8.11" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-10-24T15:45:13+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "9.2.32", From 5622d1fd8951d8e04e387fe0c519563be80d9f32 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 5 Sep 2024 18:22:28 -0400 Subject: [PATCH 176/195] fix: adjusting to cr --- app/controllers/general.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 646f190597..264072ffc3 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -33,6 +33,7 @@ use Utopia\Http\Route; use Utopia\Http\Validator\Hostname; use Utopia\Http\Validator\Text; use Utopia\Locale\Locale; +use Utopia\Logger\Adapter\Sentry; use Utopia\Logger\Log; use Utopia\Logger\Log\User; use Utopia\Logger\Logger; @@ -95,8 +96,8 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request $type = $rule->getAttribute('resourceType'); if ($type === 'function') { - $utopia->getRoute()?->label('sdk.namespace', 'functions'); - $utopia->getRoute()?->label('sdk.method', 'createExecution'); + $route->label('sdk.namespace', 'functions'); + $route->label('sdk.method', 'createExecution'); if (System::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS if ($request->getProtocol() !== 'https') { From d0cc9c8a1387089d91e99e2c996da33c66953f8a Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 5 Sep 2024 18:43:18 -0400 Subject: [PATCH 177/195] fix: expanding exception classes & conditioning experiment logger --- app/controllers/general.php | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 264072ffc3..5df113fe68 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -736,6 +736,7 @@ Http::error() Console::error('[Error] Line: ' . $line); } switch ($class) { + case 'Utopia\Servers\Exception': case 'Utopia\Http\Exception': $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); switch ($code) { @@ -786,24 +787,26 @@ Http::error() $providerName = System::getEnv('_APP_EXPERIMENT_LOGGING_PROVIDER', ''); $providerConfig = System::getEnv('_APP_EXPERIMENT_LOGGING_CONFIG', ''); - try { - $loggingProvider = new DSN($providerConfig ?? ''); - $providerName = $loggingProvider->getScheme(); + if (!(empty($providerName) || empty($providerConfig))) { + try { + $loggingProvider = new DSN($providerConfig); + $providerName = $loggingProvider->getScheme(); - if (!empty($providerName) && $providerName === 'sentry') { - $key = $loggingProvider->getPassword(); - $projectId = $loggingProvider->getUser() ?? ''; - $host = 'https://' . $loggingProvider->getHost(); + if (!empty($providerName) && $providerName === 'sentry') { + $key = $loggingProvider->getPassword(); + $projectId = $loggingProvider->getUser() ?? ''; + $host = 'https://' . $loggingProvider->getHost(); - $adapter = new Sentry($projectId, $key, $host); - $logger = new Logger($adapter); - $logger->setSample(0.04); - $publish = true; - } else { - throw new \Exception('Invalid experimental logging provider'); + $adapter = new Sentry($projectId, $key, $host); + $logger = new Logger($adapter); + $logger->setSample(0.04); + $publish = true; + } else { + throw new \Exception('Invalid experimental logging provider'); + } + } catch (\Throwable $th) { + Console::warning('Failed to initialize logging provider: ' . $th->getMessage()); } - } catch (\Throwable $th) { - Console::warning('Failed to initialize logging provider: ' . $th->getMessage()); } } From b200e66bd1d8f4ef472dbb8e3d87b6ae9588d6fa Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 6 Sep 2024 15:52:04 -0400 Subject: [PATCH 178/195] fix: adding getConsoleDB injection --- app/init.php | 61 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/app/init.php b/app/init.php index 49cb7e8920..cb97be80fe 100644 --- a/app/init.php +++ b/app/init.php @@ -507,6 +507,7 @@ $servers = new Dependency(); $register = new Dependency(); $connections = new Dependency(); $localeCodes = new Dependency(); +$getConsoleDB = new Dependency(); $getProjectDB = new Dependency(); $dbForProject = new Dependency(); $dbForConsole = new Dependency(); @@ -820,33 +821,12 @@ $dbForProject $dbForConsole ->setName('dbForConsole') - ->inject('pools') - ->inject('cache') - ->inject('authorization') + ->inject('getConsoleDB') ->inject('connections') - ->setCallback(function (array $pools, Cache $cache, Authorization $authorization, Connections $connections): Database { - $pool = $pools['pools-console-console']['pool']; - $dsn = $pools['pools-console-console']['dsn']; - $connection = $pool->get(); + ->setCallback(function (callable $getConsoleDB, Connections $connections): Database { + [$connection,$pool, $database] = $getConsoleDB(); $connections->add($connection, $pool); - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - - $database = new Database($adapter, $cache); - $database->setAuthorization($authorization); - - $database - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - return $database; }); @@ -1153,6 +1133,38 @@ $requestTimestamp } return $requestTimestamp; }); +$getConsoleDB + ->setName('getConsoleDB') + ->inject('pools') + ->inject('cache') + ->inject('authorization') + ->inject('connections') + ->setCallback(function (array $pools, Cache $cache, Authorization $authorization) { + return function () use ($pools, $cache, $authorization): array { + $pool = $pools['pools-console-console']['pool']; + $dsn = $pools['pools-console-console']['dsn']; + $connection = $pool->get(); + + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); + + $database = new Database($adapter, $cache); + $database->setAuthorization($authorization); + + $database + ->setNamespace('_console') + ->setMetadata('host', \gethostname()) + ->setMetadata('project', 'console') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + return [$connection, $pool, $database]; + }; + }); $getProjectDB ->setName('getProjectDB') @@ -1355,6 +1367,7 @@ $container->set($connections); $container->set($localeCodes); $container->set($dbForProject); $container->set($dbForConsole); +$container->set($getConsoleDB); $container->set($getProjectDB); $container->set($queueForUsage); $container->set($queueForMails); From b390485dd9434e3b3a4664a9aacd96490271c974 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 6 Sep 2024 15:52:20 -0400 Subject: [PATCH 179/195] fix: coroutine access in schedulers --- src/Appwrite/Platform/Tasks/ScheduleBase.php | 22 +++++++++++-------- .../Platform/Tasks/ScheduleExecutions.php | 18 ++++++++------- .../Platform/Tasks/ScheduleFunctions.php | 3 +-- .../Platform/Tasks/ScheduleMessages.php | 8 +++---- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index 2fbd26d4fd..c93c6ef7ba 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Utopia\Queue\Connections; use Swoole\Timer; use Utopia\CLI\Console; -use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception; @@ -27,7 +26,7 @@ abstract class ScheduleBase extends Action abstract protected function enqueueResources( array $pools, - Database $dbForConsole + callable $getConsoleDB ); public function __construct() @@ -39,9 +38,9 @@ abstract class ScheduleBase extends Action $this ->desc("Execute {$type}s scheduled in Appwrite") ->inject('pools') - ->inject('dbForConsole') + ->inject('getConsoleDB') ->inject('getProjectDB') - ->callback(fn (array $pools, Database $dbForConsole, callable $getProjectDB) => $this->action($pools, $dbForConsole, $getProjectDB)); + ->callback(fn (array $pools, callable $getConsoleDB, callable $getProjectDB) => $this->action($pools, $getConsoleDB, $getProjectDB)); }); } @@ -50,11 +49,12 @@ abstract class ScheduleBase extends Action * 2. Create timer that sync all changes from 'schedules' collection to local copy. Only reading changes thanks to 'resourceUpdatedAt' attribute * 3. Create timer that prepares coroutines for soon-to-execute schedules. When it's ready, coroutine sleeps until exact time before sending request to worker. */ - public function action(array $pools, Database $dbForConsole, callable $getProjectDB): void + public function action(array $pools, callable $getConsoleDB, callable $getProjectDB): void { Console::title(\ucfirst(static::getSupportedResource()) . ' scheduler V1'); Console::success(APP_NAME . ' ' . \ucfirst(static::getSupportedResource()) . ' scheduler v1 has started'); + [$_, $_, $dbForConsole] = $getConsoleDB(); /** * Extract only necessary attributes to lower memory used. * @@ -135,7 +135,11 @@ abstract class ScheduleBase extends Action Console::success("Starting timers at " . DateTime::now()); - Timer::tick(static::UPDATE_TIMER * 1000, function () use ($dbForConsole, &$lastSyncUpdate, $getSchedule, $pools) { + Timer::tick(static::UPDATE_TIMER * 1000, function () use ($getConsoleDB, &$lastSyncUpdate, $getSchedule, $pools) { + [$connection,$pool, $dbForConsole] = $getConsoleDB(); + $connections = new Connections(); + $connections->add($connection, $pool); + $time = DateTime::now(); $timerStart = \microtime(true); @@ -184,15 +188,15 @@ abstract class ScheduleBase extends Action $lastSyncUpdate = $time; $timerEnd = \microtime(true); - + $connections->reclaim(); Console::log("Sync tick: {$total} schedules were updated in " . ($timerEnd - $timerStart) . " seconds"); }); Timer::tick( static::ENQUEUE_TIMER * 1000, - fn () => $this->enqueueResources($pools, $dbForConsole) + fn () => $this->enqueueResources($pools, $getConsoleDB) ); - $this->enqueueResources($pools, $dbForConsole); + $this->enqueueResources($pools, $getConsoleDB); } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index 0ef53bcdf2..01a65ad57f 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -4,7 +4,6 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Func; use Swoole\Coroutine as Co; -use Utopia\Database\Database; use Utopia\Queue\Connection\Redis; class ScheduleExecutions extends ScheduleBase @@ -22,8 +21,11 @@ class ScheduleExecutions extends ScheduleBase return 'execution'; } - protected function enqueueResources(array $pools, Database $dbForConsole): void + protected function enqueueResources(array $pools, callable $getConsoleDB): void { + [$connection,$pool, $dbForConsole] = $getConsoleDB(); + $this->connections->add($connection, $pool); + $pool = $pools['pools-queue-queue']['pool']; $connection = $pool->get(); $this->connections->add($connection, $pool); @@ -50,7 +52,7 @@ class ScheduleExecutions extends ScheduleBase $delay = $scheduledAt->getTimestamp() - (new \DateTime())->getTimestamp(); - \go(function () use ($queueForFunctions, $schedule, $delay) { + \go(function () use ($queueForFunctions, $schedule, $delay, $dbForConsole) { Co::sleep($delay); $queueForFunctions @@ -65,12 +67,12 @@ class ScheduleExecutions extends ScheduleBase ->setBody($schedule['data']['body'] ?? '') ->setProject($schedule['project']) ->trigger(); - }); - $dbForConsole->deleteDocument( - 'schedules', - $schedule['$id'], - ); + $dbForConsole->deleteDocument( + 'schedules', + $schedule['$id'], + ); + }); unset($this->schedules[$schedule['resourceId']]); } diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index cfa6869662..450551400e 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Func; use Cron\CronExpression; use Utopia\CLI\Console; -use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Queue\Connection\Redis; @@ -26,7 +25,7 @@ class ScheduleFunctions extends ScheduleBase return 'function'; } - protected function enqueueResources(array $pools, Database $dbForConsole): void + protected function enqueueResources(array $pools, callable $getConsoleDB): void { $timerStart = \microtime(true); $time = DateTime::now(); diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index fc76d2a543..8203bcc9c9 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Messaging; -use Utopia\Database\Database; use Utopia\Queue\Connection\Redis; class ScheduleMessages extends ScheduleBase @@ -21,8 +20,11 @@ class ScheduleMessages extends ScheduleBase return 'message'; } - protected function enqueueResources(array $pools, Database $dbForConsole): void + protected function enqueueResources(array $pools, callable $getConsoleDB): void { + [$connection,$pool, $dbForConsole] = $getConsoleDB(); + $this->connections->add($connection, $pool); + foreach ($this->schedules as $schedule) { if (!$schedule['active']) { continue; @@ -57,8 +59,6 @@ class ScheduleMessages extends ScheduleBase ); $this->connections->reclaim(); - // $queue->reclaim(); // TODO: Do in try/catch/finally, or add to connectons resource - unset($this->schedules[$schedule['resourceId']]); }); } From 828928b609b7fa054bf94b16972810290d514b0e Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 9 Sep 2024 15:03:49 +0200 Subject: [PATCH 180/195] chore: packages update --- composer.lock | 611 +++++++++++++++++++++++++++++++------------------- 1 file changed, 383 insertions(+), 228 deletions(-) diff --git a/composer.lock b/composer.lock index 645cf08965..44a6b194c5 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": "b6820da26239716cf14a445697902a03", + "content-hash": "6017f815da50b7d4dabad66386e013e3", "packages": [ { "name": "adhocore/jwt", @@ -1130,20 +1130,20 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -1190,7 +1190,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -1206,24 +1206,24 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -1270,7 +1270,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" }, "funding": [ { @@ -1286,7 +1286,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "thecodingmachine/safe", @@ -1429,16 +1429,16 @@ }, { "name": "utopia-php/abuse", - "version": "0.43.0", + "version": "0.44.0", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "6346a3b4c5177a43160035a7289e30fdfb0790d6" + "reference": "cd37bba23778f3a0dc54b4ba6ffb36d4d91ed8f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/6346a3b4c5177a43160035a7289e30fdfb0790d6", - "reference": "6346a3b4c5177a43160035a7289e30fdfb0790d6", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/cd37bba23778f3a0dc54b4ba6ffb36d4d91ed8f1", + "reference": "cd37bba23778f3a0dc54b4ba6ffb36d4d91ed8f1", "shasum": "" }, "require": { @@ -1446,7 +1446,7 @@ "ext-pdo": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "0.53.*" + "utopia-php/database": "0.54.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1474,27 +1474,28 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.43.0" + "source": "https://github.com/utopia-php/abuse/tree/0.44.0" }, - "time": "2024-08-30T05:17:23+00:00" + "time": "2024-09-05T16:09:32+00:00" }, { "name": "utopia-php/analytics", - "version": "0.10.2", + "version": "0.13.0", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" + "reference": "3dace02af5d4190623f88fb6e02f5559a99f14c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", - "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/3dace02af5d4190623f88fb6e02f5559a99f14c4", + "reference": "3dace02af5d4190623f88fb6e02f5559a99f14c4", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "^0.15.0" + "utopia-php/cli": "0.19.*", + "utopia-php/system": "0.8.*" }, "require-dev": { "laravel/pint": "dev-main", @@ -1520,27 +1521,27 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.2" + "source": "https://github.com/utopia-php/analytics/tree/0.13.0" }, - "time": "2023-03-22T12:01:09+00:00" + "time": "2024-09-05T16:19:26+00:00" }, { "name": "utopia-php/audit", - "version": "0.43.0", + "version": "0.44.0", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "cef22b5dc6a6d28fcd522f41c7bf7ded4a4dfd3e" + "reference": "69eee24e4d6cb8fdae31235d80b9a46b18092139" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/cef22b5dc6a6d28fcd522f41c7bf7ded4a4dfd3e", - "reference": "cef22b5dc6a6d28fcd522f41c7bf7ded4a4dfd3e", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/69eee24e4d6cb8fdae31235d80b9a46b18092139", + "reference": "69eee24e4d6cb8fdae31235d80b9a46b18092139", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.53.*" + "utopia-php/database": "0.54.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1567,9 +1568,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.43.0" + "source": "https://github.com/utopia-php/audit/tree/0.44.0" }, - "time": "2024-08-30T05:17:36+00:00" + "time": "2024-09-05T16:12:41+00:00" }, { "name": "utopia-php/cache", @@ -1623,27 +1624,29 @@ }, { "name": "utopia-php/cli", - "version": "0.15.0", + "version": "0.19.0", "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" + "reference": "f8af1d6087f498bc1f0191750a118d357ded9948" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", - "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/f8af1d6087f498bc1f0191750a118d357ded9948", + "reference": "f8af1d6087f498bc1f0191750a118d357ded9948", "shasum": "" }, "require": { "php": ">=7.4", - "utopia-php/framework": "0.*.*" + "utopia-php/di": "0.1.*", + "utopia-php/framework": "1.0.*" }, "require-dev": { "laravel/pint": "1.2.*", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.3", "squizlabs/php_codesniffer": "^3.6", - "vimeo/psalm": "4.0.1" + "swoole/ide-helper": "4.8.8" }, "type": "library", "autoload": { @@ -1666,9 +1669,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.15.0" + "source": "https://github.com/utopia-php/cli/tree/0.19.0" }, - "time": "2023-03-01T05:55:14+00:00" + "time": "2024-09-05T15:46:56+00:00" }, { "name": "utopia-php/config", @@ -1723,16 +1726,16 @@ }, { "name": "utopia-php/database", - "version": "0.53.3", + "version": "0.54.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "24b29bcac7eb7a8b81698a80bb75fc5909f4975e" + "reference": "1e97fc8b212a8daf9b9a68244677ed34c9db143e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/24b29bcac7eb7a8b81698a80bb75fc5909f4975e", - "reference": "24b29bcac7eb7a8b81698a80bb75fc5909f4975e", + "url": "https://api.github.com/repos/utopia-php/database/zipball/1e97fc8b212a8daf9b9a68244677ed34c9db143e", + "reference": "1e97fc8b212a8daf9b9a68244677ed34c9db143e", "shasum": "" }, "require": { @@ -1740,7 +1743,7 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.10.*", - "utopia-php/framework": "0.33.*", + "utopia-php/framework": "1.0.*", "utopia-php/mongo": "0.3.*" }, "require-dev": { @@ -1751,7 +1754,7 @@ "phpunit/phpunit": "9.6.*", "rregeer/phpunit-coverage-check": "0.3.*", "swoole/ide-helper": "5.1.3", - "utopia-php/cli": "0.14.*" + "utopia-php/cli": "0.19.*" }, "type": "library", "autoload": { @@ -1773,30 +1776,79 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.53.3" + "source": "https://github.com/utopia-php/database/tree/0.54.0" }, - "time": "2024-09-02T06:28:50+00:00" + "time": "2024-09-05T16:00:42+00:00" }, { - "name": "utopia-php/domains", - "version": "0.5.0", + "name": "utopia-php/di", + "version": "0.1.0", "source": { "type": "git", - "url": "https://github.com/utopia-php/domains.git", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c" + "url": "https://github.com/utopia-php/di.git", + "reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/bf07f60326f8389f378ddf6fcde86217e5cfe18c", - "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c", + "url": "https://api.github.com/repos/utopia-php/di/zipball/22490c95f7ac3898ed1c33f1b1b5dd577305ee31", + "reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "laravel/pint": "^1.2", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\": "src/", + "Tests\\E2E\\": "tests/e2e" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple and lite library for managing dependency injections", + "keywords": [ + "framework", + "http", + "php", + "upf" + ], + "support": { + "issues": "https://github.com/utopia-php/di/issues", + "source": "https://github.com/utopia-php/di/tree/0.1.0" + }, + "time": "2024-08-08T14:35:19+00:00" + }, + { + "name": "utopia-php/domains", + "version": "0.6.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/domains.git", + "reference": "5c70b0f524deeb1fccc3962ad1da650ae2c94cda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/5c70b0f524deeb1fccc3962ad1da650ae2c94cda", + "reference": "5c70b0f524deeb1fccc3962ad1da650ae2c94cda", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/framework": "0.*.*" + "utopia-php/framework": "1.0.*" }, "require-dev": { "laravel/pint": "1.2.*", + "phpstan/phpstan": "1.9.x-dev", "phpunit/phpunit": "^9.3" }, "type": "library", @@ -1833,9 +1885,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.5.0" + "source": "https://github.com/utopia-php/domains/tree/0.6.0" }, - "time": "2024-01-03T22:04:27+00:00" + "time": "2024-09-05T16:21:11+00:00" }, { "name": "utopia-php/dsn", @@ -1925,26 +1977,30 @@ }, { "name": "utopia-php/framework", - "version": "0.33.8", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5" + "reference": "cc880ec41f7f163d4f9956fec26cc6be51b412cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/a7f577540a25cb90896fef2b64767bf8d700f3c5", - "reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5", + "url": "https://api.github.com/repos/utopia-php/http/zipball/cc880ec41f7f163d4f9956fec26cc6be51b412cf", + "reference": "cc880ec41f7f163d4f9956fec26cc6be51b412cf", "shasum": "" }, "require": { - "php": ">=8.0" + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/servers": "0.1.*" }, "require-dev": { + "ext-xdebug": "*", "laravel/pint": "^1.2", "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" }, "type": "library", "autoload": { @@ -1956,17 +2012,18 @@ "license": [ "MIT" ], - "description": "A simple, light and advanced PHP framework", + "description": "A simple, light and advanced PHP HTTP framework", "keywords": [ "framework", + "http", "php", "upf" ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.8" + "source": "https://github.com/utopia-php/http/tree/1.0.0" }, - "time": "2024-08-15T14:10:09+00:00" + "time": "2024-09-05T15:38:08+00:00" }, { "name": "utopia-php/image", @@ -2174,16 +2231,16 @@ }, { "name": "utopia-php/migration", - "version": "0.5.2", + "version": "0.4.4", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "f18d44d4459f78c292dac9edde856fd156fe497a" + "reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/f18d44d4459f78c292dac9edde856fd156fe497a", - "reference": "f18d44d4459f78c292dac9edde856fd156fe497a", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/a8a5d392bebf082faf289f4dfe09d9fd76844c33", + "reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33", "shasum": "" }, "require": { @@ -2193,7 +2250,6 @@ "require-dev": { "laravel/pint": "1.*", "phpunit/phpunit": "9.*", - "utopia-php/cli": "^0.18.0", "vlucas/phpdotenv": "5.*" }, "type": "library", @@ -2216,9 +2272,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.5.2" + "source": "https://github.com/utopia-php/migration/tree/0.4.4" }, - "time": "2024-07-22T09:27:07+00:00" + "time": "2024-05-17T05:25:31+00:00" }, { "name": "utopia-php/mongo", @@ -2282,26 +2338,26 @@ }, { "name": "utopia-php/orchestration", - "version": "0.9.1", + "version": "0.15.0", "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" + "reference": "cd55650ba5f13118c3580048e6dd86b604f9a5b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", - "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/cd55650ba5f13118c3580048e6dd86b604f9a5b3", + "reference": "cd55650ba5f13118c3580048e6dd86b604f9a5b3", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*" + "utopia-php/cli": "0.19.*" }, "require-dev": { "laravel/pint": "^1.2", - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.3" }, "type": "library", "autoload": { @@ -2326,31 +2382,32 @@ ], "support": { "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" + "source": "https://github.com/utopia-php/orchestration/tree/0.15.0" }, - "time": "2023-03-17T15:05:06+00:00" + "time": "2024-09-05T16:28:02+00:00" }, { "name": "utopia-php/platform", - "version": "0.7.0", + "version": "0.8.1", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52" + "reference": "95d57f38a4001c7189a66885c485ac635d305234" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", - "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/95d57f38a4001c7189a66885c485ac635d305234", + "reference": "95d57f38a4001c7189a66885c485ac635d305234", "shasum": "" }, "require": { "ext-json": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.33.*", - "utopia-php/queue": "0.7.*" + "utopia-php/cli": "0.19.*", + "utopia-php/framework": "1.0.*", + "utopia-php/queue": "0.8.*", + "utopia-php/servers": "0.1.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2376,9 +2433,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.7.0" + "source": "https://github.com/utopia-php/platform/tree/0.8.1" }, - "time": "2024-05-08T17:00:55+00:00" + "time": "2024-09-06T02:33:27+00:00" }, { "name": "utopia-php/pools", @@ -2486,22 +2543,23 @@ }, { "name": "utopia-php/queue", - "version": "0.7.0", + "version": "0.8.0", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "917565256eb94bcab7246f7a746b1a486813761b" + "reference": "a518b271f8c158d6e66e36972f767189111033c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/917565256eb94bcab7246f7a746b1a486813761b", - "reference": "917565256eb94bcab7246f7a746b1a486813761b", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/a518b271f8c158d6e66e36972f767189111033c2", + "reference": "a518b271f8c158d6e66e36972f767189111033c2", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/framework": "0.*.*" + "utopia-php/cli": "0.19.*", + "utopia-php/di": "0.1.*", + "utopia-php/servers": "0.1.*" }, "require-dev": { "laravel/pint": "^0.2.3", @@ -2511,6 +2569,7 @@ "workerman/workerman": "^4.0" }, "suggest": { + "ext-redis": "Needed to support Redis connections", "ext-swoole": "Needed to support Swoole.", "workerman/workerman": "Needed to support Workerman." }, @@ -2541,9 +2600,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.7.0" + "source": "https://github.com/utopia-php/queue/tree/0.8.0" }, - "time": "2024-01-17T19:00:43+00:00" + "time": "2024-09-05T16:33:01+00:00" }, { "name": "utopia-php/registry", @@ -2598,17 +2657,70 @@ "time": "2021-03-10T10:45:22+00:00" }, { - "name": "utopia-php/storage", - "version": "0.18.5", + "name": "utopia-php/servers", + "version": "0.1.1", "source": { "type": "git", - "url": "https://github.com/utopia-php/storage.git", - "reference": "7d355c5e3ccc8ecebc0266f8ddd30088a43be919" + "url": "https://github.com/utopia-php/servers.git", + "reference": "fd5c8d32778f265256c1936372a071b944f5ba8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/7d355c5e3ccc8ecebc0266f8ddd30088a43be919", - "reference": "7d355c5e3ccc8ecebc0266f8ddd30088a43be919", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/fd5c8d32778f265256c1936372a071b944f5ba8a", + "reference": "fd5c8d32778f265256c1936372a071b944f5ba8a", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/di": "0.1.*" + }, + "require-dev": { + "laravel/pint": "^0.2.3", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9.5.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Servers\\": "src/Servers" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Team Appwrite", + "email": "team@appwrite.io" + } + ], + "description": "A base library for building Utopia style servers.", + "keywords": [ + "framework", + "php", + "servers", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/servers/issues", + "source": "https://github.com/utopia-php/servers/tree/0.1.1" + }, + "time": "2024-09-06T02:25:56+00:00" + }, + { + "name": "utopia-php/storage", + "version": "0.19.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/storage.git", + "reference": "5013b894a776874d6010753fc9349df2225c69af" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/5013b894a776874d6010753fc9349df2225c69af", + "reference": "5013b894a776874d6010753fc9349df2225c69af", "shasum": "" }, "require": { @@ -2620,8 +2732,8 @@ "ext-zlib": "*", "ext-zstd": "*", "php": ">=8.0", - "utopia-php/framework": "0.*.*", - "utopia-php/system": "0.*.*" + "utopia-php/framework": "1.0.*", + "utopia-php/system": "0.8.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2648,60 +2760,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.5" + "source": "https://github.com/utopia-php/storage/tree/0.19.0" }, - "time": "2024-09-04T08:57:27+00:00" - }, - { - "name": "utopia-php/swoole", - "version": "0.8.2", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/swoole.git", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", - "shasum": "" - }, - "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/framework": "0.33.*" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3", - "swoole/ide-helper": "5.0.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Swoole\\": "src/Swoole" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", - "keywords": [ - "framework", - "http", - "php", - "server", - "swoole", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/swoole/issues", - "source": "https://github.com/utopia-php/swoole/tree/0.8.2" - }, - "time": "2024-02-01T14:54:12+00:00" + "time": "2024-09-05T17:00:24+00:00" }, { "name": "utopia-php/system", @@ -2761,23 +2822,24 @@ }, { "name": "utopia-php/vcs", - "version": "0.8.2", + "version": "0.9.0", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18" + "reference": "673abe2fef0750a841a4fa8fa6f99d4a602c68e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18", - "reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/673abe2fef0750a841a4fa8fa6f99d4a602c68e7", + "reference": "673abe2fef0750a841a4fa8fa6f99d4a602c68e7", "shasum": "" }, "require": { "adhocore/jwt": "^1.1", "php": ">=8.0", - "utopia-php/cache": "^0.10.0", - "utopia-php/framework": "0.*.*" + "utopia-php/cache": "0.10.*", + "utopia-php/framework": "1.0.*", + "utopia-php/system": "0.8.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2804,32 +2866,76 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.8.2" + "source": "https://github.com/utopia-php/vcs/tree/0.9.0" }, - "time": "2024-08-13T14:36:30+00:00" + "time": "2024-09-05T16:44:48+00:00" }, { - "name": "utopia-php/websocket", - "version": "0.1.0", + "name": "utopia-php/view", + "version": "0.2.0", "source": { "type": "git", - "url": "https://github.com/utopia-php/websocket.git", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" + "url": "https://github.com/utopia-php/view.git", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", - "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", + "url": "https://api.github.com/repos/utopia-php/view/zipball/6ee55e83bc014c39ed6b69390f6d399116f65e88", + "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88", "shasum": "" }, "require": { "php": ">=8.0" }, "require-dev": { + "laravel/pint": "^1.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\View\\": "src/View" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple, light and advanced PHP rendering engine", + "keywords": [ + "php", + "view" + ], + "support": { + "issues": "https://github.com/utopia-php/view/issues", + "source": "https://github.com/utopia-php/view/tree/0.2.0" + }, + "time": "2024-04-01T17:21:29+00:00" + }, + { + "name": "utopia-php/websocket", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/websocket.git", + "reference": "e9d0919b321744a61f12563f5791c47ba9f57810" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/e9d0919b321744a61f12563f5791c47ba9f57810", + "reference": "e9d0919b321744a61f12563f5791c47ba9f57810", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.15", + "phpstan/phpstan": "^1.8", "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "4.6.6", + "swoole/ide-helper": "5.1.2", "textalk/websocket": "1.5.2", - "vimeo/psalm": "^4.8.1", "workerman/workerman": "^4.0" }, "type": "library", @@ -2842,16 +2948,6 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], "description": "A simple abstraction for WebSocket servers.", "keywords": [ "framework", @@ -2862,9 +2958,9 @@ ], "support": { "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.1.0" + "source": "https://github.com/utopia-php/websocket/tree/0.2.0" }, - "time": "2021-12-20T10:50:09+00:00" + "time": "2024-04-09T08:28:11+00:00" }, { "name": "webmozart/assert", @@ -3044,16 +3140,16 @@ }, { "name": "doctrine/annotations", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/901c2ee5d26eb64ff43c47976e114bf00843acf7", + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7", "shasum": "" }, "require": { @@ -3065,10 +3161,10 @@ "require-dev": { "doctrine/cache": "^2.0", "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.8.0", + "phpstan/phpstan": "^1.10.28", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^5.4 || ^6", - "vimeo/psalm": "^4.10" + "symfony/cache": "^5.4 || ^6.4 || ^7", + "vimeo/psalm": "^4.30 || ^5.14" }, "suggest": { "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" @@ -3114,9 +3210,9 @@ ], "support": { "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.1" + "source": "https://github.com/doctrine/annotations/tree/2.0.2" }, - "time": "2023-02-02T22:02:53+00:00" + "time": "2024-09-05T10:17:24+00:00" }, { "name": "doctrine/deprecations", @@ -4185,16 +4281,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.30.0", + "version": "1.30.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "5ceb0e384997db59f38774bf79c2a6134252c08f" + "reference": "51b95ec8670af41009e2b2b56873bad96682413e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/5ceb0e384997db59f38774bf79c2a6134252c08f", - "reference": "5ceb0e384997db59f38774bf79c2a6134252c08f", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/51b95ec8670af41009e2b2b56873bad96682413e", + "reference": "51b95ec8670af41009e2b2b56873bad96682413e", "shasum": "" }, "require": { @@ -4226,9 +4322,68 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.30.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.30.1" }, - "time": "2024-08-29T09:54:52+00:00" + "time": "2024-09-07T20:13:05+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.8.11", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "46e223dd68a620da18855c23046ddb00940b4014" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014", + "reference": "46e223dd68a620da18855c23046ddb00940b4014", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.8.11" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-10-24T15:45:13+00:00" }, { "name": "phpunit/php-code-coverage", @@ -6222,20 +6377,20 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -6281,7 +6436,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -6297,24 +6452,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -6359,7 +6514,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -6375,24 +6530,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -6440,7 +6595,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -6456,7 +6611,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/process", From 1861ae3f6834b86b9ae32e5b7cf20c21d131d98f Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:48:26 +0200 Subject: [PATCH 181/195] fix: mock phone tests --- composer.lock | 24 +++++++++---------- src/Appwrite/Auth/Validator/MockNumber.php | 1 + .../Projects/ProjectsConsoleClientTest.php | 2 +- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/composer.lock b/composer.lock index 44a6b194c5..1ab5e7ae89 100644 --- a/composer.lock +++ b/composer.lock @@ -1977,16 +1977,16 @@ }, { "name": "utopia-php/framework", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "cc880ec41f7f163d4f9956fec26cc6be51b412cf" + "reference": "d9aa291f7e3efa516f7eb2dde490f0b24757fe0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/cc880ec41f7f163d4f9956fec26cc6be51b412cf", - "reference": "cc880ec41f7f163d4f9956fec26cc6be51b412cf", + "url": "https://api.github.com/repos/utopia-php/http/zipball/d9aa291f7e3efa516f7eb2dde490f0b24757fe0f", + "reference": "d9aa291f7e3efa516f7eb2dde490f0b24757fe0f", "shasum": "" }, "require": { @@ -2021,9 +2021,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/1.0.0" + "source": "https://github.com/utopia-php/http/tree/1.0.1" }, - "time": "2024-09-05T15:38:08+00:00" + "time": "2024-09-09T15:36:32+00:00" }, { "name": "utopia-php/image", @@ -3089,16 +3089,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.39.19", + "version": "0.39.20", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "d5653a2f744d2c297d44f99ff68bfc26c1a3b804" + "reference": "1ee0069a747ab0bf4ba922cecba4591ec55281b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d5653a2f744d2c297d44f99ff68bfc26c1a3b804", - "reference": "d5653a2f744d2c297d44f99ff68bfc26c1a3b804", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/1ee0069a747ab0bf4ba922cecba4591ec55281b4", + "reference": "1ee0069a747ab0bf4ba922cecba4591ec55281b4", "shasum": "" }, "require": { @@ -3134,9 +3134,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.39.19" + "source": "https://github.com/appwrite/sdk-generator/tree/0.39.20" }, - "time": "2024-08-30T12:04:18+00:00" + "time": "2024-09-09T15:34:39+00:00" }, { "name": "doctrine/annotations", diff --git a/src/Appwrite/Auth/Validator/MockNumber.php b/src/Appwrite/Auth/Validator/MockNumber.php index 141a469724..8f0f14c9da 100644 --- a/src/Appwrite/Auth/Validator/MockNumber.php +++ b/src/Appwrite/Auth/Validator/MockNumber.php @@ -52,6 +52,7 @@ class MockNumber extends Validator return false; } + $this->message = ''; return true; } diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index c41d861f1d..9fc862e85b 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -1691,7 +1691,7 @@ class ProjectsConsoleClientTest extends Scope ]); $this->assertEquals(400, $response['headers']['status-code']); - $this->assertEquals('Invalid `numbers` param: Value must a valid array no longer than 10 items and Phone number must start with a \'+\' can have a maximum of fifteen digits.', $response['body']['message']); + $this->assertEquals('Invalid `numbers` param: Value must a valid array no longer than 10 items', $response['body']['message']); /** * Test for success From 3571260a2b34ae6d5693fc994d3549a9c7326591 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:06:02 +0200 Subject: [PATCH 182/195] chore: packages update --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 1ab5e7ae89..01c7f6988f 100644 --- a/composer.lock +++ b/composer.lock @@ -1977,16 +1977,16 @@ }, { "name": "utopia-php/framework", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "d9aa291f7e3efa516f7eb2dde490f0b24757fe0f" + "reference": "fc63ec61c720190a5ea5bb484c615145850951e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/d9aa291f7e3efa516f7eb2dde490f0b24757fe0f", - "reference": "d9aa291f7e3efa516f7eb2dde490f0b24757fe0f", + "url": "https://api.github.com/repos/utopia-php/http/zipball/fc63ec61c720190a5ea5bb484c615145850951e6", + "reference": "fc63ec61c720190a5ea5bb484c615145850951e6", "shasum": "" }, "require": { @@ -2021,9 +2021,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/1.0.1" + "source": "https://github.com/utopia-php/http/tree/1.0.2" }, - "time": "2024-09-09T15:36:32+00:00" + "time": "2024-09-10T09:04:19+00:00" }, { "name": "utopia-php/image", From 8bc123848f2da0c909784ddd62361f6e51ba221c Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:46:58 +0200 Subject: [PATCH 183/195] chore: packages update --- composer.lock | 121 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 19 deletions(-) diff --git a/composer.lock b/composer.lock index 01c7f6988f..c7bc3a47c3 100644 --- a/composer.lock +++ b/composer.lock @@ -3089,16 +3089,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.39.20", + "version": "0.39.21", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "1ee0069a747ab0bf4ba922cecba4591ec55281b4" + "reference": "9754b190d33aaad56fdb8defc94f90248184c5ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/1ee0069a747ab0bf4ba922cecba4591ec55281b4", - "reference": "1ee0069a747ab0bf4ba922cecba4591ec55281b4", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9754b190d33aaad56fdb8defc94f90248184c5ac", + "reference": "9754b190d33aaad56fdb8defc94f90248184c5ac", "shasum": "" }, "require": { @@ -3107,12 +3107,12 @@ "ext-mbstring": "*", "matthiasmullie/minify": "1.3.*", "php": ">=8.0", - "twig/twig": "v3.8.*" + "twig/twig": "3.14.*" }, "require-dev": { - "brianium/paratest": "v7.4.*", - "phpunit/phpunit": "10.5.*", - "squizlabs/php_codesniffer": "3.9.*" + "brianium/paratest": "7.*", + "phpunit/phpunit": "11.*", + "squizlabs/php_codesniffer": "3.*" }, "type": "library", "autoload": { @@ -3134,9 +3134,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.39.20" + "source": "https://github.com/appwrite/sdk-generator/tree/0.39.21" }, - "time": "2024-09-09T15:34:39+00:00" + "time": "2024-09-10T08:49:29+00:00" }, { "name": "doctrine/annotations", @@ -6613,6 +6613,82 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-php81", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, { "name": "symfony/process", "version": "v7.1.3", @@ -6945,30 +7021,37 @@ }, { "name": "twig/twig", - "version": "v3.8.0", + "version": "v3.14.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" + "reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/126b2c97818dbff0cdf3fbfc881aedb3d40aae72", + "reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22" + "symfony/polyfill-php81": "^1.29" }, "require-dev": { "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -7001,7 +7084,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.8.0" + "source": "https://github.com/twigphp/Twig/tree/v3.14.0" }, "funding": [ { @@ -7013,7 +7096,7 @@ "type": "tidelift" } ], - "time": "2023-11-21T18:54:41+00:00" + "time": "2024-09-09T17:55:12+00:00" }, { "name": "webmozart/glob", From 7d588793731d97ae522f0890baba0cc636032d2d Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Mon, 16 Sep 2024 11:18:57 -0400 Subject: [PATCH 184/195] fix: architecture agnostic runtimes in cli --- app/cli.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/cli.php b/app/cli.php index 7c79250f4d..9d0d069513 100644 --- a/app/cli.php +++ b/app/cli.php @@ -1,15 +1,16 @@ getAll(supported: false)); + +// require controllers after overwriting runtimes +require_once __DIR__ . '/controllers/general.php'; + /** * @var Registry $registry * @var Container $container From de95bf677e67586e82e991dd70e288e77648e10b Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Mon, 16 Sep 2024 15:05:20 -0400 Subject: [PATCH 185/195] fix: removing unneeded coroutine --- src/Appwrite/Platform/Tasks/ScheduleBase.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index 026656a729..cb02ec60fd 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -34,14 +34,12 @@ abstract class ScheduleBase extends Action $this->connections = new Connections(); $type = static::getSupportedResource(); - go(function () use ($type) { - $this - ->desc("Execute {$type}s scheduled in Appwrite") - ->inject('pools') - ->inject('getConsoleDB') - ->inject('getProjectDB') - ->callback(fn (array $pools, callable $getConsoleDB, callable $getProjectDB) => $this->action($pools, $getConsoleDB, $getProjectDB)); - }); + $this + ->desc("Execute {$type}s scheduled in Appwrite") + ->inject('pools') + ->inject('getConsoleDB') + ->inject('getProjectDB') + ->callback(fn (array $pools, callable $getConsoleDB, callable $getProjectDB) => $this->action($pools, $getConsoleDB, $getProjectDB)); } /** From 2517605cd1b6cce1277b38eccfcad1fcd8371bf3 Mon Sep 17 00:00:00 2001 From: fogelito Date: Thu, 19 Sep 2024 18:45:00 +0300 Subject: [PATCH 186/195] Add this --- src/Appwrite/Platform/Tasks/ScheduleBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index e013220aa4..79a05dcd13 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -165,7 +165,7 @@ abstract class ScheduleBase extends Action $total = $total + $sum; foreach ($results as $document) { - $localDocument = $schedules[$document['resourceId']] ?? null; + $localDocument = $this->schedules[$document->getInternalId()] ?? null; // Check if resource has been updated since last sync $org = $localDocument !== null ? \strtotime($localDocument['resourceUpdatedAt']) : null; From fe7f39d7a504916e688a9dda12594d47824f79f1 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 19 Sep 2024 13:48:15 -0400 Subject: [PATCH 187/195] fix: reviews --- app/init.php | 1003 +--------------- app/init/resources.php | 1046 +++++++++++++++++ app/realtime.php | 12 +- .../Platform/Tasks/ScheduleExecutions.php | 8 +- 4 files changed, 1057 insertions(+), 1012 deletions(-) create mode 100644 app/init/resources.php diff --git a/app/init.php b/app/init.php index cb97be80fe..0f9e8f0ef6 100644 --- a/app/init.php +++ b/app/init.php @@ -5,8 +5,6 @@ if (\file_exists(__DIR__ . '/../vendor/autoload.php')) { } use Ahc\Jwt\JWT; -use Ahc\Jwt\JWTException; -use Appwrite\Auth\Auth; use Appwrite\Auth\Authentication; use Appwrite\Auth\MFA\Type\TOTP; use Appwrite\Event\Audit; @@ -24,7 +22,6 @@ use Appwrite\Extend\Exception; use Appwrite\GraphQL\Promises\Adapter\Swoole; use Appwrite\GraphQL\Schema; use Appwrite\Hooks\Hooks; -use Appwrite\Network\Validator\Origin; use Appwrite\URL\URL; use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Response\Models; @@ -46,17 +43,13 @@ 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\DI\Container; -use Utopia\DI\Dependency; use Utopia\Domains\Domain; use Utopia\Domains\Validator\PublicDomain; use Utopia\DSN\DSN; use Utopia\Http\Http; use Utopia\Http\Request; -use Utopia\Http\Response; use Utopia\Http\Validator\Hostname; use Utopia\Locale\Locale; use Utopia\Logger\Adapter\AppSignal; @@ -66,16 +59,8 @@ use Utopia\Logger\Adapter\Sentry; use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Queue; -use Utopia\Queue\Connection; use Utopia\Registry\Registry; -use Utopia\Storage\Device; -use Utopia\Storage\Device\Backblaze; -use Utopia\Storage\Device\DOSpaces; -use Utopia\Storage\Device\Linode; use Utopia\Storage\Device\Local; -use Utopia\Storage\Device\S3; -use Utopia\Storage\Device\Wasabi; -use Utopia\Storage\Storage; use Utopia\System\System; use Utopia\VCS\Adapter\Git\GitHub; @@ -102,88 +87,6 @@ if (!Http::isProduction()) { } -function getDevice($root): Device -{ - $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); - - if (!empty($connection)) { - $acl = 'private'; - $device = Storage::DEVICE_LOCAL; - $accessKey = ''; - $accessSecret = ''; - $bucket = ''; - $region = ''; - - try { - $dsn = new DSN($connection); - $device = $dsn->getScheme(); - $accessKey = $dsn->getUser() ?? ''; - $accessSecret = $dsn->getPassword() ?? ''; - $bucket = $dsn->getPath() ?? ''; - $region = $dsn->getParam('region'); - } catch (\Throwable $e) { - Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); - } - - switch ($device) { - case Storage::DEVICE_S3: - return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case STORAGE::DEVICE_DO_SPACES: - return new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_BACKBLAZE: - return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LINODE: - return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_WASABI: - return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - } - } else { - switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - case Storage::DEVICE_S3: - $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); - $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); - $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); - $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); - $s3Acl = 'private'; - return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); - case Storage::DEVICE_DO_SPACES: - $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); - $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); - $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); - $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); - $doSpacesAcl = 'private'; - return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); - 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); - } - } -} - $container = new Container(); $registry = new Registry(); @@ -485,910 +388,6 @@ class_exists(Authorization::class, true); class_exists(Authentication::class, true); class_exists(Queue\Connection\Redis::class, true); -$log = new Dependency(); -$mode = new Dependency(); -$user = new Dependency(); -$plan = new Dependency(); -$pools = new Dependency(); -$geodb = new Dependency(); -$cache = new Dependency(); -$pools = new Dependency(); -$queue = new Dependency(); -$hooks = new Dependency(); -$logger = new Dependency(); -$locale = new Dependency(); -$schema = new Dependency(); -$github = new Dependency(); -$session = new Dependency(); -$console = new Dependency(); -$project = new Dependency(); -$clients = new Dependency(); -$servers = new Dependency(); -$register = new Dependency(); -$connections = new Dependency(); -$localeCodes = new Dependency(); -$getConsoleDB = new Dependency(); -$getProjectDB = new Dependency(); -$dbForProject = new Dependency(); -$dbForConsole = new Dependency(); -$queueForUsage = new Dependency(); -$queueForMails = new Dependency(); -$authorization = new Dependency(); -$authentication = new Dependency(); -$queueForBuilds = new Dependency(); -$deviceForLocal = new Dependency(); -$deviceForFiles = new Dependency(); -$queueForEvents = new Dependency(); -$queueForAudits = new Dependency(); -$promiseAdapter = new Dependency(); -$schemaVariable = new Dependency(); -$deviceForBuilds = new Dependency(); -$queueForDeletes = new Dependency(); -$requestTimestamp = new Dependency(); -$queueForDatabase = new Dependency(); -$queueForMessaging = new Dependency(); -$queueForFunctions = new Dependency(); -$queueForMigrations = new Dependency(); -$deviceForFunctions = new Dependency(); -$passwordsDictionary = new Dependency(); -$queueForCertificates = new Dependency(); - - -$plan - ->setName('plan') - ->setCallback(fn () => []); - -$mode - ->setName('mode') - ->inject('request') - ->setCallback(function (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)); - }); - -$user - ->setName('user') - ->inject('mode') - ->inject('project') - ->inject('console') - ->inject('request') - ->inject('response') - ->inject('dbForProject') - ->inject('dbForConsole') - ->inject('authorization') - ->inject('authentication') - ->setCallback(function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization, Authentication $authentication) { - $authorization->setDefaultStatus(true); - $authentication->setCookieName('a_session_' . $project->getId()); - - if (APP_MODE_ADMIN === $mode) { - $authentication->setCookieName('a_session_' . $console->getId()); - } - - $session = Auth::decodeSession( - $request->getCookie( - $authentication->getCookieName(), // Get sessions - $request->getCookie($authentication->getCookieName() . '_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[$authentication->getCookieName()])) ? $fallback[$authentication->getCookieName()] : '')); - } - - $authentication->setUnique($session['id'] ?? ''); - $authentication->setSecret($session['secret'] ?? ''); - - if (APP_MODE_ADMIN !== $mode) { - if ($project->isEmpty()) { - $user = new Document([]); - } else { - if ($project->getId() === 'console') { - $user = $dbForConsole->getDocument('users', $authentication->getUnique()); - } else { - $user = $dbForProject->getDocument('users', $authentication->getUnique()); - } - } - } else { - $user = $dbForConsole->getDocument('users', $authentication->getUnique()); - } - - if ( - $user->isEmpty() // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), $authentication->getSecret()) - ) { // Validate user has valid login token - $user = new Document([]); - } - - if (APP_MODE_ADMIN === $mode) { - if ($user->find('teamId', $project->getAttribute('teamId'), '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) { - $request->removeHeader('x-appwrite-jwt'); - 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([]); - } - } - } - - // Adds logs to database queries - $dbForProject->setMetadata('user', $user->getId()); - $dbForConsole->setMetadata('user', $user->getId()); - - return $user; - }); - - -$session - ->setName('session') - ->inject('user') - ->inject('project') - ->inject('authorization') - ->inject('authentication') - ->setCallback(function (Document $user, Document $project, Authorization $authorization, Authentication $authentication) { - if ($user->isEmpty()) { - return; - } - - $sessions = $user->getAttribute('sessions', []); - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; - $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret(), $authDuration); - - if (!$sessionId) { - return; - } - - foreach ($sessions as $session) { - if ($sessionId === $session->getId()) { - return $session; - } - } - - return; - }); - -$console - ->setName('console') - ->setCallback(function () { - return new Document([ - '$id' => ID::custom('console'), - '$internalId' => ID::custom('console'), - 'name' => 'Appwrite', - '$collection' => ID::custom('projects'), - 'description' => 'Appwrite core engine', - 'logo' => '', - 'teamId' => -1, - 'webhooks' => [], - 'keys' => [], - 'platforms' => [ - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Localhost', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => 'localhost', - ], // Current host is added on app init - ], - 'legalName' => '', - 'legalCountry' => '', - 'legalState' => '', - 'legalCity' => '', - 'legalAddress' => '', - 'legalTaxId' => '', - 'auths' => [ - 'mockNumbers' => [], - 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', - 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds - 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' - ], - 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], - 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], - 'oAuthProviders' => [ - 'githubEnabled' => true, - 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), - 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') - ], - ]); - }); - - -$project - ->setName('project') - ->inject('dbForConsole') - ->inject('request') - ->inject('console') - ->inject('authorization') - ->setCallback(function (Database $dbForConsole, Request $request, Document $console, Authorization $authorization) { - $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); - - if (empty($projectId) || $projectId === 'console') { - return $console; - } - - $project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - - return $project; - }); - -$pools - ->setName('pools') - ->inject('registry') - ->setCallback(function (Registry $registry) { - return $registry->get('pools'); - }); - -$dbForProject - ->setName('dbForProject') - ->inject('cache') - ->inject('pools') - ->inject('project') - ->inject('dbForConsole') - ->inject('authorization') - ->inject('connections') - ->setCallback(function (Cache $cache, array $pools, Document $project, Database $dbForConsole, Authorization $authorization, Connections $connections) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; - $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($connectionDsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($connectionDsn->getPath()); - - $database = new Database($adapter, $cache); - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - $database->setAuthorization($authorization); - return $database; - }); - -$dbForConsole - ->setName('dbForConsole') - ->inject('getConsoleDB') - ->inject('connections') - ->setCallback(function (callable $getConsoleDB, Connections $connections): Database { - [$connection,$pool, $database] = $getConsoleDB(); - $connections->add($connection, $pool); - - return $database; - }); - -$cache - ->setName('cache') - ->inject('pools') - ->inject('connections') - ->setCallback(function (array $pools, Connections $connections) { - $adapters = []; - $databases = Config::getParam('pools-cache'); - - foreach ($databases as $database) { - $pool = $pools['pools-cache-' . $database]['pool']; - $dsn = $pools['pools-cache-' . $database]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapters[] = new CacheRedis($connection); - } - - return new Cache(new Sharding($adapters)); - }); - -$authorization - ->setName('authorization') - ->setCallback(function (): Authorization { - return new Authorization(); - }); - -$authentication - ->setName('authentication') - ->setCallback(function (): Authentication { - return new Authentication(); - }); - -$register - ->setName('registry') - ->setCallback(function () use (&$registry): Registry { - return $registry; - }); - -$pools - ->setName('pools') - ->inject('registry') - ->setCallback(function (Registry $registry) { - return $registry->get('pools'); - }); - -$logger - ->setName('logger') - ->inject('registry') - ->setCallback(function (Registry $registry) { - return $registry->get('logger'); - }); - -$log - ->setName('log') - ->setCallback(function () { - return new Log(); - }); - -$connections - ->setName('connections') - ->setCallback(function () { - return new Connections(); - }); - -$locale - ->setName('locale') - ->setCallback(fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); - -$localeCodes - ->setName('localeCodes') - ->setCallback(fn () => array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []))); - -$queue - ->setName('queue') - ->inject('pools') - ->inject('connections') - ->setCallback(function (array $pools, Connections $connections) { - $pool = $pools['pools-queue-queue']['pool']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - return new Queue\Connection\Redis($connection); - }); - -$queueForMessaging - ->setName('queueForMessaging') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Messaging($queue); - }); - -$queueForMails - ->setName('queueForMails') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Mail($queue); - }); - -$queueForBuilds - ->setName('queueForBuilds') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Build($queue); - }); - -$queueForDatabase - ->setName('queueForDatabase') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new EventDatabase($queue); - }); - -$queueForDeletes - ->setName('queueForDeletes') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Delete($queue); - }); - -$queueForEvents - ->setName('queueForEvents') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Event($queue); - }); - -$queueForAudits - ->setName('queueForAudits') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Audit($queue); - }); - -$queueForFunctions - ->setName('queueForFunctions') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Func($queue); - }); - -$queueForUsage - ->setName('queueForUsage') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Usage($queue); - }); - -$queueForCertificates - ->setName('queueForCertificates') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Certificate($queue); - }); - -$queueForMigrations - ->setName('queueForMigrations') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Migration($queue); - }); - -$deviceForLocal - ->setName('deviceForLocal') - ->setCallback(function () { - return new Local(); - }); - -$deviceForFiles - ->setName('deviceForFiles') - ->inject('project') - ->setCallback(function ($project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); - }); - -$deviceForFunctions - ->setName('deviceForFunctions') - ->inject('project') - ->setCallback(function ($project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); - }); - -$deviceForBuilds - ->setName('deviceForBuilds') - ->inject('project') - ->setCallback(function ($project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); - }); - -$clients - ->setName('clients') - ->inject('request') - ->inject('console') - ->inject('project') - ->setCallback(function (Request $request, Document $console, Document $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) && isset($node['hostname']) && !empty($node['hostname'])) - ) - ); - - $clients = \array_unique( - \array_merge( - $clientsConsole, - \array_map( - fn ($node) => $node['hostname'], - \array_filter( - $project->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) - ) - ) - ) - ); - - return $clients; - }); - -$servers - ->setName('servers') - ->setCallback(function () { - $platforms = Config::getParam('platforms'); - $server = $platforms[APP_PLATFORM_SERVER]; - - $languages = array_map(function ($language) { - return strtolower($language['name']); - }, $server['sdks']); - - return $languages; - }); - -$geodb - ->setName('geodb') - ->inject('registry') - ->setCallback(function (Registry $register) { - return $register->get('geodb'); - }); - -$passwordsDictionary - ->setName('passwordsDictionary') - ->setCallback(function () { - $content = file_get_contents(__DIR__ . '/assets/security/10k-common-passwords'); - $content = explode("\n", $content); - $content = array_flip($content); - return $content; - }); - -$hooks - ->setName('hooks') - ->inject('registry') - ->setCallback(function (Registry $registry) { - return $registry->get('hooks'); - }); - -$github - ->setName('gitHub') - ->inject('cache') - ->setCallback(function (Cache $cache) { - return new GitHub($cache); - }); - -$requestTimestamp - ->setName('requestTimestamp') - ->inject('request') - ->setCallback(function ($request) { - $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; - }); -$getConsoleDB - ->setName('getConsoleDB') - ->inject('pools') - ->inject('cache') - ->inject('authorization') - ->inject('connections') - ->setCallback(function (array $pools, Cache $cache, Authorization $authorization) { - return function () use ($pools, $cache, $authorization): array { - $pool = $pools['pools-console-console']['pool']; - $dsn = $pools['pools-console-console']['dsn']; - $connection = $pool->get(); - - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - - $database = new Database($adapter, $cache); - $database->setAuthorization($authorization); - - $database - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - return [$connection, $pool, $database]; - }; - }); - -$getProjectDB - ->setName('getProjectDB') - ->inject('pools') - ->inject('dbForConsole') - ->inject('cache') - ->inject('authorization') - ->inject('connections') - ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $authorization, Connections $connections) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $authorization, $connections): Database { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if (isset($databases[$dsn->getHost()])) { - $database = $databases[$dsn->getHost()]; - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; - } - - $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; - $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($connectionDsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - $adapter->setDatabase($connectionDsn->getPath()); - - $database = new Database($adapter, $cache); - $database->setAuthorization($authorization); - - $databases[$dsn->getHost()] = $database; - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; - }; - }); - -$promiseAdapter - ->setName('promiseAdapter') - ->setCallback(function () use ($registry) { - return $registry->get('promiseAdapter'); - }); - -$schemaVariable - ->setName('schemaVariable') - ->setCallback(fn () => new Schema()); - -$schema - ->setName('schema') - ->inject('http') - ->inject('context') - ->inject('request') - ->inject('response') - ->inject('dbForProject') - ->inject('authorization') - ->inject('schemaVariable') - ->setCallback(function (Http $http, Container $context, Request $request, Response $response, Database $dbForProject, Authorization $authorization, $schemaVariable) { - $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, $authorization) { - $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']); - - 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']); - - return [ - 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'documentId' => $documentId, - 'data' => $args, - 'permissions' => $permissions, - ]; - }, - ]; - - return $schemaVariable->build( - $http, - $request, - $response, - $context, - $complexity, - $attributes, - $urls, - $params, - ); - }); - -$container->set($log); -$container->set($mode); -$container->set($user); -$container->set($plan); -$container->set($cache); -$container->set($pools); -$container->set($queue); -$container->set($geodb); -$container->set($hooks); -$container->set($locale); -$container->set($schema); -$container->set($github); -$container->set($logger); -$container->set($session); -$container->set($console); -$container->set($project); -$container->set($clients); -$container->set($servers); -$container->set($register); -$container->set($connections); -$container->set($localeCodes); -$container->set($dbForProject); -$container->set($dbForConsole); -$container->set($getConsoleDB); -$container->set($getProjectDB); -$container->set($queueForUsage); -$container->set($queueForMails); -$container->set($authorization); -$container->set($authentication); -$container->set($schemaVariable); -$container->set($queueForBuilds); -$container->set($queueForEvents); -$container->set($queueForAudits); -$container->set($deviceForLocal); -$container->set($deviceForFiles); -$container->set($promiseAdapter); -$container->set($queueForDeletes); -$container->set($deviceForBuilds); -$container->set($queueForDatabase); -$container->set($requestTimestamp); -$container->set($queueForMessaging); -$container->set($queueForFunctions); -$container->set($queueForMigrations); -$container->set($deviceForFunctions); -$container->set($passwordsDictionary); -$container->set($queueForCertificates); +require_once __DIR__ . '/init/resources.php'; Models::init(); diff --git a/app/init/resources.php b/app/init/resources.php new file mode 100644 index 0000000000..89d49665ef --- /dev/null +++ b/app/init/resources.php @@ -0,0 +1,1046 @@ +getScheme(); + $accessKey = $dsn->getUser() ?? ''; + $accessSecret = $dsn->getPassword() ?? ''; + $bucket = $dsn->getPath() ?? ''; + $region = $dsn->getParam('region'); + } catch (\Throwable $e) { + Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); + } + + switch ($device) { + case Storage::DEVICE_S3: + return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case STORAGE::DEVICE_DO_SPACES: + return new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_BACKBLAZE: + return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_LINODE: + return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_WASABI: + return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_LOCAL: + default: + return new Local($root); + } + } else { + switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { + case Storage::DEVICE_LOCAL: + default: + return new Local($root); + case Storage::DEVICE_S3: + $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); + $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); + $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); + $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); + $s3Acl = 'private'; + return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); + case Storage::DEVICE_DO_SPACES: + $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); + $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); + $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); + $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); + $doSpacesAcl = 'private'; + return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); + 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); + } + } +} + +$log = new Dependency(); +$mode = new Dependency(); +$user = new Dependency(); +$plan = new Dependency(); +$pools = new Dependency(); +$geodb = new Dependency(); +$cache = new Dependency(); +$pools = new Dependency(); +$queue = new Dependency(); +$hooks = new Dependency(); +$logger = new Dependency(); +$locale = new Dependency(); +$schema = new Dependency(); +$github = new Dependency(); +$session = new Dependency(); +$console = new Dependency(); +$project = new Dependency(); +$clients = new Dependency(); +$servers = new Dependency(); +$register = new Dependency(); +$connections = new Dependency(); +$localeCodes = new Dependency(); +$getConsoleDB = new Dependency(); +$getProjectDB = new Dependency(); +$dbForProject = new Dependency(); +$dbForConsole = new Dependency(); +$queueForUsage = new Dependency(); +$queueForMails = new Dependency(); +$authorization = new Dependency(); +$authentication = new Dependency(); +$queueForBuilds = new Dependency(); +$deviceForLocal = new Dependency(); +$deviceForFiles = new Dependency(); +$queueForEvents = new Dependency(); +$queueForAudits = new Dependency(); +$promiseAdapter = new Dependency(); +$schemaVariable = new Dependency(); +$deviceForBuilds = new Dependency(); +$queueForDeletes = new Dependency(); +$requestTimestamp = new Dependency(); +$queueForDatabase = new Dependency(); +$queueForMessaging = new Dependency(); +$queueForFunctions = new Dependency(); +$queueForMigrations = new Dependency(); +$deviceForFunctions = new Dependency(); +$passwordsDictionary = new Dependency(); +$queueForCertificates = new Dependency(); + + +$plan + ->setName('plan') + ->setCallback(fn () => []); + +$mode + ->setName('mode') + ->inject('request') + ->setCallback(function (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)); + }); + +$user + ->setName('user') + ->inject('mode') + ->inject('project') + ->inject('console') + ->inject('request') + ->inject('response') + ->inject('dbForProject') + ->inject('dbForConsole') + ->inject('authorization') + ->inject('authentication') + ->setCallback(function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization, Authentication $authentication) { + $authorization->setDefaultStatus(true); + $authentication->setCookieName('a_session_' . $project->getId()); + + if (APP_MODE_ADMIN === $mode) { + $authentication->setCookieName('a_session_' . $console->getId()); + } + + $session = Auth::decodeSession( + $request->getCookie( + $authentication->getCookieName(), // Get sessions + $request->getCookie($authentication->getCookieName() . '_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[$authentication->getCookieName()])) ? $fallback[$authentication->getCookieName()] : '')); + } + + $authentication->setUnique($session['id'] ?? ''); + $authentication->setSecret($session['secret'] ?? ''); + + if (APP_MODE_ADMIN !== $mode) { + if ($project->isEmpty()) { + $user = new Document([]); + } else { + if ($project->getId() === 'console') { + $user = $dbForConsole->getDocument('users', $authentication->getUnique()); + } else { + $user = $dbForProject->getDocument('users', $authentication->getUnique()); + } + } + } else { + $user = $dbForConsole->getDocument('users', $authentication->getUnique()); + } + + if ( + $user->isEmpty() // Check a document has been found in the DB + || !Auth::sessionVerify($user->getAttribute('sessions', []), $authentication->getSecret()) + ) { // Validate user has valid login token + $user = new Document([]); + } + + if (APP_MODE_ADMIN === $mode) { + if ($user->find('teamId', $project->getAttribute('teamId'), '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) { + $request->removeHeader('x-appwrite-jwt'); + 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([]); + } + } + } + + // Adds logs to database queries + $dbForProject->setMetadata('user', $user->getId()); + $dbForConsole->setMetadata('user', $user->getId()); + + return $user; + }); + + +$session + ->setName('session') + ->inject('user') + ->inject('project') + ->inject('authorization') + ->inject('authentication') + ->setCallback(function (Document $user, Document $project, Authorization $authorization, Authentication $authentication) { + if ($user->isEmpty()) { + return; + } + + $sessions = $user->getAttribute('sessions', []); + $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret(), $authDuration); + + if (!$sessionId) { + return; + } + + foreach ($sessions as $session) { + if ($sessionId === $session->getId()) { + return $session; + } + } + + return; + }); + +$console + ->setName('console') + ->setCallback(function () { + return new Document([ + '$id' => ID::custom('console'), + '$internalId' => ID::custom('console'), + 'name' => 'Appwrite', + '$collection' => ID::custom('projects'), + 'description' => 'Appwrite core engine', + 'logo' => '', + 'teamId' => -1, + 'webhooks' => [], + 'keys' => [], + 'platforms' => [ + [ + '$collection' => ID::custom('platforms'), + 'name' => 'Localhost', + 'type' => Origin::CLIENT_TYPE_WEB, + 'hostname' => 'localhost', + ], // Current host is added on app init + ], + 'legalName' => '', + 'legalCountry' => '', + 'legalState' => '', + 'legalCity' => '', + 'legalAddress' => '', + 'legalTaxId' => '', + 'auths' => [ + 'mockNumbers' => [], + 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', + 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user + 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds + 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' + ], + 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], + 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], + 'oAuthProviders' => [ + 'githubEnabled' => true, + 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), + 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') + ], + ]); + }); + + +$project + ->setName('project') + ->inject('dbForConsole') + ->inject('request') + ->inject('console') + ->inject('authorization') + ->setCallback(function (Database $dbForConsole, Request $request, Document $console, Authorization $authorization) { + $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); + + if (empty($projectId) || $projectId === 'console') { + return $console; + } + + $project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + + return $project; + }); + +$pools + ->setName('pools') + ->inject('registry') + ->setCallback(function (Registry $registry) { + return $registry->get('pools'); + }); + +$dbForProject + ->setName('dbForProject') + ->inject('cache') + ->inject('pools') + ->inject('project') + ->inject('dbForConsole') + ->inject('authorization') + ->inject('connections') + ->setCallback(function (Cache $cache, array $pools, Document $project, Database $dbForConsole, Authorization $authorization, Connections $connections) { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; + $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($connectionDsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($connectionDsn->getPath()); + + $database = new Database($adapter, $cache); + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + if ($dsn->getHost() === DATABASE_SHARED_TABLES) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + $database->setAuthorization($authorization); + return $database; + }); + +$dbForConsole + ->setName('dbForConsole') + ->inject('getConsoleDB') + ->inject('connections') + ->setCallback(function (callable $getConsoleDB, Connections $connections): Database { + [$connection,$pool, $database] = $getConsoleDB(); + $connections->add($connection, $pool); + + return $database; + }); + +$cache + ->setName('cache') + ->inject('pools') + ->inject('connections') + ->setCallback(function (array $pools, Connections $connections) { + $adapters = []; + $databases = Config::getParam('pools-cache'); + + foreach ($databases as $database) { + $pool = $pools['pools-cache-' . $database]['pool']; + $dsn = $pools['pools-cache-' . $database]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + + $adapters[] = new CacheRedis($connection); + } + + return new Cache(new Sharding($adapters)); + }); + +$authorization + ->setName('authorization') + ->setCallback(function (): Authorization { + return new Authorization(); + }); + +$authentication + ->setName('authentication') + ->setCallback(function (): Authentication { + return new Authentication(); + }); + +$register + ->setName('registry') + ->setCallback(function () use (&$registry): Registry { + return $registry; + }); + +$pools + ->setName('pools') + ->inject('registry') + ->setCallback(function (Registry $registry) { + return $registry->get('pools'); + }); + +$logger + ->setName('logger') + ->inject('registry') + ->setCallback(function (Registry $registry) { + return $registry->get('logger'); + }); + +$log + ->setName('log') + ->setCallback(function () { + return new Log(); + }); + +$connections + ->setName('connections') + ->setCallback(function () { + return new Connections(); + }); + +$locale + ->setName('locale') + ->setCallback(fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); + +$localeCodes + ->setName('localeCodes') + ->setCallback(fn () => array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []))); + +$queue + ->setName('queue') + ->inject('pools') + ->inject('connections') + ->setCallback(function (array $pools, Connections $connections) { + $pool = $pools['pools-queue-queue']['pool']; + $connection = $pool->get(); + $connections->add($connection, $pool); + + return new Queue\Connection\Redis($connection); + }); + +$queueForMessaging + ->setName('queueForMessaging') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Messaging($queue); + }); + +$queueForMails + ->setName('queueForMails') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Mail($queue); + }); + +$queueForBuilds + ->setName('queueForBuilds') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Build($queue); + }); + +$queueForDatabase + ->setName('queueForDatabase') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new EventDatabase($queue); + }); + +$queueForDeletes + ->setName('queueForDeletes') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Delete($queue); + }); + +$queueForEvents + ->setName('queueForEvents') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Event($queue); + }); + +$queueForAudits + ->setName('queueForAudits') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Audit($queue); + }); + +$queueForFunctions + ->setName('queueForFunctions') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Func($queue); + }); + +$queueForUsage + ->setName('queueForUsage') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Usage($queue); + }); + +$queueForCertificates + ->setName('queueForCertificates') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Certificate($queue); + }); + +$queueForMigrations + ->setName('queueForMigrations') + ->inject('queue') + ->setCallback(function (Connection $queue) { + return new Migration($queue); + }); + +$deviceForLocal + ->setName('deviceForLocal') + ->setCallback(function () { + return new Local(); + }); + +$deviceForFiles + ->setName('deviceForFiles') + ->inject('project') + ->setCallback(function ($project) { + return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); + }); + +$deviceForFunctions + ->setName('deviceForFunctions') + ->inject('project') + ->setCallback(function ($project) { + return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); + }); + +$deviceForBuilds + ->setName('deviceForBuilds') + ->inject('project') + ->setCallback(function ($project) { + return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); + }); + +$clients + ->setName('clients') + ->inject('request') + ->inject('console') + ->inject('project') + ->setCallback(function (Request $request, Document $console, Document $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) && isset($node['hostname']) && !empty($node['hostname'])) + ) + ); + + $clients = \array_unique( + \array_merge( + $clientsConsole, + \array_map( + fn ($node) => $node['hostname'], + \array_filter( + $project->getAttribute('platforms', []), + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) + ) + ) + ) + ); + + return $clients; + }); + +$servers + ->setName('servers') + ->setCallback(function () { + $platforms = Config::getParam('platforms'); + $server = $platforms[APP_PLATFORM_SERVER]; + + $languages = array_map(function ($language) { + return strtolower($language['name']); + }, $server['sdks']); + + return $languages; + }); + +$geodb + ->setName('geodb') + ->inject('registry') + ->setCallback(function (Registry $register) { + return $register->get('geodb'); + }); + +$passwordsDictionary + ->setName('passwordsDictionary') + ->setCallback(function () { + $content = file_get_contents(__DIR__ . '/../assets/security/10k-common-passwords'); + $content = explode("\n", $content); + $content = array_flip($content); + return $content; + }); + +$hooks + ->setName('hooks') + ->inject('registry') + ->setCallback(function (Registry $registry) { + return $registry->get('hooks'); + }); + +$github + ->setName('gitHub') + ->inject('cache') + ->setCallback(function (Cache $cache) { + return new GitHub($cache); + }); + +$requestTimestamp + ->setName('requestTimestamp') + ->inject('request') + ->setCallback(function ($request) { + $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; + }); +$getConsoleDB + ->setName('getConsoleDB') + ->inject('pools') + ->inject('cache') + ->inject('authorization') + ->inject('connections') + ->setCallback(function (array $pools, Cache $cache, Authorization $authorization) { + return function () use ($pools, $cache, $authorization): array { + $pool = $pools['pools-console-console']['pool']; + $dsn = $pools['pools-console-console']['dsn']; + $connection = $pool->get(); + + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); + + $database = new Database($adapter, $cache); + $database->setAuthorization($authorization); + + $database + ->setNamespace('_console') + ->setMetadata('host', \gethostname()) + ->setMetadata('project', 'console') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + return [$connection, $pool, $database]; + }; + }); + +$getProjectDB + ->setName('getProjectDB') + ->inject('pools') + ->inject('dbForConsole') + ->inject('cache') + ->inject('authorization') + ->inject('connections') + ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $authorization, Connections $connections) { + $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools + + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $authorization, $connections): Database { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + if (isset($databases[$dsn->getHost()])) { + $database = $databases[$dsn->getHost()]; + + if ($dsn->getHost() === DATABASE_SHARED_TABLES) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + return $database; + } + + $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; + $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; + + $connection = $pool->get(); + $connections->add($connection, $pool); + $adapter = match ($connectionDsn->getScheme()) { + 'mariadb' => new MariaDB($connection), + 'mysql' => new MySQL($connection), + default => null + }; + $adapter->setDatabase($connectionDsn->getPath()); + + $database = new Database($adapter, $cache); + $database->setAuthorization($authorization); + + $databases[$dsn->getHost()] = $database; + + if ($dsn->getHost() === DATABASE_SHARED_TABLES) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + return $database; + }; + }); + +$promiseAdapter + ->setName('promiseAdapter') + ->setCallback(function () use ($registry) { + return $registry->get('promiseAdapter'); + }); + +$schemaVariable + ->setName('schemaVariable') + ->setCallback(fn () => new Schema()); + +$schema + ->setName('schema') + ->inject('http') + ->inject('context') + ->inject('request') + ->inject('response') + ->inject('dbForProject') + ->inject('authorization') + ->inject('schemaVariable') + ->setCallback(function (Http $http, Container $context, Request $request, Response $response, Database $dbForProject, Authorization $authorization, $schemaVariable) { + $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, $authorization) { + $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']); + + 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']); + + return [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'documentId' => $documentId, + 'data' => $args, + 'permissions' => $permissions, + ]; + }, + ]; + + return $schemaVariable->build( + $http, + $request, + $response, + $context, + $complexity, + $attributes, + $urls, + $params, + ); + }); + +$container->set($log); +$container->set($mode); +$container->set($user); +$container->set($plan); +$container->set($cache); +$container->set($pools); +$container->set($queue); +$container->set($geodb); +$container->set($hooks); +$container->set($locale); +$container->set($schema); +$container->set($github); +$container->set($logger); +$container->set($session); +$container->set($console); +$container->set($project); +$container->set($clients); +$container->set($servers); +$container->set($register); +$container->set($connections); +$container->set($localeCodes); +$container->set($dbForProject); +$container->set($dbForConsole); +$container->set($getConsoleDB); +$container->set($getProjectDB); +$container->set($queueForUsage); +$container->set($queueForMails); +$container->set($authorization); +$container->set($authentication); +$container->set($schemaVariable); +$container->set($queueForBuilds); +$container->set($queueForEvents); +$container->set($queueForAudits); +$container->set($deviceForLocal); +$container->set($deviceForFiles); +$container->set($promiseAdapter); +$container->set($queueForDeletes); +$container->set($deviceForBuilds); +$container->set($queueForDatabase); +$container->set($requestTimestamp); +$container->set($queueForMessaging); +$container->set($queueForFunctions); +$container->set($queueForMigrations); +$container->set($deviceForFunctions); +$container->set($passwordsDictionary); +$container->set($queueForCertificates); diff --git a/app/realtime.php b/app/realtime.php index 4d2cceae9a..f4460e7a11 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -273,10 +273,10 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats $start = time(); $pools = $container->get('pools'); + $pool = $pools['pools-pubsub-pubsub']['pool']; + /** @var Connections $connections */ $connections = $container->get('connections'); - - $pool = $pools['pools-pubsub-pubsub']['pool']; $connection = $pool->get(); $connections->add($connection, $pool); @@ -301,12 +301,12 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) { $connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId])); - $consoleDatabase = $container->get('dbForConsole'); + $dbForConsole = $container->get('dbForConsole'); - $project = $authorization->skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); - $database = $container->get('getProjectDB')($project); + $project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $dbForProject = $container->get('getProjectDB')($project); - $user = $database->getDocument('users', $userId); + $user = $dbForProject->getDocument('users', $userId); $roles = Auth::getRoles($user, $authorization); $channels = $realtime->connections[$connection]['channels']; diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index 3654c7d75d..b67a892b2e 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -26,11 +26,11 @@ class ScheduleExecutions extends ScheduleBase [$connection,$pool, $dbForConsole] = $getConsoleDB(); $this->connections->add($connection, $pool); - $pool = $pools['pools-queue-queue']['pool']; - $connection = $pool->get(); - $this->connections->add($connection, $pool); + $queuePool = $pools['pools-queue-queue']['pool']; + $queueConnection = $queuePool->get(); + $this->connections->add($queueConnection, $queuePool); - $queueForFunctions = new Func(new Redis($connection)); + $queueForFunctions = new Func(new Redis($queueConnection)); $intervalEnd = (new \DateTime())->modify('+' . self::ENQUEUE_TIMER . ' seconds'); foreach ($this->schedules as $schedule) { From 24eef9accf3cdf4e528d8aa24c71fc97c04a97e2 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:28:24 -0400 Subject: [PATCH 188/195] fix: injection name --- app/controllers/api/avatars.php | 24 ++++++++++++------------ app/controllers/api/vcs.php | 30 +++++++++++++++--------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 54ba096c5d..2b8327e898 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -604,9 +604,9 @@ Http::get('/v1/cards/cloud') ->inject('contributors') ->inject('employees') ->inject('logger') - ->inject('auth') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { - $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); + ->inject('authorization') + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) { + $user = $authorization->skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -617,7 +617,7 @@ Http::get('/v1/cards/cloud') $email = $user->getAttribute('email', ''); $createdAt = new \DateTime($user->getCreatedAt()); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $auth); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $authorization); $githubName = $gitHub['name'] ?? ''; $githubId = $gitHub['id'] ?? ''; @@ -812,9 +812,9 @@ Http::get('/v1/cards/cloud-back') ->inject('contributors') ->inject('employees') ->inject('logger') - ->inject('auth') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { - $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); + ->inject('authorization') + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) { + $user = $authorization->skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -824,7 +824,7 @@ Http::get('/v1/cards/cloud-back') $userId = $user->getId(); $email = $user->getAttribute('email', ''); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $auth); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $authorization); $githubId = $gitHub['id'] ?? ''; $isHero = \array_key_exists($email, $heroes); @@ -891,9 +891,9 @@ Http::get('/v1/cards/cloud-og') ->inject('contributors') ->inject('employees') ->inject('logger') - ->inject('auth') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { - $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); + ->inject('authorization') + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) { + $user = $authorization->skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -908,7 +908,7 @@ Http::get('/v1/cards/cloud-og') $email = $user->getAttribute('email', ''); $createdAt = new \DateTime($user->getCreatedAt()); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $auth); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $authorization); $githubName = $gitHub['name'] ?? ''; $githubId = $gitHub['id'] ?? ''; diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 987d84bb7e..ef325ee56b 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -900,9 +900,9 @@ Http::post('/v1/vcs/github/events') ->inject('dbForConsole') ->inject('getProjectDB') ->inject('queueForBuilds') - ->inject('auth') + ->inject('authorization') ->action( - function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $auth) use ($createGitDeployments) { + function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $authorization) use ($createGitDeployments) { $payload = $request->getRawPayload(); $signatureRemote = $request->getHeader('x-hub-signature-256', ''); $signatureLocal = System::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', ''); @@ -936,14 +936,14 @@ Http::post('/v1/vcs/github/events') $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); //find functionId from functions table - $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ + $repositories = $authorization->skip(fn () => $dbForConsole->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::limit(100), ])); // create new deployment only on push and not when branch is created if (!$providerBranchCreated) { - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $auth); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $authorization); } } elseif ($event == $github::EVENT_INSTALLATION) { if ($parsedPayload["action"] == "deleted") { @@ -956,13 +956,13 @@ Http::post('/v1/vcs/github/events') ]); foreach ($installations as $installation) { - $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ + $repositories = $authorization->skip(fn () => $dbForConsole->find('repositories', [ Query::equal('installationInternalId', [$installation->getInternalId()]), Query::limit(1000) ])); foreach ($repositories as $repository) { - $auth->skip(fn () => $dbForConsole->deleteDocument('repositories', $repository->getId())); + $authorization->skip(fn () => $dbForConsole->deleteDocument('repositories', $repository->getId())); } $dbForConsole->deleteDocument('installations', $installation->getId()); @@ -994,12 +994,12 @@ Http::post('/v1/vcs/github/events') $providerCommitAuthor = $commitDetails["commitAuthor"] ?? ''; $providerCommitMessage = $commitDetails["commitMessage"] ?? ''; - $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ + $repositories = $authorization->skip(fn () => $dbForConsole->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::orderDesc('$createdAt') ])); - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $auth); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $authorization); } elseif ($parsedPayload["action"] == "closed") { // Allowed external contributions cleanup @@ -1008,7 +1008,7 @@ Http::post('/v1/vcs/github/events') $external = $parsedPayload["external"] ?? true; if ($external) { - $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ + $repositories = $authorization->skip(fn () => $dbForConsole->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::orderDesc('$createdAt') ])); @@ -1019,7 +1019,7 @@ Http::post('/v1/vcs/github/events') if (\in_array($providerPullRequestId, $providerPullRequestIds)) { $providerPullRequestIds = \array_diff($providerPullRequestIds, [$providerPullRequestId]); $repository = $repository->setAttribute('providerPullRequestIds', $providerPullRequestIds); - $repository = $auth->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); + $repository = $authorization->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); } } } @@ -1172,15 +1172,15 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito ->inject('dbForConsole') ->inject('getProjectDB') ->inject('queueForBuilds') - ->inject('auth') - ->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $auth) use ($createGitDeployments) { + ->inject('authorization') + ->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $authorization) use ($createGitDeployments) { $installation = $dbForConsole->getDocument('installations', $installationId); if ($installation->isEmpty()) { throw new Exception(Exception::INSTALLATION_NOT_FOUND); } - $repository = $auth->skip(fn () => $dbForConsole->getDocument('repositories', $repositoryId, [ + $repository = $authorization->skip(fn () => $dbForConsole->getDocument('repositories', $repositoryId, [ Query::equal('projectInternalId', [$project->getInternalId()]) ])); @@ -1197,7 +1197,7 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito // TODO: Delete from array when PR is closed - $repository = $auth->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); + $repository = $authorization->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); $privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); $githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID'); @@ -1221,7 +1221,7 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito $providerBranch = \explode(':', $pullRequestResponse['head']['label'])[1] ?? ''; $providerCommitHash = $pullRequestResponse['head']['sha'] ?? ''; - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $auth); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $authorization); $response->noContent(); }); From 904e4c86507a26369e409b033336adc3c8d71e09 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:24:12 -0400 Subject: [PATCH 189/195] Revert "Fix auth injection" --- app/controllers/api/avatars.php | 24 ++++++++++++------------ app/controllers/api/vcs.php | 30 +++++++++++++++--------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 2b8327e898..54ba096c5d 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -604,9 +604,9 @@ Http::get('/v1/cards/cloud') ->inject('contributors') ->inject('employees') ->inject('logger') - ->inject('authorization') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) { - $user = $authorization->skip(fn () => $dbForConsole->getDocument('users', $userId)); + ->inject('auth') + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { + $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -617,7 +617,7 @@ Http::get('/v1/cards/cloud') $email = $user->getAttribute('email', ''); $createdAt = new \DateTime($user->getCreatedAt()); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $authorization); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $auth); $githubName = $gitHub['name'] ?? ''; $githubId = $gitHub['id'] ?? ''; @@ -812,9 +812,9 @@ Http::get('/v1/cards/cloud-back') ->inject('contributors') ->inject('employees') ->inject('logger') - ->inject('authorization') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) { - $user = $authorization->skip(fn () => $dbForConsole->getDocument('users', $userId)); + ->inject('auth') + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { + $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -824,7 +824,7 @@ Http::get('/v1/cards/cloud-back') $userId = $user->getId(); $email = $user->getAttribute('email', ''); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $authorization); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $auth); $githubId = $gitHub['id'] ?? ''; $isHero = \array_key_exists($email, $heroes); @@ -891,9 +891,9 @@ Http::get('/v1/cards/cloud-og') ->inject('contributors') ->inject('employees') ->inject('logger') - ->inject('authorization') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) { - $user = $authorization->skip(fn () => $dbForConsole->getDocument('users', $userId)); + ->inject('auth') + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { + $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -908,7 +908,7 @@ Http::get('/v1/cards/cloud-og') $email = $user->getAttribute('email', ''); $createdAt = new \DateTime($user->getCreatedAt()); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $authorization); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $auth); $githubName = $gitHub['name'] ?? ''; $githubId = $gitHub['id'] ?? ''; diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index ef325ee56b..987d84bb7e 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -900,9 +900,9 @@ Http::post('/v1/vcs/github/events') ->inject('dbForConsole') ->inject('getProjectDB') ->inject('queueForBuilds') - ->inject('authorization') + ->inject('auth') ->action( - function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $authorization) use ($createGitDeployments) { + function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $auth) use ($createGitDeployments) { $payload = $request->getRawPayload(); $signatureRemote = $request->getHeader('x-hub-signature-256', ''); $signatureLocal = System::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', ''); @@ -936,14 +936,14 @@ Http::post('/v1/vcs/github/events') $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); //find functionId from functions table - $repositories = $authorization->skip(fn () => $dbForConsole->find('repositories', [ + $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::limit(100), ])); // create new deployment only on push and not when branch is created if (!$providerBranchCreated) { - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $authorization); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $auth); } } elseif ($event == $github::EVENT_INSTALLATION) { if ($parsedPayload["action"] == "deleted") { @@ -956,13 +956,13 @@ Http::post('/v1/vcs/github/events') ]); foreach ($installations as $installation) { - $repositories = $authorization->skip(fn () => $dbForConsole->find('repositories', [ + $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ Query::equal('installationInternalId', [$installation->getInternalId()]), Query::limit(1000) ])); foreach ($repositories as $repository) { - $authorization->skip(fn () => $dbForConsole->deleteDocument('repositories', $repository->getId())); + $auth->skip(fn () => $dbForConsole->deleteDocument('repositories', $repository->getId())); } $dbForConsole->deleteDocument('installations', $installation->getId()); @@ -994,12 +994,12 @@ Http::post('/v1/vcs/github/events') $providerCommitAuthor = $commitDetails["commitAuthor"] ?? ''; $providerCommitMessage = $commitDetails["commitMessage"] ?? ''; - $repositories = $authorization->skip(fn () => $dbForConsole->find('repositories', [ + $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::orderDesc('$createdAt') ])); - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $authorization); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $auth); } elseif ($parsedPayload["action"] == "closed") { // Allowed external contributions cleanup @@ -1008,7 +1008,7 @@ Http::post('/v1/vcs/github/events') $external = $parsedPayload["external"] ?? true; if ($external) { - $repositories = $authorization->skip(fn () => $dbForConsole->find('repositories', [ + $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::orderDesc('$createdAt') ])); @@ -1019,7 +1019,7 @@ Http::post('/v1/vcs/github/events') if (\in_array($providerPullRequestId, $providerPullRequestIds)) { $providerPullRequestIds = \array_diff($providerPullRequestIds, [$providerPullRequestId]); $repository = $repository->setAttribute('providerPullRequestIds', $providerPullRequestIds); - $repository = $authorization->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); + $repository = $auth->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); } } } @@ -1172,15 +1172,15 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito ->inject('dbForConsole') ->inject('getProjectDB') ->inject('queueForBuilds') - ->inject('authorization') - ->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $authorization) use ($createGitDeployments) { + ->inject('auth') + ->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $auth) use ($createGitDeployments) { $installation = $dbForConsole->getDocument('installations', $installationId); if ($installation->isEmpty()) { throw new Exception(Exception::INSTALLATION_NOT_FOUND); } - $repository = $authorization->skip(fn () => $dbForConsole->getDocument('repositories', $repositoryId, [ + $repository = $auth->skip(fn () => $dbForConsole->getDocument('repositories', $repositoryId, [ Query::equal('projectInternalId', [$project->getInternalId()]) ])); @@ -1197,7 +1197,7 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito // TODO: Delete from array when PR is closed - $repository = $authorization->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); + $repository = $auth->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); $privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); $githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID'); @@ -1221,7 +1221,7 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito $providerBranch = \explode(':', $pullRequestResponse['head']['label'])[1] ?? ''; $providerCommitHash = $pullRequestResponse['head']['sha'] ?? ''; - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $authorization); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $auth); $response->noContent(); }); From d2aaa990765e1748cd705eba6f0d86fe70f7cd5c Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:26:17 -0400 Subject: [PATCH 190/195] Revert "Fix typo in scheduler base" --- src/Appwrite/Platform/Tasks/ScheduleBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index ada5e63b07..cb02ec60fd 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -166,7 +166,7 @@ abstract class ScheduleBase extends Action $total = $total + $sum; foreach ($results as $document) { - $localDocument = $this->schedules[$document->getInternalId()] ?? null; + $localDocument = $schedules[$document['resourceId']] ?? null; // Check if resource has been updated since last sync $org = $localDocument !== null ? \strtotime($localDocument['resourceUpdatedAt']) : null; From 5512340cdddb609cae6fc6bb10a69d0aec60af75 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:30:05 -0400 Subject: [PATCH 191/195] Revert "Feat eldad4 coroutines" --- .github/workflows/codeql-phpstan.yml | 16 - app/cli.php | 264 ++- app/config/variables.php | 2 +- app/controllers/api/account.php | 561 +++-- app/controllers/api/avatars.php | 96 +- app/controllers/api/console.php | 10 +- app/controllers/api/databases.php | 427 ++-- app/controllers/api/functions.php | 211 +- app/controllers/api/graphql.php | 29 +- app/controllers/api/health.php | 340 ++- app/controllers/api/locale.php | 18 +- app/controllers/api/messaging.php | 181 +- app/controllers/api/migrations.php | 50 +- app/controllers/api/project.php | 25 +- app/controllers/api/projects.php | 274 ++- app/controllers/api/proxy.php | 18 +- app/controllers/api/storage.php | 209 +- app/controllers/api/teams.php | 106 +- app/controllers/api/users.php | 111 +- app/controllers/api/vcs.php | 77 +- app/controllers/general.php | 263 +-- app/controllers/mock.php | 34 +- app/controllers/shared/api.php | 118 +- app/controllers/shared/api/auth.php | 22 +- app/controllers/web/console.php | 6 +- app/controllers/web/home.php | 4 +- app/http.php | 491 +++-- app/init.php | 1828 +++++++++++++++-- app/init/config.php | 37 - app/init/constants.php | 194 -- app/init/database/filters.php | 397 ---- app/init/database/formats.php | 43 - app/init/locale.php | 23 - app/init/resources.php | 1046 ---------- app/realtime.php | 277 ++- app/views/install/compose.phtml | 5 +- app/worker.php | 363 +++- composer.json | 39 +- composer.lock | 499 ++--- docker-compose.yml | 6 +- docs/tutorials/add-route.md | 30 +- phpstan.neon | 11 - src/Appwrite/Auth/Auth.php | 36 +- src/Appwrite/Auth/Authentication.php | 57 - src/Appwrite/Auth/Validator/MockNumber.php | 5 +- src/Appwrite/Auth/Validator/Password.php | 2 +- src/Appwrite/Auth/Validator/Phone.php | 2 +- src/Appwrite/Event/Func.php | 4 +- src/Appwrite/Event/Validator/Event.php | 2 +- src/Appwrite/Functions/Validator/Headers.php | 2 +- src/Appwrite/Functions/Validator/Payload.php | 2 +- .../Validator/RuntimeSpecification.php | 2 +- src/Appwrite/GraphQL/Resolvers.php | 170 +- src/Appwrite/GraphQL/Schema.php | 119 +- src/Appwrite/GraphQL/Types/Mapper.php | 64 +- src/Appwrite/Migration/Migration.php | 36 +- src/Appwrite/Network/Validator/CNAME.php | 2 +- src/Appwrite/Network/Validator/Email.php | 4 +- src/Appwrite/Network/Validator/Origin.php | 4 +- src/Appwrite/Platform/Tasks/Doctor.php | 104 +- src/Appwrite/Platform/Tasks/Install.php | 11 +- src/Appwrite/Platform/Tasks/Migrate.php | 22 +- src/Appwrite/Platform/Tasks/QueueCount.php | 4 +- src/Appwrite/Platform/Tasks/QueueRetry.php | 4 +- src/Appwrite/Platform/Tasks/SSL.php | 4 +- src/Appwrite/Platform/Tasks/ScheduleBase.php | 129 +- .../Platform/Tasks/ScheduleExecutions.php | 30 +- .../Platform/Tasks/ScheduleFunctions.php | 17 +- .../Platform/Tasks/ScheduleMessages.php | 24 +- src/Appwrite/Platform/Tasks/Specs.php | 50 +- src/Appwrite/Platform/Tasks/Upgrade.php | 4 +- src/Appwrite/Platform/Workers/Audits.php | 6 +- src/Appwrite/Platform/Workers/Builds.php | 66 +- .../Platform/Workers/Certificates.php | 30 +- src/Appwrite/Platform/Workers/Deletes.php | 14 +- src/Appwrite/Platform/Workers/Mails.php | 6 +- src/Appwrite/Promises/Promise.php | 2 +- src/Appwrite/Promises/Swoole.php | 17 +- src/Appwrite/Specification/Format.php | 10 +- .../Specification/Format/OpenAPI3.php | 53 +- .../Specification/Format/Swagger2.php | 44 +- src/Appwrite/Task/Validator/Cron.php | 2 +- .../Utopia/Database/Validator/CompoundUID.php | 2 +- .../Utopia/Database/Validator/ProjectId.php | 2 +- src/Appwrite/Utopia/Queue/Connections.php | 36 - src/Appwrite/Utopia/Request.php | 36 +- src/Appwrite/Utopia/Response.php | 318 ++- src/Appwrite/Utopia/Response/Models.php | 304 --- src/Appwrite/Utopia/View.php | 2 +- .../e2e/Services/Databases/DatabasesBase.php | 4 +- .../Databases/DatabasesCustomClientTest.php | 4 +- .../DatabasesPermissionsGuestTest.php | 20 +- .../e2e/Services/Functions/FunctionsBase.php | 5 +- .../Functions/FunctionsCustomServerTest.php | 6 +- tests/e2e/Services/GraphQL/Base.php | 2 +- .../Services/GraphQL/StorageClientTest.php | 1 + .../Services/GraphQL/StorageServerTest.php | 1 + .../Projects/ProjectsConsoleClientTest.php | 2 +- .../Realtime/RealtimeCustomClientTest.php | 5 +- .../Storage/StorageCustomClientTest.php | 4 +- .../Webhooks/WebhooksCustomServerTest.php | 5 +- tests/resources/docker/docker-compose.yml | 2 +- tests/unit/Auth/AuthTest.php | 35 +- tests/unit/Event/EventTest.php | 10 +- tests/unit/GraphQL/BuilderTest.php | 9 +- .../unit/Messaging/MessagingChannelsTest.php | 9 +- tests/unit/Migration/MigrationTest.php | 2 +- tests/unit/Utopia/RequestTest.php | 7 +- tests/unit/Utopia/ResponseTest.php | 10 +- 109 files changed, 5063 insertions(+), 5707 deletions(-) delete mode 100644 .github/workflows/codeql-phpstan.yml delete mode 100644 app/init/config.php delete mode 100644 app/init/constants.php delete mode 100644 app/init/database/filters.php delete mode 100644 app/init/database/formats.php delete mode 100644 app/init/locale.php delete mode 100644 app/init/resources.php delete mode 100644 phpstan.neon delete mode 100644 src/Appwrite/Auth/Authentication.php delete mode 100644 src/Appwrite/Utopia/Queue/Connections.php delete mode 100644 src/Appwrite/Utopia/Response/Models.php diff --git a/.github/workflows/codeql-phpstan.yml b/.github/workflows/codeql-phpstan.yml deleted file mode 100644 index 3253e2c38b..0000000000 --- a/.github/workflows/codeql-phpstan.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: "CodeQL" - -on: [pull_request] -jobs: - lint: - name: CodeQL - runs-on: ubuntu-latest - - steps: - - name: Check out the repo - uses: actions/checkout@v2 - - - name: Run CodeQL - run: | - docker run --rm -v $PWD:/app composer sh -c \ - "composer install --profile --ignore-platform-reqs && composer check" \ No newline at end of file diff --git a/app/cli.php b/app/cli.php index 9d0d069513..ecff5180a2 100644 --- a/app/cli.php +++ b/app/cli.php @@ -7,130 +7,210 @@ use Appwrite\Event\Delete; use Appwrite\Event\Func; use Appwrite\Platform\Appwrite; use Appwrite\Runtimes\Runtimes; -use Swoole\Runtime; -use Utopia\CLI\Adapters\Swoole as SwooleCLI; +use Utopia\Cache\Adapter\Sharding; +use Utopia\Cache\Cache; +use Utopia\CLI\CLI; use Utopia\CLI\Console; use Utopia\Config\Config; +use Utopia\Database\Database; +use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\DI\Dependency; +use Utopia\DSN\DSN; use Utopia\Logger\Log; use Utopia\Platform\Service; +use Utopia\Pools\Group; use Utopia\Queue\Connection; use Utopia\Registry\Registry; use Utopia\System\System; -global $registry, $container; - -Runtime::enableCoroutine(SWOOLE_HOOK_ALL); - // overwriting runtimes to be architectur agnostic for CLI Config::setParam('runtimes', (new Runtimes('v4'))->getAll(supported: false)); // require controllers after overwriting runtimes require_once __DIR__ . '/controllers/general.php'; -/** - * @var Registry $registry - * @var Container $container - */ -$context = new Dependency(); -$register = new Dependency(); -$logError = new Dependency(); -$queueForDeletes = new Dependency(); -$queueForFunctions = new Dependency(); -$queueForCertificates = new Dependency(); +Authorization::disable(); -$context - ->setName('context') - ->setCallback(fn () => $container); +CLI::setResource('register', fn () => $register); -$register - ->setName('register') - ->setCallback(function () use (&$registry): Registry { - return $registry; - }); +CLI::setResource('cache', function ($pools) { + $list = Config::getParam('pools-cache', []); + $adapters = []; -$queueForFunctions - ->setName('queueForFunctions') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Func($queue); - }); + foreach ($list as $value) { + $adapters[] = $pools + ->get($value) + ->pop() + ->getResource() + ; + } + return new Cache(new Sharding($adapters)); +}, ['pools']); -$queueForDeletes - ->setName('queueForDeletes') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Delete($queue); - }); +CLI::setResource('pools', function (Registry $register) { + return $register->get('pools'); +}, ['register']); -$queueForCertificates - ->setName('queueForCertificates') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Certificate($queue); - }); +CLI::setResource('dbForConsole', function ($pools, $cache) { + $sleep = 3; + $maxAttempts = 5; + $attempts = 0; + $ready = false; -$logError - ->setName('logError') - ->inject('register') - ->setCallback(function (Registry $register) { - return function (Throwable $error, string $namespace, string $action) use ($register) { - $logger = $register->get('logger'); + do { + $attempts++; + try { + // Prepare database connection + $dbAdapter = $pools + ->get('console') + ->pop() + ->getResource(); - if ($logger) { - $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); + $dbForConsole = new Database($dbAdapter, $cache); - $log = new Log(); - $log->setNamespace($namespace); - $log->setServer(\gethostname()); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($error->getMessage()); + $dbForConsole + ->setNamespace('_console') + ->setMetadata('host', \gethostname()) + ->setMetadata('project', 'console'); - $log->addTag('code', $error->getCode()); - $log->addTag('verboseType', get_class($error)); + // Ensure tables exist + $collections = Config::getParam('collections', [])['console']; + $last = \array_key_last($collections); - $log->addExtra('file', $error->getFile()); - $log->addExtra('line', $error->getLine()); - $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('trace', $error->getTraceAsString()); - - $log->setAction($action); - - $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; - - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - - $responseCode = $logger->addLog($log); - Console::info('Usage stats log pushed with status code: ' . $responseCode); + if (!($dbForConsole->exists($dbForConsole->getDatabase(), $last))) { /** TODO cache ready variable using registry */ + throw new Exception('Tables not ready yet.'); } - Console::warning("Failed: {$error->getMessage()}"); - Console::warning($error->getTraceAsString()); - }; - }); + $ready = true; + } catch (\Throwable $err) { + Console::warning($err->getMessage()); + $pools->get('console')->reclaim(); + sleep($sleep); + } + } while ($attempts < $maxAttempts && !$ready); -$container->set($context); -$container->set($logError); -$container->set($register); -$container->set($queueForDeletes); -$container->set($queueForFunctions); -$container->set($queueForCertificates); + if (!$ready) { + throw new Exception("Console is not ready yet. Please try again later."); + } + + return $dbForConsole; +}, ['pools', 'cache']); + +CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { + $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools + + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + if (isset($databases[$dsn->getHost()])) { + $database = $databases[$dsn->getHost()]; + + if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + return $database; + } + + $dbAdapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); + + $databases[$dsn->getHost()] = $database; + + if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + $database + ->setMetadata('host', \gethostname()) + ->setMetadata('project', $project->getId()); + + return $database; + }; +}, ['pools', 'dbForConsole', 'cache']); + +CLI::setResource('queue', function (Group $pools) { + return $pools->get('queue')->pop()->getResource(); +}, ['pools']); +CLI::setResource('queueForFunctions', function (Connection $queue) { + return new Func($queue); +}, ['queue']); +CLI::setResource('queueForDeletes', function (Connection $queue) { + return new Delete($queue); +}, ['queue']); +CLI::setResource('queueForCertificates', function (Connection $queue) { + return new Certificate($queue); +}, ['queue']); +CLI::setResource('logError', function (Registry $register) { + return function (Throwable $error, string $namespace, string $action) use ($register) { + $logger = $register->get('logger'); + + if ($logger) { + $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); + + $log = new Log(); + $log->setNamespace($namespace); + $log->setServer(\gethostname()); + $log->setVersion($version); + $log->setType(Log::TYPE_ERROR); + $log->setMessage($error->getMessage()); + + $log->addTag('code', $error->getCode()); + $log->addTag('verboseType', get_class($error)); + + $log->addExtra('file', $error->getFile()); + $log->addExtra('line', $error->getLine()); + $log->addExtra('trace', $error->getTraceAsString()); + + $log->setAction($action); + + $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; + $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); + + $responseCode = $logger->addLog($log); + Console::info('Usage stats log pushed with status code: ' . $responseCode); + } + + Console::warning("Failed: {$error->getMessage()}"); + Console::warning($error->getTraceAsString()); + }; +}, ['register']); $platform = new Appwrite(); -$platform->init(Service::TYPE_TASK, ['adapter' => new SwooleCLI(1)]); +$platform->init(Service::TYPE_TASK); $cli = $platform->getCli(); -$cli - ->init() - ->inject('authorization') - ->action(function (Authorization $authorization) { - $authorization->disable(); - }); - $cli ->error() ->inject('error') @@ -138,6 +218,4 @@ $cli Console::error($error->getMessage()); }); -$cli - ->setContainer($container) - ->run(); +$cli->run(); diff --git a/app/config/variables.php b/app/config/variables.php index 161951e3de..113fbae335 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -254,7 +254,7 @@ return [ 'name' => '_APP_WORKER_PER_CORE', 'description' => 'Internal Worker per core for the API, Realtime and Executor containers. Can be configured to optimize performance.', 'introduction' => '0.13.0', - 'default' => 2, + 'default' => 6, 'required' => false, 'question' => '', 'filter' => '' diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index a76b9dbea5..737bd3e09d 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2,7 +2,6 @@ use Ahc\Jwt\JWT; use Appwrite\Auth\Auth; -use Appwrite\Auth\Authentication; use Appwrite\Auth\MFA\Challenge; use Appwrite\Auth\MFA\Type; use Appwrite\Auth\MFA\Type\TOTP; @@ -29,6 +28,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Identities; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; +use Utopia\App; use Utopia\Audit\Audit as EventAudit; use Utopia\Config\Config; use Utopia\Database\Database; @@ -45,16 +45,15 @@ use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; -use Utopia\Http\Http; -use Utopia\Http\Validator\ArrayList; -use Utopia\Http\Validator\Assoc; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\Host; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\URL; -use Utopia\Http\Validator\WhiteList; use Utopia\Locale\Locale; use Utopia\System\System; +use Utopia\Validator\ArrayList; +use Utopia\Validator\Assoc; +use Utopia\Validator\Boolean; +use Utopia\Validator\Host; +use Utopia\Validator\Text; +use Utopia\Validator\URL; +use Utopia\Validator\WhiteList; $oauthDefaultSuccess = '/console/auth/oauth2/success'; $oauthDefaultFailure = '/console/auth/oauth2/failure'; @@ -145,13 +144,14 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc ->trigger(); }; -$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Authorization $authorization, Authentication $authentication) { - $roles = $authorization->getRoles(); + +$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails) { + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); /** @var Utopia\Database\Document $user */ - $userFromRequest = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId)); + $userFromRequest = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId)); if ($userFromRequest->isEmpty()) { throw new Exception(Exception::USER_INVALID_TOKEN); @@ -197,7 +197,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res $detector->getDevice() )); - $authorization->addRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); $session = $dbForProject->createDocument('sessions', $session ->setAttribute('$permissions', [ @@ -206,7 +206,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res Permission::delete(Role::user($user->getId())), ])); - $authorization->skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId())); + Authorization::skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId())); $dbForProject->purgeCachedDocument('users', $user->getId()); // Magic URL + Email OTP @@ -247,15 +247,15 @@ $createSession = function (string $userId, string $secret, Request $request, Res ->setParam('sessionId', $session->getId()); if (!Config::getParam('domainVerification')) { - $response->addHeader('X-Fallback-Cookies', \json_encode([$authentication->getCookieName() => Auth::encodeSession($user->getId(), $sessionSecret)])); + $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $sessionSecret)])); } $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $protocol = $request->getProtocol(); $response - ->addCookie($authentication->getCookieName() . '_legacy', Auth::encodeSession($user->getId(), $sessionSecret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie($authentication->getCookieName(), Auth::encodeSession($user->getId(), $sessionSecret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $sessionSecret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $sessionSecret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ->setStatusCode(Response::STATUS_CODE_CREATED); $countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); @@ -270,7 +270,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res $response->dynamic($session, Response::MODEL_SESSION); }; -Http::post('/v1/account') +App::post('/v1/account') ->desc('Create account') ->groups(['api', 'account', 'auth']) ->label('event', 'users.[userId].create') @@ -298,8 +298,7 @@ Http::post('/v1/account') ->inject('dbForProject') ->inject('queueForEvents') ->inject('hooks') - ->inject('authorization') - ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks, Authorization $authorization) { + ->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks) { $email = \strtolower($email); if ('console' === $project->getId()) { @@ -377,9 +376,9 @@ Http::post('/v1/account') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); + $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); try { - $target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([ + $target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([ '$permissions' => [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), @@ -405,9 +404,9 @@ Http::post('/v1/account') throw new Exception(Exception::USER_ALREADY_EXISTS); } - $authorization->removeRole(Role::guests()->toString()); - $authorization->addRole(Role::user($user->getId())->toString()); - $authorization->addRole(Role::users()->toString()); + Authorization::unsetRole(Role::guests()->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::users()->toString()); $queueForEvents->setParam('userId', $user->getId()); @@ -416,7 +415,7 @@ Http::post('/v1/account') ->dynamic($user, Response::MODEL_ACCOUNT); }); -Http::get('/v1/account') +App::get('/v1/account') ->desc('Get account') ->groups(['api', 'account']) ->label('scope', 'account') @@ -439,7 +438,7 @@ Http::get('/v1/account') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -Http::delete('/v1/account') +App::delete('/v1/account') ->desc('Delete account') ->groups(['api', 'account']) ->label('event', 'users.[userId].delete') @@ -487,7 +486,7 @@ Http::delete('/v1/account') $response->noContent(); }); -Http::get('/v1/account/sessions') +App::get('/v1/account/sessions') ->desc('List sessions') ->groups(['api', 'account']) ->label('scope', 'account') @@ -502,16 +501,15 @@ Http::get('/v1/account/sessions') ->inject('response') ->inject('user') ->inject('locale') - ->inject('authorization') - ->inject('authentication') - ->action(function (Response $response, Document $user, Locale $locale, Authorization $authorization, Authentication $authentication) { + ->inject('project') + ->action(function (Response $response, Document $user, Locale $locale, Document $project) { - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); $sessions = $user->getAttribute('sessions', []); - $current = Auth::sessionVerify($sessions, $authentication->getSecret()); + $current = Auth::sessionVerify($sessions, Auth::$secret); foreach ($sessions as $key => $session) {/** @var Document $session */ $countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); @@ -529,7 +527,7 @@ Http::get('/v1/account/sessions') ]), Response::MODEL_SESSION_LIST); }); -Http::delete('/v1/account/sessions') +App::delete('/v1/account/sessions') ->desc('Delete sessions') ->groups(['api', 'account']) ->label('scope', 'account') @@ -550,8 +548,7 @@ Http::delete('/v1/account/sessions') ->inject('locale') ->inject('queueForEvents') ->inject('queueForDeletes') - ->inject('authentication') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes, Authentication $authentication) { + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes) { $protocol = $request->getProtocol(); $sessions = $user->getAttribute('sessions', []); @@ -567,13 +564,13 @@ Http::delete('/v1/account/sessions') ->setAttribute('current', false) ->setAttribute('countryName', $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'))); - if ($session->getAttribute('secret') == Auth::hash($authentication->getSecret())) { + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { $session->setAttribute('current', true); // If current session delete the cookies too $response - ->addCookie($authentication->getCookieName() . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie($authentication->getCookieName(), '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); + ->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); // Use current session for events. $queueForEvents @@ -595,7 +592,7 @@ Http::delete('/v1/account/sessions') $response->noContent(); }); -Http::get('/v1/account/sessions/:sessionId') +App::get('/v1/account/sessions/:sessionId') ->desc('Get session') ->groups(['api', 'account']) ->label('scope', 'account') @@ -612,17 +609,16 @@ Http::get('/v1/account/sessions/:sessionId') ->inject('response') ->inject('user') ->inject('locale') - ->inject('authorization') - ->inject('authentication') - ->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Authorization $authorization, Authentication $authentication) { + ->inject('project') + ->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Document $project) { - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); $sessions = $user->getAttribute('sessions', []); $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret()) + ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) : $sessionId; foreach ($sessions as $session) {/** @var Document $session */ @@ -630,7 +626,7 @@ Http::get('/v1/account/sessions/:sessionId') $countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); $session - ->setAttribute('current', ($session->getAttribute('secret') == Auth::hash($authentication->getSecret()))) + ->setAttribute('current', ($session->getAttribute('secret') == Auth::hash(Auth::$secret))) ->setAttribute('countryName', $countryName) ->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $session->getAttribute('secret', '') : '') ; @@ -642,7 +638,7 @@ Http::get('/v1/account/sessions/:sessionId') throw new Exception(Exception::USER_SESSION_NOT_FOUND); }); -Http::delete('/v1/account/sessions/:sessionId') +App::delete('/v1/account/sessions/:sessionId') ->desc('Delete session') ->groups(['api', 'account', 'mfa']) ->label('scope', 'account') @@ -665,12 +661,12 @@ Http::delete('/v1/account/sessions/:sessionId') ->inject('locale') ->inject('queueForEvents') ->inject('queueForDeletes') - ->inject('authentication') - ->action(function (?string $sessionId, ?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes, Authentication $authentication) { + ->inject('project') + ->action(function (?string $sessionId, ?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes, Document $project) { $protocol = $request->getProtocol(); $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret()) + ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) : $sessionId; $sessions = $user->getAttribute('sessions', []); @@ -689,7 +685,7 @@ Http::delete('/v1/account/sessions/:sessionId') $session->setAttribute('current', false); - if ($session->getAttribute('secret') == Auth::hash($authentication->getSecret())) { // If current session delete the cookies too + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too $session ->setAttribute('current', true) ->setAttribute('countryName', $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'))); @@ -699,8 +695,8 @@ Http::delete('/v1/account/sessions/:sessionId') } $response - ->addCookie($authentication->getCookieName() . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie($authentication->getCookieName(), '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); + ->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); } $dbForProject->purgeCachedDocument('users', $user->getId()); @@ -722,7 +718,7 @@ Http::delete('/v1/account/sessions/:sessionId') throw new Exception(Exception::USER_SESSION_NOT_FOUND); }); -Http::patch('/v1/account/sessions/:sessionId') +App::patch('/v1/account/sessions/:sessionId') ->desc('Update session') ->groups(['api', 'account']) ->label('scope', 'account') @@ -744,11 +740,10 @@ Http::patch('/v1/account/sessions/:sessionId') ->inject('dbForProject') ->inject('project') ->inject('queueForEvents') - ->inject('authentication') - ->action(function (?string $sessionId, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Authentication $authentication) { + ->action(function (?string $sessionId, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents) { $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret()) + ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) : $sessionId; $sessions = $user->getAttribute('sessions', []); @@ -799,7 +794,7 @@ Http::patch('/v1/account/sessions/:sessionId') return $response->dynamic($session, Response::MODEL_SESSION); }); -Http::post('/v1/account/sessions/email') +App::post('/v1/account/sessions/email') ->alias('/v1/account/sessions') ->desc('Create email password session') ->groups(['api', 'account', 'auth', 'session']) @@ -830,9 +825,7 @@ Http::post('/v1/account/sessions/email') ->inject('queueForEvents') ->inject('queueForMails') ->inject('hooks') - ->inject('authorization') - ->inject('authentication') - ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Authorization $authorization, Authentication $authentication) { + ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks) { $email = \strtolower($email); $protocol = $request->getProtocol(); @@ -848,7 +841,7 @@ Http::post('/v1/account/sessions/email') throw new Exception(Exception::USER_BLOCKED); // User is in status blocked } - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -879,7 +872,7 @@ Http::post('/v1/account/sessions/email') $detector->getDevice() )); - $authorization->addRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); // Re-hash if not using recommended algo if ($user->getAttribute('hash') !== Auth::DEFAULT_ALGO) { @@ -900,15 +893,15 @@ Http::post('/v1/account/sessions/email') if (!Config::getParam('domainVerification')) { $response - ->addHeader('X-Fallback-Cookies', \json_encode([$authentication->getCookieName() => Auth::encodeSession($user->getId(), $secret)])) + ->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])) ; } $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $response - ->addCookie($authentication->getCookieName() . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie($authentication->getCookieName(), Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ->setStatusCode(Response::STATUS_CODE_CREATED) ; @@ -936,7 +929,7 @@ Http::post('/v1/account/sessions/email') $response->dynamic($session, Response::MODEL_SESSION); }); -Http::post('/v1/account/sessions/anonymous') +App::post('/v1/account/sessions/anonymous') ->desc('Create anonymous session') ->groups(['api', 'account', 'auth', 'session']) ->label('event', 'users.[userId].sessions.[sessionId].create') @@ -962,11 +955,9 @@ Http::post('/v1/account/sessions/anonymous') ->inject('dbForProject') ->inject('geodb') ->inject('queueForEvents') - ->inject('authorization') - ->inject('authentication') - ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization, Authentication $authentication) { + ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) { $protocol = $request->getProtocol(); - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1012,7 +1003,7 @@ Http::post('/v1/account/sessions/anonymous') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); + Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); // Create session token $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; @@ -1038,7 +1029,7 @@ Http::post('/v1/account/sessions/anonymous') $detector->getDevice() )); - $authorization->addRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); $session = $dbForProject->createDocument('sessions', $session-> setAttribute('$permissions', [ Permission::read(Role::user($user->getId())), @@ -1054,14 +1045,14 @@ Http::post('/v1/account/sessions/anonymous') ; if (!Config::getParam('domainVerification')) { - $response->addHeader('X-Fallback-Cookies', \json_encode([$authentication->getCookieName() => Auth::encodeSession($user->getId(), $secret)])); + $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])); } $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $response - ->addCookie($authentication->getCookieName() . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie($authentication->getCookieName(), Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ->setStatusCode(Response::STATUS_CODE_CREATED) ; @@ -1076,7 +1067,7 @@ Http::post('/v1/account/sessions/anonymous') $response->dynamic($session, Response::MODEL_SESSION); }); -Http::post('/v1/account/sessions/token') +App::post('/v1/account/sessions/token') ->desc('Create session') ->label('event', 'users.[userId].sessions.[sessionId].create') ->groups(['api', 'account', 'session']) @@ -1104,11 +1095,9 @@ Http::post('/v1/account/sessions/token') ->inject('geodb') ->inject('queueForEvents') ->inject('queueForMails') - ->inject('authorization') - ->inject('authentication') ->action($createSession); -Http::get('/v1/account/sessions/oauth2/:provider') +App::get('/v1/account/sessions/oauth2/:provider') ->desc('Create OAuth2 session') ->groups(['api', 'account']) ->label('error', __DIR__ . '/../../views/general/error.phtml') @@ -1178,7 +1167,7 @@ Http::get('/v1/account/sessions/oauth2/:provider') ->redirect($oauth2->getLoginURL()); }); -Http::get('/v1/account/sessions/oauth2/callback/:provider/:projectId') +App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId') ->desc('OAuth2 callback') ->groups(['account']) ->label('error', __DIR__ . '/../../views/general/error.phtml') @@ -1208,7 +1197,7 @@ Http::get('/v1/account/sessions/oauth2/callback/:provider/:projectId') . \http_build_query($params)); }); -Http::post('/v1/account/sessions/oauth2/callback/:provider/:projectId') +App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId') ->desc('OAuth2 callback') ->groups(['account']) ->label('error', __DIR__ . '/../../views/general/error.phtml') @@ -1239,7 +1228,7 @@ Http::post('/v1/account/sessions/oauth2/callback/:provider/:projectId') . \http_build_query($params)); }); -Http::get('/v1/account/sessions/oauth2/:provider/redirect') +App::get('/v1/account/sessions/oauth2/:provider/redirect') ->desc('OAuth2 redirect') ->groups(['api', 'account', 'session']) ->label('error', __DIR__ . '/../../views/general/error.phtml') @@ -1263,9 +1252,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('dbForProject') ->inject('geodb') ->inject('queueForEvents') - ->inject('authorization') - ->inject('authentication') - ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization, Authentication $authentication) use ($oauthDefaultSuccess) { + ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) { $protocol = $request->getProtocol(); $callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId(); $defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => '']; @@ -1402,7 +1389,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') } $sessions = $user->getAttribute('sessions', []); - $current = Auth::sessionVerify($sessions, $authentication->getSecret()); + $current = Auth::sessionVerify($sessions, Auth::$secret); if ($current) { // Delete current session of new one. $currentDocument = $dbForProject->getDocument('sessions', $current); @@ -1499,16 +1486,15 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') 'accessedAt' => DateTime::now(), ]); $user->removeAttribute('$internalId'); - $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); - + $userDoc = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); $dbForProject->createDocument('targets', new Document([ '$permissions' => [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), Permission::delete(Role::user($user->getId())), ], - 'userId' => $user->getId(), - 'userInternalId' => $user->getInternalId(), + 'userId' => $userDoc->getId(), + 'userInternalId' => $userDoc->getInternalId(), 'providerType' => MESSAGE_TYPE_EMAIL, 'identifier' => $email, ])); @@ -1518,8 +1504,8 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') } } - $authorization->addRole(Role::user($user->getId())->toString()); - $authorization->addRole(Role::users()->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::users()->toString()); if (false === $user->getAttribute('status')) { // Account is blocked $failureRedirect(Exception::USER_BLOCKED); // User is in status blocked @@ -1578,7 +1564,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') $dbForProject->updateDocument('users', $user->getId(), $user); - $authorization->addRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); $state['success'] = URLParser::parse($state['success']); $query = URLParser::parseQuery($state['success']['query']); @@ -1600,7 +1586,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') 'ip' => $request->getIP(), ]); - $authorization->addRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -1650,7 +1636,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') $session->setAttribute('expire', $expire); if (!Config::getParam('domainVerification')) { - $response->addHeader('X-Fallback-Cookies', \json_encode([$authentication->getCookieName() => Auth::encodeSession($user->getId(), $secret)])); + $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])); } $queueForEvents @@ -1663,13 +1649,13 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') if ($state['success']['path'] == $oauthDefaultSuccess) { $query['project'] = $project->getId(); $query['domain'] = Config::getParam('cookieDomain'); - $query['key'] = $authentication->getCookieName(); + $query['key'] = Auth::$cookieName; $query['secret'] = Auth::encodeSession($user->getId(), $secret); } $response - ->addCookie($authentication->getCookieName() . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie($authentication->getCookieName(), Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); + ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); } if (isset($sessionUpgrade) && $sessionUpgrade) { @@ -1700,7 +1686,7 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect') ; }); -Http::get('/v1/account/tokens/oauth2/:provider') +App::get('/v1/account/tokens/oauth2/:provider') ->desc('Create OAuth2 token') ->groups(['api', 'account']) ->label('error', __DIR__ . '/../../views/general/error.phtml') @@ -1769,7 +1755,7 @@ Http::get('/v1/account/tokens/oauth2/:provider') ->redirect($oauth2->getLoginURL()); }); -Http::post('/v1/account/tokens/magic-url') +App::post('/v1/account/tokens/magic-url') ->alias('/v1/account/sessions/magic-url') ->desc('Create magic URL token') ->groups(['api', 'account', 'auth']) @@ -1799,8 +1785,7 @@ Http::post('/v1/account/tokens/magic-url') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->inject('authorization') - ->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { + ->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); } @@ -1810,7 +1795,7 @@ Http::post('/v1/account/tokens/magic-url') $phrase = Phrase::generate(); } - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -1865,7 +1850,7 @@ Http::post('/v1/account/tokens/magic-url') ]); $user->removeAttribute('$internalId'); - $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); + Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); } $tokenSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_MAGIC_URL); @@ -1882,7 +1867,7 @@ Http::post('/v1/account/tokens/magic-url') 'ip' => $request->getIP(), ]); - $authorization->addRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -2014,7 +1999,7 @@ Http::post('/v1/account/tokens/magic-url') ->dynamic($token, Response::MODEL_TOKEN); }); -Http::post('/v1/account/tokens/email') +App::post('/v1/account/tokens/email') ->desc('Create email token (OTP)') ->groups(['api', 'account', 'auth']) ->label('scope', 'sessions.write') @@ -2042,8 +2027,7 @@ Http::post('/v1/account/tokens/email') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->inject('authorization') - ->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { + ->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); } @@ -2052,7 +2036,7 @@ Http::post('/v1/account/tokens/email') $phrase = Phrase::generate(); } - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -2105,7 +2089,7 @@ Http::post('/v1/account/tokens/email') ]); $user->removeAttribute('$internalId'); - $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); + Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); } $tokenSecret = Auth::codeGenerator(6); @@ -2122,7 +2106,7 @@ Http::post('/v1/account/tokens/email') 'ip' => $request->getIP(), ]); - $authorization->addRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -2244,7 +2228,7 @@ Http::post('/v1/account/tokens/email') ->dynamic($token, Response::MODEL_TOKEN); }); -Http::put('/v1/account/sessions/magic-url') +App::put('/v1/account/sessions/magic-url') ->desc('Update magic URL session') ->label('event', 'users.[userId].sessions.[sessionId].create') ->groups(['api', 'account', 'session']) @@ -2273,11 +2257,9 @@ Http::put('/v1/account/sessions/magic-url') ->inject('geodb') ->inject('queueForEvents') ->inject('queueForMails') - ->inject('authorization') - ->inject('authentication') ->action($createSession); -Http::put('/v1/account/sessions/phone') +App::put('/v1/account/sessions/phone') ->desc('Update phone session') ->label('event', 'users.[userId].sessions.[sessionId].create') ->groups(['api', 'account', 'session']) @@ -2306,11 +2288,9 @@ Http::put('/v1/account/sessions/phone') ->inject('geodb') ->inject('queueForEvents') ->inject('queueForMails') - ->inject('authorization') - ->inject('authentication') ->action($createSession); -Http::post('/v1/account/tokens/phone') +App::post('/v1/account/tokens/phone') ->alias('/v1/account/sessions/phone') ->desc('Create phone token') ->groups(['api', 'account']) @@ -2338,13 +2318,12 @@ Http::post('/v1/account/tokens/phone') ->inject('queueForEvents') ->inject('queueForMessaging') ->inject('locale') - ->inject('authorization') - ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, Authorization $authorization) { + ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale) { if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -2388,9 +2367,9 @@ Http::post('/v1/account/tokens/phone') ]); $user->removeAttribute('$internalId'); - $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); + Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); try { - $target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([ + $target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([ '$permissions' => [ Permission::read(Role::user($user->getId())), Permission::update(Role::user($user->getId())), @@ -2436,7 +2415,7 @@ Http::post('/v1/account/tokens/phone') 'ip' => $request->getIP(), ]); - $authorization->addRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); $token = $dbForProject->createDocument('tokens', $token ->setAttribute('$permissions', [ @@ -2492,7 +2471,7 @@ Http::post('/v1/account/tokens/phone') ->dynamic($token, Response::MODEL_TOKEN); }); -Http::post('/v1/account/jwts') +App::post('/v1/account/jwts') ->alias('/v1/account/jwt') ->desc('Create JWT') ->groups(['api', 'account', 'auth']) @@ -2510,15 +2489,14 @@ Http::post('/v1/account/jwts') ->inject('response') ->inject('user') ->inject('dbForProject') - ->inject('authentication') - ->action(function (Response $response, Document $user, Database $dbForProject, Authentication $authentication) { + ->action(function (Response $response, Document $user, Database $dbForProject) { $sessions = $user->getAttribute('sessions', []); $current = new Document(); foreach ($sessions as $session) { /** @var Utopia\Database\Document $session */ - if ($session->getAttribute('secret') == Auth::hash($authentication->getSecret())) { // If current session delete the cookies too + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too $current = $session; } } @@ -2537,7 +2515,7 @@ Http::post('/v1/account/jwts') ])]), Response::MODEL_JWT); }); -Http::get('/v1/account/prefs') +App::get('/v1/account/prefs') ->desc('Get account preferences') ->groups(['api', 'account']) ->label('scope', 'account') @@ -2559,7 +2537,7 @@ Http::get('/v1/account/prefs') $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); -Http::get('/v1/account/logs') +App::get('/v1/account/logs') ->desc('List logs') ->groups(['api', 'account']) ->label('scope', 'account') @@ -2576,8 +2554,7 @@ Http::get('/v1/account/logs') ->inject('locale') ->inject('geodb') ->inject('dbForProject') - ->inject('authorization') - ->action(function (array $queries, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject, Authorization $authorization) { + ->action(function (array $queries, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject) { try { $queries = Query::parseQueries($queries); @@ -2589,7 +2566,7 @@ Http::get('/v1/account/logs') $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; - $audit = new EventAudit($dbForProject, $authorization); + $audit = new EventAudit($dbForProject); $logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset); @@ -2625,101 +2602,7 @@ Http::get('/v1/account/logs') ]), Response::MODEL_LOG_LIST); }); -Http::patch('/v1/account/email') - ->desc('Update email') - ->groups(['api', 'account']) - ->label('event', 'users.[userId].update.email') - ->label('scope', 'account') - ->label('audits.event', 'user.update') - ->label('audits.resource', 'user/{response.$id}') - ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) - ->label('sdk.namespace', 'account') - ->label('sdk.method', 'updateEmail') - ->label('sdk.description', '/docs/references/account/update-email.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_USER) - ->label('sdk.offline.model', '/account') - ->label('sdk.offline.key', 'current') - ->param('email', '', new Email(), 'User email.') - ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') - ->inject('requestTimestamp') - ->inject('response') - ->inject('user') - ->inject('dbForProject') - ->inject('queueForEvents') - ->inject('project') - ->inject('hooks') - ->inject('authorization') - ->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $authorization) { - // passwordUpdate will be empty if the user has never set a password - $passwordUpdate = $user->getAttribute('passwordUpdate'); - - if ( - !empty($passwordUpdate) && - !Auth::passwordVerify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions')) - ) { // Double check user password - throw new Exception(Exception::USER_INVALID_CREDENTIALS); - } - - $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]); - - $oldEmail = $user->getAttribute('email'); - - $email = \strtolower($email); - - // Makes sure this email is not already used in another identity - $identityWithMatchingEmail = $dbForProject->findOne('identities', [ - Query::equal('providerEmail', [$email]), - Query::notEqual('userInternalId', $user->getInternalId()), - ]); - if ($identityWithMatchingEmail !== false && !$identityWithMatchingEmail->isEmpty()) { - throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ - } - - $user - ->setAttribute('email', $email) - ->setAttribute('emailVerification', false) // After this user needs to confirm mail again - ; - - if (empty($passwordUpdate)) { - $user - ->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS)) - ->setAttribute('hash', Auth::DEFAULT_ALGO) - ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS) - ->setAttribute('passwordUpdate', DateTime::now()); - } - - $target = $authorization->skip(fn () => $dbForProject->findOne('targets', [ - Query::equal('identifier', [$email]), - ])); - - if ($target instanceof Document && !$target->isEmpty()) { - throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS); - } - - try { - $user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user)); - /** - * @var Document $oldTarget - */ - $oldTarget = $user->find('identifier', $oldEmail, 'targets'); - - if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) { - $authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email))); - } - $dbForProject->purgeCachedDocument('users', $user->getId()); - } catch (Duplicate) { - throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ - } - - $queueForEvents->setParam('userId', $user->getId()); - - $response->dynamic($user, Response::MODEL_ACCOUNT); - }); - - -Http::patch('/v1/account/name') +App::patch('/v1/account/name') ->desc('Update name') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.name') @@ -2752,7 +2635,7 @@ Http::patch('/v1/account/name') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -Http::patch('/v1/account/password') +App::patch('/v1/account/password') ->desc('Update password') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.password') @@ -2822,7 +2705,99 @@ Http::patch('/v1/account/password') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -Http::patch('/v1/account/phone') +App::patch('/v1/account/email') + ->desc('Update email') + ->groups(['api', 'account']) + ->label('event', 'users.[userId].update.email') + ->label('scope', 'account') + ->label('audits.event', 'user.update') + ->label('audits.resource', 'user/{response.$id}') + ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT]) + ->label('sdk.namespace', 'account') + ->label('sdk.method', 'updateEmail') + ->label('sdk.description', '/docs/references/account/update-email.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_USER) + ->label('sdk.offline.model', '/account') + ->label('sdk.offline.key', 'current') + ->param('email', '', new Email(), 'User email.') + ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') + ->inject('requestTimestamp') + ->inject('response') + ->inject('user') + ->inject('dbForProject') + ->inject('queueForEvents') + ->inject('project') + ->inject('hooks') + ->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks) { + // passwordUpdate will be empty if the user has never set a password + $passwordUpdate = $user->getAttribute('passwordUpdate'); + + if ( + !empty($passwordUpdate) && + !Auth::passwordVerify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions')) + ) { // Double check user password + throw new Exception(Exception::USER_INVALID_CREDENTIALS); + } + + $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]); + + $oldEmail = $user->getAttribute('email'); + + $email = \strtolower($email); + + // Makes sure this email is not already used in another identity + $identityWithMatchingEmail = $dbForProject->findOne('identities', [ + Query::equal('providerEmail', [$email]), + Query::notEqual('userInternalId', $user->getInternalId()), + ]); + if ($identityWithMatchingEmail !== false && !$identityWithMatchingEmail->isEmpty()) { + throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ + } + + $user + ->setAttribute('email', $email) + ->setAttribute('emailVerification', false) // After this user needs to confirm mail again + ; + + if (empty($passwordUpdate)) { + $user + ->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS)) + ->setAttribute('hash', Auth::DEFAULT_ALGO) + ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS) + ->setAttribute('passwordUpdate', DateTime::now()); + } + + $target = Authorization::skip(fn () => $dbForProject->findOne('targets', [ + Query::equal('identifier', [$email]), + ])); + + if ($target instanceof Document && !$target->isEmpty()) { + throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS); + } + + try { + $user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user)); + /** + * @var Document $oldTarget + */ + $oldTarget = $user->find('identifier', $oldEmail, 'targets'); + + if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) { + Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email))); + } + $dbForProject->purgeCachedDocument('users', $user->getId()); + } catch (Duplicate) { + throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ + } + + $queueForEvents->setParam('userId', $user->getId()); + + $response->dynamic($user, Response::MODEL_ACCOUNT); + }); + +App::patch('/v1/account/phone') ->desc('Update phone') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.phone') @@ -2847,8 +2822,7 @@ Http::patch('/v1/account/phone') ->inject('queueForEvents') ->inject('project') ->inject('hooks') - ->inject('authorization') - ->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $authorization) { + ->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks) { // passwordUpdate will be empty if the user has never set a password $passwordUpdate = $user->getAttribute('passwordUpdate'); @@ -2861,7 +2835,7 @@ Http::patch('/v1/account/phone') $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]); - $target = $authorization->skip(fn () => $dbForProject->findOne('targets', [ + $target = Authorization::skip(fn () => $dbForProject->findOne('targets', [ Query::equal('identifier', [$phone]), ])); @@ -2892,7 +2866,7 @@ Http::patch('/v1/account/phone') $oldTarget = $user->find('identifier', $oldPhone, 'targets'); if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) { - $authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone))); + Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone))); } $dbForProject->purgeCachedDocument('users', $user->getId()); } catch (Duplicate $th) { @@ -2904,7 +2878,7 @@ Http::patch('/v1/account/phone') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -Http::patch('/v1/account/prefs') +App::patch('/v1/account/prefs') ->desc('Update preferences') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.prefs') @@ -2937,7 +2911,7 @@ Http::patch('/v1/account/prefs') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -Http::patch('/v1/account/status') +App::patch('/v1/account/status') ->desc('Update status') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.status') @@ -2957,8 +2931,7 @@ Http::patch('/v1/account/status') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authentication') - ->action(function (?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authentication $authentication) { + ->action(function (?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $user->setAttribute('status', false); @@ -2974,14 +2947,14 @@ Http::patch('/v1/account/status') $protocol = $request->getProtocol(); $response - ->addCookie($authentication->getCookieName() . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie($authentication->getCookieName(), '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ; $response->dynamic($user, Response::MODEL_ACCOUNT); }); -Http::post('/v1/account/recovery') +App::post('/v1/account/recovery') ->desc('Create password recovery') ->groups(['api', 'account']) ->label('scope', 'sessions.write') @@ -3008,15 +2981,14 @@ Http::post('/v1/account/recovery') ->inject('locale') ->inject('queueForMails') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); } $url = htmlentities($url); - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -3050,7 +3022,7 @@ Http::post('/v1/account/recovery') 'ip' => $request->getIP(), ]); - $authorization->addRole(Role::user($profile->getId())->toString()); + Authorization::setRole(Role::user($profile->getId())->toString()); $recovery = $dbForProject->createDocument('tokens', $recovery ->setAttribute('$permissions', [ @@ -3072,7 +3044,7 @@ Http::post('/v1/account/recovery') $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl'); $message - ->setParam('{{body}}', $body, escape: false) + ->setParam('{{body}}', $body, escapeHtml: false) ->setParam('{{hello}}', $locale->getText("emails.recovery.hello")) ->setParam('{{footer}}', $locale->getText("emails.recovery.footer")) ->setParam('{{thanks}}', $locale->getText("emails.recovery.thanks")) @@ -3162,7 +3134,7 @@ Http::post('/v1/account/recovery') ->dynamic($recovery, Response::MODEL_TOKEN); }); -Http::put('/v1/account/recovery') +App::put('/v1/account/recovery') ->desc('Create password recovery (confirmation)') ->groups(['api', 'account']) ->label('scope', 'sessions.write') @@ -3188,8 +3160,7 @@ Http::put('/v1/account/recovery') ->inject('project') ->inject('queueForEvents') ->inject('hooks') - ->inject('authorization') - ->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, Authorization $authorization) { + ->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks) { $profile = $dbForProject->getDocument('users', $userId); if ($profile->isEmpty()) { @@ -3203,7 +3174,7 @@ Http::put('/v1/account/recovery') throw new Exception(Exception::USER_INVALID_TOKEN); } - $authorization->addRole(Role::user($profile->getId())->toString()); + Authorization::setRole(Role::user($profile->getId())->toString()); $newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS); @@ -3248,7 +3219,7 @@ Http::put('/v1/account/recovery') $response->dynamic($recoveryDocument, Response::MODEL_TOKEN); }); -Http::post('/v1/account/verification') +App::post('/v1/account/verification') ->desc('Create email verification') ->groups(['api', 'account']) ->label('scope', 'account') @@ -3273,8 +3244,7 @@ Http::post('/v1/account/verification') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->inject('authorization') - ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { + ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -3285,7 +3255,7 @@ Http::post('/v1/account/verification') throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED); } - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); $verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION); @@ -3302,7 +3272,7 @@ Http::post('/v1/account/verification') 'ip' => $request->getIP(), ]); - $authorization->addRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); $verification = $dbForProject->createDocument('tokens', $verification ->setAttribute('$permissions', [ @@ -3324,7 +3294,7 @@ Http::post('/v1/account/verification') $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl'); $message - ->setParam('{{body}}', $body, escape: false) + ->setParam('{{body}}', $body, escapeHtml: false) ->setParam('{{hello}}', $locale->getText("emails.verification.hello")) ->setParam('{{footer}}', $locale->getText("emails.verification.footer")) ->setParam('{{thanks}}', $locale->getText("emails.verification.thanks")) @@ -3414,7 +3384,7 @@ Http::post('/v1/account/verification') ->dynamic($verification, Response::MODEL_TOKEN); }); -Http::put('/v1/account/verification') +App::put('/v1/account/verification') ->desc('Create email verification (confirmation)') ->groups(['api', 'account']) ->label('scope', 'public') @@ -3436,10 +3406,9 @@ Http::put('/v1/account/verification') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { - $profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId)); + $profile = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); @@ -3452,7 +3421,7 @@ Http::put('/v1/account/verification') throw new Exception(Exception::USER_INVALID_TOKEN); } - $authorization->addRole(Role::user($profile->getId())->toString()); + Authorization::setRole(Role::user($profile->getId())->toString()); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true)); @@ -3474,7 +3443,7 @@ Http::put('/v1/account/verification') $response->dynamic($verification, Response::MODEL_TOKEN); }); -Http::post('/v1/account/verification/phone') +App::post('/v1/account/verification/phone') ->desc('Create phone verification') ->groups(['api', 'account', 'auth']) ->label('scope', 'account') @@ -3499,8 +3468,7 @@ Http::post('/v1/account/verification/phone') ->inject('queueForMessaging') ->inject('project') ->inject('locale') - ->inject('authorization') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, Authorization $authorization) { + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale) { if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -3514,7 +3482,7 @@ Http::post('/v1/account/verification/phone') throw new Exception(Exception::USER_PHONE_ALREADY_VERIFIED); } - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -3543,7 +3511,7 @@ Http::post('/v1/account/verification/phone') 'ip' => $request->getIP(), ]); - $authorization->addRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); $verification = $dbForProject->createDocument('tokens', $verification ->setAttribute('$permissions', [ @@ -3603,7 +3571,7 @@ Http::post('/v1/account/verification/phone') ->dynamic($verification, Response::MODEL_TOKEN); }); -Http::put('/v1/account/verification/phone') +App::put('/v1/account/verification/phone') ->desc('Update phone verification (confirmation)') ->groups(['api', 'account']) ->label('scope', 'public') @@ -3625,10 +3593,9 @@ Http::put('/v1/account/verification/phone') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { - $profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId)); + $profile = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); @@ -3640,7 +3607,7 @@ Http::put('/v1/account/verification/phone') throw new Exception(Exception::USER_INVALID_TOKEN); } - $authorization->addRole(Role::user($profile->getId())->toString()); + Authorization::setRole(Role::user($profile->getId())->toString()); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('phoneVerification', true)); @@ -3662,7 +3629,7 @@ Http::put('/v1/account/verification/phone') $response->dynamic($verificationDocument, Response::MODEL_TOKEN); }); -Http::patch('/v1/account/mfa') +App::patch('/v1/account/mfa') ->desc('Update MFA') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.mfa') @@ -3715,7 +3682,7 @@ Http::patch('/v1/account/mfa') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -Http::get('/v1/account/mfa/factors') +App::get('/v1/account/mfa/factors') ->desc('List Factors') ->groups(['api', 'account', 'mfa']) ->label('scope', 'account') @@ -3747,7 +3714,7 @@ Http::get('/v1/account/mfa/factors') $response->dynamic($factors, Response::MODEL_MFA_FACTORS); }); -Http::post('/v1/account/mfa/authenticators/:type') +App::post('/v1/account/mfa/authenticators/:type') ->desc('Create Authenticator') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.mfa') @@ -3819,7 +3786,7 @@ Http::post('/v1/account/mfa/authenticators/:type') $response->dynamic($model, Response::MODEL_MFA_TYPE); }); -Http::put('/v1/account/mfa/authenticators/:type') +App::put('/v1/account/mfa/authenticators/:type') ->desc('Verify Authenticator') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.mfa') @@ -3884,7 +3851,7 @@ Http::put('/v1/account/mfa/authenticators/:type') $response->dynamic($user, Response::MODEL_ACCOUNT); }); -Http::post('/v1/account/mfa/recovery-codes') +App::post('/v1/account/mfa/recovery-codes') ->desc('Create MFA Recovery Codes') ->groups(['api', 'account']) ->label('event', 'users.[userId].update.mfa') @@ -3926,7 +3893,7 @@ Http::post('/v1/account/mfa/recovery-codes') $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); }); -Http::patch('/v1/account/mfa/recovery-codes') +App::patch('/v1/account/mfa/recovery-codes') ->desc('Regenerate MFA Recovery Codes') ->groups(['api', 'account', 'mfaProtected']) ->label('event', 'users.[userId].update.mfa') @@ -3967,7 +3934,7 @@ Http::patch('/v1/account/mfa/recovery-codes') $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); }); -Http::get('/v1/account/mfa/recovery-codes') +App::get('/v1/account/mfa/recovery-codes') ->desc('Get MFA Recovery Codes') ->groups(['api', 'account', 'mfaProtected']) ->label('scope', 'account') @@ -3997,7 +3964,7 @@ Http::get('/v1/account/mfa/recovery-codes') $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); }); -Http::delete('/v1/account/mfa/authenticators/:type') +App::delete('/v1/account/mfa/authenticators/:type') ->desc('Delete Authenticator') ->groups(['api', 'account', 'mfaProtected']) ->label('event', 'users.[userId].delete.mfa') @@ -4035,7 +4002,7 @@ Http::delete('/v1/account/mfa/authenticators/:type') $response->noContent(); }); -Http::post('/v1/account/mfa/challenge') +App::post('/v1/account/mfa/challenge') ->desc('Create MFA Challenge') ->groups(['api', 'account', 'mfa']) ->label('scope', 'account') @@ -4223,7 +4190,7 @@ Http::post('/v1/account/mfa/challenge') $response->dynamic($challenge, Response::MODEL_MFA_CHALLENGE); }); -Http::put('/v1/account/mfa/challenge') +App::put('/v1/account/mfa/challenge') ->desc('Create MFA Challenge (confirmation)') ->groups(['api', 'account', 'mfa']) ->label('scope', 'account') @@ -4310,7 +4277,7 @@ Http::put('/v1/account/mfa/challenge') $response->dynamic($session, Response::MODEL_SESSION); }); -Http::post('/v1/account/targets/push') +App::post('/v1/account/targets/push') ->desc('Create push target') ->groups(['api', 'account']) ->label('scope', 'targets.write') @@ -4331,14 +4298,12 @@ Http::post('/v1/account/targets/push') ->inject('request') ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->inject('authentication') - ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization, Authentication $authentication) { + ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) { $targetId = $targetId == 'unique()' ? ID::unique() : $targetId; - $provider = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId)); + $provider = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId)); - $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId)); if (!$target->isEmpty()) { throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS); @@ -4349,7 +4314,7 @@ Http::post('/v1/account/targets/push') $device = $detector->getDevice(); - $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret()); + $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); $session = $dbForProject->getDocument('sessions', $sessionId); try { @@ -4385,7 +4350,7 @@ Http::post('/v1/account/targets/push') ->dynamic($target, Response::MODEL_TARGET); }); -Http::put('/v1/account/targets/:targetId/push') +App::put('/v1/account/targets/:targetId/push') ->desc('Update push target') ->groups(['api', 'account']) ->label('scope', 'targets.write') @@ -4405,10 +4370,9 @@ Http::put('/v1/account/targets/:targetId/push') ->inject('request') ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) { - $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); @@ -4441,7 +4405,7 @@ Http::put('/v1/account/targets/:targetId/push') ->dynamic($target, Response::MODEL_TARGET); }); -Http::delete('/v1/account/targets/:targetId/push') +App::delete('/v1/account/targets/:targetId/push') ->desc('Delete push target') ->groups(['api', 'account']) ->label('scope', 'targets.write') @@ -4461,9 +4425,8 @@ Http::delete('/v1/account/targets/:targetId/push') ->inject('request') ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) { - $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); + ->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject) { + $target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); @@ -4488,7 +4451,7 @@ Http::delete('/v1/account/targets/:targetId/push') $response->noContent(); }); -Http::get('/v1/account/identities') +App::get('/v1/account/identities') ->desc('List Identities') ->groups(['api', 'account']) ->label('scope', 'account') @@ -4544,7 +4507,7 @@ Http::get('/v1/account/identities') ]), Response::MODEL_IDENTITY_LIST); }); -Http::delete('/v1/account/identities/:identityId') +App::delete('/v1/account/identities/:identityId') ->desc('Delete identity') ->groups(['api', 'account']) ->label('scope', 'account') diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 54ba096c5d..a3bd47595d 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -5,7 +5,7 @@ use Appwrite\URL\URL as URLParse; use Appwrite\Utopia\Response; use chillerlan\QRCode\QRCode; use chillerlan\QRCode\QROptions; -use Utopia\CLI\Console; +use Utopia\App; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\DateTime; @@ -14,17 +14,15 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; use Utopia\Fetch\Client; -use Utopia\Http\Http; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\HexColor; -use Utopia\Http\Validator\Range; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\URL; -use Utopia\Http\Validator\WhiteList; use Utopia\Image\Image; -use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\System\System; +use Utopia\Validator\Boolean; +use Utopia\Validator\HexColor; +use Utopia\Validator\Range; +use Utopia\Validator\Text; +use Utopia\Validator\URL; +use Utopia\Validator\WhiteList; $avatarCallback = function (string $type, string $code, int $width, int $height, int $quality, Response $response) { @@ -63,9 +61,9 @@ $avatarCallback = function (string $type, string $code, int $width, int $height, unset($image); }; -$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForConsole, ?Logger $logger, Authorization $auth) { +$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForConsole, ?Logger $logger) { try { - $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); + $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId)); $sessions = $user->getAttribute('sessions', []); @@ -116,7 +114,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro ->setAttribute('providerRefreshToken', $refreshToken) ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry(''))); - $auth->skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession)); + Authorization::skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession)); $dbForProject->purgeCachedDocument('users', $user->getId()); } catch (Throwable $err) { @@ -124,7 +122,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro do { $previousAccessToken = $gitHubSession->getAttribute('providerAccessToken'); - $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); + $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId)); $sessions = $user->getAttribute('sessions', []); $gitHubSession = new Document(); @@ -156,42 +154,11 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro 'id' => $githubId ]; } catch (Exception $error) { - if ($logger) { - $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - - $log = new Log(); - $log->setNamespace('console'); - $log->setServer(\gethostname()); - $log->setVersion($version); - $log->setType(Log::TYPE_ERROR); - $log->setMessage($error->getMessage()); - - $log->addTag('code', $error->getCode()); - $log->addTag('verboseType', get_class($error)); - - $log->addExtra('file', $error->getFile()); - $log->addExtra('line', $error->getLine()); - $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('detailedTrace', $error->getTrace()); - - $log->setAction('avatarsGetGitHub'); - - $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; - - $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - - $responseCode = $logger->addLog($log); - Console::info('GitHub error log pushed with status code: ' . $responseCode); - } - - Console::warning("Failed: {$error->getMessage()}"); - Console::warning($error->getTraceAsString()); - return []; } }; -Http::get('/v1/avatars/credit-cards/:code') +App::get('/v1/avatars/credit-cards/:code') ->desc('Get credit card icon') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -211,7 +178,7 @@ Http::get('/v1/avatars/credit-cards/:code') ->inject('response') ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response)); -Http::get('/v1/avatars/browsers/:code') +App::get('/v1/avatars/browsers/:code') ->desc('Get browser icon') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -231,7 +198,7 @@ Http::get('/v1/avatars/browsers/:code') ->inject('response') ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response)); -Http::get('/v1/avatars/flags/:code') +App::get('/v1/avatars/flags/:code') ->desc('Get country flag') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -251,7 +218,7 @@ Http::get('/v1/avatars/flags/:code') ->inject('response') ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response)); -Http::get('/v1/avatars/image') +App::get('/v1/avatars/image') ->desc('Get image from URL') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -314,7 +281,7 @@ Http::get('/v1/avatars/image') unset($image); }); -Http::get('/v1/avatars/favicon') +App::get('/v1/avatars/favicon') ->desc('Get favicon') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -459,7 +426,7 @@ Http::get('/v1/avatars/favicon') unset($image); }); -Http::get('/v1/avatars/qr') +App::get('/v1/avatars/qr') ->desc('Get QR code') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -499,7 +466,7 @@ Http::get('/v1/avatars/qr') ->send($image->output('png', 9)); }); -Http::get('/v1/avatars/initials') +App::get('/v1/avatars/initials') ->desc('Get user initials') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -582,7 +549,7 @@ Http::get('/v1/avatars/initials') ->file($image->getImageBlob()); }); -Http::get('/v1/cards/cloud') +App::get('/v1/cards/cloud') ->desc('Get Front Of Cloud Card') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -604,9 +571,8 @@ Http::get('/v1/cards/cloud') ->inject('contributors') ->inject('employees') ->inject('logger') - ->inject('auth') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { - $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) { + $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -617,7 +583,7 @@ Http::get('/v1/cards/cloud') $email = $user->getAttribute('email', ''); $createdAt = new \DateTime($user->getCreatedAt()); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $auth); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger); $githubName = $gitHub['name'] ?? ''; $githubId = $gitHub['id'] ?? ''; @@ -790,7 +756,7 @@ Http::get('/v1/cards/cloud') ->file($baseImage->getImageBlob()); }); -Http::get('/v1/cards/cloud-back') +App::get('/v1/cards/cloud-back') ->desc('Get Back Of Cloud Card') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -812,9 +778,8 @@ Http::get('/v1/cards/cloud-back') ->inject('contributors') ->inject('employees') ->inject('logger') - ->inject('auth') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { - $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) { + $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -824,7 +789,7 @@ Http::get('/v1/cards/cloud-back') $userId = $user->getId(); $email = $user->getAttribute('email', ''); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $auth); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger); $githubId = $gitHub['id'] ?? ''; $isHero = \array_key_exists($email, $heroes); @@ -869,7 +834,7 @@ Http::get('/v1/cards/cloud-back') ->file($baseImage->getImageBlob()); }); -Http::get('/v1/cards/cloud-og') +App::get('/v1/cards/cloud-og') ->desc('Get OG Image From Cloud Card') ->groups(['api', 'avatars']) ->label('scope', 'avatars.read') @@ -891,9 +856,8 @@ Http::get('/v1/cards/cloud-og') ->inject('contributors') ->inject('employees') ->inject('logger') - ->inject('auth') - ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $auth) use ($getUserGitHub) { - $user = $auth->skip(fn () => $dbForConsole->getDocument('users', $userId)); + ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) { + $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId)); if ($user->isEmpty() && empty($mock)) { throw new Exception(Exception::USER_NOT_FOUND); @@ -908,7 +872,7 @@ Http::get('/v1/cards/cloud-og') $email = $user->getAttribute('email', ''); $createdAt = new \DateTime($user->getCreatedAt()); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger, $auth); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger); $githubName = $gitHub['name'] ?? ''; $githubId = $gitHub['id'] ?? ''; diff --git a/app/controllers/api/console.php b/app/controllers/api/console.php index 2a393497ae..82d7c75592 100644 --- a/app/controllers/api/console.php +++ b/app/controllers/api/console.php @@ -2,12 +2,12 @@ use Appwrite\Extend\Exception; use Appwrite\Utopia\Response; +use Utopia\App; use Utopia\Database\Document; -use Utopia\Http\Http; -use Utopia\Http\Validator\Text; use Utopia\System\System; +use Utopia\Validator\Text; -Http::init() +App::init() ->groups(['console']) ->inject('project') ->action(function (Document $project) { @@ -17,7 +17,7 @@ Http::init() }); -Http::get('/v1/console/variables') +App::get('/v1/console/variables') ->desc('Get variables') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -56,7 +56,7 @@ Http::get('/v1/console/variables') $response->dynamic($variables, Response::MODEL_CONSOLE_VARIABLES); }); -Http::post('/v1/console/assistant') +App::post('/v1/console/assistant') ->desc('Ask Query') ->groups(['api', 'assistant']) ->label('scope', 'assistant.read') diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index be22b66cb8..a9bb58df4b 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -15,6 +15,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Indexes; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; +use Utopia\App; use Utopia\Audit\Audit; use Utopia\Config\Config; use Utopia\Database\Database; @@ -33,7 +34,6 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Index as IndexValidator; use Utopia\Database\Validator\Key; @@ -43,19 +43,18 @@ use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\Structure; use Utopia\Database\Validator\UID; -use Utopia\Http\Http; -use Utopia\Http\Validator\ArrayList; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\FloatValidator; -use Utopia\Http\Validator\Integer; -use Utopia\Http\Validator\IP; -use Utopia\Http\Validator\JSON; -use Utopia\Http\Validator\Nullable; -use Utopia\Http\Validator\Range; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\URL; -use Utopia\Http\Validator\WhiteList; use Utopia\Locale\Locale; +use Utopia\Validator\ArrayList; +use Utopia\Validator\Boolean; +use Utopia\Validator\FloatValidator; +use Utopia\Validator\Integer; +use Utopia\Validator\IP; +use Utopia\Validator\JSON; +use Utopia\Validator\Nullable; +use Utopia\Validator\Range; +use Utopia\Validator\Text; +use Utopia\Validator\URL; +use Utopia\Validator\WhiteList; /** * * Create attribute of varying type @@ -77,7 +76,7 @@ use Utopia\Locale\Locale; * @throws ConflictException * @throws Exception */ -function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): Document +function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): Document { $key = $attribute->getAttribute('key'); $type = $attribute->getAttribute('type', ''); @@ -91,7 +90,7 @@ function createAttribute(string $databaseId, string $collectionId, Document $att $default = $attribute->getAttribute('default'); $options = $attribute->getAttribute('options', []); - $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -226,7 +225,6 @@ function createAttribute(string $databaseId, string $collectionId, Document $att } function updateAttribute( - Authorization $authorization, string $databaseId, string $collectionId, string $key, @@ -240,10 +238,10 @@ function updateAttribute( int|float $min = null, int|float $max = null, array $elements = null, - string $newKey = null, array $options = [], + string $newKey = null, ): Document { - $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -422,19 +420,19 @@ function updateAttribute( return $attribute; } -Http::init() +App::init() ->groups(['api', 'database']) ->inject('request') ->inject('dbForProject') ->action(function (Request $request, Database $dbForProject) { $timeout = \intval($request->getHeader('x-appwrite-timeout')); - if (!empty($timeout) && Http::isDevelopment()) { + if (!empty($timeout) && App::isDevelopment()) { $dbForProject->setTimeout($timeout); } }); -Http::post('/v1/databases') +App::post('/v1/databases') ->desc('Create database') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].create') @@ -510,7 +508,7 @@ Http::post('/v1/databases') ->dynamic($database, Response::MODEL_DATABASE); }); -Http::get('/v1/databases') +App::get('/v1/databases') ->desc('List databases') ->groups(['api', 'database']) ->label('scope', 'databases.read') @@ -563,7 +561,7 @@ Http::get('/v1/databases') ]), Response::MODEL_DATABASE_LIST); }); -Http::get('/v1/databases/:databaseId') +App::get('/v1/databases/:databaseId') ->desc('Get database') ->groups(['api', 'database']) ->label('scope', 'databases.read') @@ -588,7 +586,7 @@ Http::get('/v1/databases/:databaseId') $response->dynamic($database, Response::MODEL_DATABASE); }); -Http::get('/v1/databases/:databaseId/logs') +App::get('/v1/databases/:databaseId/logs') ->desc('List database logs') ->groups(['api', 'database']) ->label('scope', 'databases.read') @@ -605,8 +603,7 @@ Http::get('/v1/databases/:databaseId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('authorization') - ->action(function (string $databaseId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { + ->action(function (string $databaseId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -680,7 +677,7 @@ Http::get('/v1/databases/:databaseId/logs') }); -Http::put('/v1/databases/:databaseId') +App::put('/v1/databases/:databaseId') ->desc('Update database') ->groups(['api', 'database', 'schema']) ->label('scope', 'databases.write') @@ -718,7 +715,7 @@ Http::put('/v1/databases/:databaseId') $response->dynamic($database, Response::MODEL_DATABASE); }); -Http::delete('/v1/databases/:databaseId') +App::delete('/v1/databases/:databaseId') ->desc('Delete database') ->groups(['api', 'database', 'schema']) ->label('scope', 'databases.write') @@ -762,7 +759,7 @@ Http::delete('/v1/databases/:databaseId') $response->noContent(); }); -Http::post('/v1/databases/:databaseId/collections') +App::post('/v1/databases/:databaseId/collections') ->desc('Create collection') ->groups(['api', 'database']) ->label('event', 'databases.[databaseId].collections.[collectionId].create') @@ -786,10 +783,9 @@ Http::post('/v1/databases/:databaseId/collections') ->inject('dbForProject') ->inject('mode') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents) { - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -830,7 +826,7 @@ Http::post('/v1/databases/:databaseId/collections') ->dynamic($collection, Response::MODEL_COLLECTION); }); -Http::get('/v1/databases/:databaseId/collections') +App::get('/v1/databases/:databaseId/collections') ->alias('/v1/database/collections', ['databaseId' => 'default']) ->desc('List collections') ->groups(['api', 'database']) @@ -848,10 +844,9 @@ Http::get('/v1/databases/:databaseId/collections') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('authorization') - ->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { + ->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -894,7 +889,7 @@ Http::get('/v1/databases/:databaseId/collections') ]), Response::MODEL_COLLECTION_LIST); }); -Http::get('/v1/databases/:databaseId/collections/:collectionId') +App::get('/v1/databases/:databaseId/collections/:collectionId') ->alias('/v1/database/collections/:collectionId', ['databaseId' => 'default']) ->desc('Get collection') ->groups(['api', 'database']) @@ -911,10 +906,9 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, string $mode) { - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -929,7 +923,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId') $response->dynamic($collection, Response::MODEL_COLLECTION); }); -Http::get('/v1/databases/:databaseId/collections/:collectionId/logs') +App::get('/v1/databases/:databaseId/collections/:collectionId/logs') ->alias('/v1/database/collections/:collectionId/logs', ['databaseId' => 'default']) ->desc('List collection logs') ->groups(['api', 'database']) @@ -948,10 +942,9 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1030,7 +1023,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/logs') }); -Http::put('/v1/databases/:databaseId/collections/:collectionId') +App::put('/v1/databases/:databaseId/collections/:collectionId') ->alias('/v1/database/collections/:collectionId', ['databaseId' => 'default']) ->desc('Update collection') ->groups(['api', 'database', 'schema']) @@ -1055,10 +1048,9 @@ Http::put('/v1/databases/:databaseId/collections/:collectionId') ->inject('dbForProject') ->inject('mode') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, Response $response, Database $dbForProject, string $mode, Event $queueForEvents) { - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1094,7 +1086,7 @@ Http::put('/v1/databases/:databaseId/collections/:collectionId') $response->dynamic($collection, Response::MODEL_COLLECTION); }); -Http::delete('/v1/databases/:databaseId/collections/:collectionId') +App::delete('/v1/databases/:databaseId/collections/:collectionId') ->alias('/v1/database/collections/:collectionId', ['databaseId' => 'default']) ->desc('Delete collection') ->groups(['api', 'database', 'schema']) @@ -1115,10 +1107,9 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId') ->inject('queueForDatabase') ->inject('queueForEvents') ->inject('mode') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode) { - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1150,7 +1141,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId') $response->noContent(); }); -Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string') +App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string') ->alias('/v1/database/collections/:collectionId/attributes/string', ['databaseId' => 'default']) ->desc('Create string attribute') ->groups(['api', 'database', 'schema']) @@ -1177,8 +1168,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/strin ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, bool $encrypt, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { // Ensure attribute default is within required size $validator = new Text($size, 0); @@ -1200,7 +1190,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/strin 'default' => $default, 'array' => $array, 'filters' => $filters, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response @@ -1208,7 +1198,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/strin ->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING); }); -Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email') +App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email') ->alias('/v1/database/collections/:collectionId/attributes/email', ['databaseId' => 'default']) ->desc('Create email attribute') ->groups(['api', 'database', 'schema']) @@ -1233,8 +1223,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1244,14 +1233,14 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_EMAIL, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) ->dynamic($attribute, Response::MODEL_ATTRIBUTE_EMAIL); }); -Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') +App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') ->alias('/v1/database/collections/:collectionId/attributes/enum', ['databaseId' => 'default']) ->desc('Create enum attribute') ->groups(['api', 'database', 'schema']) @@ -1277,8 +1266,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum' ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { if (!is_null($default) && !in_array($default, $elements)) { throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements'); } @@ -1292,14 +1280,14 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum' 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_ENUM, 'formatOptions' => ['elements' => $elements], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) ->dynamic($attribute, Response::MODEL_ATTRIBUTE_ENUM); }); -Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') +App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->alias('/v1/database/collections/:collectionId/attributes/ip', ['databaseId' => 'default']) ->desc('Create IP address attribute') ->groups(['api', 'database', 'schema']) @@ -1324,8 +1312,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1335,14 +1322,14 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip') 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_IP, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) ->dynamic($attribute, Response::MODEL_ATTRIBUTE_IP); }); -Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') +App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->alias('/v1/database/collections/:collectionId/attributes/url', ['databaseId' => 'default']) ->desc('Create URL attribute') ->groups(['api', 'database', 'schema']) @@ -1367,8 +1354,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1378,14 +1364,14 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url') 'default' => $default, 'array' => $array, 'format' => APP_DATABASE_ATTRIBUTE_URL, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) ->dynamic($attribute, Response::MODEL_ATTRIBUTE_URL); }); -Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integer') +App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integer') ->alias('/v1/database/collections/:collectionId/attributes/integer', ['databaseId' => 'default']) ->desc('Create integer attribute') ->groups(['api', 'database', 'schema']) @@ -1412,8 +1398,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integ ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') - ->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, Authorization $authorization) { + ->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 : \intval($min); @@ -1443,7 +1428,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integ 'min' => $min, 'max' => $max, ], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $formatOptions = $attribute->getAttribute('formatOptions', []); @@ -1457,7 +1442,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/integ ->dynamic($attribute, Response::MODEL_ATTRIBUTE_INTEGER); }); -Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float') +App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float') ->alias('/v1/database/collections/:collectionId/attributes/float', ['databaseId' => 'default']) ->desc('Create float attribute') ->groups(['api', 'database', 'schema']) @@ -1484,8 +1469,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') - ->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, Authorization $authorization) { + ->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 : \floatval($min); @@ -1518,7 +1502,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float 'min' => $min, 'max' => $max, ], - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $formatOptions = $attribute->getAttribute('formatOptions', []); @@ -1532,7 +1516,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float ->dynamic($attribute, Response::MODEL_ATTRIBUTE_FLOAT); }); -Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean') +App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean') ->alias('/v1/database/collections/:collectionId/attributes/boolean', ['databaseId' => 'default']) ->desc('Create boolean attribute') ->groups(['api', 'database', 'schema']) @@ -1557,8 +1541,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boole ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $attribute = createAttribute($databaseId, $collectionId, new Document([ 'key' => $key, @@ -1567,14 +1550,14 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boole 'required' => $required, 'default' => $default, 'array' => $array, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) ->dynamic($attribute, Response::MODEL_ATTRIBUTE_BOOLEAN); }); -Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime') +App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime') ->alias('/v1/database/collections/:collectionId/attributes/datetime', ['databaseId' => 'default']) ->desc('Create datetime attribute') ->groups(['api', 'database']) @@ -1599,8 +1582,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datet ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { $filters[] = 'datetime'; @@ -1612,14 +1594,14 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/datet 'default' => $default, 'array' => $array, 'filters' => $filters, - ]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); + ]), $response, $dbForProject, $queueForDatabase, $queueForEvents); $response ->setStatusCode(Response::STATUS_CODE_ACCEPTED) ->dynamic($attribute, Response::MODEL_ATTRIBUTE_DATETIME); }); -Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relationship') +App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relationship') ->alias('/v1/database/collections/:collectionId/attributes/relationship', ['databaseId' => 'default']) ->desc('Create relationship attribute') ->groups(['api', 'database']) @@ -1646,7 +1628,6 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relat ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') ->action(function ( string $databaseId, string $collectionId, @@ -1659,13 +1640,12 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relat Response $response, Database $dbForProject, EventDatabase $queueForDatabase, - Event $queueForEvents, - Authorization $authorization + Event $queueForEvents ) { $key ??= $relatedCollectionId; $twoWayKey ??= $collectionId; - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1735,8 +1715,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relat $response, $dbForProject, $queueForDatabase, - $queueForEvents, - $authorization + $queueForEvents ); $options = $attribute->getAttribute('options', []); @@ -1750,7 +1729,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relat ->dynamic($attribute, Response::MODEL_ATTRIBUTE_RELATIONSHIP); }); -Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes') +App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') ->alias('/v1/database/collections/:collectionId/attributes', ['databaseId' => 'default']) ->desc('List attributes') ->groups(['api', 'database']) @@ -1767,10 +1746,9 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes') ->param('queries', [], new Attributes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Attributes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject) { /** @var Document $database */ - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1804,7 +1782,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes') if ($cursor) { $attributeId = $cursor->getValue(); - $cursorDocument = $authorization->skip(fn () => $dbForProject->find('attributes', [ + $cursorDocument = Authorization::skip(fn () => $dbForProject->find('attributes', [ Query::equal('collectionInternalId', [$collection->getInternalId()]), Query::equal('databaseInternalId', [$database->getInternalId()]), Query::equal('key', [$attributeId]), @@ -1829,7 +1807,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes') ]), Response::MODEL_ATTRIBUTE_LIST); }); -Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') +App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') ->alias('/v1/database/collections/:collectionId/attributes/:key', ['databaseId' => 'default']) ->desc('Get attribute') ->groups(['api', 'database']) @@ -1856,10 +1834,9 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') ->param('key', '', new Key(), 'Attribute Key.') ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject) { - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -1905,7 +1882,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') $response->dynamic($attribute, $model); }); -Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/string/:key') +App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/string/:key') ->desc('Update string attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -1928,11 +1905,9 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/stri ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?int $size, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?int $size, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( - authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -1950,7 +1925,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/stri ->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING); }); -Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email/:key') +App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email/:key') ->desc('Update email attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -1972,10 +1947,8 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/emai ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( - authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -1993,7 +1966,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/emai ->dynamic($attribute, Response::MODEL_ATTRIBUTE_EMAIL); }); -Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/:key') +App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/:key') ->desc('Update enum attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2016,10 +1989,8 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( - authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2038,7 +2009,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum ->dynamic($attribute, Response::MODEL_ATTRIBUTE_ENUM); }); -Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:key') +App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:key') ->desc('Update IP address attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2060,10 +2031,8 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/: ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( - authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2081,7 +2050,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/: ->dynamic($attribute, Response::MODEL_ATTRIBUTE_IP); }); -Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:key') +App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:key') ->desc('Update URL attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2103,10 +2072,8 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/ ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( - authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2124,7 +2091,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/ ->dynamic($attribute, Response::MODEL_ATTRIBUTE_URL); }); -Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integer/:key') +App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integer/:key') ->desc('Update integer attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2148,10 +2115,8 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/inte ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( - authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2177,7 +2142,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/inte ->dynamic($attribute, Response::MODEL_ATTRIBUTE_INTEGER); }); -Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float/:key') +App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float/:key') ->desc('Update float attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2201,10 +2166,8 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/floa ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( - authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2230,7 +2193,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/floa ->dynamic($attribute, Response::MODEL_ATTRIBUTE_FLOAT); }); -Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean/:key') +App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boolean/:key') ->desc('Update boolean attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2252,10 +2215,8 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/bool ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( - authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2272,7 +2233,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/bool ->dynamic($attribute, Response::MODEL_ATTRIBUTE_BOOLEAN); }); -Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime/:key') +App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datetime/:key') ->desc('Update dateTime attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2294,10 +2255,8 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/date ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, Response $response, Database $dbForProject, Event $queueForEvents) { $attribute = updateAttribute( - authorization: $authorization, databaseId: $databaseId, collectionId: $collectionId, key: $key, @@ -2314,7 +2273,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/date ->dynamic($attribute, Response::MODEL_ATTRIBUTE_DATETIME); }); -Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/relationship') +App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/relationship') ->desc('Update relationship attribute') ->groups(['api', 'database', 'schema']) ->label('scope', 'collections.write') @@ -2335,7 +2294,6 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') ->action(function ( string $databaseId, string $collectionId, @@ -2344,11 +2302,9 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ?string $newKey, Response $response, Database $dbForProject, - Event $queueForEvents, - Authorization $authorization + Event $queueForEvents ) { $attribute = updateAttribute( - $authorization, $databaseId, $collectionId, $key, @@ -2373,7 +2329,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->dynamic($attribute, Response::MODEL_ATTRIBUTE_RELATIONSHIP); }); -Http::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') +App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') ->alias('/v1/database/collections/:collectionId/attributes/:key', ['databaseId' => 'default']) ->desc('Delete attribute') ->groups(['api', 'database', 'schema']) @@ -2394,10 +2350,9 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:ke ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2483,7 +2438,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:ke $response->noContent(); }); -Http::post('/v1/databases/:databaseId/collections/:collectionId/indexes') +App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->alias('/v1/database/collections/:collectionId/indexes', ['databaseId' => 'default']) ->desc('Create index') ->groups(['api', 'database']) @@ -2508,10 +2463,9 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2654,7 +2608,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/indexes') ->dynamic($index, Response::MODEL_INDEX); }); -Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes') +App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') ->alias('/v1/database/collections/:collectionId/indexes', ['databaseId' => 'default']) ->desc('List indexes') ->groups(['api', 'database']) @@ -2671,10 +2625,9 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes') ->param('queries', [], new Indexes(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Indexes::ALLOWED_ATTRIBUTES), true) ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject) { /** @var Document $database */ - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2704,7 +2657,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes') if ($cursor) { $indexId = $cursor->getValue(); - $cursorDocument = $authorization->skip(fn () => $dbForProject->find('indexes', [ + $cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [ Query::equal('collectionInternalId', [$collection->getInternalId()]), Query::equal('databaseInternalId', [$database->getInternalId()]), Query::equal('key', [$indexId]), @@ -2725,7 +2678,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes') ]), Response::MODEL_INDEX_LIST); }); -Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') +App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->alias('/v1/database/collections/:collectionId/indexes/:key', ['databaseId' => 'default']) ->desc('Get index') ->groups(['api', 'database']) @@ -2742,10 +2695,9 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->param('key', null, new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject) { - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2765,7 +2717,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') }); -Http::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') +App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->alias('/v1/database/collections/:collectionId/indexes/:key', ['databaseId' => 'default']) ->desc('Delete index') ->groups(['api', 'database']) @@ -2786,10 +2738,9 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { - $db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -2830,7 +2781,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') $response->noContent(); }); -Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') +App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->alias('/v1/database/collections/:collectionId/documents', ['databaseId' => 'default']) ->desc('Create document') ->groups(['api', 'database']) @@ -2860,8 +2811,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('user') ->inject('queueForEvents') ->inject('mode') - ->inject('authorization') - ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Authorization $authorization) { + ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -2873,16 +2823,16 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new documents, try update instead'); } - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -2920,8 +2870,8 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$authorization->isRole($role)) { - throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $authorization->getRoles()) . ')'); + if (!Authorization::isRole($role)) { + throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')'); } } } @@ -2932,16 +2882,17 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') $data['$permissions'] = $permissions; $document = new Document($data); - $checkPermissions = function (Document $collection, Document $document, string $permission) use (&$checkPermissions, $dbForProject, $database, $authorization) { + $checkPermissions = function (Document $collection, Document $document, string $permission) use (&$checkPermissions, $dbForProject, $database) { $documentSecurity = $collection->getAttribute('documentSecurity', false); + $validator = new Authorization($permission); - $valid = $authorization->isValid(new Input($permission, $collection->getPermissionsByType($permission))); + $valid = $validator->isValid($collection->getPermissionsByType($permission)); if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } if ($permission === Database::PERMISSION_UPDATE) { - $valid = $valid || $authorization->isValid($document->getUpdate()); + $valid = $valid || $validator->isValid($document->getUpdate()); if ($documentSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -2968,7 +2919,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $authorization->skip( + $relatedCollection = Authorization::skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -2982,7 +2933,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') $relation = new Document($relation); } if ($relation instanceof Document) { - $current = $authorization->skip( + $current = Authorization::skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), $relation->getId()) ); @@ -3022,7 +2973,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') } // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $authorization) { + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { $document->setAttribute('$databaseId', $database->getId()); $document->setAttribute('$collectionId', $collection->getId()); @@ -3042,7 +2993,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $authorization->skip( + $relatedCollection = Authorization::skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3078,7 +3029,7 @@ Http::post('/v1/databases/:databaseId/collections/:collectionId/documents') }); -Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') +App::get('/v1/databases/:databaseId/collections/:collectionId/documents') ->alias('/v1/database/collections/:collectionId/documents', ['databaseId' => 'default']) ->desc('List documents') ->groups(['api', 'database']) @@ -3097,17 +3048,16 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode) { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -3131,7 +3081,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') if ($cursor) { $documentId = $cursor->getValue(); - $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Document '{$documentId}' for the 'cursor' value not found."); @@ -3150,7 +3100,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') } // Add $collectionId and $databaseId for all documents - $processDocument = (function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $authorization): bool { + $processDocument = (function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database): bool { if ($document->isEmpty()) { return false; } @@ -3177,7 +3127,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId)); + $relatedCollection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId)); foreach ($relations as $index => $doc) { if ($doc instanceof Document) { @@ -3234,7 +3184,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents') ]), Response::MODEL_DOCUMENT_LIST); }); -Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') +App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') ->alias('/v1/database/collections/:collectionId/documents/:documentId', ['databaseId' => 'default']) ->desc('Get document') ->groups(['api', 'database']) @@ -3255,18 +3205,17 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, string $mode) { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -3286,7 +3235,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume } // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $authorization) { + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { if ($document->isEmpty()) { return; } @@ -3310,7 +3259,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $authorization->skip( + $relatedCollection = Authorization::skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3327,7 +3276,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume $response->dynamic($document, Response::MODEL_DOCUMENT); }); -Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/logs') +App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId/logs') ->alias('/v1/database/collections/:collectionId/documents/:documentId/logs', ['databaseId' => 'default']) ->desc('List document logs') ->groups(['api', 'database']) @@ -3347,10 +3296,9 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { throw new Exception(Exception::DATABASE_NOT_FOUND); @@ -3432,7 +3380,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/documents/:docume ]), Response::MODEL_LOG_LIST); }); -Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') +App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') ->alias('/v1/database/collections/:collectionId/documents/:documentId', ['databaseId' => 'default']) ->desc('Update document') ->groups(['api', 'database']) @@ -3462,8 +3410,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->inject('dbForProject') ->inject('queueForEvents') ->inject('mode') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Authorization $authorization) { + ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, string $mode) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -3471,16 +3418,16 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD); } - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); @@ -3488,7 +3435,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu // Read permission should not be required for update /** @var Document $document */ - $document = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); if ($document->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); @@ -3502,7 +3449,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ]); // Users can only manage their own roles, API keys and Admin users can manage any - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -3515,7 +3462,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$authorization->isRole($role)) { + if (!Authorization::isRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -3530,7 +3477,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $data['$permissions'] = $permissions; $newDocument = new Document($data); - $setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database, $authorization) { + $setCollection = (function (Document $collection, Document $document) use (&$setCollection, $dbForProject, $database) { $relationships = \array_filter( $collection->getAttribute('attributes', []), fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP @@ -3552,7 +3499,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $authorization->skip( + $relatedCollection = Authorization::skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3567,7 +3514,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $relation = new Document($relation); } if ($relation instanceof Document) { - $oldDocument = $authorization->skip(fn () => $dbForProject->getDocument( + $oldDocument = Authorization::skip(fn () => $dbForProject->getDocument( 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(), $relation->getId() )); @@ -3616,7 +3563,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu } // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $authorization) { + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { $document->setAttribute('$databaseId', $database->getId()); $document->setAttribute('$collectionId', $collection->getId()); @@ -3636,7 +3583,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $authorization->skip( + $relatedCollection = Authorization::skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3669,7 +3616,7 @@ Http::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->setPayload($response->getPayload(), sensitive: $relationships); }); -Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') +App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:documentId') ->alias('/v1/database/collections/:collectionId/documents/:documentId', ['databaseId' => 'default']) ->desc('Delete document') ->groups(['api', 'database']) @@ -3697,25 +3644,24 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:doc ->inject('queueForDeletes') ->inject('queueForEvents') ->inject('mode') - ->inject('authorization') - ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, string $mode, Authorization $authorization) { - $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); + ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, string $mode) { + $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); } - $collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); + $collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId)); if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::COLLECTION_NOT_FOUND); } // Read permission should not be required for delete - $document = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); + $document = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); if ($document->isEmpty()) { throw new Exception(Exception::DOCUMENT_NOT_FOUND); @@ -3729,7 +3675,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:doc }); // Add $collectionId and $databaseId for all documents - $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database, $authorization) { + $processDocument = function (Document $collection, Document $document) use (&$processDocument, $dbForProject, $database) { $document->setAttribute('$databaseId', $database->getId()); $document->setAttribute('$collectionId', $collection->getId()); @@ -3749,7 +3695,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:doc } $relatedCollectionId = $relationship->getAttribute('relatedCollection'); - $relatedCollection = $authorization->skip( + $relatedCollection = Authorization::skip( fn () => $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId) ); @@ -3786,7 +3732,7 @@ Http::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:doc $response->noContent(); }); -Http::get('/v1/databases/usage') +App::get('/v1/databases/usage') ->desc('Get databases usage stats') ->groups(['api', 'database', 'usage']) ->label('scope', 'collections.read') @@ -3799,8 +3745,7 @@ Http::get('/v1/databases/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $range, Response $response, Database $dbForProject) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -3811,7 +3756,7 @@ Http::get('/v1/databases/usage') METRIC_DOCUMENTS, ]; - $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -3865,7 +3810,7 @@ Http::get('/v1/databases/usage') ]), Response::MODEL_USAGE_DATABASES); }); -Http::get('/v1/databases/:databaseId/usage') +App::get('/v1/databases/:databaseId/usage') ->desc('Get database usage stats') ->groups(['api', 'database', 'usage']) ->label('scope', 'collections.read') @@ -3879,8 +3824,7 @@ Http::get('/v1/databases/:databaseId/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), '`Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $databaseId, string $range, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $databaseId, string $range, Response $response, Database $dbForProject) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -3896,7 +3840,7 @@ Http::get('/v1/databases/:databaseId/usage') str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS), ]; - $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -3949,7 +3893,7 @@ Http::get('/v1/databases/:databaseId/usage') ]), Response::MODEL_USAGE_DATABASE); }); -Http::get('/v1/databases/:databaseId/collections/:collectionId/usage') +App::get('/v1/databases/:databaseId/collections/:collectionId/usage') ->alias('/v1/database/:collectionId/usage', ['databaseId' => 'default']) ->desc('Get collection usage stats') ->groups(['api', 'database', 'usage']) @@ -3965,8 +3909,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/usage') ->param('collectionId', '', new UID(), 'Collection ID.') ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $databaseId, string $range, string $collectionId, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $databaseId, string $range, string $collectionId, Response $response, Database $dbForProject) { $database = $dbForProject->getDocument('databases', $databaseId); $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); @@ -3983,7 +3926,7 @@ Http::get('/v1/databases/:databaseId/collections/:collectionId/usage') str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collectionDocument->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), ]; - $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 4792343a3e..6e429fd8cf 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -2,7 +2,6 @@ use Ahc\Jwt\JWT; use Appwrite\Auth\Auth; -use Appwrite\Auth\Authentication; use Appwrite\Event\Build; use Appwrite\Event\Delete; use Appwrite\Event\Event; @@ -21,11 +20,11 @@ use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\Queries\Deployments; use Appwrite\Utopia\Database\Validator\Queries\Executions; use Appwrite\Utopia\Database\Validator\Queries\Functions; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model\Rule; use Executor\Executor; use MaxMind\Db\Reader; +use Utopia\App; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -38,25 +37,24 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Roles; use Utopia\Database\Validator\UID; -use Utopia\Http\Http; -use Utopia\Http\Validator\AnyOf; -use Utopia\Http\Validator\ArrayList; -use Utopia\Http\Validator\Assoc; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\Nullable; -use Utopia\Http\Validator\Range; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; use Utopia\Storage\Device; use Utopia\Storage\Validator\File; use Utopia\Storage\Validator\FileExt; use Utopia\Storage\Validator\FileSize; use Utopia\Storage\Validator\Upload; +use Utopia\Swoole\Request; use Utopia\System\System; +use Utopia\Validator\AnyOf; +use Utopia\Validator\ArrayList; +use Utopia\Validator\Assoc; +use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; +use Utopia\Validator\Range; +use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub; use Utopia\VCS\Exception\RepositoryNotFound; @@ -135,7 +133,7 @@ $redeployVcs = function (Request $request, Document $function, Document $project ->setTemplate($template); }; -Http::post('/v1/functions') +App::post('/v1/functions') ->groups(['api', 'functions']) ->desc('Create function') ->label('scope', 'functions.write') @@ -173,8 +171,8 @@ Http::post('/v1/functions') ->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification( $plan, Config::getParam('runtime-specifications', []), - System::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT), - System::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT) + App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT), + App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT) ), 'Runtime specification for the function and builds.', true, ['plan']) ->inject('request') ->inject('response') @@ -185,8 +183,7 @@ Http::post('/v1/functions') ->inject('queueForBuilds') ->inject('dbForConsole') ->inject('gitHub') - ->inject('authorization') - ->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, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $authorization) 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, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) { $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId; $allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', ''))); @@ -250,7 +247,7 @@ Http::post('/v1/functions') 'specification' => $specification ])); - $schedule = $authorization->skip( + $schedule = Authorization::skip( fn () => $dbForConsole->createDocument('schedules', new Document([ 'region' => System::getEnv('_APP_REGION', 'default'), // Todo replace with projects region 'resourceType' => 'function', @@ -332,7 +329,7 @@ Http::post('/v1/functions') $routeSubdomain = ID::unique(); $domain = "{$routeSubdomain}.{$functionsDomain}"; - $rule = $authorization->skip( + $rule = Authorization::skip( fn () => $dbForConsole->createDocument('rules', new Document([ '$id' => $ruleId, 'projectId' => $project->getId(), @@ -399,7 +396,7 @@ Http::post('/v1/functions') ->dynamic($function, Response::MODEL_FUNCTION); }); -Http::get('/v1/functions') +App::get('/v1/functions') ->groups(['api', 'functions']) ->desc('List functions') ->label('scope', 'functions.read') @@ -453,7 +450,7 @@ Http::get('/v1/functions') ]), Response::MODEL_FUNCTION_LIST); }); -Http::get('/v1/functions/runtimes') +App::get('/v1/functions/runtimes') ->groups(['api', 'functions']) ->desc('List runtimes') ->label('scope', 'functions.read') @@ -486,7 +483,7 @@ Http::get('/v1/functions/runtimes') ]), Response::MODEL_RUNTIME_LIST); }); -Http::get('/v1/functions/specifications') +App::get('/v1/functions/specifications') ->groups(['api', 'functions']) ->desc('List available function runtime specifications') ->label('scope', 'functions.read') @@ -522,7 +519,7 @@ Http::get('/v1/functions/specifications') ]), Response::MODEL_SPECIFICATION_LIST); }); -Http::get('/v1/functions/:functionId') +App::get('/v1/functions/:functionId') ->groups(['api', 'functions']) ->desc('Get function') ->label('scope', 'functions.read') @@ -546,7 +543,7 @@ Http::get('/v1/functions/:functionId') $response->dynamic($function, Response::MODEL_FUNCTION); }); -Http::get('/v1/functions/:functionId/usage') +App::get('/v1/functions/:functionId/usage') ->desc('Get function usage') ->groups(['api', 'functions', 'usage']) ->label('scope', 'functions.read') @@ -560,8 +557,7 @@ Http::get('/v1/functions/:functionId/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $functionId, string $range, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $functionId, string $range, Response $response, Database $dbForProject) { $function = $dbForProject->getDocument('functions', $functionId); @@ -584,7 +580,7 @@ Http::get('/v1/functions/:functionId/usage') str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS) ]; - $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -651,7 +647,7 @@ Http::get('/v1/functions/:functionId/usage') ]), Response::MODEL_USAGE_FUNCTION); }); -Http::get('/v1/functions/usage') +App::get('/v1/functions/usage') ->desc('Get functions usage') ->groups(['api', 'functions']) ->label('scope', 'functions.read') @@ -664,8 +660,7 @@ Http::get('/v1/functions/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $range, Response $response, Database $dbForProject) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -683,7 +678,7 @@ Http::get('/v1/functions/usage') METRIC_EXECUTIONS_MB_SECONDS, ]; - $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -751,7 +746,7 @@ Http::get('/v1/functions/usage') ]), Response::MODEL_USAGE_FUNCTIONS); }); -Http::put('/v1/functions/:functionId') +App::put('/v1/functions/:functionId') ->groups(['api', 'functions']) ->desc('Update function') ->label('scope', 'functions.write') @@ -785,8 +780,8 @@ Http::put('/v1/functions/:functionId') ->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification( $plan, Config::getParam('runtime-specifications', []), - System::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT), - System::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT) + App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT), + App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT) ), 'Runtime specification for the function and builds.', true, ['plan']) ->inject('request') ->inject('response') @@ -796,8 +791,7 @@ Http::put('/v1/functions/:functionId') ->inject('queueForBuilds') ->inject('dbForConsole') ->inject('gitHub') - ->inject('authorization') - ->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 $specification, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github, Authorization $authorization) 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 $specification, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) { // TODO: If only branch changes, re-deploy $function = $dbForProject->getDocument('functions', $functionId); @@ -900,7 +894,7 @@ Http::put('/v1/functions/:functionId') // Enforce Cold Start if spec limits change. if ($function->getAttribute('specification') !== $specification && !empty($function->getAttribute('deployment'))) { - $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); + $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); try { $executor->deleteRuntime($project->getId(), $function->getAttribute('deployment')); } catch (\Throwable $th) { @@ -947,14 +941,14 @@ Http::put('/v1/functions/:functionId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForEvents->setParam('functionId', $function->getId()); $response->dynamic($function, Response::MODEL_FUNCTION); }); -Http::get('/v1/functions/:functionId/deployments/:deploymentId/download') +App::get('/v1/functions/:functionId/deployments/:deploymentId/download') ->groups(['api', 'functions']) ->desc('Download deployment') ->label('scope', 'functions.read') @@ -1039,7 +1033,7 @@ Http::get('/v1/functions/:functionId/deployments/:deploymentId/download') } }); -Http::patch('/v1/functions/:functionId/deployments/:deploymentId') +App::patch('/v1/functions/:functionId/deployments/:deploymentId') ->groups(['api', 'functions']) ->desc('Update deployment') ->label('scope', 'functions.write') @@ -1059,8 +1053,7 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId') ->inject('dbForProject') ->inject('queueForEvents') ->inject('dbForConsole') - ->inject('authorization') - ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Event $queueForEvents, Database $dbForConsole, Authorization $authorization) { + ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Event $queueForEvents, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); $deployment = $dbForProject->getDocument('deployments', $deploymentId); @@ -1093,7 +1086,7 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForEvents ->setParam('functionId', $function->getId()) @@ -1102,7 +1095,7 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId') $response->dynamic($function, Response::MODEL_FUNCTION); }); -Http::delete('/v1/functions/:functionId') +App::delete('/v1/functions/:functionId') ->groups(['api', 'functions']) ->desc('Delete function') ->label('scope', 'functions.write') @@ -1121,8 +1114,7 @@ Http::delete('/v1/functions/:functionId') ->inject('queueForDeletes') ->inject('queueForEvents') ->inject('dbForConsole') - ->inject('authorization') - ->action(function (string $functionId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Database $dbForConsole, Authorization $authorization) { + ->action(function (string $functionId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); @@ -1139,7 +1131,7 @@ Http::delete('/v1/functions/:functionId') $schedule ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('active', false); - $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) @@ -1150,7 +1142,7 @@ Http::delete('/v1/functions/:functionId') $response->noContent(); }); -Http::post('/v1/functions/:functionId/deployments') +App::post('/v1/functions/:functionId/deployments') ->groups(['api', 'functions']) ->desc('Create deployment') ->label('scope', 'functions.write') @@ -1369,7 +1361,7 @@ Http::post('/v1/functions/:functionId/deployments') ->dynamic($deployment, Response::MODEL_DEPLOYMENT); }); -Http::get('/v1/functions/:functionId/deployments') +App::get('/v1/functions/:functionId/deployments') ->groups(['api', 'functions']) ->desc('List deployments') ->label('scope', 'functions.read') @@ -1446,7 +1438,7 @@ Http::get('/v1/functions/:functionId/deployments') ]), Response::MODEL_DEPLOYMENT_LIST); }); -Http::get('/v1/functions/:functionId/deployments/:deploymentId') +App::get('/v1/functions/:functionId/deployments/:deploymentId') ->groups(['api', 'functions']) ->desc('Get deployment') ->label('scope', 'functions.read') @@ -1489,7 +1481,7 @@ Http::get('/v1/functions/:functionId/deployments/:deploymentId') $response->dynamic($deployment, Response::MODEL_DEPLOYMENT); }); -Http::delete('/v1/functions/:functionId/deployments/:deploymentId') +App::delete('/v1/functions/:functionId/deployments/:deploymentId') ->groups(['api', 'functions']) ->desc('Delete deployment') ->label('scope', 'functions.write') @@ -1553,7 +1545,7 @@ Http::delete('/v1/functions/:functionId/deployments/:deploymentId') $response->noContent(); }); -Http::post('/v1/functions/:functionId/deployments/:deploymentId/build') +App::post('/v1/functions/:functionId/deployments/:deploymentId/build') ->alias('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') ->groups(['api', 'functions']) ->desc('Rebuild deployment') @@ -1576,8 +1568,7 @@ Http::post('/v1/functions/:functionId/deployments/:deploymentId/build') ->inject('queueForEvents') ->inject('queueForBuilds') ->inject('deviceForFunctions') - ->inject('authorization') - ->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Device $deviceForFunctions, Authorization $authorization) { + ->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Device $deviceForFunctions) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -1623,7 +1614,7 @@ Http::post('/v1/functions/:functionId/deployments/:deploymentId/build') $response->noContent(); }); -Http::patch('/v1/functions/:functionId/deployments/:deploymentId/build') +App::patch('/v1/functions/:functionId/deployments/:deploymentId/build') ->groups(['api', 'functions']) ->desc('Cancel deployment') ->label('scope', 'functions.write') @@ -1641,8 +1632,7 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId/build') ->inject('dbForProject') ->inject('project') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -1655,7 +1645,7 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId/build') throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } - $build = $authorization->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); + $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); if ($build->isEmpty()) { $buildId = ID::unique(); @@ -1697,7 +1687,7 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId/build') $dbForProject->purgeCachedDocument('deployments', $deployment->getId()); try { - $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); + $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); $executor->deleteRuntime($project->getId(), $deploymentId . "-build"); } catch (\Throwable $th) { // Don't throw if the deployment doesn't exist @@ -1713,7 +1703,7 @@ Http::patch('/v1/functions/:functionId/deployments/:deploymentId/build') $response->dynamic($build, Response::MODEL_BUILD); }); -Http::post('/v1/functions/:functionId/executions') +App::post('/v1/functions/:functionId/executions') ->groups(['api', 'functions']) ->desc('Create execution') ->label('scope', 'execution.write') @@ -1743,9 +1733,7 @@ Http::post('/v1/functions/:functionId/executions') ->inject('queueForUsage') ->inject('queueForFunctions') ->inject('geodb') - ->inject('authorization') - ->inject('authentication') - ->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, Authorization $authorization, Authentication $authentication) { + ->action(function (string $functionId, string $body, bool $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) { if (!$async && !is_null($scheduledAt)) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Scheduled executions must run asynchronously. Set scheduledAt to a future date, or set async to true.'); @@ -1774,10 +1762,10 @@ Http::post('/v1/functions/:functionId/executions') throw new Exception($validator->getDescription(), 400); } - $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); @@ -1793,7 +1781,7 @@ Http::post('/v1/functions/:functionId/executions') throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } - $deployment = $authorization->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); + $deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); if ($deployment->getAttribute('resourceId') !== $function->getId()) { throw new Exception(Exception::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); @@ -1804,7 +1792,7 @@ Http::post('/v1/functions/:functionId/executions') } /** Check if build has completed */ - $build = $authorization->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); + $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); if ($build->isEmpty()) { throw new Exception(Exception::BUILD_NOT_FOUND); } @@ -1813,8 +1801,10 @@ Http::post('/v1/functions/:functionId/executions') throw new Exception(Exception::BUILD_NOT_READY); } - if (!$authorization->isValid(new Input('execute', $function->getAttribute('execute')))) { // Check if user has write access to execute function - throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); + $validator = new Authorization('execute'); + + if (!$validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function + throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription()); } $jwt = ''; // initialize @@ -1824,7 +1814,7 @@ Http::post('/v1/functions/:functionId/executions') foreach ($sessions as $session) { /** @var Utopia\Database\Document $session */ - if ($session->getAttribute('secret') == Auth::hash($authentication->getSecret())) { // If current session delete the cookies too + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too $current = $session; } } @@ -1908,9 +1898,8 @@ Http::post('/v1/functions/:functionId/executions') ->setContext('function', $function); if ($async) { - if (is_null($scheduledAt)) { - $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); $queueForFunctions ->setType('http') ->setExecution($execution) @@ -1951,7 +1940,7 @@ Http::post('/v1/functions/:functionId/executions') ->setAttribute('scheduleInternalId', $schedule->getInternalId()) ->setAttribute('scheduledAt', $scheduledAt); - $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); } return $response @@ -2037,7 +2026,8 @@ Http::post('/v1/functions/:functionId/executions') runtimeEntrypoint: $command, cpus: $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT, memory: $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT, - logging: $function->getAttribute('logging', true) + logging: $function->getAttribute('logging', true), + requestTimeout: 30 ); $headersFiltered = []; @@ -2078,10 +2068,10 @@ Http::post('/v1/functions/:functionId/executions') ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT))) ; - $execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); } - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -2114,7 +2104,7 @@ Http::post('/v1/functions/:functionId/executions') ->dynamic($execution, Response::MODEL_EXECUTION); }); -Http::get('/v1/functions/:functionId/executions') +App::get('/v1/functions/:functionId/executions') ->groups(['api', 'functions']) ->desc('List executions') ->label('scope', 'execution.read') @@ -2131,12 +2121,11 @@ Http::get('/v1/functions/:functionId/executions') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('authorization') - ->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { - $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + ->action(function (string $functionId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { + $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); @@ -2179,7 +2168,7 @@ Http::get('/v1/functions/:functionId/executions') $results = $dbForProject->find('executions', $queries); $total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT); - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); if (!$isPrivilegedUser && !$isAppUser) { @@ -2196,7 +2185,7 @@ Http::get('/v1/functions/:functionId/executions') ]), Response::MODEL_EXECUTION_LIST); }); -Http::get('/v1/functions/:functionId/executions/:executionId') +App::get('/v1/functions/:functionId/executions/:executionId') ->groups(['api', 'functions']) ->desc('Get execution') ->label('scope', 'execution.read') @@ -2212,12 +2201,11 @@ Http::get('/v1/functions/:functionId/executions/:executionId') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('authorization') - ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { - $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, string $mode) { + $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); @@ -2233,7 +2221,7 @@ Http::get('/v1/functions/:functionId/executions/:executionId') throw new Exception(Exception::EXECUTION_NOT_FOUND); } - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); if (!$isPrivilegedUser && !$isAppUser) { @@ -2244,7 +2232,7 @@ Http::get('/v1/functions/:functionId/executions/:executionId') $response->dynamic($execution, Response::MODEL_EXECUTION); }); -Http::delete('/v1/functions/:functionId/executions/:executionId') +App::delete('/v1/functions/:functionId/executions/:executionId') ->groups(['api', 'functions']) ->desc('Delete execution') ->label('scope', 'execution.write') @@ -2263,8 +2251,7 @@ Http::delete('/v1/functions/:functionId/executions/:executionId') ->inject('dbForProject') ->inject('dbForConsole') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, Database $dbForConsole, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, Database $dbForConsole, Event $queueForEvents) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -2301,7 +2288,7 @@ Http::delete('/v1/functions/:functionId/executions/:executionId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('active', false); - $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); } } @@ -2315,7 +2302,7 @@ Http::delete('/v1/functions/:functionId/executions/:executionId') // Variables -Http::post('/v1/functions/:functionId/variables') +App::post('/v1/functions/:functionId/variables') ->desc('Create variable') ->groups(['api', 'functions']) ->label('scope', 'functions.write') @@ -2334,8 +2321,7 @@ Http::post('/v1/functions/:functionId/variables') ->inject('response') ->inject('dbForProject') ->inject('dbForConsole') - ->inject('authorization') - ->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization) { + ->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -2373,14 +2359,14 @@ Http::post('/v1/functions/:functionId/variables') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $response ->setStatusCode(Response::STATUS_CODE_CREATED) ->dynamic($variable, Response::MODEL_VARIABLE); }); -Http::get('/v1/functions/:functionId/variables') +App::get('/v1/functions/:functionId/variables') ->desc('List variables') ->groups(['api', 'functions']) ->label('scope', 'functions.read') @@ -2407,7 +2393,7 @@ Http::get('/v1/functions/:functionId/variables') ]), Response::MODEL_VARIABLE_LIST); }); -Http::get('/v1/functions/:functionId/variables/:variableId') +App::get('/v1/functions/:functionId/variables/:variableId') ->desc('Get variable') ->groups(['api', 'functions']) ->label('scope', 'functions.read') @@ -2446,7 +2432,7 @@ Http::get('/v1/functions/:functionId/variables/:variableId') $response->dynamic($variable, Response::MODEL_VARIABLE); }); -Http::put('/v1/functions/:functionId/variables/:variableId') +App::put('/v1/functions/:functionId/variables/:variableId') ->desc('Update variable') ->groups(['api', 'functions']) ->label('scope', 'functions.write') @@ -2466,8 +2452,7 @@ Http::put('/v1/functions/:functionId/variables/:variableId') ->inject('response') ->inject('dbForProject') ->inject('dbForConsole') - ->inject('authorization') - ->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization) { + ->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); @@ -2503,12 +2488,12 @@ Http::put('/v1/functions/:functionId/variables/:variableId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $response->dynamic($variable, Response::MODEL_VARIABLE); }); -Http::delete('/v1/functions/:functionId/variables/:variableId') +App::delete('/v1/functions/:functionId/variables/:variableId') ->desc('Delete variable') ->groups(['api', 'functions']) ->label('scope', 'functions.write') @@ -2525,8 +2510,7 @@ Http::delete('/v1/functions/:functionId/variables/:variableId') ->inject('response') ->inject('dbForProject') ->inject('dbForConsole') - ->inject('authorization') - ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization) { + ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -2552,12 +2536,12 @@ Http::delete('/v1/functions/:functionId/variables/:variableId') ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - $authorization->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $response->noContent(); }); -Http::get('/v1/functions/templates') +App::get('/v1/functions/templates') ->groups(['api']) ->desc('List function templates') ->label('scope', 'public') @@ -2595,7 +2579,7 @@ Http::get('/v1/functions/templates') ]), Response::MODEL_TEMPLATE_FUNCTION_LIST); }); -Http::get('/v1/functions/templates/:templateId') +App::get('/v1/functions/templates/:templateId') ->desc('Get function template') ->label('scope', 'public') ->label('sdk.namespace', 'functions') @@ -2610,10 +2594,9 @@ Http::get('/v1/functions/templates/:templateId') ->action(function (string $templateId, Response $response) { $templates = Config::getParam('function-templates', []); - $array = \array_filter($templates, function ($template) use ($templateId) { + $template = array_shift(\array_filter($templates, function ($template) use ($templateId) { return $template['id'] === $templateId; - }); - $template = array_shift($array); + })); if (empty($template)) { throw new Exception(Exception::FUNCTION_TEMPLATE_NOT_FOUND); diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index 0e7ddc783d..f79f433b5c 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -14,28 +14,27 @@ use GraphQL\Validator\Rules\DisableIntrospection; use GraphQL\Validator\Rules\QueryComplexity; use GraphQL\Validator\Rules\QueryDepth; use Swoole\Coroutine\WaitGroup; +use Utopia\App; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\Http\Http; -use Utopia\Http\Validator\JSON; -use Utopia\Http\Validator\Text; use Utopia\System\System; +use Utopia\Validator\JSON; +use Utopia\Validator\Text; -Http::init() +App::init() ->groups(['graphql']) ->inject('project') - ->inject('authorization') - ->action(function (Document $project, Authorization $authorization) { + ->action(function (Document $project) { if ( array_key_exists('graphql', $project->getAttribute('apis', [])) && !$project->getAttribute('apis', [])['graphql'] - && !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles())) + && !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles())) ) { throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } }); -Http::get('/v1/graphql') +App::get('/v1/graphql') ->desc('GraphQL endpoint') ->groups(['graphql']) ->label('scope', 'graphql') @@ -75,7 +74,7 @@ Http::get('/v1/graphql') ->json($output); }); -Http::post('/v1/graphql/mutation') +App::post('/v1/graphql/mutation') ->desc('GraphQL endpoint') ->groups(['graphql']) ->label('scope', 'graphql') @@ -120,7 +119,7 @@ Http::post('/v1/graphql/mutation') ->json($output); }); -Http::post('/v1/graphql') +App::post('/v1/graphql') ->desc('GraphQL endpoint') ->groups(['graphql']) ->label('scope', 'graphql') @@ -157,6 +156,7 @@ Http::post('/v1/graphql') if (\str_starts_with($type, 'multipart/form-data')) { $query = parseMultipart($query, $request); } + $output = execute($schema, $promiseAdapter, $query); $response @@ -205,7 +205,7 @@ function execute( $validations[] = new QueryComplexity($maxComplexity); $validations[] = new QueryDepth($maxDepth); } - if (Http::getMode() === Http::MODE_TYPE_PRODUCTION) { + if (App::getMode() === App::MODE_TYPE_PRODUCTION) { $flags = DebugFlag::NONE; } @@ -306,10 +306,9 @@ function processResult($result, $debugFlags): array ); } -Http::shutdown() +App::shutdown() ->groups(['schema']) ->inject('project') - ->inject('schemaVariable') - ->action(function (Document $project, Schema $schemaVariable) { - $schemaVariable->setDirty($project->getId()); + ->action(function (Document $project) { + Schema::setDirty($project->getId()); }); diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 47d80dbf71..f4581df8e4 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -3,19 +3,12 @@ use Appwrite\ClamAV\Network; use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Response; +use Utopia\App; use Utopia\Config\Config; -use Utopia\Database\Adapter\MariaDB; -use Utopia\Database\Adapter\MySQL; use Utopia\Database\Document; use Utopia\Domains\Validator\PublicDomain; -use Utopia\Http\Http; -use Utopia\Http\Validator\Domain; -use Utopia\Http\Validator\Integer; -use Utopia\Http\Validator\Multiple; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; +use Utopia\Pools\Group; use Utopia\Queue\Client; use Utopia\Queue\Connection; use Utopia\Registry\Registry; @@ -23,8 +16,13 @@ use Utopia\Storage\Device; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; use Utopia\System\System; +use Utopia\Validator\Domain; +use Utopia\Validator\Integer; +use Utopia\Validator\Multiple; +use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; -Http::get('/v1/health') +App::get('/v1/health') ->desc('Get HTTP') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -47,7 +45,7 @@ Http::get('/v1/health') $response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS); }); -Http::get('/v1/health/version') +App::get('/v1/health/version') ->desc('Get version') ->groups(['api', 'health']) ->label('scope', 'public') @@ -59,7 +57,7 @@ Http::get('/v1/health/version') $response->dynamic(new Document([ 'version' => APP_VERSION_STABLE ]), Response::MODEL_HEALTH_VERSION); }); -Http::get('/v1/health/db') +App::get('/v1/health/db') ->desc('Get DB') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -72,34 +70,21 @@ Http::get('/v1/health/db') ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) ->inject('response') ->inject('pools') - ->inject('connections') - ->action(function (Response $response, array $pools, Connections $connections) { + ->action(function (Response $response, Group $pools) { $output = []; $configs = [ - 'console' => Config::getParam('pools-console'), - 'database' => Config::getParam('pools-database'), + 'Console.DB' => Config::getParam('pools-console'), + 'Projects.DB' => Config::getParam('pools-database'), ]; foreach ($configs as $key => $config) { foreach ($config as $database) { - $checkStart = \microtime(true); - try { + $adapter = $pools->get($database)->pop()->getResource(); - $pool = $pools['pools-'.$key.'-'.$database]['pool']; - $dsn = $pools['pools-'.$key.'-'.$database]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - $adapter->setDatabase($dsn->getPath()); - + $checkStart = \microtime(true); if ($adapter->ping()) { $output[] = new Document([ @@ -126,7 +111,7 @@ Http::get('/v1/health/db') ]), Response::MODEL_HEALTH_STATUS_LIST); }); -Http::get('/v1/health/cache') +App::get('/v1/health/cache') ->desc('Get cache') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -139,8 +124,7 @@ Http::get('/v1/health/cache') ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) ->inject('response') ->inject('pools') - ->inject('connections') - ->action(function (Response $response, array $pools, Connections $connections) { + ->action(function (Response $response, Group $pools) { $output = []; @@ -150,142 +134,8 @@ Http::get('/v1/health/cache') foreach ($configs as $key => $config) { foreach ($config as $database) { - $checkStart = \microtime(true); try { - $pool = $pools['pools-cache-' . $database]['pool']; - $dsn = $pools['pools-cache-' . $database]['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapter = new Connection\Redis($dsn->getHost(), $dsn->getPort()); - - - if ($adapter->ping()) { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } else { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'fail', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } - } catch (\Throwable $th) { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'fail', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } finally { - $connections->reclaim(); - } - } - } - - $response->dynamic(new Document([ - 'statuses' => $output, - 'total' => count($output), - ]), Response::MODEL_HEALTH_STATUS_LIST); - }); - -Http::get('/v1/health/queue') - ->desc('Get queue') - ->groups(['api', 'health']) - ->label('scope', 'health.read') - ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) - ->label('sdk.namespace', 'health') - ->label('sdk.method', 'getQueue') - ->label('sdk.description', '/docs/references/health/get-queue.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) - ->inject('response') - ->inject('pools') - ->inject('connections') - ->action(function (Response $response, array $pools, Connections $connections) { - - $output = []; - - $configs = [ - 'Queue' => Config::getParam('pools-queue'), - ]; - - foreach ($configs as $key => $config) { - $checkStart = \microtime(true); - - foreach ($config as $database) { - try { - $pool = $pools['pools-queue-' . $database]['pool']; - $dsn = $pools['pools-queue-' . $database]['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapter = new Connection\Redis($dsn->getHost(), $dsn->getPort()); - if ($adapter->ping()) { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } else { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'fail', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } - } catch (\Throwable $th) { - $output[] = new Document([ - 'name' => $key . " ($database)", - 'status' => 'fail', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) - ]); - } finally { - $connections->reclaim(); - } - } - } - - $response->dynamic(new Document([ - 'statuses' => $output, - 'total' => count($output), - ]), Response::MODEL_HEALTH_STATUS_LIST); - }); - -Http::get('/v1/health/pubsub') - ->desc('Get pubsub') - ->groups(['api', 'health']) - ->label('scope', 'health.read') - ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) - ->label('sdk.namespace', 'health') - ->label('sdk.method', 'getPubSub') - ->label('sdk.description', '/docs/references/health/get-pubsub.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) - ->inject('response') - ->inject('pools') - ->inject('connections') - ->action(function (Response $response, array $pools, Connections $connections) { - - $output = []; - - $configs = [ - 'PubSub' => Config::getParam('pools-pubsub'), - ]; - - foreach ($configs as $key => $config) { - foreach ($config as $database) { - try { - $pool = $pools['pools-pubsub-' . $database]['pool']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapter = new Connection\Redis($connection); + $adapter = $pools->get($database)->pop()->getResource(); $checkStart = \microtime(true); @@ -308,8 +158,6 @@ Http::get('/v1/health/pubsub') 'status' => 'fail', 'ping' => \round((\microtime(true) - $checkStart) / 1000) ]); - } finally { - $connections->reclaim(); } } } @@ -320,7 +168,121 @@ Http::get('/v1/health/pubsub') ]), Response::MODEL_HEALTH_STATUS_LIST); }); -Http::get('/v1/health/time') +App::get('/v1/health/queue') + ->desc('Get queue') + ->groups(['api', 'health']) + ->label('scope', 'health.read') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'health') + ->label('sdk.method', 'getQueue') + ->label('sdk.description', '/docs/references/health/get-queue.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) + ->inject('response') + ->inject('pools') + ->action(function (Response $response, Group $pools) { + + $output = []; + + $configs = [ + 'Queue' => Config::getParam('pools-queue'), + ]; + + foreach ($configs as $key => $config) { + foreach ($config as $database) { + try { + $adapter = $pools->get($database)->pop()->getResource(); + + $checkStart = \microtime(true); + + if ($adapter->ping()) { + $output[] = new Document([ + 'name' => $key . " ($database)", + 'status' => 'pass', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); + } else { + $output[] = new Document([ + 'name' => $key . " ($database)", + 'status' => 'fail', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); + } + } catch (\Throwable $th) { + $output[] = new Document([ + 'name' => $key . " ($database)", + 'status' => 'fail', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); + } + } + } + + $response->dynamic(new Document([ + 'statuses' => $output, + 'total' => count($output), + ]), Response::MODEL_HEALTH_STATUS_LIST); + }); + +App::get('/v1/health/pubsub') + ->desc('Get pubsub') + ->groups(['api', 'health']) + ->label('scope', 'health.read') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'health') + ->label('sdk.method', 'getPubSub') + ->label('sdk.description', '/docs/references/health/get-pubsub.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) + ->inject('response') + ->inject('pools') + ->action(function (Response $response, Group $pools) { + + $output = []; + + $configs = [ + 'PubSub' => Config::getParam('pools-pubsub'), + ]; + + foreach ($configs as $key => $config) { + foreach ($config as $database) { + try { + $adapter = $pools->get($database)->pop()->getResource(); + + $checkStart = \microtime(true); + + if ($adapter->ping()) { + $output[] = new Document([ + 'name' => $key . " ($database)", + 'status' => 'pass', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); + } else { + $output[] = new Document([ + 'name' => $key . " ($database)", + 'status' => 'fail', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); + } + } catch (\Throwable $th) { + $output[] = new Document([ + 'name' => $key . " ($database)", + 'status' => 'fail', + 'ping' => \round((\microtime(true) - $checkStart) / 1000) + ]); + } + } + } + + $response->dynamic(new Document([ + 'statuses' => $output, + 'total' => count($output), + ]), Response::MODEL_HEALTH_STATUS_LIST); + }); + +App::get('/v1/health/time') ->desc('Get time') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -377,7 +339,7 @@ Http::get('/v1/health/time') $response->dynamic(new Document($output), Response::MODEL_HEALTH_TIME); }); -Http::get('/v1/health/queue/webhooks') +App::get('/v1/health/queue/webhooks') ->desc('Get webhooks queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -404,7 +366,7 @@ Http::get('/v1/health/queue/webhooks') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -Http::get('/v1/health/queue/logs') +App::get('/v1/health/queue/logs') ->desc('Get logs queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -431,7 +393,7 @@ Http::get('/v1/health/queue/logs') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -Http::get('/v1/health/certificate') +App::get('/v1/health/certificate') ->desc('Get the SSL certificate for a domain') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -481,7 +443,7 @@ Http::get('/v1/health/certificate') ]), Response::MODEL_HEALTH_CERTIFICATE); }, ['response']); -Http::get('/v1/health/queue/certificates') +App::get('/v1/health/queue/certificates') ->desc('Get certificates queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -508,7 +470,7 @@ Http::get('/v1/health/queue/certificates') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -Http::get('/v1/health/queue/builds') +App::get('/v1/health/queue/builds') ->desc('Get builds queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -535,7 +497,7 @@ Http::get('/v1/health/queue/builds') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -Http::get('/v1/health/queue/databases') +App::get('/v1/health/queue/databases') ->desc('Get databases queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -563,7 +525,7 @@ Http::get('/v1/health/queue/databases') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -Http::get('/v1/health/queue/deletes') +App::get('/v1/health/queue/deletes') ->desc('Get deletes queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -590,7 +552,7 @@ Http::get('/v1/health/queue/deletes') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -Http::get('/v1/health/queue/mails') +App::get('/v1/health/queue/mails') ->desc('Get mails queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -617,7 +579,7 @@ Http::get('/v1/health/queue/mails') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -Http::get('/v1/health/queue/messaging') +App::get('/v1/health/queue/messaging') ->desc('Get messaging queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -644,7 +606,7 @@ Http::get('/v1/health/queue/messaging') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -Http::get('/v1/health/queue/migrations') +App::get('/v1/health/queue/migrations') ->desc('Get migrations queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -671,7 +633,7 @@ Http::get('/v1/health/queue/migrations') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -Http::get('/v1/health/queue/functions') +App::get('/v1/health/queue/functions') ->desc('Get functions queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -698,7 +660,7 @@ Http::get('/v1/health/queue/functions') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -Http::get('/v1/health/queue/usage') +App::get('/v1/health/queue/usage') ->desc('Get usage queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -725,7 +687,7 @@ Http::get('/v1/health/queue/usage') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }); -Http::get('/v1/health/queue/usage-dump') +App::get('/v1/health/queue/usage-dump') ->desc('Get usage dump queue') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -752,7 +714,7 @@ Http::get('/v1/health/queue/usage-dump') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }); -Http::get('/v1/health/storage/local') +App::get('/v1/health/storage/local') ->desc('Get local storage') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -795,7 +757,7 @@ Http::get('/v1/health/storage/local') $response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS); }); -Http::get('/v1/health/storage') +App::get('/v1/health/storage') ->desc('Get storage') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -836,7 +798,7 @@ Http::get('/v1/health/storage') $response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS); }); -Http::get('/v1/health/anti-virus') +App::get('/v1/health/anti-virus') ->desc('Get antivirus') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -875,7 +837,7 @@ Http::get('/v1/health/anti-virus') $response->dynamic(new Document($output), Response::MODEL_HEALTH_ANTIVIRUS); }); -Http::get('/v1/health/queue/failed/:name') +App::get('/v1/health/queue/failed/:name') ->desc('Get number of failed queue jobs') ->groups(['api', 'health']) ->label('scope', 'health.read') @@ -916,7 +878,7 @@ Http::get('/v1/health/queue/failed/:name') $response->dynamic(new Document([ 'size' => $failed ]), Response::MODEL_HEALTH_QUEUE); }); -Http::get('/v1/health/stats') // Currently only used internally +App::get('/v1/health/stats') // Currently only used internally ->desc('Get system stats') ->groups(['api', 'health']) ->label('scope', 'root') diff --git a/app/controllers/api/locale.php b/app/controllers/api/locale.php index fcaf0c03cb..abb47ab3c4 100644 --- a/app/controllers/api/locale.php +++ b/app/controllers/api/locale.php @@ -3,12 +3,12 @@ use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; +use Utopia\App; use Utopia\Config\Config; use Utopia\Database\Document; -use Utopia\Http\Http; use Utopia\Locale\Locale; -Http::get('/v1/locale') +App::get('/v1/locale') ->desc('Get user locale') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -68,7 +68,7 @@ Http::get('/v1/locale') $response->dynamic(new Document($output), Response::MODEL_LOCALE); }); -Http::get('/v1/locale/codes') +App::get('/v1/locale/codes') ->desc('List Locale Codes') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -90,7 +90,7 @@ Http::get('/v1/locale/codes') ]), Response::MODEL_LOCALE_CODE_LIST); }); -Http::get('/v1/locale/countries') +App::get('/v1/locale/countries') ->desc('List countries') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -123,7 +123,7 @@ Http::get('/v1/locale/countries') $response->dynamic(new Document(['countries' => $output, 'total' => \count($output)]), Response::MODEL_COUNTRY_LIST); }); -Http::get('/v1/locale/countries/eu') +App::get('/v1/locale/countries/eu') ->desc('List EU countries') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -158,7 +158,7 @@ Http::get('/v1/locale/countries/eu') $response->dynamic(new Document(['countries' => $output, 'total' => \count($output)]), Response::MODEL_COUNTRY_LIST); }); -Http::get('/v1/locale/countries/phones') +App::get('/v1/locale/countries/phones') ->desc('List countries phone codes') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -192,7 +192,7 @@ Http::get('/v1/locale/countries/phones') $response->dynamic(new Document(['phones' => $output, 'total' => \count($output)]), Response::MODEL_PHONE_LIST); }); -Http::get('/v1/locale/continents') +App::get('/v1/locale/continents') ->desc('List continents') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -224,7 +224,7 @@ Http::get('/v1/locale/continents') $response->dynamic(new Document(['continents' => $output, 'total' => \count($output)]), Response::MODEL_CONTINENT_LIST); }); -Http::get('/v1/locale/currencies') +App::get('/v1/locale/currencies') ->desc('List currencies') ->groups(['api', 'locale']) ->label('scope', 'locale.read') @@ -247,7 +247,7 @@ Http::get('/v1/locale/currencies') }); -Http::get('/v1/locale/languages') +App::get('/v1/locale/languages') ->desc('List languages') ->groups(['api', 'locale']) ->label('scope', 'locale.read') diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 8beb38c7ca..7da0348a8f 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -20,6 +20,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Targets; use Appwrite\Utopia\Database\Validator\Queries\Topics; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; +use Utopia\App; use Utopia\Audit\Audit; use Utopia\Database\Database; use Utopia\Database\DateTime; @@ -29,27 +30,25 @@ use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\Roles; use Utopia\Database\Validator\UID; -use Utopia\Http\Http; -use Utopia\Http\Validator\ArrayList; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\Integer; -use Utopia\Http\Validator\JSON; -use Utopia\Http\Validator\Range; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; use Utopia\Locale\Locale; use Utopia\System\System; +use Utopia\Validator\ArrayList; +use Utopia\Validator\Boolean; +use Utopia\Validator\Integer; +use Utopia\Validator\JSON; +use Utopia\Validator\Range; +use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; use function Swoole\Coroutine\batch; -Http::post('/v1/messaging/providers/mailgun') +App::post('/v1/messaging/providers/mailgun') ->desc('Create Mailgun provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -136,7 +135,7 @@ Http::post('/v1/messaging/providers/mailgun') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::post('/v1/messaging/providers/sendgrid') +App::post('/v1/messaging/providers/sendgrid') ->desc('Create Sendgrid provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -211,7 +210,7 @@ Http::post('/v1/messaging/providers/sendgrid') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::post('/v1/messaging/providers/smtp') +App::post('/v1/messaging/providers/smtp') ->desc('Create SMTP provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -299,7 +298,7 @@ Http::post('/v1/messaging/providers/smtp') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::post('/v1/messaging/providers/msg91') +App::post('/v1/messaging/providers/msg91') ->desc('Create Msg91 provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -375,7 +374,7 @@ Http::post('/v1/messaging/providers/msg91') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::post('/v1/messaging/providers/telesign') +App::post('/v1/messaging/providers/telesign') ->desc('Create Telesign provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -452,7 +451,7 @@ Http::post('/v1/messaging/providers/telesign') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::post('/v1/messaging/providers/textmagic') +App::post('/v1/messaging/providers/textmagic') ->desc('Create Textmagic provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -529,7 +528,7 @@ Http::post('/v1/messaging/providers/textmagic') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::post('/v1/messaging/providers/twilio') +App::post('/v1/messaging/providers/twilio') ->desc('Create Twilio provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -606,7 +605,7 @@ Http::post('/v1/messaging/providers/twilio') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::post('/v1/messaging/providers/vonage') +App::post('/v1/messaging/providers/vonage') ->desc('Create Vonage provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -683,7 +682,7 @@ Http::post('/v1/messaging/providers/vonage') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::post('/v1/messaging/providers/fcm') +App::post('/v1/messaging/providers/fcm') ->desc('Create FCM provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -746,7 +745,7 @@ Http::post('/v1/messaging/providers/fcm') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::post('/v1/messaging/providers/apns') +App::post('/v1/messaging/providers/apns') ->desc('Create APNS provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.create') @@ -832,7 +831,7 @@ Http::post('/v1/messaging/providers/apns') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::get('/v1/messaging/providers') +App::get('/v1/messaging/providers') ->desc('List providers') ->groups(['api', 'messaging']) ->label('scope', 'providers.read') @@ -847,8 +846,7 @@ Http::get('/v1/messaging/providers') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') ->inject('response') - ->inject('authorization') - ->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $authorization) { + ->action(function (array $queries, string $search, Database $dbForProject, Response $response) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -869,7 +867,7 @@ Http::get('/v1/messaging/providers') if ($cursor) { $providerId = $cursor->getValue(); - $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId)); + $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Provider '{$providerId}' for the 'cursor' value not found."); @@ -884,7 +882,7 @@ Http::get('/v1/messaging/providers') ]), Response::MODEL_PROVIDER_LIST); }); -Http::get('/v1/messaging/providers/:providerId/logs') +App::get('/v1/messaging/providers/:providerId/logs') ->desc('List provider logs') ->groups(['api', 'messaging']) ->label('scope', 'providers.read') @@ -972,7 +970,7 @@ Http::get('/v1/messaging/providers/:providerId/logs') ]), Response::MODEL_LOG_LIST); }); -Http::get('/v1/messaging/providers/:providerId') +App::get('/v1/messaging/providers/:providerId') ->desc('Get provider') ->groups(['api', 'messaging']) ->label('scope', 'providers.read') @@ -996,7 +994,7 @@ Http::get('/v1/messaging/providers/:providerId') $response->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::patch('/v1/messaging/providers/mailgun/:providerId') +App::patch('/v1/messaging/providers/mailgun/:providerId') ->desc('Update Mailgun provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1102,7 +1100,7 @@ Http::patch('/v1/messaging/providers/mailgun/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::patch('/v1/messaging/providers/sendgrid/:providerId') +App::patch('/v1/messaging/providers/sendgrid/:providerId') ->desc('Update Sendgrid provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1193,7 +1191,7 @@ Http::patch('/v1/messaging/providers/sendgrid/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::patch('/v1/messaging/providers/smtp/:providerId') +App::patch('/v1/messaging/providers/smtp/:providerId') ->desc('Update SMTP provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1315,7 +1313,7 @@ Http::patch('/v1/messaging/providers/smtp/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::patch('/v1/messaging/providers/msg91/:providerId') +App::patch('/v1/messaging/providers/msg91/:providerId') ->desc('Update Msg91 provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1395,7 +1393,7 @@ Http::patch('/v1/messaging/providers/msg91/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::patch('/v1/messaging/providers/telesign/:providerId') +App::patch('/v1/messaging/providers/telesign/:providerId') ->desc('Update Telesign provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1477,7 +1475,7 @@ Http::patch('/v1/messaging/providers/telesign/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::patch('/v1/messaging/providers/textmagic/:providerId') +App::patch('/v1/messaging/providers/textmagic/:providerId') ->desc('Update Textmagic provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1559,7 +1557,7 @@ Http::patch('/v1/messaging/providers/textmagic/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::patch('/v1/messaging/providers/twilio/:providerId') +App::patch('/v1/messaging/providers/twilio/:providerId') ->desc('Update Twilio provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1641,7 +1639,7 @@ Http::patch('/v1/messaging/providers/twilio/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::patch('/v1/messaging/providers/vonage/:providerId') +App::patch('/v1/messaging/providers/vonage/:providerId') ->desc('Update Vonage provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1723,7 +1721,7 @@ Http::patch('/v1/messaging/providers/vonage/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::patch('/v1/messaging/providers/fcm/:providerId') +App::patch('/v1/messaging/providers/fcm/:providerId') ->desc('Update FCM provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1792,7 +1790,7 @@ Http::patch('/v1/messaging/providers/fcm/:providerId') }); -Http::patch('/v1/messaging/providers/apns/:providerId') +App::patch('/v1/messaging/providers/apns/:providerId') ->desc('Update APNS provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.update') @@ -1887,7 +1885,7 @@ Http::patch('/v1/messaging/providers/apns/:providerId') ->dynamic($provider, Response::MODEL_PROVIDER); }); -Http::delete('/v1/messaging/providers/:providerId') +App::delete('/v1/messaging/providers/:providerId') ->desc('Delete provider') ->groups(['api', 'messaging']) ->label('audits.event', 'provider.delete') @@ -1922,7 +1920,7 @@ Http::delete('/v1/messaging/providers/:providerId') ->noContent(); }); -Http::post('/v1/messaging/topics') +App::post('/v1/messaging/topics') ->desc('Create topic') ->groups(['api', 'messaging']) ->label('audits.event', 'topic.create') @@ -1965,7 +1963,7 @@ Http::post('/v1/messaging/topics') ->dynamic($topic, Response::MODEL_TOPIC); }); -Http::get('/v1/messaging/topics') +App::get('/v1/messaging/topics') ->desc('List topics') ->groups(['api', 'messaging']) ->label('scope', 'topics.read') @@ -1980,8 +1978,7 @@ Http::get('/v1/messaging/topics') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') ->inject('response') - ->inject('authorization') - ->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $authorization) { + ->action(function (array $queries, string $search, Database $dbForProject, Response $response) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -2002,7 +1999,7 @@ Http::get('/v1/messaging/topics') if ($cursor) { $topicId = $cursor->getValue(); - $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Topic '{$topicId}' for the 'cursor' value not found."); @@ -2017,7 +2014,7 @@ Http::get('/v1/messaging/topics') ]), Response::MODEL_TOPIC_LIST); }); -Http::get('/v1/messaging/topics/:topicId/logs') +App::get('/v1/messaging/topics/:topicId/logs') ->desc('List topic logs') ->groups(['api', 'messaging']) ->label('scope', 'topics.read') @@ -2034,8 +2031,7 @@ Http::get('/v1/messaging/topics/:topicId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('authorization') - ->action(function (string $topicId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { + ->action(function (string $topicId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { $topic = $dbForProject->getDocument('topics', $topicId); if ($topic->isEmpty()) { @@ -2107,7 +2103,7 @@ Http::get('/v1/messaging/topics/:topicId/logs') ]), Response::MODEL_LOG_LIST); }); -Http::get('/v1/messaging/topics/:topicId') +App::get('/v1/messaging/topics/:topicId') ->desc('Get topic') ->groups(['api', 'messaging']) ->label('scope', 'topics.read') @@ -2132,7 +2128,7 @@ Http::get('/v1/messaging/topics/:topicId') ->dynamic($topic, Response::MODEL_TOPIC); }); -Http::patch('/v1/messaging/topics/:topicId') +App::patch('/v1/messaging/topics/:topicId') ->desc('Update topic') ->groups(['api', 'messaging']) ->label('audits.event', 'topic.update') @@ -2176,7 +2172,7 @@ Http::patch('/v1/messaging/topics/:topicId') ->dynamic($topic, Response::MODEL_TOPIC); }); -Http::delete('/v1/messaging/topics/:topicId') +App::delete('/v1/messaging/topics/:topicId') ->desc('Delete topic') ->groups(['api', 'messaging']) ->label('audits.event', 'topic.delete') @@ -2216,7 +2212,7 @@ Http::delete('/v1/messaging/topics/:topicId') ->noContent(); }); -Http::post('/v1/messaging/topics/:topicId/subscribers') +App::post('/v1/messaging/topics/:topicId/subscribers') ->desc('Create subscriber') ->groups(['api', 'messaging']) ->label('audits.event', 'subscriber.create') @@ -2236,27 +2232,28 @@ Http::post('/v1/messaging/topics/:topicId/subscribers') ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') - ->inject('authorization') - ->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response, Authorization $authorization) { + ->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response) { $subscriberId = $subscriberId == 'unique()' ? ID::unique() : $subscriberId; - $topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); } - if (!$authorization->isValid(new Input('subscribe', $topic->getAttribute('subscribe')))) { - throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); + $validator = new Authorization('subscribe'); + + if (!$validator->isValid($topic->getAttribute('subscribe'))) { + throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription()); } - $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId)); + $target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId)); if ($target->isEmpty()) { throw new Exception(Exception::USER_TARGET_NOT_FOUND); } - $user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); + $user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); $subscriber = new Document([ '$id' => $subscriberId, @@ -2289,7 +2286,7 @@ Http::post('/v1/messaging/topics/:topicId/subscribers') default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE), }; - $authorization->skip(fn () => $dbForProject->increaseDocumentAttribute( + Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute( 'topics', $topicId, $totalAttribute, @@ -2311,7 +2308,7 @@ Http::post('/v1/messaging/topics/:topicId/subscribers') ->dynamic($subscriber, Response::MODEL_SUBSCRIBER); }); -Http::get('/v1/messaging/topics/:topicId/subscribers') +App::get('/v1/messaging/topics/:topicId/subscribers') ->desc('List subscribers') ->groups(['api', 'messaging']) ->label('scope', 'subscribers.read') @@ -2327,8 +2324,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') ->inject('response') - ->inject('authorization') - ->action(function (string $topicId, array $queries, string $search, Database $dbForProject, Response $response, Authorization $authorization) { + ->action(function (string $topicId, array $queries, string $search, Database $dbForProject, Response $response) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -2339,7 +2335,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') $queries[] = Query::search('search', $search); } - $topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + $topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -2357,7 +2353,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') if ($cursor) { $subscriberId = $cursor->getValue(); - $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId)); + $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Subscriber '{$subscriberId}' for the 'cursor' value not found."); @@ -2368,10 +2364,10 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') $subscribers = $dbForProject->find('subscribers', $queries); - $subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject, $authorization) { - return function () use ($subscriber, $dbForProject, $authorization) { - $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); - $user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); + $subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) { + return function () use ($subscriber, $dbForProject) { + $target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); + $user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); return $subscriber ->setAttribute('target', $target) @@ -2386,7 +2382,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers') ]), Response::MODEL_SUBSCRIBER_LIST); }); -Http::get('/v1/messaging/subscribers/:subscriberId/logs') +App::get('/v1/messaging/subscribers/:subscriberId/logs') ->desc('List subscriber logs') ->groups(['api', 'messaging']) ->label('scope', 'subscribers.read') @@ -2403,8 +2399,7 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('authorization') - ->action(function (string $subscriberId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { + ->action(function (string $subscriberId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { $subscriber = $dbForProject->getDocument('subscribers', $subscriberId); if ($subscriber->isEmpty()) { @@ -2476,7 +2471,7 @@ Http::get('/v1/messaging/subscribers/:subscriberId/logs') ]), Response::MODEL_LOG_LIST); }); -Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') +App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->desc('Get subscriber') ->groups(['api', 'messaging']) ->label('scope', 'subscribers.read') @@ -2491,9 +2486,8 @@ Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->param('subscriberId', '', new UID(), 'Subscriber ID.') ->inject('dbForProject') ->inject('response') - ->inject('authorization') - ->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response, Authorization $authorization) { - $topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + ->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response) { + $topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -2505,8 +2499,8 @@ Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') throw new Exception(Exception::SUBSCRIBER_NOT_FOUND); } - $target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); - $user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); + $target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId'))); + $user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId'))); $subscriber ->setAttribute('target', $target) @@ -2516,7 +2510,7 @@ Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->dynamic($subscriber, Response::MODEL_SUBSCRIBER); }); -Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') +App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->desc('Delete subscriber') ->groups(['api', 'messaging']) ->label('audits.event', 'subscriber.delete') @@ -2535,9 +2529,8 @@ Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') - ->inject('authorization') - ->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response, Authorization $authorization) { - $topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId)); + ->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response) { + $topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId)); if ($topic->isEmpty()) { throw new Exception(Exception::TOPIC_NOT_FOUND); @@ -2560,7 +2553,7 @@ Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE), }; - $authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute( + Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute( 'topics', $topicId, $totalAttribute, @@ -2576,7 +2569,7 @@ Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') ->noContent(); }); -Http::post('/v1/messaging/messages/email') +App::post('/v1/messaging/messages/email') ->desc('Create email') ->groups(['api', 'messaging']) ->label('audits.event', 'message.create') @@ -2728,7 +2721,7 @@ Http::post('/v1/messaging/messages/email') ->dynamic($message, Response::MODEL_MESSAGE); }); -Http::post('/v1/messaging/messages/sms') +App::post('/v1/messaging/messages/sms') ->desc('Create SMS') ->groups(['api', 'messaging']) ->label('audits.event', 'message.create') @@ -2844,7 +2837,7 @@ Http::post('/v1/messaging/messages/sms') ->dynamic($message, Response::MODEL_MESSAGE); }); -Http::post('/v1/messaging/messages/push') +App::post('/v1/messaging/messages/push') ->desc('Create push notification') ->groups(['api', 'messaging']) ->label('audits.event', 'message.create') @@ -3020,7 +3013,7 @@ Http::post('/v1/messaging/messages/push') ->dynamic($message, Response::MODEL_MESSAGE); }); -Http::get('/v1/messaging/messages') +App::get('/v1/messaging/messages') ->desc('List messages') ->groups(['api', 'messaging']) ->label('scope', 'messages.read') @@ -3035,8 +3028,7 @@ Http::get('/v1/messaging/messages') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('dbForProject') ->inject('response') - ->inject('authorization') - ->action(function (array $queries, string $search, Database $dbForProject, Response $response, Authorization $authorization) { + ->action(function (array $queries, string $search, Database $dbForProject, Response $response) { try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -3057,7 +3049,7 @@ Http::get('/v1/messaging/messages') if ($cursor) { $messageId = $cursor->getValue(); - $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('messages', $messageId)); + $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('messages', $messageId)); if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Message '{$messageId}' for the 'cursor' value not found."); @@ -3072,7 +3064,7 @@ Http::get('/v1/messaging/messages') ]), Response::MODEL_MESSAGE_LIST); }); -Http::get('/v1/messaging/messages/:messageId/logs') +App::get('/v1/messaging/messages/:messageId/logs') ->desc('List message logs') ->groups(['api', 'messaging']) ->label('scope', 'messages.read') @@ -3089,8 +3081,7 @@ Http::get('/v1/messaging/messages/:messageId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('authorization') - ->action(function (string $messageId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { + ->action(function (string $messageId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { $message = $dbForProject->getDocument('messages', $messageId); if ($message->isEmpty()) { @@ -3162,7 +3153,7 @@ Http::get('/v1/messaging/messages/:messageId/logs') ]), Response::MODEL_LOG_LIST); }); -Http::get('/v1/messaging/messages/:messageId/targets') +App::get('/v1/messaging/messages/:messageId/targets') ->desc('List message targets') ->groups(['api', 'messaging']) ->label('scope', 'messages.read') @@ -3227,7 +3218,7 @@ Http::get('/v1/messaging/messages/:messageId/targets') ]), Response::MODEL_TARGET_LIST); }); -Http::get('/v1/messaging/messages/:messageId') +App::get('/v1/messaging/messages/:messageId') ->desc('Get message') ->groups(['api', 'messaging']) ->label('scope', 'messages.read') @@ -3251,7 +3242,7 @@ Http::get('/v1/messaging/messages/:messageId') $response->dynamic($message, Response::MODEL_MESSAGE); }); -Http::patch('/v1/messaging/messages/email/:messageId') +App::patch('/v1/messaging/messages/email/:messageId') ->desc('Update email') ->groups(['api', 'messaging']) ->label('audits.event', 'message.update') @@ -3451,7 +3442,7 @@ Http::patch('/v1/messaging/messages/email/:messageId') ->dynamic($message, Response::MODEL_MESSAGE); }); -Http::patch('/v1/messaging/messages/sms/:messageId') +App::patch('/v1/messaging/messages/sms/:messageId') ->desc('Update SMS') ->groups(['api', 'messaging']) ->label('audits.event', 'message.update') @@ -3606,7 +3597,7 @@ Http::patch('/v1/messaging/messages/sms/:messageId') ->dynamic($message, Response::MODEL_MESSAGE); }); -Http::patch('/v1/messaging/messages/push/:messageId') +App::patch('/v1/messaging/messages/push/:messageId') ->desc('Update push notification') ->groups(['api', 'messaging']) ->label('audits.event', 'message.update') @@ -3844,7 +3835,7 @@ Http::patch('/v1/messaging/messages/push/:messageId') ->dynamic($message, Response::MODEL_MESSAGE); }); -Http::delete('/v1/messaging/messages/:messageId') +App::delete('/v1/messaging/messages/:messageId') ->desc('Delete message') ->groups(['api', 'messaging']) ->label('audits.event', 'message.delete') diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index f39527c3e4..3899b26ad4 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -9,6 +9,7 @@ use Appwrite\Role; use Appwrite\Utopia\Database\Validator\Queries\Migrations; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; +use Utopia\App; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -16,22 +17,21 @@ use Utopia\Database\Exception\Query as QueryException; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\UID; -use Utopia\Http\Http; -use Utopia\Http\Validator\ArrayList; -use Utopia\Http\Validator\Host; -use Utopia\Http\Validator\Integer; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\URL; -use Utopia\Http\Validator\WhiteList; use Utopia\Migration\Sources\Appwrite; use Utopia\Migration\Sources\Firebase; use Utopia\Migration\Sources\NHost; use Utopia\Migration\Sources\Supabase; use Utopia\System\System; +use Utopia\Validator\ArrayList; +use Utopia\Validator\Host; +use Utopia\Validator\Integer; +use Utopia\Validator\Text; +use Utopia\Validator\URL; +use Utopia\Validator\WhiteList; include_once __DIR__ . '/../shared/api.php'; -Http::post('/v1/migrations/appwrite') +App::post('/v1/migrations/appwrite') ->groups(['api', 'migrations']) ->desc('Migrate Appwrite Data') ->label('scope', 'migrations.write') @@ -85,7 +85,7 @@ Http::post('/v1/migrations/appwrite') ->dynamic($migration, Response::MODEL_MIGRATION); }); -Http::post('/v1/migrations/firebase/oauth') +App::post('/v1/migrations/firebase/oauth') ->groups(['api', 'migrations']) ->desc('Migrate Firebase Data (OAuth)') ->label('scope', 'migrations.write') @@ -187,7 +187,7 @@ Http::post('/v1/migrations/firebase/oauth') ->dynamic($migration, Response::MODEL_MIGRATION); }); -Http::post('/v1/migrations/firebase') +App::post('/v1/migrations/firebase') ->groups(['api', 'migrations']) ->desc('Migrate Firebase Data (Service Account)') ->label('scope', 'migrations.write') @@ -247,7 +247,7 @@ Http::post('/v1/migrations/firebase') ->dynamic($migration, Response::MODEL_MIGRATION); }); -Http::post('/v1/migrations/supabase') +App::post('/v1/migrations/supabase') ->groups(['api', 'migrations']) ->desc('Migrate Supabase Data') ->label('scope', 'migrations.write') @@ -307,7 +307,7 @@ Http::post('/v1/migrations/supabase') ->dynamic($migration, Response::MODEL_MIGRATION); }); -Http::post('/v1/migrations/nhost') +App::post('/v1/migrations/nhost') ->groups(['api', 'migrations']) ->desc('Migrate NHost Data') ->label('scope', 'migrations.write') @@ -369,7 +369,7 @@ Http::post('/v1/migrations/nhost') ->dynamic($migration, Response::MODEL_MIGRATION); }); -Http::get('/v1/migrations') +App::get('/v1/migrations') ->groups(['api', 'migrations']) ->desc('List Migrations') ->label('scope', 'migrations.read') @@ -422,7 +422,7 @@ Http::get('/v1/migrations') ]), Response::MODEL_MIGRATION_LIST); }); -Http::get('/v1/migrations/:migrationId') +App::get('/v1/migrations/:migrationId') ->groups(['api', 'migrations']) ->desc('Get Migration') ->label('scope', 'migrations.read') @@ -446,7 +446,7 @@ Http::get('/v1/migrations/:migrationId') $response->dynamic($migration, Response::MODEL_MIGRATION); }); -Http::get('/v1/migrations/appwrite/report') +App::get('/v1/migrations/appwrite/report') ->groups(['api', 'migrations']) ->desc('Generate a report on Appwrite Data') ->label('scope', 'migrations.write') @@ -488,7 +488,7 @@ Http::get('/v1/migrations/appwrite/report') ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); -Http::get('/v1/migrations/firebase/report') +App::get('/v1/migrations/firebase/report') ->groups(['api', 'migrations']) ->desc('Generate a report on Firebase Data') ->label('scope', 'migrations.write') @@ -535,7 +535,7 @@ Http::get('/v1/migrations/firebase/report') ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); -Http::get('/v1/migrations/firebase/report/oauth') +App::get('/v1/migrations/firebase/report/oauth') ->groups(['api', 'migrations']) ->desc('Generate a report on Firebase Data using OAuth') ->label('scope', 'migrations.write') @@ -626,7 +626,7 @@ Http::get('/v1/migrations/firebase/report/oauth') ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); -Http::get('/v1/migrations/firebase/connect') +App::get('/v1/migrations/firebase/connect') ->desc('Authorize with firebase') ->groups(['api', 'migrations']) ->label('scope', 'migrations.write') @@ -668,7 +668,7 @@ Http::get('/v1/migrations/firebase/connect') ->redirect($url); }); -Http::get('/v1/migrations/firebase/redirect') +App::get('/v1/migrations/firebase/redirect') ->desc('Capture and receive data on Firebase authorization') ->groups(['api', 'migrations']) ->label('scope', 'public') @@ -780,7 +780,7 @@ Http::get('/v1/migrations/firebase/redirect') ->redirect($redirect); }); -Http::get('/v1/migrations/firebase/projects') +App::get('/v1/migrations/firebase/projects') ->desc('List Firebase Projects') ->groups(['api', 'migrations']) ->label('scope', 'migrations.read') @@ -869,7 +869,7 @@ Http::get('/v1/migrations/firebase/projects') ]), Response::MODEL_MIGRATION_FIREBASE_PROJECT_LIST); }); -Http::get('/v1/migrations/firebase/deauthorize') +App::get('/v1/migrations/firebase/deauthorize') ->desc('Revoke Appwrite\'s authorization to access Firebase Projects') ->groups(['api', 'migrations']) ->label('scope', 'migrations.write') @@ -897,7 +897,7 @@ Http::get('/v1/migrations/firebase/deauthorize') $response->noContent(); }); -Http::get('/v1/migrations/supabase/report') +App::get('/v1/migrations/supabase/report') ->groups(['api', 'migrations']) ->desc('Generate a report on Supabase Data') ->label('scope', 'migrations.write') @@ -940,7 +940,7 @@ Http::get('/v1/migrations/supabase/report') ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); -Http::get('/v1/migrations/nhost/report') +App::get('/v1/migrations/nhost/report') ->groups(['api', 'migrations']) ->desc('Generate a report on NHost Data') ->label('scope', 'migrations.write') @@ -983,7 +983,7 @@ Http::get('/v1/migrations/nhost/report') ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); -Http::patch('/v1/migrations/:migrationId') +App::patch('/v1/migrations/:migrationId') ->groups(['api', 'migrations']) ->desc('Retry Migration') ->label('scope', 'migrations.write') @@ -1028,7 +1028,7 @@ Http::patch('/v1/migrations/:migrationId') $response->noContent(); }); -Http::delete('/v1/migrations/:migrationId') +App::delete('/v1/migrations/:migrationId') ->groups(['api', 'migrations']) ->desc('Delete Migration') ->label('scope', 'migrations.write') diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 177e040bc4..d885e980df 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -2,6 +2,7 @@ use Appwrite\Extend\Exception; use Appwrite\Utopia\Response; +use Utopia\App; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; @@ -12,11 +13,10 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Datetime as DateTimeValidator; use Utopia\Database\Validator\UID; -use Utopia\Http\Http; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; +use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; -Http::get('/v1/project/usage') +App::get('/v1/project/usage') ->desc('Get project usage stats') ->groups(['api', 'usage']) ->label('scope', 'projects.read') @@ -31,8 +31,7 @@ Http::get('/v1/project/usage') ->param('period', '1d', new WhiteList(['1h', '1d']), 'Period used', true) ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $startDate, string $endDate, string $period, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $startDate, string $endDate, string $period, Response $response, Database $dbForProject) { $stats = $total = $usage = []; $format = 'Y-m-d 00:00:00'; $firstDay = (new DateTime($startDate))->format($format); @@ -77,7 +76,7 @@ Http::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, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) { foreach ($metrics['total'] as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -277,6 +276,8 @@ Http::get('/v1/project/usage') 'buildsStorageTotal' => $total[METRIC_BUILDS_STORAGE], 'deploymentsStorageTotal' => $total[METRIC_DEPLOYMENTS_STORAGE], 'executionsBreakdown' => $executionsBreakdown, + 'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown, + 'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown, 'bucketsBreakdown' => $bucketsBreakdown, 'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown, 'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown, @@ -286,7 +287,7 @@ Http::get('/v1/project/usage') // Variables -Http::post('/v1/project/variables') +App::post('/v1/project/variables') ->desc('Create Variable') ->groups(['api']) ->label('scope', 'projects.write') @@ -341,7 +342,7 @@ Http::post('/v1/project/variables') ->dynamic($variable, Response::MODEL_VARIABLE); }); -Http::get('/v1/project/variables') +App::get('/v1/project/variables') ->desc('List Variables') ->groups(['api']) ->label('scope', 'projects.read') @@ -366,7 +367,7 @@ Http::get('/v1/project/variables') ]), Response::MODEL_VARIABLE_LIST); }); -Http::get('/v1/project/variables/:variableId') +App::get('/v1/project/variables/:variableId') ->desc('Get Variable') ->groups(['api']) ->label('scope', 'projects.read') @@ -390,7 +391,7 @@ Http::get('/v1/project/variables/:variableId') $response->dynamic($variable, Response::MODEL_VARIABLE); }); -Http::put('/v1/project/variables/:variableId') +App::put('/v1/project/variables/:variableId') ->desc('Update Variable') ->groups(['api']) ->label('scope', 'projects.write') @@ -436,7 +437,7 @@ Http::put('/v1/project/variables/:variableId') $response->dynamic($variable, Response::MODEL_VARIABLE); }); -Http::delete('/v1/project/variables/:variableId') +App::delete('/v1/project/variables/:variableId') ->desc('Delete Variable') ->groups(['api']) ->label('scope', 'projects.write') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index da5b4b882c..3a8c232195 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -13,16 +13,14 @@ use Appwrite\Network\Validator\Origin; use Appwrite\Template\Template; use Appwrite\Utopia\Database\Validator\ProjectId; use Appwrite\Utopia\Database\Validator\Queries\Projects; -use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use PHPMailer\PHPMailer\PHPMailer; use Utopia\Abuse\Adapters\Database\TimeLimit; +use Utopia\App; use Utopia\Audit\Audit; use Utopia\Cache\Cache; use Utopia\Config\Config; -use Utopia\Database\Adapter\MariaDB; -use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -32,25 +30,24 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Datetime as DatetimeValidator; use Utopia\Database\Validator\UID; use Utopia\Domains\Validator\PublicDomain; use Utopia\DSN\DSN; -use Utopia\Http\Http; -use Utopia\Http\Validator\ArrayList; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\Hostname; -use Utopia\Http\Validator\Integer; -use Utopia\Http\Validator\Multiple; -use Utopia\Http\Validator\Range; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\URL; -use Utopia\Http\Validator\WhiteList; use Utopia\Locale\Locale; +use Utopia\Pools\Group; use Utopia\System\System; +use Utopia\Validator\ArrayList; +use Utopia\Validator\Boolean; +use Utopia\Validator\Hostname; +use Utopia\Validator\Integer; +use Utopia\Validator\Multiple; +use Utopia\Validator\Range; +use Utopia\Validator\Text; +use Utopia\Validator\URL; +use Utopia\Validator\WhiteList; -Http::init() +App::init() ->groups(['projects']) ->inject('project') ->action(function (Document $project) { @@ -59,7 +56,7 @@ Http::init() } }); -Http::post('/v1/projects') +App::post('/v1/projects') ->desc('Create project') ->groups(['api', 'projects']) ->label('audits.event', 'projects.create') @@ -89,9 +86,8 @@ Http::post('/v1/projects') ->inject('cache') ->inject('pools') ->inject('hooks') - ->inject('authorization') - ->inject('connections') - ->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Request $request, Response $response, Database $dbForConsole, Cache $cache, array $pools, Hooks $hooks, Authorization $authorization, Connections $connections) { + ->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Request $request, Response $response, Database $dbForConsole, Cache $cache, Group $pools, Hooks $hooks) { + $team = $dbForConsole->getDocument('teams', $teamId); if ($team->isEmpty()) { @@ -193,21 +189,8 @@ Http::post('/v1/projects') $dsn = new DSN('mysql://' . $dsn); } - $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; - $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapter = match ($connectionDsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($connectionDsn->getPath()); - + $adapter = $pools->get($dsn->getHost())->pop()->getResource(); $dbForProject = new Database($adapter, $cache); - $dbForProject->setAuthorization($authorization); if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { $dbForProject @@ -225,8 +208,10 @@ Http::post('/v1/projects') $audit = new Audit($dbForProject); $audit->setup(); + $abuse = new TimeLimit('', 0, 1, $dbForProject); $abuse->setup(); + /** @var array $collections */ $collections = Config::getParam('collections', [])['projects'] ?? []; @@ -249,17 +234,17 @@ Http::post('/v1/projects') // Collection already exists } } - $connections->reclaim(); + // Hook allowing instant project mirroring during migration // Outside of migration, hook is not registered and has no effect - $hooks->trigger('afterProjectCreation', [$project, $pools, $cache]); + $hooks->trigger('afterProjectCreation', [ $project, $pools, $cache ]); $response ->setStatusCode(Response::STATUS_CODE_CREATED) ->dynamic($project, Response::MODEL_PROJECT); }); -Http::get('/v1/projects') +App::get('/v1/projects') ->desc('List projects') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -274,6 +259,7 @@ Http::get('/v1/projects') ->inject('response') ->inject('dbForConsole') ->action(function (array $queries, string $search, Response $response, Database $dbForConsole) { + try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -311,7 +297,7 @@ Http::get('/v1/projects') ]), Response::MODEL_PROJECT_LIST); }); -Http::get('/v1/projects/:projectId') +App::get('/v1/projects/:projectId') ->desc('Get project') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -325,6 +311,7 @@ Http::get('/v1/projects/:projectId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -334,7 +321,7 @@ Http::get('/v1/projects/:projectId') $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId') +App::patch('/v1/projects/:projectId') ->desc('Update project') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -358,34 +345,31 @@ Http::patch('/v1/projects/:projectId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $name, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } - $project = $dbForConsole->updateDocument( - 'projects', - $project->getId(), - $project - ->setAttribute('name', $name) - ->setAttribute('description', $description) - ->setAttribute('logo', $logo) - ->setAttribute('url', $url) - ->setAttribute('legalName', $legalName) - ->setAttribute('legalCountry', $legalCountry) - ->setAttribute('legalState', $legalState) - ->setAttribute('legalCity', $legalCity) - ->setAttribute('legalAddress', $legalAddress) - ->setAttribute('legalTaxId', $legalTaxId) - ->setAttribute('search', implode(' ', [$projectId, $name])) - ); + $project = $dbForConsole->updateDocument('projects', $project->getId(), $project + ->setAttribute('name', $name) + ->setAttribute('description', $description) + ->setAttribute('logo', $logo) + ->setAttribute('url', $url) + ->setAttribute('legalName', $legalName) + ->setAttribute('legalCountry', $legalCountry) + ->setAttribute('legalState', $legalState) + ->setAttribute('legalCity', $legalCity) + ->setAttribute('legalAddress', $legalAddress) + ->setAttribute('legalTaxId', $legalTaxId) + ->setAttribute('search', implode(' ', [$projectId, $name]))); $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/team') - ->desc('Update Project Team') +App::patch('/v1/projects/:projectId/team') + ->desc('Update project team') ->groups(['api', 'projects']) ->label('scope', 'projects.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) @@ -399,6 +383,7 @@ Http::patch('/v1/projects/:projectId/team') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $teamId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); $team = $dbForConsole->getDocument('teams', $teamId); @@ -451,7 +436,7 @@ Http::patch('/v1/projects/:projectId/team') $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/service') +App::patch('/v1/projects/:projectId/service') ->desc('Update service status') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -467,6 +452,7 @@ Http::patch('/v1/projects/:projectId/service') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $service, bool $status, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -481,7 +467,7 @@ Http::patch('/v1/projects/:projectId/service') $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/service/all') +App::patch('/v1/projects/:projectId/service/all') ->desc('Update all service status') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -496,6 +482,7 @@ Http::patch('/v1/projects/:projectId/service/all') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, bool $status, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -514,7 +501,7 @@ Http::patch('/v1/projects/:projectId/service/all') $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/api') +App::patch('/v1/projects/:projectId/api') ->desc('Update API status') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -530,6 +517,7 @@ Http::patch('/v1/projects/:projectId/api') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $api, bool $status, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -544,7 +532,7 @@ Http::patch('/v1/projects/:projectId/api') $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/api/all') +App::patch('/v1/projects/:projectId/api/all') ->desc('Update all API status') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -559,6 +547,7 @@ Http::patch('/v1/projects/:projectId/api/all') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, bool $status, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -577,7 +566,7 @@ Http::patch('/v1/projects/:projectId/api/all') $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/oauth2') +App::patch('/v1/projects/:projectId/oauth2') ->desc('Update project OAuth2') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -595,6 +584,7 @@ Http::patch('/v1/projects/:projectId/oauth2') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $provider, ?string $appId, ?string $secret, ?bool $enabled, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -620,7 +610,7 @@ Http::patch('/v1/projects/:projectId/oauth2') $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/auth/session-alerts') +App::patch('/v1/projects/:projectId/auth/session-alerts') ->desc('Update project sessions emails') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -651,7 +641,7 @@ Http::patch('/v1/projects/:projectId/auth/session-alerts') $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/auth/limit') +App::patch('/v1/projects/:projectId/auth/limit') ->desc('Update project users limit') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -666,6 +656,7 @@ Http::patch('/v1/projects/:projectId/auth/limit') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -675,17 +666,13 @@ Http::patch('/v1/projects/:projectId/auth/limit') $auths = $project->getAttribute('auths', []); $auths['limit'] = $limit; - $dbForConsole->updateDocument( - 'projects', - $project->getId(), - $project - ->setAttribute('auths', $auths) - ); + $dbForConsole->updateDocument('projects', $project->getId(), $project + ->setAttribute('auths', $auths)); $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/auth/duration') +App::patch('/v1/projects/:projectId/auth/duration') ->desc('Update project authentication duration') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -700,6 +687,7 @@ Http::patch('/v1/projects/:projectId/auth/duration') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, int $duration, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -709,17 +697,13 @@ Http::patch('/v1/projects/:projectId/auth/duration') $auths = $project->getAttribute('auths', []); $auths['duration'] = $duration; - $dbForConsole->updateDocument( - 'projects', - $project->getId(), - $project - ->setAttribute('auths', $auths) - ); + $dbForConsole->updateDocument('projects', $project->getId(), $project + ->setAttribute('auths', $auths)); $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/auth/:method') +App::patch('/v1/projects/:projectId/auth/:method') ->desc('Update project auth method status. Use this endpoint to enable or disable a given auth method for this project.') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -735,9 +719,10 @@ Http::patch('/v1/projects/:projectId/auth/:method') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $method, bool $status, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); - $authConfig = Config::getParam('auth')[$method] ?? []; - $authKey = $authConfig['key'] ?? ''; + $auth = Config::getParam('auth')[$method] ?? []; + $authKey = $auth['key'] ?? ''; $status = ($status === '1' || $status === 'true' || $status === 1 || $status === true); if ($project->isEmpty()) { @@ -752,7 +737,7 @@ Http::patch('/v1/projects/:projectId/auth/:method') $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/auth/password-history') +App::patch('/v1/projects/:projectId/auth/password-history') ->desc('Update authentication password history. Use this endpoint to set the number of password history to save and 0 to disable password history.') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -767,6 +752,7 @@ Http::patch('/v1/projects/:projectId/auth/password-history') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -776,17 +762,13 @@ Http::patch('/v1/projects/:projectId/auth/password-history') $auths = $project->getAttribute('auths', []); $auths['passwordHistory'] = $limit; - $dbForConsole->updateDocument( - 'projects', - $project->getId(), - $project - ->setAttribute('auths', $auths) - ); + $dbForConsole->updateDocument('projects', $project->getId(), $project + ->setAttribute('auths', $auths)); $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/auth/password-dictionary') +App::patch('/v1/projects/:projectId/auth/password-dictionary') ->desc('Update authentication password dictionary status. Use this endpoint to enable or disable the dicitonary check for user password') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -801,6 +783,7 @@ Http::patch('/v1/projects/:projectId/auth/password-dictionary') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -810,17 +793,13 @@ Http::patch('/v1/projects/:projectId/auth/password-dictionary') $auths = $project->getAttribute('auths', []); $auths['passwordDictionary'] = $enabled; - $dbForConsole->updateDocument( - 'projects', - $project->getId(), - $project - ->setAttribute('auths', $auths) - ); + $dbForConsole->updateDocument('projects', $project->getId(), $project + ->setAttribute('auths', $auths)); $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/auth/personal-data') +App::patch('/v1/projects/:projectId/auth/personal-data') ->desc('Enable or disable checking user passwords for similarity with their personal data.') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -835,6 +814,7 @@ Http::patch('/v1/projects/:projectId/auth/personal-data') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, bool $enabled, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -844,17 +824,13 @@ Http::patch('/v1/projects/:projectId/auth/personal-data') $auths = $project->getAttribute('auths', []); $auths['personalDataCheck'] = $enabled; - $dbForConsole->updateDocument( - 'projects', - $project->getId(), - $project - ->setAttribute('auths', $auths) - ); + $dbForConsole->updateDocument('projects', $project->getId(), $project + ->setAttribute('auths', $auths)); $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/auth/max-sessions') +App::patch('/v1/projects/:projectId/auth/max-sessions') ->desc('Update project user sessions limit') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -869,6 +845,7 @@ Http::patch('/v1/projects/:projectId/auth/max-sessions') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -878,17 +855,13 @@ Http::patch('/v1/projects/:projectId/auth/max-sessions') $auths = $project->getAttribute('auths', []); $auths['maxSessions'] = $limit; - $dbForConsole->updateDocument( - 'projects', - $project->getId(), - $project - ->setAttribute('auths', $auths) - ); + $dbForConsole->updateDocument('projects', $project->getId(), $project + ->setAttribute('auths', $auths)); $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::patch('/v1/projects/:projectId/auth/mock-numbers') +App::patch('/v1/projects/:projectId/auth/mock-numbers') ->desc('Update the mock numbers for the project') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -927,7 +900,7 @@ Http::patch('/v1/projects/:projectId/auth/mock-numbers') $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::delete('/v1/projects/:projectId') +App::delete('/v1/projects/:projectId') ->desc('Delete project') ->groups(['api', 'projects']) ->label('audits.event', 'projects.delete') @@ -962,7 +935,7 @@ Http::delete('/v1/projects/:projectId') // Webhooks -Http::post('/v1/projects/:projectId/webhooks') +App::post('/v1/projects/:projectId/webhooks') ->desc('Create webhook') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -983,13 +956,14 @@ Http::post('/v1/projects/:projectId/webhooks') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $name, bool $enabled, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } - $security = (bool)filter_var($security, FILTER_VALIDATE_BOOLEAN); + $security = (bool) filter_var($security, FILTER_VALIDATE_BOOLEAN); $webhook = new Document([ '$id' => ID::unique(), @@ -1019,7 +993,7 @@ Http::post('/v1/projects/:projectId/webhooks') ->dynamic($webhook, Response::MODEL_WEBHOOK); }); -Http::get('/v1/projects/:projectId/webhooks') +App::get('/v1/projects/:projectId/webhooks') ->desc('List webhooks') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -1033,6 +1007,7 @@ Http::get('/v1/projects/:projectId/webhooks') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1050,7 +1025,7 @@ Http::get('/v1/projects/:projectId/webhooks') ]), Response::MODEL_WEBHOOK_LIST); }); -Http::get('/v1/projects/:projectId/webhooks/:webhookId') +App::get('/v1/projects/:projectId/webhooks/:webhookId') ->desc('Get webhook') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -1065,6 +1040,7 @@ Http::get('/v1/projects/:projectId/webhooks/:webhookId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1083,7 +1059,7 @@ Http::get('/v1/projects/:projectId/webhooks/:webhookId') $response->dynamic($webhook, Response::MODEL_WEBHOOK); }); -Http::put('/v1/projects/:projectId/webhooks/:webhookId') +App::put('/v1/projects/:projectId/webhooks/:webhookId') ->desc('Update webhook') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1105,6 +1081,7 @@ Http::put('/v1/projects/:projectId/webhooks/:webhookId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $webhookId, string $name, bool $enabled, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1141,7 +1118,7 @@ Http::put('/v1/projects/:projectId/webhooks/:webhookId') $response->dynamic($webhook, Response::MODEL_WEBHOOK); }); -Http::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') +App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') ->desc('Update webhook signature key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1156,6 +1133,7 @@ Http::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1179,7 +1157,7 @@ Http::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') $response->dynamic($webhook, Response::MODEL_WEBHOOK); }); -Http::delete('/v1/projects/:projectId/webhooks/:webhookId') +App::delete('/v1/projects/:projectId/webhooks/:webhookId') ->desc('Delete webhook') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1193,6 +1171,7 @@ Http::delete('/v1/projects/:projectId/webhooks/:webhookId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1217,7 +1196,7 @@ Http::delete('/v1/projects/:projectId/webhooks/:webhookId') // Keys -Http::post('/v1/projects/:projectId/keys') +App::post('/v1/projects/:projectId/keys') ->desc('Create key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1234,6 +1213,7 @@ Http::post('/v1/projects/:projectId/keys') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1266,7 +1246,7 @@ Http::post('/v1/projects/:projectId/keys') ->dynamic($key, Response::MODEL_KEY); }); -Http::get('/v1/projects/:projectId/keys') +App::get('/v1/projects/:projectId/keys') ->desc('List keys') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -1280,6 +1260,7 @@ Http::get('/v1/projects/:projectId/keys') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1297,7 +1278,7 @@ Http::get('/v1/projects/:projectId/keys') ]), Response::MODEL_KEY_LIST); }); -Http::get('/v1/projects/:projectId/keys/:keyId') +App::get('/v1/projects/:projectId/keys/:keyId') ->desc('Get key') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -1312,6 +1293,7 @@ Http::get('/v1/projects/:projectId/keys/:keyId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1330,7 +1312,7 @@ Http::get('/v1/projects/:projectId/keys/:keyId') $response->dynamic($key, Response::MODEL_KEY); }); -Http::put('/v1/projects/:projectId/keys/:keyId') +App::put('/v1/projects/:projectId/keys/:keyId') ->desc('Update key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1348,6 +1330,7 @@ Http::put('/v1/projects/:projectId/keys/:keyId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1375,7 +1358,7 @@ Http::put('/v1/projects/:projectId/keys/:keyId') $response->dynamic($key, Response::MODEL_KEY); }); -Http::delete('/v1/projects/:projectId/keys/:keyId') +App::delete('/v1/projects/:projectId/keys/:keyId') ->desc('Delete key') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1389,6 +1372,7 @@ Http::delete('/v1/projects/:projectId/keys/:keyId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1413,7 +1397,7 @@ Http::delete('/v1/projects/:projectId/keys/:keyId') // JWT Keys -Http::post('/v1/projects/:projectId/jwts') +App::post('/v1/projects/:projectId/jwts') ->groups(['api', 'projects']) ->desc('Create JWT') ->label('scope', 'projects.write') @@ -1448,7 +1432,7 @@ Http::post('/v1/projects/:projectId/jwts') // Platforms -Http::post('/v1/projects/:projectId/platforms') +App::post('/v1/projects/:projectId/platforms') ->desc('Create platform') ->groups(['api', 'projects']) ->label('audits.event', 'platforms.create') @@ -1499,7 +1483,7 @@ Http::post('/v1/projects/:projectId/platforms') ->dynamic($platform, Response::MODEL_PLATFORM); }); -Http::get('/v1/projects/:projectId/platforms') +App::get('/v1/projects/:projectId/platforms') ->desc('List platforms') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -1513,6 +1497,7 @@ Http::get('/v1/projects/:projectId/platforms') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1530,7 +1515,7 @@ Http::get('/v1/projects/:projectId/platforms') ]), Response::MODEL_PLATFORM_LIST); }); -Http::get('/v1/projects/:projectId/platforms/:platformId') +App::get('/v1/projects/:projectId/platforms/:platformId') ->desc('Get platform') ->groups(['api', 'projects']) ->label('scope', 'projects.read') @@ -1545,6 +1530,7 @@ Http::get('/v1/projects/:projectId/platforms/:platformId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1563,7 +1549,7 @@ Http::get('/v1/projects/:projectId/platforms/:platformId') $response->dynamic($platform, Response::MODEL_PLATFORM); }); -Http::put('/v1/projects/:projectId/platforms/:platformId') +App::put('/v1/projects/:projectId/platforms/:platformId') ->desc('Update platform') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1610,7 +1596,7 @@ Http::put('/v1/projects/:projectId/platforms/:platformId') $response->dynamic($platform, Response::MODEL_PLATFORM); }); -Http::delete('/v1/projects/:projectId/platforms/:platformId') +App::delete('/v1/projects/:projectId/platforms/:platformId') ->desc('Delete platform') ->groups(['api', 'projects']) ->label('audits.event', 'platforms.delete') @@ -1625,6 +1611,7 @@ Http::delete('/v1/projects/:projectId/platforms/:platformId') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1649,7 +1636,7 @@ Http::delete('/v1/projects/:projectId/platforms/:platformId') // CUSTOM SMTP and Templates -Http::patch('/v1/projects/:projectId/smtp') +App::patch('/v1/projects/:projectId/smtp') ->desc('Update SMTP') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1672,6 +1659,7 @@ Http::patch('/v1/projects/:projectId/smtp') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, bool $enabled, string $senderName, string $senderEmail, string $replyTo, string $host, int $port, string $username, string $password, string $secure, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1738,7 +1726,7 @@ Http::patch('/v1/projects/:projectId/smtp') $response->dynamic($project, Response::MODEL_PROJECT); }); -Http::post('/v1/projects/:projectId/smtp/tests') +App::post('/v1/projects/:projectId/smtp/tests') ->desc('Create SMTP test') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1797,7 +1785,7 @@ Http::post('/v1/projects/:projectId/smtp/tests') return $response->noContent(); }); -Http::get('/v1/projects/:projectId/templates/sms/:type/:locale') +App::get('/v1/projects/:projectId/templates/sms/:type/:locale') ->desc('Get custom SMS template') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1813,6 +1801,7 @@ Http::get('/v1/projects/:projectId/templates/sms/:type/:locale') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) { + throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED); $project = $dbForConsole->getDocument('projects', $projectId); @@ -1822,7 +1811,7 @@ Http::get('/v1/projects/:projectId/templates/sms/:type/:locale') } $templates = $project->getAttribute('templates', []); - $template = $templates['sms.' . $type . '-' . $locale] ?? null; + $template = $templates['sms.' . $type . '-' . $locale] ?? null; if (is_null($template)) { $template = [ @@ -1837,7 +1826,7 @@ Http::get('/v1/projects/:projectId/templates/sms/:type/:locale') }); -Http::get('/v1/projects/:projectId/templates/email/:type/:locale') +App::get('/v1/projects/:projectId/templates/email/:type/:locale') ->desc('Get custom email template') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1853,6 +1842,7 @@ Http::get('/v1/projects/:projectId/templates/email/:type/:locale') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1860,7 +1850,7 @@ Http::get('/v1/projects/:projectId/templates/email/:type/:locale') } $templates = $project->getAttribute('templates', []); - $template = $templates['email.' . $type . '-' . $locale] ?? null; + $template = $templates['email.' . $type . '-' . $locale] ?? null; $localeObj = new Locale($locale); if (is_null($template)) { @@ -1868,7 +1858,7 @@ Http::get('/v1/projects/:projectId/templates/email/:type/:locale') $message ->setParam('{{hello}}', $localeObj->getText("emails.{$type}.hello")) ->setParam('{{footer}}', $localeObj->getText("emails.{$type}.footer")) - ->setParam('{{body}}', $localeObj->getText('emails.' . $type . '.body'), escape: false) + ->setParam('{{body}}', $localeObj->getText('emails.' . $type . '.body'), escapeHtml: false) ->setParam('{{thanks}}', $localeObj->getText("emails.{$type}.thanks")) ->setParam('{{signature}}', $localeObj->getText("emails.{$type}.signature")) ->setParam('{{direction}}', $localeObj->getText('settings.direction')); @@ -1888,7 +1878,7 @@ Http::get('/v1/projects/:projectId/templates/email/:type/:locale') $response->dynamic(new Document($template), Response::MODEL_EMAIL_TEMPLATE); }); -Http::patch('/v1/projects/:projectId/templates/sms/:type/:locale') +App::patch('/v1/projects/:projectId/templates/sms/:type/:locale') ->desc('Update custom SMS template') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1905,6 +1895,7 @@ Http::patch('/v1/projects/:projectId/templates/sms/:type/:locale') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, string $message, Response $response, Database $dbForConsole) { + throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED); $project = $dbForConsole->getDocument('projects', $projectId); @@ -1927,7 +1918,7 @@ Http::patch('/v1/projects/:projectId/templates/sms/:type/:locale') ]), Response::MODEL_SMS_TEMPLATE); }); -Http::patch('/v1/projects/:projectId/templates/email/:type/:locale') +App::patch('/v1/projects/:projectId/templates/email/:type/:locale') ->desc('Update custom email templates') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1948,6 +1939,7 @@ Http::patch('/v1/projects/:projectId/templates/email/:type/:locale') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, string $subject, string $message, string $senderName, string $senderEmail, string $replyTo, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -1976,7 +1968,7 @@ Http::patch('/v1/projects/:projectId/templates/email/:type/:locale') ]), Response::MODEL_EMAIL_TEMPLATE); }); -Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale') +App::delete('/v1/projects/:projectId/templates/sms/:type/:locale') ->desc('Reset custom SMS template') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -1992,6 +1984,7 @@ Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) { + throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED); $project = $dbForConsole->getDocument('projects', $projectId); @@ -2001,7 +1994,7 @@ Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale') } $templates = $project->getAttribute('templates', []); - $template = $templates['sms.' . $type . '-' . $locale] ?? null; + $template = $templates['sms.' . $type . '-' . $locale] ?? null; if (is_null($template)) { throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION); @@ -2018,7 +2011,7 @@ Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale') ]), Response::MODEL_SMS_TEMPLATE); }); -Http::delete('/v1/projects/:projectId/templates/email/:type/:locale') +App::delete('/v1/projects/:projectId/templates/email/:type/:locale') ->desc('Reset custom email template') ->groups(['api', 'projects']) ->label('scope', 'projects.write') @@ -2034,6 +2027,7 @@ Http::delete('/v1/projects/:projectId/templates/email/:type/:locale') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForConsole) { + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -2041,7 +2035,7 @@ Http::delete('/v1/projects/:projectId/templates/email/:type/:locale') } $templates = $project->getAttribute('templates', []); - $template = $templates['email.' . $type . '-' . $locale] ?? null; + $template = $templates['email.' . $type . '-' . $locale] ?? null; if (is_null($template)) { throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION); diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php index 326c164dd3..f60a639302 100644 --- a/app/controllers/api/proxy.php +++ b/app/controllers/api/proxy.php @@ -7,6 +7,7 @@ use Appwrite\Extend\Exception; use Appwrite\Network\Validator\CNAME; use Appwrite\Utopia\Database\Validator\Queries\Rules; use Appwrite\Utopia\Response; +use Utopia\App; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Query as QueryException; @@ -14,14 +15,13 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; -use Utopia\Http\Http; -use Utopia\Http\Validator\Domain as ValidatorDomain; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; use Utopia\Logger\Log; use Utopia\System\System; +use Utopia\Validator\Domain as ValidatorDomain; +use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; -Http::post('/v1/proxy/rules') +App::post('/v1/proxy/rules') ->groups(['api', 'proxy']) ->desc('Create Rule') ->label('scope', 'rules.write') @@ -147,7 +147,7 @@ Http::post('/v1/proxy/rules') ->dynamic($rule, Response::MODEL_PROXY_RULE); }); -Http::get('/v1/proxy/rules') +App::get('/v1/proxy/rules') ->groups(['api', 'proxy']) ->desc('List Rules') ->label('scope', 'rules.read') @@ -210,7 +210,7 @@ Http::get('/v1/proxy/rules') ]), Response::MODEL_PROXY_RULE_LIST); }); -Http::get('/v1/proxy/rules/:ruleId') +App::get('/v1/proxy/rules/:ruleId') ->groups(['api', 'proxy']) ->desc('Get Rule') ->label('scope', 'rules.read') @@ -239,7 +239,7 @@ Http::get('/v1/proxy/rules/:ruleId') $response->dynamic($rule, Response::MODEL_PROXY_RULE); }); -Http::delete('/v1/proxy/rules/:ruleId') +App::delete('/v1/proxy/rules/:ruleId') ->groups(['api', 'proxy']) ->desc('Delete Rule') ->label('scope', 'rules.write') @@ -276,7 +276,7 @@ Http::delete('/v1/proxy/rules/:ruleId') $response->noContent(); }); -Http::patch('/v1/proxy/rules/:ruleId/verification') +App::patch('/v1/proxy/rules/:ruleId/verification') ->desc('Update Rule Verification Status') ->groups(['api', 'proxy']) ->label('scope', 'rules.write') diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index e0408acb37..b080066653 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -11,8 +11,8 @@ use Appwrite\OpenSSL\OpenSSL; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\Queries\Buckets; use Appwrite\Utopia\Database\Validator\Queries\Files; -use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; +use Utopia\App; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -23,16 +23,8 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\Authorization\Input; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; -use Utopia\Http\Http; -use Utopia\Http\Validator\ArrayList; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\HexColor; -use Utopia\Http\Validator\Range; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; use Utopia\Image\Image; use Utopia\Storage\Compression\Algorithms\GZIP; use Utopia\Storage\Compression\Algorithms\Zstd; @@ -43,9 +35,16 @@ use Utopia\Storage\Validator\File; use Utopia\Storage\Validator\FileExt; use Utopia\Storage\Validator\FileSize; use Utopia\Storage\Validator\Upload; +use Utopia\Swoole\Request; use Utopia\System\System; +use Utopia\Validator\ArrayList; +use Utopia\Validator\Boolean; +use Utopia\Validator\HexColor; +use Utopia\Validator\Range; +use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; -Http::post('/v1/storage/buckets') +App::post('/v1/storage/buckets') ->desc('Create bucket') ->groups(['api', 'storage']) ->label('scope', 'buckets.write') @@ -143,7 +142,7 @@ Http::post('/v1/storage/buckets') ->dynamic($bucket, Response::MODEL_BUCKET); }); -Http::get('/v1/storage/buckets') +App::get('/v1/storage/buckets') ->desc('List buckets') ->groups(['api', 'storage']) ->label('scope', 'buckets.read') @@ -197,7 +196,7 @@ Http::get('/v1/storage/buckets') ]), Response::MODEL_BUCKET_LIST); }); -Http::get('/v1/storage/buckets/:bucketId') +App::get('/v1/storage/buckets/:bucketId') ->desc('Get bucket') ->groups(['api', 'storage']) ->label('scope', 'buckets.read') @@ -222,7 +221,7 @@ Http::get('/v1/storage/buckets/:bucketId') $response->dynamic($bucket, Response::MODEL_BUCKET); }); -Http::put('/v1/storage/buckets/:bucketId') +App::put('/v1/storage/buckets/:bucketId') ->desc('Update bucket') ->groups(['api', 'storage']) ->label('scope', 'buckets.write') @@ -289,7 +288,7 @@ Http::put('/v1/storage/buckets/:bucketId') $response->dynamic($bucket, Response::MODEL_BUCKET); }); -Http::delete('/v1/storage/buckets/:bucketId') +App::delete('/v1/storage/buckets/:bucketId') ->desc('Delete bucket') ->groups(['api', 'storage']) ->label('scope', 'buckets.write') @@ -330,7 +329,7 @@ Http::delete('/v1/storage/buckets/:bucketId') $response->noContent(); }); -Http::post('/v1/storage/buckets/:bucketId/files') +App::post('/v1/storage/buckets/:bucketId/files') ->alias('/v1/storage/files', ['bucketId' => 'default']) ->desc('Create file') ->groups(['api', 'storage']) @@ -362,19 +361,19 @@ Http::post('/v1/storage/buckets/:bucketId/files') ->inject('mode') ->inject('deviceForFiles') ->inject('deviceForLocal') - ->inject('authorization') - ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal, Authorization $authorization) { + ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal) { - $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { + $validator = new Authorization(Database::PERMISSION_CREATE); + if (!$validator->isValid($bucket->getCreate())) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -398,7 +397,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') } // Users can only manage their own roles, API keys and Admin users can manage any - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -411,7 +410,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$authorization->isRole($role)) { + if (!Authorization::isRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -631,10 +630,11 @@ Http::post('/v1/storage/buckets/:bucketId/files') * However as with chunk upload even if we are updating, we are essentially creating a file * adding it's new chunk so we validate create permission instead of update */ - if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { + $validator = new Authorization(Database::PERMISSION_CREATE); + if (!$validator->isValid($bucket->getCreate())) { throw new Exception(Exception::USER_UNAUTHORIZED); } - $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); } } else { if ($file->isEmpty()) { @@ -669,10 +669,11 @@ Http::post('/v1/storage/buckets/:bucketId/files') * However as with chunk upload even if we are updating, we are essentially creating a file * adding it's new chunk so we validate create permission instead of update */ - if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) { + $validator = new Authorization(Database::PERMISSION_CREATE); + if (!$validator->isValid($bucket->getCreate())) { throw new Exception(Exception::USER_UNAUTHORIZED); } - $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); } } @@ -689,7 +690,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') ->dynamic($file, Response::MODEL_FILE); }); -Http::get('/v1/storage/buckets/:bucketId/files') +App::get('/v1/storage/buckets/:bucketId/files') ->alias('/v1/storage/files', ['bucketId' => 'default']) ->desc('List files') ->groups(['api', 'storage']) @@ -707,19 +708,19 @@ Http::get('/v1/storage/buckets/:bucketId/files') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('authorization') - ->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { - $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->action(function (string $bucketId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) { + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $validator = new Authorization(Database::PERMISSION_READ); + $valid = $validator->isValid($bucket->getRead()); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -748,7 +749,7 @@ Http::get('/v1/storage/buckets/:bucketId/files') if ($fileSecurity && !$valid) { $cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($cursorDocument->isEmpty()) { @@ -764,8 +765,8 @@ Http::get('/v1/storage/buckets/:bucketId/files') $files = $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries); $total = $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT); } else { - $files = $authorization->skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries)); - $total = $authorization->skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT)); + $files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries)); + $total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT)); } $response->dynamic(new Document([ @@ -774,7 +775,7 @@ Http::get('/v1/storage/buckets/:bucketId/files') ]), Response::MODEL_FILE_LIST); }); -Http::get('/v1/storage/buckets/:bucketId/files/:fileId') +App::get('/v1/storage/buckets/:bucketId/files/:fileId') ->alias('/v1/storage/files/:fileId', ['bucketId' => 'default']) ->desc('Get file') ->groups(['api', 'storage']) @@ -791,19 +792,19 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('authorization') - ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode, Authorization $authorization) { - $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode) { + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $validator = new Authorization(Database::PERMISSION_READ); + $valid = $validator->isValid($bucket->getRead()); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -811,7 +812,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId') if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -821,7 +822,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId') $response->dynamic($file, Response::MODEL_FILE); }); -Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') +App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->alias('/v1/storage/files/:fileId/preview', ['bucketId' => 'default']) ->desc('Get file preview') ->groups(['api', 'storage']) @@ -856,24 +857,24 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->inject('mode') ->inject('deviceForFiles') ->inject('deviceForLocal') - ->inject('authorization') - ->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, Authorization $authorization) { + ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Request $request, Response $response, Document $project, Database $dbForProject, string $mode, Device $deviceForFiles, Device $deviceForLocal) { if (!\extension_loaded('imagick')) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); } - $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $validator = new Authorization(Database::PERMISSION_READ); + $valid = $validator->isValid($bucket->getRead()); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -881,7 +882,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -997,7 +998,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') unset($image); }); -Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download') +App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->alias('/v1/storage/files/:fileId/download', ['bucketId' => 'default']) ->desc('Get file for download') ->groups(['api', 'storage']) @@ -1016,20 +1017,20 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->inject('dbForProject') ->inject('mode') ->inject('deviceForFiles') - ->inject('authorization') - ->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Device $deviceForFiles, Authorization $authorization) { + ->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Device $deviceForFiles) { - $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $validator = new Authorization(Database::PERMISSION_READ); + $valid = $validator->isValid($bucket->getRead()); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1037,7 +1038,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download') if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -1137,7 +1138,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/download') } }); -Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view') +App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->alias('/v1/storage/files/:fileId/view', ['bucketId' => 'default']) ->desc('Get file for view') ->groups(['api', 'storage']) @@ -1156,19 +1157,19 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->inject('dbForProject') ->inject('mode') ->inject('deviceForFiles') - ->inject('authorization') - ->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceForFiles, Authorization $authorization) { - $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceForFiles) { + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $validator = new Authorization(Database::PERMISSION_READ); + $valid = $validator->isValid($bucket->getRead()); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1176,7 +1177,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view') if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -1289,7 +1290,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/view') } }); -Http::get('/v1/storage/buckets/:bucketId/files/:fileId/push') +App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') ->desc('Get file for push notification') ->groups(['api', 'storage']) ->label('scope', 'public') @@ -1305,9 +1306,8 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/push') ->inject('project') ->inject('mode') ->inject('deviceForFiles') - ->inject('authorization') - ->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles, Authorization $authorization) { - $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles) { + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); @@ -1325,14 +1325,14 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/push') throw new Exception(Exception::USER_UNAUTHORIZED); } - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } - $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -1443,7 +1443,7 @@ Http::get('/v1/storage/buckets/:bucketId/files/:fileId/push') } }); -Http::put('/v1/storage/buckets/:bucketId/files/:fileId') +App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->alias('/v1/storage/files/:fileId', ['bucketId' => 'default']) ->desc('Update file') ->groups(['api', 'storage']) @@ -1470,26 +1470,26 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('user') ->inject('mode') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents) { - $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $authorization->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate())); + $validator = new Authorization(Database::PERMISSION_UPDATE); + $valid = $validator->isValid($bucket->getUpdate()); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } // Read permission should not be required for update - $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); @@ -1503,7 +1503,7 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') ]); // Users can only manage their own roles, API keys and Admin users can manage any - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles) && !\is_null($permissions)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { @@ -1516,7 +1516,7 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') $permission->getIdentifier(), $permission->getDimension() ))->toString(); - if (!$authorization->isRole($role)) { + if (!Authorization::isRole($role)) { throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')'); } } @@ -1536,7 +1536,7 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') if ($fileSecurity && !$valid) { $file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file); } else { - $file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); + $file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file)); } $queueForEvents @@ -1548,7 +1548,7 @@ Http::put('/v1/storage/buckets/:bucketId/files/:fileId') $response->dynamic($file, Response::MODEL_FILE); }); -Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') +App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->desc('Delete File') ->groups(['api', 'storage']) ->label('scope', 'files.write') @@ -1572,32 +1572,32 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('mode') ->inject('deviceForFiles') ->inject('queueForDeletes') - ->inject('authorization') - ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes, Authorization $authorization) { - $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes) { + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $authorization->isValid(new Input(Database::PERMISSION_DELETE, $bucket->getDelete())); + $validator = new Authorization(Database::PERMISSION_DELETE); + $valid = $validator->isValid($bucket->getDelete()); if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } // Read permission should not be required for delete - $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } // Make sure we don't delete the file before the document permission check occurs - if ($fileSecurity && !$valid && !$authorization->isValid(new Input(Database::PERMISSION_DELETE, $file->getDelete()))) { + if ($fileSecurity && !$valid && !$validator->isValid($file->getDelete())) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -1621,7 +1621,7 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') if ($fileSecurity && !$valid) { $deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $deleted = $authorization->skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $deleted = Authorization::skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if (!$deleted) { @@ -1641,7 +1641,7 @@ Http::delete('/v1/storage/buckets/:bucketId/files/:fileId') $response->noContent(); }); -Http::get('/v1/storage/usage') +App::get('/v1/storage/usage') ->desc('Get storage usage stats') ->groups(['api', 'storage']) ->label('scope', 'files.read') @@ -1654,8 +1654,7 @@ Http::get('/v1/storage/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $range, Response $response, Database $dbForProject) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -1667,7 +1666,7 @@ Http::get('/v1/storage/usage') ]; $total = []; - $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), @@ -1721,7 +1720,7 @@ Http::get('/v1/storage/usage') ]), Response::MODEL_USAGE_STORAGE); }); -Http::get('/v1/storage/:bucketId/usage') +App::get('/v1/storage/:bucketId/usage') ->desc('Get bucket usage stats') ->groups(['api', 'storage']) ->label('scope', 'files.read') @@ -1735,8 +1734,7 @@ Http::get('/v1/storage/:bucketId/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $bucketId, string $range, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $bucketId, string $range, Response $response, Database $dbForProject) { $bucket = $dbForProject->getDocument('buckets', $bucketId); @@ -1752,7 +1750,8 @@ Http::get('/v1/storage/:bucketId/usage') str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE), ]; - $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { + + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) { foreach ($metrics as $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 16529d3dbd..f98cdd721c 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -1,7 +1,6 @@ desc('Create team') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].create') @@ -66,16 +65,15 @@ Http::post('/v1/teams') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - $isAppUser = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAppUser = Auth::isAppUser(Authorization::getRoles()); $teamId = $teamId == 'unique()' ? ID::unique() : $teamId; try { - $team = $authorization->skip(fn () => $dbForProject->createDocument('teams', new Document([ + $team = Authorization::skip(fn () => $dbForProject->createDocument('teams', new Document([ '$id' => $teamId, '$permissions' => [ Permission::read(Role::team($teamId)), @@ -134,7 +132,7 @@ Http::post('/v1/teams') ->dynamic($team, Response::MODEL_TEAM); }); -Http::get('/v1/teams') +App::get('/v1/teams') ->desc('List teams') ->groups(['api', 'teams']) ->label('scope', 'teams.read') @@ -192,7 +190,7 @@ Http::get('/v1/teams') ]), Response::MODEL_TEAM_LIST); }); -Http::get('/v1/teams/:teamId') +App::get('/v1/teams/:teamId') ->desc('Get team') ->groups(['api', 'teams']) ->label('scope', 'teams.read') @@ -219,7 +217,7 @@ Http::get('/v1/teams/:teamId') $response->dynamic($team, Response::MODEL_TEAM); }); -Http::get('/v1/teams/:teamId/prefs') +App::get('/v1/teams/:teamId/prefs') ->desc('Get team preferences') ->groups(['api', 'teams']) ->label('scope', 'teams.read') @@ -247,7 +245,7 @@ Http::get('/v1/teams/:teamId/prefs') $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); -Http::put('/v1/teams/:teamId') +App::put('/v1/teams/:teamId') ->desc('Update name') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].update') @@ -290,7 +288,7 @@ Http::put('/v1/teams/:teamId') $response->dynamic($team, Response::MODEL_TEAM); }); -Http::put('/v1/teams/:teamId/prefs') +App::put('/v1/teams/:teamId/prefs') ->desc('Update preferences') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].update.prefs') @@ -326,7 +324,7 @@ Http::put('/v1/teams/:teamId/prefs') $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); -Http::delete('/v1/teams/:teamId') +App::delete('/v1/teams/:teamId') ->desc('Delete team') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].delete') @@ -375,7 +373,7 @@ Http::delete('/v1/teams/:teamId') $response->noContent(); }); -Http::post('/v1/teams/:teamId/memberships') +App::post('/v1/teams/:teamId/memberships') ->desc('Create team membership') ->groups(['api', 'teams', 'auth']) ->label('event', 'teams.[teamId].memberships.[membershipId].create') @@ -407,10 +405,9 @@ Http::post('/v1/teams/:teamId/memberships') ->inject('queueForMails') ->inject('queueForMessaging') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, Authorization $authorization) { - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents) { + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); $url = htmlentities($url); if (empty($url)) { @@ -422,8 +419,8 @@ Http::post('/v1/teams/:teamId/memberships') if (empty($userId) && empty($email) && empty($phone)) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'At least one of userId, email, or phone is required'); } - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - $isAppUser = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAppUser = Auth::isAppUser(Authorization::getRoles()); if (!$isPrivilegedUser && !$isAppUser && empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED); @@ -483,7 +480,7 @@ Http::post('/v1/teams/:teamId/memberships') try { $userId = ID::unique(); - $invitee = $authorization->skip(fn () => $dbForProject->createDocument('users', new Document([ + $invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([ '$id' => $userId, '$permissions' => [ Permission::read(Role::any()), @@ -519,7 +516,7 @@ Http::post('/v1/teams/:teamId/memberships') } } - $isOwner = $authorization->isRole('team:' . $team->getId() . '/owner'); + $isOwner = Authorization::isRole('team:' . $team->getId() . '/owner'); if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server) throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to send invitations for this team'); @@ -551,12 +548,12 @@ Http::post('/v1/teams/:teamId/memberships') if ($isPrivilegedUser || $isAppUser) { // Allow admin to create membership try { - $membership = $authorization->skip(fn () => $dbForProject->createDocument('memberships', $membership)); + $membership = Authorization::skip(fn () => $dbForProject->createDocument('memberships', $membership)); } catch (Duplicate $th) { throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS); } - $authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); + Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); $dbForProject->purgeCachedDocument('users', $invitee->getId()); } else { @@ -578,7 +575,7 @@ Http::post('/v1/teams/:teamId/memberships') $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl'); $message - ->setParam('{{body}}', $body, escape: false) + ->setParam('{{body}}', $body, escapeHtml: false) ->setParam('{{hello}}', $locale->getText("emails.invitation.hello")) ->setParam('{{footer}}', $locale->getText("emails.invitation.footer")) ->setParam('{{thanks}}', $locale->getText("emails.invitation.thanks")) @@ -696,7 +693,7 @@ Http::post('/v1/teams/:teamId/memberships') ); }); -Http::get('/v1/teams/:teamId/memberships') +App::get('/v1/teams/:teamId/memberships') ->desc('List team memberships') ->groups(['api', 'teams']) ->label('scope', 'teams.read') @@ -799,7 +796,7 @@ Http::get('/v1/teams/:teamId/memberships') ]), Response::MODEL_MEMBERSHIP_LIST); }); -Http::get('/v1/teams/:teamId/memberships/:membershipId') +App::get('/v1/teams/:teamId/memberships/:membershipId') ->desc('Get team membership') ->groups(['api', 'teams']) ->label('scope', 'teams.read') @@ -855,7 +852,7 @@ Http::get('/v1/teams/:teamId/memberships/:membershipId') $response->dynamic($membership, Response::MODEL_MEMBERSHIP); }); -Http::patch('/v1/teams/:teamId/memberships/:membershipId') +App::patch('/v1/teams/:teamId/memberships/:membershipId') ->desc('Update membership') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].memberships.[membershipId].update') @@ -877,8 +874,7 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { @@ -895,9 +891,9 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId') throw new Exception(Exception::USER_NOT_FOUND); } - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - $isAppUser = Auth::isAppUser($authorization->getRoles()); - $isOwner = $authorization->isRole('team:' . $team->getId() . '/owner'); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAppUser = Auth::isAppUser(Authorization::getRoles()); + $isOwner = Authorization::isRole('team:' . $team->getId() . '/owner'); if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server) throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to modify roles'); @@ -928,7 +924,7 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId') ); }); -Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') +App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->desc('Update team membership status') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].memberships.[membershipId].update.status') @@ -954,9 +950,7 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->inject('project') ->inject('geodb') ->inject('queueForEvents') - ->inject('authorization') - ->inject('authentication') - ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Authorization $authorization, Authentication $authentication) { + ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents) { $protocol = $request->getProtocol(); $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -965,7 +959,7 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') throw new Exception(Exception::MEMBERSHIP_NOT_FOUND); } - $team = $authorization->skip(fn () => $dbForProject->getDocument('teams', $teamId)); + $team = Authorization::skip(fn () => $dbForProject->getDocument('teams', $teamId)); if ($team->isEmpty()) { throw new Exception(Exception::TEAM_NOT_FOUND); @@ -1000,11 +994,11 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->setAttribute('confirm', true) ; - $authorization->skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true))); + Authorization::skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true))); // Log user in - $authorization->addRole(Role::user($user->getId())->toString()); + Authorization::setRole(Role::user($user->getId())->toString()); $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); @@ -1034,13 +1028,13 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') $dbForProject->purgeCachedDocument('users', $user->getId()); - $authorization->addRole(Role::user($userId)->toString()); + Authorization::setRole(Role::user($userId)->toString()); $membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership); $dbForProject->purgeCachedDocument('users', $user->getId()); - $authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); + Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); $queueForEvents ->setParam('userId', $user->getId()) @@ -1050,13 +1044,13 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') if (!Config::getParam('domainVerification')) { $response - ->addHeader('X-Fallback-Cookies', \json_encode([$authentication->getCookieName() => Auth::encodeSession($user->getId(), $secret)])) + ->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])) ; } $response - ->addCookie($authentication->getCookieName() . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie($authentication->getCookieName(), Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ; $response->dynamic( @@ -1068,7 +1062,7 @@ Http::patch('/v1/teams/:teamId/memberships/:membershipId/status') ); }); -Http::delete('/v1/teams/:teamId/memberships/:membershipId') +App::delete('/v1/teams/:teamId/memberships/:membershipId') ->desc('Delete team membership') ->groups(['api', 'teams']) ->label('event', 'teams.[teamId].memberships.[membershipId].delete') @@ -1086,8 +1080,7 @@ Http::delete('/v1/teams/:teamId/memberships/:membershipId') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('authorization') - ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents) { $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -1122,7 +1115,7 @@ Http::delete('/v1/teams/:teamId/memberships/:membershipId') $dbForProject->purgeCachedDocument('users', $user->getId()); if ($membership->getAttribute('confirm')) { // Count only confirmed members - $authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0)); + Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0)); } $queueForEvents @@ -1135,7 +1128,7 @@ Http::delete('/v1/teams/:teamId/memberships/:membershipId') $response->noContent(); }); -Http::get('/v1/teams/:teamId/logs') +App::get('/v1/teams/:teamId/logs') ->desc('List team logs') ->groups(['api', 'teams']) ->label('scope', 'teams.read') @@ -1152,8 +1145,7 @@ Http::get('/v1/teams/:teamId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('authorization') - ->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { + ->action(function (string $teamId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { $team = $dbForProject->getDocument('teams', $teamId); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index d3ac1b75e5..081e6a85bd 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -22,6 +22,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Users; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use MaxMind\Db\Reader; +use Utopia\App; use Utopia\Audit\Audit; use Utopia\Config\Config; use Utopia\Database\Database; @@ -38,16 +39,15 @@ use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; -use Utopia\Http\Http; -use Utopia\Http\Validator\ArrayList; -use Utopia\Http\Validator\Assoc; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\Integer; -use Utopia\Http\Validator\Range; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; use Utopia\Locale\Locale; use Utopia\System\System; +use Utopia\Validator\ArrayList; +use Utopia\Validator\Assoc; +use Utopia\Validator\Boolean; +use Utopia\Validator\Integer; +use Utopia\Validator\Range; +use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; /** TODO: Remove function when we move to using utopia/platform */ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks): Document @@ -180,7 +180,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e return $user; } -Http::post('/v1/users') +App::post('/v1/users') ->desc('Create user') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -211,7 +211,7 @@ Http::post('/v1/users') ->dynamic($user, Response::MODEL_USER); }); -Http::post('/v1/users/bcrypt') +App::post('/v1/users/bcrypt') ->desc('Create user with bcrypt password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -242,7 +242,7 @@ Http::post('/v1/users/bcrypt') ->dynamic($user, Response::MODEL_USER); }); -Http::post('/v1/users/md5') +App::post('/v1/users/md5') ->desc('Create user with MD5 password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -273,7 +273,7 @@ Http::post('/v1/users/md5') ->dynamic($user, Response::MODEL_USER); }); -Http::post('/v1/users/argon2') +App::post('/v1/users/argon2') ->desc('Create user with Argon2 password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -304,7 +304,7 @@ Http::post('/v1/users/argon2') ->dynamic($user, Response::MODEL_USER); }); -Http::post('/v1/users/sha') +App::post('/v1/users/sha') ->desc('Create user with SHA password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -342,7 +342,7 @@ Http::post('/v1/users/sha') ->dynamic($user, Response::MODEL_USER); }); -Http::post('/v1/users/phpass') +App::post('/v1/users/phpass') ->desc('Create user with PHPass password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -373,7 +373,7 @@ Http::post('/v1/users/phpass') ->dynamic($user, Response::MODEL_USER); }); -Http::post('/v1/users/scrypt') +App::post('/v1/users/scrypt') ->desc('Create user with Scrypt password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -417,7 +417,7 @@ Http::post('/v1/users/scrypt') ->dynamic($user, Response::MODEL_USER); }); -Http::post('/v1/users/scrypt-modified') +App::post('/v1/users/scrypt-modified') ->desc('Create user with Scrypt modified password') ->groups(['api', 'users']) ->label('event', 'users.[userId].create') @@ -451,7 +451,7 @@ Http::post('/v1/users/scrypt-modified') ->dynamic($user, Response::MODEL_USER); }); -Http::post('/v1/users/:userId/targets') +App::post('/v1/users/:userId/targets') ->desc('Create User Target') ->groups(['api', 'users']) ->label('audits.event', 'target.create') @@ -540,7 +540,7 @@ Http::post('/v1/users/:userId/targets') ->dynamic($target, Response::MODEL_TARGET); }); -Http::get('/v1/users') +App::get('/v1/users') ->desc('List users') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -594,7 +594,7 @@ Http::get('/v1/users') ]), Response::MODEL_USER_LIST); }); -Http::get('/v1/users/:userId') +App::get('/v1/users/:userId') ->desc('Get user') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -619,7 +619,7 @@ Http::get('/v1/users/:userId') $response->dynamic($user, Response::MODEL_USER); }); -Http::get('/v1/users/:userId/prefs') +App::get('/v1/users/:userId/prefs') ->desc('Get user preferences') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -646,7 +646,7 @@ Http::get('/v1/users/:userId/prefs') $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); -Http::get('/v1/users/:userId/targets/:targetId') +App::get('/v1/users/:userId/targets/:targetId') ->desc('Get User Target') ->groups(['api', 'users']) ->label('scope', 'targets.read') @@ -678,7 +678,7 @@ Http::get('/v1/users/:userId/targets/:targetId') $response->dynamic($target, Response::MODEL_TARGET); }); -Http::get('/v1/users/:userId/sessions') +App::get('/v1/users/:userId/sessions') ->desc('List user sessions') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -719,7 +719,7 @@ Http::get('/v1/users/:userId/sessions') ]), Response::MODEL_SESSION_LIST); }); -Http::get('/v1/users/:userId/memberships') +App::get('/v1/users/:userId/memberships') ->desc('List user memberships') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -758,7 +758,7 @@ Http::get('/v1/users/:userId/memberships') ]), Response::MODEL_MEMBERSHIP_LIST); }); -Http::get('/v1/users/:userId/logs') +App::get('/v1/users/:userId/logs') ->desc('List user logs') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -775,8 +775,7 @@ Http::get('/v1/users/:userId/logs') ->inject('dbForProject') ->inject('locale') ->inject('geodb') - ->inject('authorization') - ->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization) { + ->action(function (string $userId, array $queries, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) { $user = $dbForProject->getDocument('users', $userId); @@ -848,7 +847,7 @@ Http::get('/v1/users/:userId/logs') ]), Response::MODEL_LOG_LIST); }); -Http::get('/v1/users/:userId/targets') +App::get('/v1/users/:userId/targets') ->desc('List User Targets') ->groups(['api', 'users']) ->label('scope', 'targets.read') @@ -903,7 +902,7 @@ Http::get('/v1/users/:userId/targets') ]), Response::MODEL_TARGET_LIST); }); -Http::get('/v1/users/identities') +App::get('/v1/users/identities') ->desc('List Identities') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -957,7 +956,7 @@ Http::get('/v1/users/identities') ]), Response::MODEL_IDENTITY_LIST); }); -Http::patch('/v1/users/:userId/status') +App::patch('/v1/users/:userId/status') ->desc('Update user status') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.status') @@ -993,7 +992,7 @@ Http::patch('/v1/users/:userId/status') $response->dynamic($user, Response::MODEL_USER); }); -Http::put('/v1/users/:userId/labels') +App::put('/v1/users/:userId/labels') ->desc('Update user labels') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.labels') @@ -1030,7 +1029,7 @@ Http::put('/v1/users/:userId/labels') $response->dynamic($user, Response::MODEL_USER); }); -Http::patch('/v1/users/:userId/verification/phone') +App::patch('/v1/users/:userId/verification/phone') ->desc('Update phone verification') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.verification') @@ -1065,7 +1064,7 @@ Http::patch('/v1/users/:userId/verification/phone') $response->dynamic($user, Response::MODEL_USER); }); -Http::patch('/v1/users/:userId/name') +App::patch('/v1/users/:userId/name') ->desc('Update name') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.name') @@ -1102,7 +1101,7 @@ Http::patch('/v1/users/:userId/name') $response->dynamic($user, Response::MODEL_USER); }); -Http::patch('/v1/users/:userId/password') +App::patch('/v1/users/:userId/password') ->desc('Update password') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.password') @@ -1179,7 +1178,7 @@ Http::patch('/v1/users/:userId/password') $response->dynamic($user, Response::MODEL_USER); }); -Http::patch('/v1/users/:userId/email') +App::patch('/v1/users/:userId/email') ->desc('Update email') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.email') @@ -1274,7 +1273,7 @@ Http::patch('/v1/users/:userId/email') $response->dynamic($user, Response::MODEL_USER); }); -Http::patch('/v1/users/:userId/phone') +App::patch('/v1/users/:userId/phone') ->desc('Update phone') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.phone') @@ -1357,7 +1356,7 @@ Http::patch('/v1/users/:userId/phone') $response->dynamic($user, Response::MODEL_USER); }); -Http::patch('/v1/users/:userId/verification') +App::patch('/v1/users/:userId/verification') ->desc('Update email verification') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.verification') @@ -1392,7 +1391,7 @@ Http::patch('/v1/users/:userId/verification') $response->dynamic($user, Response::MODEL_USER); }); -Http::patch('/v1/users/:userId/prefs') +App::patch('/v1/users/:userId/prefs') ->desc('Update user preferences') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.prefs') @@ -1425,7 +1424,7 @@ Http::patch('/v1/users/:userId/prefs') $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); -Http::patch('/v1/users/:userId/targets/:targetId') +App::patch('/v1/users/:userId/targets/:targetId') ->desc('Update User target') ->groups(['api', 'users']) ->label('audits.event', 'target.update') @@ -1519,7 +1518,7 @@ Http::patch('/v1/users/:userId/targets/:targetId') ->dynamic($target, Response::MODEL_TARGET); }); -Http::patch('/v1/users/:userId/mfa') +App::patch('/v1/users/:userId/mfa') ->desc('Update MFA') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.mfa') @@ -1557,7 +1556,7 @@ Http::patch('/v1/users/:userId/mfa') $response->dynamic($user, Response::MODEL_USER); }); -Http::get('/v1/users/:userId/mfa/factors') +App::get('/v1/users/:userId/mfa/factors') ->desc('List Factors') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -1590,7 +1589,7 @@ Http::get('/v1/users/:userId/mfa/factors') $response->dynamic($factors, Response::MODEL_MFA_FACTORS); }); -Http::get('/v1/users/:userId/mfa/recovery-codes') +App::get('/v1/users/:userId/mfa/recovery-codes') ->desc('Get MFA Recovery Codes') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -1625,7 +1624,7 @@ Http::get('/v1/users/:userId/mfa/recovery-codes') $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); }); -Http::patch('/v1/users/:userId/mfa/recovery-codes') +App::patch('/v1/users/:userId/mfa/recovery-codes') ->desc('Create MFA Recovery Codes') ->groups(['api', 'users']) ->label('event', 'users.[userId].create.mfa.recovery-codes') @@ -1671,7 +1670,7 @@ Http::patch('/v1/users/:userId/mfa/recovery-codes') $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); }); -Http::put('/v1/users/:userId/mfa/recovery-codes') +App::put('/v1/users/:userId/mfa/recovery-codes') ->desc('Regenerate MFA Recovery Codes') ->groups(['api', 'users']) ->label('event', 'users.[userId].update.mfa.recovery-codes') @@ -1716,7 +1715,7 @@ Http::put('/v1/users/:userId/mfa/recovery-codes') $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); }); -Http::delete('/v1/users/:userId/mfa/authenticators/:type') +App::delete('/v1/users/:userId/mfa/authenticators/:type') ->desc('Delete Authenticator') ->groups(['api', 'users']) ->label('event', 'users.[userId].delete.mfa') @@ -1758,7 +1757,7 @@ Http::delete('/v1/users/:userId/mfa/authenticators/:type') $response->noContent(); }); -Http::post('/v1/users/:userId/sessions') +App::post('/v1/users/:userId/sessions') ->desc('Create session') ->groups(['api', 'users']) ->label('event', 'users.[userId].sessions.[sessionId].create') @@ -1828,7 +1827,7 @@ Http::post('/v1/users/:userId/sessions') ->dynamic($session, Response::MODEL_SESSION); }); -Http::post('/v1/users/:userId/tokens') +App::post('/v1/users/:userId/tokens') ->desc('Create token') ->groups(['api', 'users']) ->label('event', 'users.[userId].tokens.[tokenId].create') @@ -1885,7 +1884,7 @@ Http::post('/v1/users/:userId/tokens') ->dynamic($token, Response::MODEL_TOKEN); }); -Http::delete('/v1/users/:userId/sessions/:sessionId') +App::delete('/v1/users/:userId/sessions/:sessionId') ->desc('Delete user session') ->groups(['api', 'users']) ->label('event', 'users.[userId].sessions.[sessionId].delete') @@ -1928,7 +1927,7 @@ Http::delete('/v1/users/:userId/sessions/:sessionId') $response->noContent(); }); -Http::delete('/v1/users/:userId/sessions') +App::delete('/v1/users/:userId/sessions') ->desc('Delete user sessions') ->groups(['api', 'users']) ->label('event', 'users.[userId].sessions.delete') @@ -1970,7 +1969,7 @@ Http::delete('/v1/users/:userId/sessions') $response->noContent(); }); -Http::delete('/v1/users/:userId') +App::delete('/v1/users/:userId') ->desc('Delete user') ->groups(['api', 'users']) ->label('event', 'users.[userId].delete') @@ -2012,7 +2011,7 @@ Http::delete('/v1/users/:userId') $response->noContent(); }); -Http::delete('/v1/users/:userId/targets/:targetId') +App::delete('/v1/users/:userId/targets/:targetId') ->desc('Delete user target') ->groups(['api', 'users']) ->label('audits.event', 'target.delete') @@ -2063,7 +2062,7 @@ Http::delete('/v1/users/:userId/targets/:targetId') $response->noContent(); }); -Http::delete('/v1/users/identities/:identityId') +App::delete('/v1/users/identities/:identityId') ->desc('Delete identity') ->groups(['api', 'users']) ->label('event', 'users.[userId].identities.[identityId].delete') @@ -2098,7 +2097,7 @@ Http::delete('/v1/users/identities/:identityId') return $response->noContent(); }); -Http::post('/v1/users/:userId/jwts') +App::post('/v1/users/:userId/jwts') ->desc('Create user JWT') ->groups(['api', 'users']) ->label('scope', 'users.write') @@ -2148,7 +2147,7 @@ Http::post('/v1/users/:userId/jwts') ])]), Response::MODEL_JWT); }); -Http::get('/v1/users/usage') +App::get('/v1/users/usage') ->desc('Get users usage stats') ->groups(['api', 'users']) ->label('scope', 'users.read') @@ -2161,8 +2160,8 @@ Http::get('/v1/users/usage') ->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->inject('authorization') - ->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) { + ->inject('register') + ->action(function (string $range, Response $response, Database $dbForProject) { $periods = Config::getParam('usage', []); $stats = $usage = []; @@ -2172,7 +2171,7 @@ Http::get('/v1/users/usage') METRIC_SESSIONS, ]; - $authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) { + Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { foreach ($metrics as $count => $metric) { $result = $dbForProject->findOne('stats', [ Query::equal('metric', [$metric]), diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 987d84bb7e..9610f44ace 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -8,6 +8,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Installations; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Appwrite\Vcs\Comment; +use Utopia\App; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -31,17 +32,17 @@ use Utopia\Detector\Adapter\Python; use Utopia\Detector\Adapter\Ruby; use Utopia\Detector\Adapter\Swift; use Utopia\Detector\Detector; -use Utopia\Http\Http; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\Host; -use Utopia\Http\Validator\Text; use Utopia\System\System; +use Utopia\Validator\Boolean; +use Utopia\Validator\Host; +use Utopia\Validator\Text; use Utopia\VCS\Adapter\Git\GitHub; use Utopia\VCS\Exception\RepositoryNotFound; use function Swoole\Coroutine\batch; -$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForConsole, Build $queueForBuilds, callable $getProjectDB, Request $request, Authorization $auth) { +$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForConsole, Build $queueForBuilds, callable $getProjectDB, Request $request) { + $errors = []; foreach ($repositories as $resource) { try { $resourceType = $resource->getAttribute('resourceType'); @@ -51,11 +52,11 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId } $projectId = $resource->getAttribute('projectId'); - $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); $dbForProject = $getProjectDB($project); $functionId = $resource->getAttribute('resourceId'); - $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); $functionInternalId = $function->getInternalId(); $deploymentId = ID::unique(); @@ -101,8 +102,8 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $latestCommentId = ''; - if (!empty($providerPullRequestId) && $function->getAttribute('providerSilentMode', false)) { - $latestComment = $auth->skip(fn () => $dbForConsole->findOne('vcsComments', [ + if (!empty($providerPullRequestId) && $function->getAttribute('providerSilentMode', false) === false) { + $latestComment = Authorization::skip(fn () => $dbForConsole->findOne('vcsComments', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::equal('providerPullRequestId', [$providerPullRequestId]), Query::orderDesc('$createdAt'), @@ -123,7 +124,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId if (!empty($latestCommentId)) { $teamId = $project->getAttribute('teamId', ''); - $latestComment = $auth->skip(fn () => $dbForConsole->createDocument('vcsComments', new Document([ + $latestComment = Authorization::skip(fn () => $dbForConsole->createDocument('vcsComments', new Document([ '$id' => ID::unique(), '$permissions' => [ Permission::read(Role::team(ID::custom($teamId))), @@ -144,7 +145,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId } } } elseif (!empty($providerBranch)) { - $latestComments = $auth->skip(fn () => $dbForConsole->find('vcsComments', [ + $latestComments = Authorization::skip(fn () => $dbForConsole->find('vcsComments', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::equal('providerBranch', [$providerBranch]), Query::orderDesc('$createdAt'), @@ -261,7 +262,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId } }; -Http::get('/v1/vcs/github/authorize') +App::get('/v1/vcs/github/authorize') ->desc('Install GitHub App') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -303,7 +304,7 @@ Http::get('/v1/vcs/github/authorize') ->redirect($url); }); -Http::get('/v1/vcs/github/callback') +App::get('/v1/vcs/github/callback') ->desc('Capture installation and authorization from GitHub App') ->groups(['api', 'vcs']) ->label('scope', 'public') @@ -463,7 +464,7 @@ Http::get('/v1/vcs/github/callback') ->redirect($redirectSuccess); }); -Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/contents') +App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/contents') ->desc('Get files and directories of a VCS repository') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -524,7 +525,7 @@ Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pr ]), Response::MODEL_VCS_CONTENT_LIST); }); -Http::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection') +App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection') ->desc('Detect runtime settings from source code') ->groups(['api', 'vcs']) ->label('scope', 'vcs.write') @@ -596,7 +597,7 @@ Http::post('/v1/vcs/github/installations/:installationId/providerRepositories/:p $response->dynamic(new Document($detection), Response::MODEL_DETECTION); }); -Http::get('/v1/vcs/github/installations/:installationId/providerRepositories') +App::get('/v1/vcs/github/installations/:installationId/providerRepositories') ->desc('List Repositories') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -691,7 +692,7 @@ Http::get('/v1/vcs/github/installations/:installationId/providerRepositories') ]), Response::MODEL_PROVIDER_REPOSITORY_LIST); }); -Http::post('/v1/vcs/github/installations/:installationId/providerRepositories') +App::post('/v1/vcs/github/installations/:installationId/providerRepositories') ->desc('Create repository') ->groups(['api', 'vcs']) ->label('scope', 'vcs.write') @@ -792,7 +793,7 @@ Http::post('/v1/vcs/github/installations/:installationId/providerRepositories') $response->dynamic(new Document($repository), Response::MODEL_PROVIDER_REPOSITORY); }); -Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId') +App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId') ->desc('Get repository') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -841,7 +842,7 @@ Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pr $response->dynamic(new Document($repository), Response::MODEL_PROVIDER_REPOSITORY); }); -Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/branches') +App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/branches') ->desc('List Repository Branches') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -890,7 +891,7 @@ Http::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pr ]), Response::MODEL_BRANCH_LIST); }); -Http::post('/v1/vcs/github/events') +App::post('/v1/vcs/github/events') ->desc('Create Event') ->groups(['api', 'vcs']) ->label('scope', 'public') @@ -900,9 +901,8 @@ Http::post('/v1/vcs/github/events') ->inject('dbForConsole') ->inject('getProjectDB') ->inject('queueForBuilds') - ->inject('auth') ->action( - function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $auth) use ($createGitDeployments) { + function (GitHub $github, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) { $payload = $request->getRawPayload(); $signatureRemote = $request->getHeader('x-hub-signature-256', ''); $signatureLocal = System::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', ''); @@ -936,14 +936,14 @@ Http::post('/v1/vcs/github/events') $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); //find functionId from functions table - $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ + $repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::limit(100), ])); // create new deployment only on push and not when branch is created if (!$providerBranchCreated) { - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $auth); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForConsole, $queueForBuilds, $getProjectDB, $request); } } elseif ($event == $github::EVENT_INSTALLATION) { if ($parsedPayload["action"] == "deleted") { @@ -956,13 +956,13 @@ Http::post('/v1/vcs/github/events') ]); foreach ($installations as $installation) { - $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ + $repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [ Query::equal('installationInternalId', [$installation->getInternalId()]), Query::limit(1000) ])); foreach ($repositories as $repository) { - $auth->skip(fn () => $dbForConsole->deleteDocument('repositories', $repository->getId())); + Authorization::skip(fn () => $dbForConsole->deleteDocument('repositories', $repository->getId())); } $dbForConsole->deleteDocument('installations', $installation->getId()); @@ -994,12 +994,12 @@ Http::post('/v1/vcs/github/events') $providerCommitAuthor = $commitDetails["commitAuthor"] ?? ''; $providerCommitMessage = $commitDetails["commitMessage"] ?? ''; - $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ + $repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::orderDesc('$createdAt') ])); - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $auth); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForConsole, $queueForBuilds, $getProjectDB, $request); } elseif ($parsedPayload["action"] == "closed") { // Allowed external contributions cleanup @@ -1008,7 +1008,7 @@ Http::post('/v1/vcs/github/events') $external = $parsedPayload["external"] ?? true; if ($external) { - $repositories = $auth->skip(fn () => $dbForConsole->find('repositories', [ + $repositories = Authorization::skip(fn () => $dbForConsole->find('repositories', [ Query::equal('providerRepositoryId', [$providerRepositoryId]), Query::orderDesc('$createdAt') ])); @@ -1019,7 +1019,7 @@ Http::post('/v1/vcs/github/events') if (\in_array($providerPullRequestId, $providerPullRequestIds)) { $providerPullRequestIds = \array_diff($providerPullRequestIds, [$providerPullRequestId]); $repository = $repository->setAttribute('providerPullRequestIds', $providerPullRequestIds); - $repository = $auth->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); + $repository = Authorization::skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); } } } @@ -1030,7 +1030,7 @@ Http::post('/v1/vcs/github/events') } ); -Http::get('/v1/vcs/installations') +App::get('/v1/vcs/installations') ->desc('List installations') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -1090,7 +1090,7 @@ Http::get('/v1/vcs/installations') ]), Response::MODEL_INSTALLATION_LIST); }); -Http::get('/v1/vcs/installations/:installationId') +App::get('/v1/vcs/installations/:installationId') ->desc('Get installation') ->groups(['api', 'vcs']) ->label('scope', 'vcs.read') @@ -1119,7 +1119,7 @@ Http::get('/v1/vcs/installations/:installationId') $response->dynamic($installation, Response::MODEL_INSTALLATION); }); -Http::delete('/v1/vcs/installations/:installationId') +App::delete('/v1/vcs/installations/:installationId') ->desc('Delete Installation') ->groups(['api', 'vcs']) ->label('scope', 'vcs.write') @@ -1152,7 +1152,7 @@ Http::delete('/v1/vcs/installations/:installationId') $response->noContent(); }); -Http::patch('/v1/vcs/github/installations/:installationId/repositories/:repositoryId') +App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositoryId') ->desc('Authorize external deployment') ->groups(['api', 'vcs']) ->label('scope', 'vcs.write') @@ -1172,15 +1172,14 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito ->inject('dbForConsole') ->inject('getProjectDB') ->inject('queueForBuilds') - ->inject('auth') - ->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds, Authorization $auth) use ($createGitDeployments) { + ->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForConsole, callable $getProjectDB, Build $queueForBuilds) use ($createGitDeployments) { $installation = $dbForConsole->getDocument('installations', $installationId); if ($installation->isEmpty()) { throw new Exception(Exception::INSTALLATION_NOT_FOUND); } - $repository = $auth->skip(fn () => $dbForConsole->getDocument('repositories', $repositoryId, [ + $repository = Authorization::skip(fn () => $dbForConsole->getDocument('repositories', $repositoryId, [ Query::equal('projectInternalId', [$project->getInternalId()]) ])); @@ -1197,7 +1196,7 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito // TODO: Delete from array when PR is closed - $repository = $auth->skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); + $repository = Authorization::skip(fn () => $dbForConsole->updateDocument('repositories', $repository->getId(), $repository)); $privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY'); $githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID'); @@ -1221,7 +1220,7 @@ Http::patch('/v1/vcs/github/installations/:installationId/repositories/:reposito $providerBranch = \explode(':', $pullRequestResponse['head']['label'])[1] ?? ''; $providerCommitHash = $pullRequestResponse['head']['sha'] ?? ''; - $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForConsole, $queueForBuilds, $getProjectDB, $request, $auth); + $createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerCommitHash, $providerPullRequestId, true, $dbForConsole, $queueForBuilds, $getProjectDB, $request); $response->noContent(); }); diff --git a/app/controllers/general.php b/app/controllers/general.php index fb258182d3..0bbfa2b694 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1,5 +1,7 @@ label('error', __DIR__ . '/../views/general/error.phtml'); + $utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml'); $host = $request->getHostname() ?? ''; - $rule = $auth->skip( + $route = Authorization::skip( fn () => $dbForConsole->find('rules', [ Query::equal('domain', [$host]), Query::limit(1) ]) )[0] ?? null; - if ($rule === null) { + if ($route === null) { if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) { throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.'); } @@ -72,12 +73,12 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request } // Act as API - no Proxy logic - $route?->label('error', ''); + $utopia->getRoute()?->label('error', ''); return false; } - $projectId = $rule->getAttribute('projectId'); - $project = $auth->skip( + $projectId = $route->getAttribute('projectId'); + $project = Authorization::skip( fn () => $dbForConsole->getDocument('projects', $projectId) ); if (array_key_exists('proxy', $project->getAttribute('services', []))) { @@ -88,16 +89,16 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request } // Skip Appwrite Router for ACME challenge. Nessessary for certificate generation - $path = ($request->getURI() ?? '/'); + $path = ($swooleRequest->server['request_uri'] ?? '/'); if (\str_starts_with($path, '/.well-known/acme-challenge')) { return false; } - $type = $rule->getAttribute('resourceType'); + $type = $route->getAttribute('resourceType'); if ($type === 'function') { - $route->label('sdk.namespace', 'functions'); - $route->label('sdk.method', 'createExecution'); + $utopia->getRoute()?->label('sdk.namespace', 'functions'); + $utopia->getRoute()?->label('sdk.method', 'createExecution'); if (System::getEnv('_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS if ($request->getProtocol() !== 'https') { @@ -109,25 +110,26 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request } } - $functionId = $rule->getAttribute('resourceId'); - $projectId = $rule->getAttribute('projectId'); + $functionId = $route->getAttribute('resourceId'); + $projectId = $route->getAttribute('projectId'); - $path = ($request->getURI() ?? '/'); - $query = ($request->getQueryString() ?? ''); + $path = ($swooleRequest->server['request_uri'] ?? '/'); + $query = ($swooleRequest->server['query_string'] ?? ''); if (!empty($query)) { $path .= '?' . $query; } - $body = $request->getRawPayload() ?? ''; - $method = $request->getMethod(); + + $body = $swooleRequest->getContent() ?? ''; + $method = $swooleRequest->server['request_method']; $requestHeaders = $request->getHeaders(); - $project = $auth->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); $dbForProject = $getProjectDB($project); - $function = $auth->skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty() || !$function->getAttribute('enabled')) { throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND); @@ -143,7 +145,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } - $deployment = $auth->skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); + $deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); if ($deployment->getAttribute('resourceId') !== $function->getId()) { throw new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); @@ -154,7 +156,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request } /** Check if build has completed */ - $build = $auth->skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); + $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); if ($build->isEmpty()) { throw new AppwriteException(AppwriteException::BUILD_NOT_FOUND); } @@ -215,7 +217,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request 'deploymentInternalId' => $deployment->getInternalId(), 'deploymentId' => $deployment->getId(), 'trigger' => 'http', // http / schedule / event - 'status' => 'processing', // waiting / processing / completed / failed + 'status' => 'processing', // waiting / processing / completed / failed 'responseStatusCode' => 0, 'responseHeaders' => [], 'requestPath' => $path, @@ -311,6 +313,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request cpus: $spec['cpus'] ?? APP_FUNCTION_CPUS_DEFAULT, memory: $spec['memory'] ?? APP_FUNCTION_MEMORY_DEFAULT, logging: $function->getAttribute('logging', true), + requestTimeout: 30 ); $headersFiltered = []; @@ -328,6 +331,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request $execution->setAttribute('logs', $executionResponse['logs']); $execution->setAttribute('errors', $executionResponse['errors']); $execution->setAttribute('duration', $executionResponse['duration']); + } catch (\Throwable $th) { $durationEnd = \microtime(true); @@ -362,8 +366,7 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request ->trigger() ; - /** @var Document $execution */ - $execution = $auth->skip(fn () => $dbForProject->createDocument('executions', $execution)); + $execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution)); } $execution->setAttribute('logs', ''); @@ -395,15 +398,18 @@ function router(Database $dbForConsole, callable $getProjectDB, Request $request return true; } elseif ($type === 'api') { - $route?->label('error', ''); + $utopia->getRoute()?->label('error', ''); return false; } else { throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown resource type ' . $type); } + + $utopia->getRoute()?->label('error', ''); + return false; } /* -Http::init() +App::init() ->groups(['api']) ->inject('project') ->inject('mode') @@ -414,7 +420,7 @@ Http::init() }); */ -Http::init() +App::init() ->groups(['database', 'functions', 'storage', 'messaging']) ->inject('project') ->inject('request') @@ -427,11 +433,12 @@ Http::init() } }); -Http::init() +App::init() ->groups(['api', 'web']) + ->inject('utopia') + ->inject('swooleRequest') ->inject('request') ->inject('response') - ->inject('route') ->inject('console') ->inject('project') ->inject('dbForConsole') @@ -443,27 +450,7 @@ Http::init() ->inject('queueForUsage') ->inject('queueForEvents') ->inject('queueForCertificates') - ->inject('authorization') - ->action(function ( - Request $request, - Response $response, - Route $route, - Document $console, - Document $project, - Database $dbForConsole, - $getProjectDB, - Locale $locale, - array $localeCodes, - array $clients, - /** - * @disregard P1009 Undefined type - */ - Reader $geodb, - Usage $queueForUsage, - Event $queueForEvents, - Certificate $queueForCertificates, - Authorization $authorization - ) { + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates) { /* * Appwrite Router */ @@ -471,7 +458,7 @@ Http::init() $mainDomain = System::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain if ($host !== $mainDomain) { - if (router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization)) { + if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb)) { return; } } @@ -479,8 +466,8 @@ Http::init() /* * Request format */ - //$route = $utopia->getRoute(); - //Request::setRoute($route); + $route = $utopia->getRoute(); + Request::setRoute($route); if ($route === null) { return $response @@ -512,7 +499,7 @@ Http::init() } elseif (str_starts_with($request->getURI(), '/.well-known/acme-challenge')) { Console::warning('Skipping SSL certificates generation on ACME challenge.'); } else { - $authorization->disable(); + Authorization::disable(); $envDomain = System::getEnv('_APP_DOMAIN', ''); $mainDomain = null; @@ -551,12 +538,12 @@ Http::init() } $domains[$domain->get()] = true; - $authorization->reset(); // ensure authorization is re-enabled + Authorization::reset(); // ensure authorization is re-enabled } Config::setParam('domains', $domains); } - $localeParam = (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', '')); + $localeParam = (string) $request->getParam('locale', $request->getHeader('x-appwrite-locale', '')); if (\in_array($localeParam, $localeCodes)) { $locale->setDefault($localeParam); } @@ -584,7 +571,7 @@ Http::init() Config::setParam( 'domainVerification', ($selfDomain->getRegisterable() === $endDomain->getRegisterable()) && - $endDomain->getRegisterable() !== '' + $endDomain->getRegisterable() !== '' ); $isLocalHost = $request->getHostname() === 'localhost' || $request->getHostname() === 'localhost:' . $request->getPort(); @@ -599,8 +586,8 @@ Http::init() ? null : ( $isConsoleProject && $isConsoleRootSession - ? '.' . $selfDomain->getRegisterable() - : '.' . $request->getHostname() + ? '.' . $selfDomain->getRegisterable() + : '.' . $request->getHostname() ) ); @@ -619,7 +606,7 @@ Http::init() $response->addFilter(new ResponseV18()); } if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) { - $response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is " . APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks"); + $response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is ". APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks"); } } @@ -630,9 +617,7 @@ Http::init() * @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers */ if (System::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS - if ($request->getProtocol() !== 'https' // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations - && ($request->getHeader('host') ?? '') !== 'localhost' - && ($request->getHeader('host') ?? '') !== APP_HOSTNAME_INTERNAL) { + if ($request->getProtocol() !== 'https' && ($swooleRequest->header['host'] ?? '') !== 'localhost' && ($swooleRequest->header['host'] ?? '') !== APP_HOSTNAME_INTERNAL) { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations if ($request->getMethod() !== Request::METHOD_GET) { throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.'); } @@ -672,8 +657,9 @@ Http::init() } }); -Http::options() - ->inject('route') +App::options() + ->inject('utopia') + ->inject('swooleRequest') ->inject('request') ->inject('response') ->inject('dbForConsole') @@ -681,8 +667,7 @@ Http::options() ->inject('queueForEvents') ->inject('queueForUsage') ->inject('geodb') - ->inject('authorization') - ->action(function (Route $route, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Authorization $authorization) { + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) { /* * Appwrite Router */ @@ -690,7 +675,7 @@ Http::options() $mainDomain = System::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain if ($host !== $mainDomain) { - if (router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization)) { + if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb)) { return; } } @@ -707,25 +692,18 @@ Http::options() ->noContent(); }); -Http::error() +App::error() ->inject('error') - ->inject('user') - ->inject('route') + ->inject('utopia') ->inject('request') ->inject('response') ->inject('project') ->inject('logger') ->inject('log') - ->inject('authorization') - ->inject('connections') ->inject('queueForUsage') - ->action(function (Throwable $error, Document $user, ?Route $route, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Authorization $authorization, Connections $connections, Usage $queueForUsage) { + ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); - - if (is_null($route)) { - $route = new Route($request->getMethod(), $request->getURI()); - } - + $route = $utopia->getRoute(); $class = \get_class($error); $code = $error->getCode(); $message = $error->getMessage(); @@ -746,9 +724,9 @@ Http::error() Console::error('[Error] File: ' . $file); Console::error('[Error] Line: ' . $line); } + switch ($class) { - case 'Utopia\Servers\Exception': - case 'Utopia\Http\Exception': + case 'Utopia\Exception': $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); switch ($code) { case 400: @@ -793,36 +771,35 @@ Http::error() } else { $publish = $error->getCode() === 0 || $error->getCode() >= 500; } + if ($error->getCode() >= 400 && $error->getCode() < 500) { // Register error logger $providerName = System::getEnv('_APP_EXPERIMENT_LOGGING_PROVIDER', ''); $providerConfig = System::getEnv('_APP_EXPERIMENT_LOGGING_CONFIG', ''); - if (!(empty($providerName) || empty($providerConfig))) { - try { - $loggingProvider = new DSN($providerConfig); - $providerName = $loggingProvider->getScheme(); + try { + $loggingProvider = new DSN($providerConfig ?? ''); + $providerName = $loggingProvider->getScheme(); - if (!empty($providerName) && $providerName === 'sentry') { - $key = $loggingProvider->getPassword(); - $projectId = $loggingProvider->getUser() ?? ''; - $host = 'https://' . $loggingProvider->getHost(); + if (!empty($providerName) && $providerName === 'sentry') { + $key = $loggingProvider->getPassword(); + $projectId = $loggingProvider->getUser() ?? ''; + $host = 'https://' . $loggingProvider->getHost(); - $adapter = new Sentry($projectId, $key, $host); - $logger = new Logger($adapter); - $logger->setSample(0.04); - $publish = true; - } else { - throw new \Exception('Invalid experimental logging provider'); - } - } catch (\Throwable $th) { - Console::warning('Failed to initialize logging provider: ' . $th->getMessage()); + $adapter = new Sentry($projectId, $key, $host); + $logger = new Logger($adapter); + $logger->setSample(0.04); + $publish = true; + } else { + throw new \Exception('Invalid experimental logging provider'); } + } catch (\Throwable $th) { + Console::warning('Failed to initialize logging provider: ' . $th->getMessage()); } } if ($publish && $project->getId() !== 'console') { - if (!Auth::isPrivilegedUser($authorization->getRoles())) { + if (!Auth::isPrivilegedUser(Authorization::getRoles())) { $fileSize = 0; $file = $request->getFiles('file'); if (!empty($file)) { @@ -841,7 +818,14 @@ Http::error() } - if ($logger && ($publish || $error->getCode() === 0)) { + if ($logger && $publish) { + try { + /** @var Utopia\Database\Document $user */ + $user = $utopia->getResource('user'); + } catch (\Throwable) { + // All good, user is optional information for logger + } + if (isset($user) && !$user->isEmpty()) { $log->setUser(new User($user->getId())); } @@ -871,7 +855,7 @@ Http::error() $log->addExtra('file', $error->getFile()); $log->addExtra('line', $error->getLine()); $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('roles', $authorization->getRoles()); + $log->addExtra('roles', Authorization::getRoles()); $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); $log->setAction($action); @@ -885,7 +869,7 @@ Http::error() /** Wrap all exceptions inside Appwrite\Extend\Exception */ if (!($error instanceof AppwriteException)) { - $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, (int)$code, $error); + $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); } switch ($code) { // Don't show 500 errors! @@ -905,14 +889,14 @@ Http::error() break; default: $code = 500; // All other errors get the generic 500 server error status code - $message = (Http::getMode() === Http::MODE_TYPE_DEVELOPMENT) ? $message : 'Server Error'; + $message = 'Server Error'; } //$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised $type = $error->getType(); - $output = ((Http::isDevelopment())) ? [ + $output = ((App::isDevelopment())) ? [ 'message' => $message, 'code' => $code, 'file' => $file, @@ -940,7 +924,7 @@ Http::error() $layout ->setParam('title', $project->getAttribute('name') . ' - Error') - ->setParam('development', Http::isDevelopment()) + ->setParam('development', App::isDevelopment()) ->setParam('projectName', $project->getAttribute('name')) ->setParam('projectURL', $project->getAttribute('url')) ->setParam('message', $output['message'] ?? '') @@ -951,18 +935,18 @@ Http::error() $response->html($layout->render()); } - $connections->reclaim(); - $response->dynamic( new Document($output), - Http::isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR + $utopia->isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR ); }); -Http::get('/robots.txt') +App::get('/robots.txt') ->desc('Robots.txt File') ->label('scope', 'public') ->label('docs', false) + ->inject('utopia') + ->inject('swooleRequest') ->inject('request') ->inject('response') ->inject('dbForConsole') @@ -970,9 +954,7 @@ Http::get('/robots.txt') ->inject('queueForEvents') ->inject('queueForUsage') ->inject('geodb') - ->inject('route') - ->inject('authorization') - ->action(function (Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, ?Route $route, Authorization $authorization) { + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) { $host = $request->getHostname() ?? ''; $mainDomain = System::getEnv('_APP_DOMAIN', ''); @@ -980,17 +962,16 @@ Http::get('/robots.txt') $template = new View(__DIR__ . '/../views/general/robots.phtml'); $response->text($template->render(false)); } else { - if (is_null($route)) { - $route = new Route($request->getMethod(), $request->getURI()); - } - router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization); + router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb); } }); -Http::get('/humans.txt') +App::get('/humans.txt') ->desc('Humans.txt File') ->label('scope', 'public') ->label('docs', false) + ->inject('utopia') + ->inject('swooleRequest') ->inject('request') ->inject('response') ->inject('dbForConsole') @@ -998,9 +979,7 @@ Http::get('/humans.txt') ->inject('queueForEvents') ->inject('queueForUsage') ->inject('geodb') - ->inject('route') - ->inject('authorization') - ->action(function (Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb, Route $route, Authorization $authorization) { + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) { $host = $request->getHostname() ?? ''; $mainDomain = System::getEnv('_APP_DOMAIN', ''); @@ -1008,11 +987,11 @@ Http::get('/humans.txt') $template = new View(__DIR__ . '/../views/general/humans.phtml'); $response->text($template->render(false)); } else { - router($dbForConsole, $getProjectDB, $request, $response, $route, $queueForEvents, $queueForUsage, $geodb, $authorization); + router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb); } }); -Http::get('/.well-known/acme-challenge/*') +App::get('/.well-known/acme-challenge/*') ->desc('SSL Verification') ->label('scope', 'public') ->label('docs', false) @@ -1062,32 +1041,16 @@ Http::get('/.well-known/acme-challenge/*') $response->text($content); }); -Http::wildcard() +include_once __DIR__ . '/shared/api.php'; +include_once __DIR__ . '/shared/api/auth.php'; + +App::wildcard() ->groups(['api']) ->label('scope', 'global') ->action(function () { throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND); }); -include_once 'mock.php'; -include_once 'shared/api.php'; -include_once 'shared/api/auth.php'; -include_once 'api/account.php'; -include_once 'api/avatars.php'; -include_once 'api/console.php'; -include_once 'api/databases.php'; -include_once 'api/functions.php'; -include_once 'api/graphql.php'; -include_once 'api/health.php'; -include_once 'api/locale.php'; -include_once 'api/messaging.php'; -include_once 'api/migrations.php'; -include_once 'api/project.php'; -include_once 'api/projects.php'; -include_once 'api/proxy.php'; -include_once 'api/storage.php'; -include_once 'api/teams.php'; -include_once 'api/users.php'; -include_once 'api/vcs.php'; -include_once 'web/console.php'; -include_once 'web/home.php'; +foreach (Config::getParam('services', []) as $service) { + include_once $service['controller']; +} diff --git a/app/controllers/mock.php b/app/controllers/mock.php index 70ca5e9048..fdb1d80dcc 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -3,7 +3,9 @@ global $utopia, $request, $response; use Appwrite\Extend\Exception; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; +use Utopia\App; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -11,15 +13,13 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\UID; -use Utopia\Http\Http; -use Utopia\Http\Route; -use Utopia\Http\Validator\Host; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; use Utopia\System\System; +use Utopia\Validator\Host; +use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub; -Http::get('/v1/mock/tests/general/oauth2') +App::get('/v1/mock/tests/general/oauth2') ->desc('OAuth Login') ->groups(['mock']) ->label('scope', 'public') @@ -35,7 +35,7 @@ Http::get('/v1/mock/tests/general/oauth2') $response->redirect($redirectURI . '?' . \http_build_query(['code' => 'abcdef', 'state' => $state])); }); -Http::get('/v1/mock/tests/general/oauth2/token') +App::get('/v1/mock/tests/general/oauth2/token') ->desc('OAuth2 Token') ->groups(['mock']) ->label('scope', 'public') @@ -81,7 +81,7 @@ Http::get('/v1/mock/tests/general/oauth2/token') } }); -Http::get('/v1/mock/tests/general/oauth2/user') +App::get('/v1/mock/tests/general/oauth2/user') ->desc('OAuth2 User') ->groups(['mock']) ->label('scope', 'public') @@ -101,7 +101,7 @@ Http::get('/v1/mock/tests/general/oauth2/user') ]); }); -Http::get('/v1/mock/tests/general/oauth2/success') +App::get('/v1/mock/tests/general/oauth2/success') ->desc('OAuth2 Success') ->groups(['mock']) ->label('scope', 'public') @@ -114,7 +114,7 @@ Http::get('/v1/mock/tests/general/oauth2/success') ]); }); -Http::get('/v1/mock/tests/general/oauth2/failure') +App::get('/v1/mock/tests/general/oauth2/failure') ->desc('OAuth2 Failure') ->groups(['mock']) ->label('scope', 'public') @@ -129,7 +129,7 @@ Http::get('/v1/mock/tests/general/oauth2/failure') ]); }); -Http::patch('/v1/mock/functions-v2') +App::patch('/v1/mock/functions-v2') ->desc('Update Function Version to V2 (outdated code syntax)') ->groups(['mock', 'api', 'functions']) ->label('scope', 'functions.write') @@ -155,7 +155,7 @@ Http::patch('/v1/mock/functions-v2') $response->noContent(); }); -Http::post('/v1/mock/api-key-unprefixed') +App::post('/v1/mock/api-key-unprefixed') ->desc('Create API Key (without standard prefix)') ->groups(['mock', 'api', 'projects']) ->label('scope', 'projects.write') @@ -204,7 +204,7 @@ Http::post('/v1/mock/api-key-unprefixed') ->dynamic($key, Response::MODEL_KEY); }); -Http::get('/v1/mock/github/callback') +App::get('/v1/mock/github/callback') ->desc('Create installation document using GitHub installation id') ->groups(['mock', 'api', 'vcs']) ->label('scope', 'public') @@ -264,13 +264,15 @@ Http::get('/v1/mock/github/callback') ]); }); -Http::shutdown() +App::shutdown() ->groups(['mock']) - ->inject('route') + ->inject('utopia') ->inject('response') - ->action(function (Route $route, Response $response) { + ->inject('request') + ->action(function (App $utopia, Response $response, Request $request) { $result = []; + $route = $utopia->getRoute(); $path = APP_STORAGE_CACHE . '/tests.json'; $tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : []; diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 55fb3a880e..6d87940ff7 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -15,11 +15,11 @@ use Appwrite\Event\Usage; use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Messaging\Adapter\Realtime; -use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\Database\TimeLimit; +use Utopia\App; use Utopia\Cache\Adapter\Filesystem; use Utopia\Cache\Cache; use Utopia\Config\Config; @@ -28,11 +28,8 @@ use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\Authorization\Input; -use Utopia\Http\Http; -use Utopia\Http\Route; -use Utopia\Http\Validator\WhiteList; use Utopia\System\System; +use Utopia\Validator\WhiteList; $parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) { preg_match_all('/{(.*?)}/', $label, $matches); @@ -153,9 +150,9 @@ $databaseListener = function (string $event, Document $document, Document $proje } }; -Http::init() +App::init() ->groups(['api']) - ->inject('route') + ->inject('utopia') ->inject('request') ->inject('dbForConsole') ->inject('project') @@ -163,8 +160,9 @@ Http::init() ->inject('session') ->inject('servers') ->inject('mode') - ->inject('authorization') - ->action(function (Route $route, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode, Authorization $authorization) { + ->action(function (App $utopia, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode) { + $route = $utopia->getRoute(); + if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } @@ -243,8 +241,8 @@ Http::init() $role = Auth::USER_ROLE_APPS; $scopes = \array_merge($roles[$role]['scopes'], $tokenScopes); - $authorization->addRole(Auth::USER_ROLE_APPS); - $authorization->setDefaultStatus(false); // Cancel security segmentation for API keys. + Authorization::setRole(Auth::USER_ROLE_APPS); + Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. } } elseif ($keyType === API_KEY_STANDARD) { // No underline means no prefix. Backwards compatibility. @@ -269,8 +267,8 @@ Http::init() throw new Exception(Exception::PROJECT_KEY_EXPIRED); } - $authorization->addRole(Auth::USER_ROLE_APPS); - $authorization->setDefaultStatus(false); // Cancel security segmentation for API keys. + Authorization::setRole(Auth::USER_ROLE_APPS); + Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. $accessedAt = $key->getAttribute('accessedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { @@ -297,10 +295,10 @@ Http::init() } } - $authorization->addRole($role); + Authorization::setRole($role); - foreach (Auth::getRoles($user, $authorization) as $authRole) { - $authorization->addRole($authRole); + foreach (Auth::getRoles($user) as $authRole) { + Authorization::setRole($authRole); } $service = $route->getLabel('sdk.namespace', ''); @@ -308,7 +306,7 @@ Http::init() if ( array_key_exists($service, $project->getAttribute('services', [])) && !$project->getAttribute('services', [])[$service] - && !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles())) + && !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles())) ) { throw new Exception(Exception::GENERAL_SERVICE_DISABLED); } @@ -343,9 +341,9 @@ Http::init() } }); -Http::init() +App::init() ->groups(['api']) - ->inject('route') + ->inject('utopia') ->inject('request') ->inject('response') ->inject('project') @@ -359,12 +357,14 @@ Http::init() ->inject('queueForUsage') ->inject('dbForProject') ->inject('mode') - ->inject('authorization') - ->action(function (Route $route, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode, Authorization $authorization) use ($databaseListener) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode) use ($databaseListener) { + + $route = $utopia->getRoute(); + if ( array_key_exists('rest', $project->getAttribute('apis', [])) && !$project->getAttribute('apis', [])['rest'] - && !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles())) + && !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles())) ) { throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } @@ -394,7 +394,7 @@ Http::init() $closestLimit = null; - $roles = $authorization->getRoles(); + $roles = Authorization::getRoles(); $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); @@ -458,7 +458,7 @@ Http::init() $useCache = $route->getLabel('cache', false); if ($useCache) { $key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER); - $cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key)); + $cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key)); $cache = new Cache( new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId()) ); @@ -471,18 +471,19 @@ Http::init() if ($type === 'bucket') { $bucketId = $parts[1] ?? null; + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $fileSecurity = $bucket->getAttribute('fileSecurity', false); - $valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead())); + $validator = new Authorization(Database::PERMISSION_READ); + $valid = $validator->isValid($bucket->getRead()); + if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } @@ -493,7 +494,7 @@ Http::init() if ($fileSecurity && !$valid) { $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); } else { - $file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); + $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId)); } if ($file->isEmpty()) { @@ -517,7 +518,7 @@ Http::init() } }); -Http::init() +App::init() ->groups(['session']) ->inject('user') ->inject('request') @@ -537,12 +538,14 @@ Http::init() * Delete older sessions if the number of sessions have crossed * the session limit set for the project */ -Http::shutdown() +App::shutdown() ->groups(['session']) + ->inject('utopia') + ->inject('request') ->inject('response') ->inject('project') ->inject('dbForProject') - ->action(function (Response $response, Document $project, Database $dbForProject) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Database $dbForProject) { $sessionLimit = $project->getAttribute('auths', [])['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT; $session = $response->getPayload(); $userId = $session['userId'] ?? ''; @@ -569,9 +572,9 @@ Http::shutdown() $dbForProject->purgeCachedDocument('users', $userId); }); -Http::shutdown() +App::shutdown() ->groups(['api']) - ->inject('route') + ->inject('utopia') ->inject('request') ->inject('response') ->inject('project') @@ -587,29 +590,7 @@ Http::shutdown() ->inject('queueForFunctions') ->inject('mode') ->inject('dbForConsole') - ->inject('authorization') - ->action(function ( - Route $route, - Request $request, - Response $response, - Document $project, - Document $user, - Event $queueForEvents, - Audit $queueForAudits, - Usage $queueForUsage, - Delete $queueForDeletes, - EventDatabase $queueForDatabase, - Build $queueForBuilds, - Messaging $queueForMessaging, - Database $dbForProject, - Func $queueForFunctions, - string $mode, - Database $dbForConsole, - Authorization $authorization, - ) use ($parseLabel) { - if (!empty($user) && !$user->isEmpty() && empty($user->getInternalId())) { - $user = $authorization->skip(fn () => $dbForProject->getDocument('users', $user->getId())); - } + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole) use ($parseLabel) { $responsePayload = $response->getPayload(); @@ -669,6 +650,7 @@ Http::shutdown() } } + $route = $utopia->getRoute(); $requestParams = $route->getParamsValues(); /** @@ -738,11 +720,11 @@ Http::shutdown() $key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER); $signature = md5($data['payload']); - $cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key)); + $cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key)); $accessedAt = $cacheLog->getAttribute('accessedAt', ''); $now = DateTime::now(); if ($cacheLog->isEmpty()) { - $authorization->skip(fn () => $dbForProject->createDocument('cache', new Document([ + Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([ '$id' => $key, 'resource' => $resource, 'resourceType' => $resourceType, @@ -752,7 +734,7 @@ Http::shutdown() ]))); } elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) { $cacheLog->setAttribute('accessedAt', $now); - $authorization->skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog)); + Authorization::skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog)); } if ($signature !== $cacheLog->getAttribute('signature')) { @@ -764,8 +746,10 @@ Http::shutdown() } } + + if ($project->getId() !== 'console') { - if (!Auth::isPrivilegedUser($authorization->getRoles())) { + if (!Auth::isPrivilegedUser(Authorization::getRoles())) { $fileSize = 0; $file = $request->getFiles('file'); if (!empty($file)) { @@ -790,7 +774,7 @@ Http::shutdown() $accessedAt = $project->getAttribute('accessedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) { $project->setAttribute('accessedAt', DateTime::now()); - $authorization->skip(fn () => $dbForConsole->updateDocument('projects', $project->getId(), $project)); + Authorization::skip(fn () => $dbForConsole->updateDocument('projects', $project->getId(), $project)); } } @@ -811,16 +795,10 @@ Http::shutdown() } }); -Http::init() +App::init() ->groups(['usage']) ->action(function () { if (System::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') { throw new Exception(Exception::GENERAL_USAGE_DISABLED); } }); - -Http::shutdown() - ->inject('connections') - ->action(function (Connections $connections) { - $connections->reclaim(); - }); diff --git a/app/controllers/shared/api/auth.php b/app/controllers/shared/api/auth.php index 224dcb3392..53aacabe21 100644 --- a/app/controllers/shared/api/auth.php +++ b/app/controllers/shared/api/auth.php @@ -4,14 +4,13 @@ use Appwrite\Auth\Auth; use Appwrite\Extend\Exception; use Appwrite\Utopia\Request; use MaxMind\Db\Reader; +use Utopia\App; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\Http\Http; -use Utopia\Http\Route; use Utopia\System\System; -Http::init() +App::init() ->groups(['mfaProtected']) ->inject('session') ->action(function (Document $session) { @@ -30,14 +29,13 @@ Http::init() } }); -Http::init() +App::init() ->groups(['auth']) - ->inject('route') + ->inject('utopia') ->inject('request') ->inject('project') ->inject('geodb') - ->inject('authorization') - ->action(function (Route $route, Request $request, Document $project, Reader $geodb, Authorization $authorization) { + ->action(function (App $utopia, Request $request, Document $project, Reader $geodb) { $denylist = System::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', ''); if (!empty($denylist && $project->getId() === 'console')) { $countries = explode(',', $denylist); @@ -48,17 +46,15 @@ Http::init() } } - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - $isAppUser = Auth::isAppUser($authorization->getRoles()); + $route = $utopia->match($request); + + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $isAppUser = Auth::isAppUser(Authorization::getRoles()); if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs return; } - if ($route->getLabel('sdk.namespace', '') === 'graphql') { // Skip for graphQL recursive call - return; - } - $auths = $project->getAttribute('auths', []); switch ($route->getLabel('auth.type', '')) { case 'emailPassword': diff --git a/app/controllers/web/console.php b/app/controllers/web/console.php index 60be4acf54..c02e140270 100644 --- a/app/controllers/web/console.php +++ b/app/controllers/web/console.php @@ -2,9 +2,9 @@ use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Utopia\Http\Http; +use Utopia\App; -Http::init() +App::init() ->groups(['web']) ->inject('request') ->inject('response') @@ -16,7 +16,7 @@ Http::init() ; }); -Http::get('/') +App::get('/') ->alias('auth/*') ->alias('/invite') ->alias('/login') diff --git a/app/controllers/web/home.php b/app/controllers/web/home.php index 3577061d69..27b2614c37 100644 --- a/app/controllers/web/home.php +++ b/app/controllers/web/home.php @@ -1,10 +1,10 @@ desc('Get Version') ->groups(['home', 'web']) ->label('scope', 'public') diff --git a/app/http.php b/app/http.php index 320621ad33..7e1291142b 100644 --- a/app/http.php +++ b/app/http.php @@ -1,242 +1,331 @@ true, - 'http_compression' => true, - 'http_compression_level' => 6, - 'package_max_length' => $payloadSize, - 'buffer_output_size' => $payloadSize, - // Server - // 'log_level' => 0, - 'dispatch_mode' => 2, - 'worker_num' => $workerNumber, - 'reactor_num' => swoole_cpu_num() * 2, - 'open_cpu_affinity' => true, - // Coroutine - 'enable_coroutine' => true, - 'send_yield' => true, - 'tcp_fastopen' => true, -]); +$http + ->set([ + 'worker_num' => $workerNumber, + 'open_http2_protocol' => true, + 'http_compression' => true, + 'http_compression_level' => 6, + 'package_max_length' => $payloadSize, + 'buffer_output_size' => $payloadSize, + ]); -$http = new Http($server, $container, 'UTC'); -$http->setRequestClass(Request::class); -$http->setResponseClass(Response::class); +$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) { + Console::success('Worker ' . ++$workerId . ' started successfully'); +}); -Http::onStart() - ->inject('authorization') - ->inject('cache') - ->inject('pools') - ->inject('connections') - ->action(function (Authorization $authorization, Cache $cache, array $pools, Connections $connections) { - try { - // wait for database to be ready - $attempts = 0; - $max = 15; - $sleep = 2; +$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) { + Console::success('Starting reload...'); +}); - do { - try { - $attempts++; - $pool = $pools['pools-console-console']['pool']; - $dsn = $pools['pools-console-console']['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); +$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) { + Console::success('Reload completed...'); +}); - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; +include __DIR__ . '/controllers/general.php'; - $adapter->setDatabase($dsn->getPath()); +$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) { + $app = new App('UTC'); - $dbForConsole = new Database($adapter, $cache); - $dbForConsole->setAuthorization($authorization); + go(function () use ($register, $app) { + $pools = $register->get('pools'); + /** @var Group $pools */ + App::setResource('pools', fn () => $pools); - $dbForConsole - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - $dbForConsole->ping(); - break; // leave the do-while if successful - } catch (\Throwable $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: ' . $e->getMessage()); - } - sleep($sleep); - } - } while ($attempts < $max); - - Console::success('[Setup] - Server database init started...'); + // wait for database to be ready + $attempts = 0; + $max = 10; + $sleep = 1; + do { try { - Console::success('[Setup] - Creating database: appwrite...'); - $dbForConsole->create(); + $attempts++; + $dbForConsole = $app->getResource('dbForConsole'); + /** @var Utopia\Database\Database $dbForConsole */ + break; // leave the do-while if successful } catch (\Throwable $e) { - Console::success('[Setup] - Skip: metadata table already exists'); - return true; + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: ' . $e->getMessage()); + } + sleep($sleep); } + } while ($attempts < $max); - if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) { - $audit = new Audit($dbForConsole); - $audit->setup(); - } + Console::success('[Setup] - Server database init started...'); - if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { - $abuse = new TimeLimit("", 0, 1, $dbForConsole); - $abuse->setup(); - } - - /** @var array $collections */ - $collections = Config::getParam('collections', []); - $consoleCollections = $collections['console']; - foreach ($consoleCollections as $key => $collection) { - if (($collection['$collection'] ?? '') !== Database::METADATA) { - continue; - } - if (!$dbForConsole->getCollection($key)->isEmpty()) { - continue; - } - - Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); - - $attributes = []; - $indexes = []; - - foreach ($collection['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]); - } - - foreach ($collection['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } - - $dbForConsole->createCollection($key, $attributes, $indexes); - } - - if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) { - Console::success('[Setup] - Creating default bucket...'); - $dbForConsole->createDocument('buckets', new Document([ - '$id' => ID::custom('default'), - '$collection' => ID::custom('buckets'), - 'name' => 'Default', - 'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB - 'allowedFileExtensions' => [], - 'enabled' => true, - 'compression' => 'gzip', - 'encryption' => true, - 'antivirus' => true, - 'fileSecurity' => true, - '$permissions' => [ - Permission::create(Role::any()), - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'search' => 'buckets Default', - ])); - - $bucket = $dbForConsole->getDocument('buckets', 'default'); - - Console::success('[Setup] - Creating files collection for default bucket...'); - - $files = $collections['buckets']['files'] ?? []; - if (empty($files)) { - throw new Exception('Files collection is not configured.'); - } - - $attributes = []; - $indexes = []; - - foreach ($files['attributes'] as $attribute) { - $attributes[] = new Document([ - '$id' => ID::custom($attribute['$id']), - 'type' => $attribute['type'], - 'size' => $attribute['size'], - 'required' => $attribute['required'], - 'signed' => $attribute['signed'], - 'array' => $attribute['array'], - 'filters' => $attribute['filters'], - 'default' => $attribute['default'] ?? null, - 'format' => $attribute['format'] ?? '' - ]); - } - - foreach ($files['indexes'] as $index) { - $indexes[] = new Document([ - '$id' => ID::custom($index['$id']), - 'type' => $index['type'], - 'attributes' => $index['attributes'], - 'lengths' => $index['lengths'], - 'orders' => $index['orders'], - ]); - } - - $dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); - } - - $connections->reclaim(); - - Console::success('[Setup] - Server database init completed...'); - Console::success('Server started successfully'); + try { + Console::success('[Setup] - Creating database: appwrite...'); + $dbForConsole->create(); } catch (\Throwable $e) { - Console::warning('Database not ready: ' . $e->getMessage()); - exit(1); + Console::success('[Setup] - Skip: metadata table already exists'); } + + if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) { + $audit = new Audit($dbForConsole); + $audit->setup(); + } + + if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) { + $adapter = new TimeLimit("", 0, 1, $dbForConsole); + $adapter->setup(); + } + + /** @var array $collections */ + $collections = Config::getParam('collections', []); + $consoleCollections = $collections['console']; + foreach ($consoleCollections as $key => $collection) { + if (($collection['$collection'] ?? '') !== Database::METADATA) { + continue; + } + if (!$dbForConsole->getCollection($key)->isEmpty()) { + continue; + } + + Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...'); + + $attributes = []; + $indexes = []; + + foreach ($collection['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]); + } + + foreach ($collection['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); + } + + $dbForConsole->createCollection($key, $attributes, $indexes); + } + + if ($dbForConsole->getDocument('buckets', 'default')->isEmpty() && !$dbForConsole->exists($dbForConsole->getDatabase(), 'bucket_1')) { + Console::success('[Setup] - Creating default bucket...'); + $dbForConsole->createDocument('buckets', new Document([ + '$id' => ID::custom('default'), + '$collection' => ID::custom('buckets'), + 'name' => 'Default', + 'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB + 'allowedFileExtensions' => [], + 'enabled' => true, + 'compression' => 'gzip', + 'encryption' => true, + 'antivirus' => true, + 'fileSecurity' => true, + '$permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'search' => 'buckets Default', + ])); + + $bucket = $dbForConsole->getDocument('buckets', 'default'); + + Console::success('[Setup] - Creating files collection for default bucket...'); + $files = $collections['buckets']['files'] ?? []; + if (empty($files)) { + throw new Exception('Files collection is not configured.'); + } + + $attributes = []; + $indexes = []; + + foreach ($files['attributes'] as $attribute) { + $attributes[] = new Document([ + '$id' => ID::custom($attribute['$id']), + 'type' => $attribute['type'], + 'size' => $attribute['size'], + 'required' => $attribute['required'], + 'signed' => $attribute['signed'], + 'array' => $attribute['array'], + 'filters' => $attribute['filters'], + 'default' => $attribute['default'] ?? null, + 'format' => $attribute['format'] ?? '' + ]); + } + + foreach ($files['indexes'] as $index) { + $indexes[] = new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]); + } + + $dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); + } + + $pools->reclaim(); + + Console::success('[Setup] - Server database init completed...'); }); -Http::init() - ->inject('authorization') - ->action(function (Authorization $authorization) { - $authorization->cleanRoles(); - $authorization->addRole(Role::any()->toString()); + Console::success('Server started successfully (max payload is ' . number_format($payloadSize) . ' bytes)'); + Console::info("Master pid {$http->master_pid}, manager pid {$http->manager_pid}"); + + // listen ctrl + c + Process::signal(2, function () use ($http) { + Console::log('Stop by Ctrl+C'); + $http->shutdown(); }); +}); + +$http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register) { + App::setResource('swooleRequest', fn () => $swooleRequest); + App::setResource('swooleResponse', fn () => $swooleResponse); + + $request = new Request($swooleRequest); + $response = new Response($swooleResponse); + + if (Files::isFileLoaded($request->getURI())) { + $time = (60 * 60 * 24 * 365 * 2); // 45 days cache + + $response + ->setContentType(Files::getFileMimeType($request->getURI())) + ->addHeader('Cache-Control', 'public, max-age=' . $time) + ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time) . ' GMT') // 45 days cache + ->send(Files::getFileContents($request->getURI())); + + return; + } + + $app = new App('UTC'); + + $pools = $register->get('pools'); + App::setResource('pools', fn () => $pools); + + try { + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); + + $app->run($request, $response); + } catch (\Throwable $th) { + $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); + + $logger = $app->getResource("logger"); + if ($logger) { + try { + /** @var Utopia\Database\Document $user */ + $user = $app->getResource('user'); + } catch (\Throwable $_th) { + // All good, user is optional information for logger + } + + $route = $app->getRoute(); + + $log = $app->getResource("log"); + + if (isset($user) && !$user->isEmpty()) { + $log->setUser(new User($user->getId())); + } + + $log->setNamespace("http"); + $log->setServer(\gethostname()); + $log->setVersion($version); + $log->setType(Log::TYPE_ERROR); + $log->setMessage($th->getMessage()); + + $log->addTag('method', $route->getMethod()); + $log->addTag('url', $route->getPath()); + $log->addTag('verboseType', get_class($th)); + $log->addTag('code', $th->getCode()); + // $log->addTag('projectId', $project->getId()); // TODO: Figure out how to get ProjectID, if it becomes relevant + $log->addTag('hostname', $request->getHostname()); + $log->addTag('locale', (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', ''))); + + $log->addExtra('file', $th->getFile()); + $log->addExtra('line', $th->getLine()); + $log->addExtra('trace', $th->getTraceAsString()); + $log->addExtra('roles', Authorization::getRoles()); + + $action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD"); + $log->setAction($action); + + $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; + $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); + + $responseCode = $logger->addLog($log); + Console::info('Log pushed with status code: ' . $responseCode); + } + + Console::error('[Error] Type: ' . get_class($th)); + Console::error('[Error] Message: ' . $th->getMessage()); + Console::error('[Error] File: ' . $th->getFile()); + Console::error('[Error] Line: ' . $th->getLine()); + + $swooleResponse->setStatusCode(500); + + $output = ((App::isDevelopment())) ? [ + 'message' => 'Error: ' . $th->getMessage(), + 'code' => 500, + 'file' => $th->getFile(), + 'line' => $th->getLine(), + 'trace' => $th->getTrace(), + 'version' => $version, + ] : [ + 'message' => 'Error: Server Error', + 'code' => 500, + 'version' => $version, + ]; + + $swooleResponse->end(\json_encode($output)); + } finally { + $pools->reclaim(); + } +}); $http->start(); diff --git a/app/init.php b/app/init.php index 0f9e8f0ef6..b4ab772e0e 100644 --- a/app/init.php +++ b/app/init.php @@ -1,12 +1,26 @@ $value], JSON_PRESERVE_ZERO_FRACTION); + }, + function (mixed $value) { + if (is_null($value)) { + return; + } -$registry->set('logger', function () { + return json_decode($value, true)['value']; + } +); + +Database::addFilter( + 'enum', + function (mixed $value, Document $attribute) { + if ($attribute->isSet('elements')) { + $attribute->removeAttribute('elements'); + } + + return $value; + }, + function (mixed $value, Document $attribute) { + $formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true); + if (isset($formatOptions['elements'])) { + $attribute->setAttribute('elements', $formatOptions['elements']); + } + + return $value; + } +); + +Database::addFilter( + 'range', + function (mixed $value, Document $attribute) { + if ($attribute->isSet('min')) { + $attribute->removeAttribute('min'); + } + if ($attribute->isSet('max')) { + $attribute->removeAttribute('max'); + } + + return $value; + }, + function (mixed $value, Document $attribute) { + $formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true); + if (isset($formatOptions['min']) || isset($formatOptions['max'])) { + $attribute + ->setAttribute('min', $formatOptions['min']) + ->setAttribute('max', $formatOptions['max']); + } + + return $value; + } +); + +Database::addFilter( + 'subQueryAttributes', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + $attributes = $database->find('attributes', [ + Query::equal('collectionInternalId', [$document->getInternalId()]), + Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), + Query::limit($database->getLimitForAttributes()), + ]); + + foreach ($attributes as $attribute) { + if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { + $options = $attribute->getAttribute('options'); + foreach ($options as $key => $value) { + $attribute->setAttribute($key, $value); + } + $attribute->removeAttribute('options'); + } + } + + return $attributes; + } +); + +Database::addFilter( + 'subQueryIndexes', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('indexes', [ + Query::equal('collectionInternalId', [$document->getInternalId()]), + Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), + Query::limit($database->getLimitForIndexes()), + ]); + } +); + +Database::addFilter( + 'subQueryPlatforms', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('platforms', [ + Query::equal('projectInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ]); + } +); + +Database::addFilter( + 'subQueryKeys', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('keys', [ + Query::equal('projectInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ]); + } +); + +Database::addFilter( + 'subQueryWebhooks', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('webhooks', [ + Query::equal('projectInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ]); + } +); + +Database::addFilter( + 'subQuerySessions', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return Authorization::skip(fn () => $database->find('sessions', [ + Query::equal('userInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ])); + } +); + +Database::addFilter( + 'subQueryTokens', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return Authorization::skip(fn () => $database + ->find('tokens', [ + Query::equal('userInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ])); + } +); + +Database::addFilter( + 'subQueryChallenges', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return Authorization::skip(fn () => $database + ->find('challenges', [ + Query::equal('userInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ])); + } +); + +Database::addFilter( + 'subQueryAuthenticators', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return Authorization::skip(fn () => $database + ->find('authenticators', [ + Query::equal('userInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ])); + } +); + +Database::addFilter( + 'subQueryMemberships', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return Authorization::skip(fn () => $database + ->find('memberships', [ + Query::equal('userInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY), + ])); + } +); + +Database::addFilter( + 'subQueryVariables', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('variables', [ + Query::equal('resourceInternalId', [$document->getInternalId()]), + Query::equal('resourceType', ['function']), + Query::limit(APP_LIMIT_SUBQUERY), + ]); + } +); + +Database::addFilter( + 'encrypt', + function (mixed $value) { + $key = System::getEnv('_APP_OPENSSL_KEY_V1'); + $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); + $tag = null; + + return json_encode([ + 'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag), + 'method' => OpenSSL::CIPHER_AES_128_GCM, + 'iv' => \bin2hex($iv), + 'tag' => \bin2hex($tag ?? ''), + 'version' => '1', + ]); + }, + function (mixed $value) { + if (is_null($value)) { + return; + } + $value = json_decode($value, true); + $key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']); + + return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag'])); + } +); + +Database::addFilter( + 'subQueryProjectVariables', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return $database + ->find('variables', [ + Query::equal('resourceType', ['project']), + Query::limit(APP_LIMIT_SUBQUERY) + ]); + } +); + +Database::addFilter( + 'userSearch', + function (mixed $value, Document $user) { + $searchValues = [ + $user->getId(), + $user->getAttribute('email', ''), + $user->getAttribute('name', ''), + $user->getAttribute('phone', '') + ]; + + foreach ($user->getAttribute('labels', []) as $label) { + $searchValues[] = 'label:' . $label; + } + + $search = implode(' ', \array_filter($searchValues)); + + return $search; + }, + function (mixed $value) { + return $value; + } +); + +Database::addFilter( + 'subQueryTargets', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + return Authorization::skip(fn () => $database + ->find('targets', [ + Query::equal('userInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBQUERY) + ])); + } +); + +Database::addFilter( + 'subQueryTopicTargets', + function (mixed $value) { + return; + }, + function (mixed $value, Document $document, Database $database) { + $targetIds = Authorization::skip(fn () => \array_map( + fn ($document) => $document->getAttribute('targetInternalId'), + $database->find('subscribers', [ + Query::equal('topicInternalId', [$document->getInternalId()]), + Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY) + ]) + )); + if (\count($targetIds) > 0) { + return $database->skipValidation(fn () => $database->find('targets', [ + Query::equal('$internalId', $targetIds) + ])); + } + return []; + } +); + +Database::addFilter( + 'providerSearch', + function (mixed $value, Document $provider) { + $searchValues = [ + $provider->getId(), + $provider->getAttribute('name', ''), + $provider->getAttribute('provider', ''), + $provider->getAttribute('type', '') + ]; + + $search = \implode(' ', \array_filter($searchValues)); + + return $search; + }, + function (mixed $value) { + return $value; + } +); + +Database::addFilter( + 'topicSearch', + function (mixed $value, Document $topic) { + $searchValues = [ + $topic->getId(), + $topic->getAttribute('name', ''), + $topic->getAttribute('description', ''), + ]; + + $search = \implode(' ', \array_filter($searchValues)); + + return $search; + }, + function (mixed $value) { + return $value; + } +); + +Database::addFilter( + 'messageSearch', + function (mixed $value, Document $message) { + $searchValues = [ + $message->getId(), + $message->getAttribute('description', ''), + $message->getAttribute('status', ''), + ]; + + $data = \json_decode($message->getAttribute('data', []), true); + $providerType = $message->getAttribute('providerType', ''); + + if ($providerType === MESSAGE_TYPE_EMAIL) { + $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); + } elseif ($providerType === MESSAGE_TYPE_SMS) { + $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); + } else { + $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); + } + + $search = \implode(' ', \array_filter($searchValues)); + + return $search; + }, + function (mixed $value) { + return $value; + } +); + +/** + * DB Formats + */ +Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () { + return new Email(); +}, Database::VAR_STRING); + +Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () { + return new DatetimeValidator(); +}, Database::VAR_DATETIME); + +Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) { + $elements = $attribute['formatOptions']['elements']; + return new WhiteList($elements, true); +}, Database::VAR_STRING); + +Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () { + return new IP(); +}, Database::VAR_STRING); + +Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () { + return new URL(); +}, Database::VAR_STRING); + +Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) { + $min = $attribute['formatOptions']['min'] ?? -INF; + $max = $attribute['formatOptions']['max'] ?? INF; + return new Range($min, $max, Range::TYPE_INTEGER); +}, Database::VAR_INTEGER); + +Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) { + $min = $attribute['formatOptions']['min'] ?? -INF; + $max = $attribute['formatOptions']['max'] ?? INF; + return new Range($min, $max, Range::TYPE_FLOAT); +}, Database::VAR_FLOAT); + +/* + * Registry + */ +$register->set('logger', function () { // Register error logger $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); $providerConfig = System::getEnv('_APP_LOGGING_CONFIG', ''); @@ -105,12 +770,12 @@ $registry->set('logger', function () { default => ['key' => $loggingProvider->getHost()], }; } catch (Throwable $th) { - Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage()); // 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' => '',], + 'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',], 'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''], default => ['key' => $providerConfig], }; @@ -141,167 +806,200 @@ $registry->set('logger', function () { return; } - $logger = new Logger($adapter); - $logger->setSample(0.4); - return $logger; + return new Logger($adapter); }); -$registry->set('geodb', function () { - /** - * @disregard P1009 Undefined type - */ - return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-09.mmdb'); -}); +$register->set('pools', function () { + $group = new Group(); -$registry->set('hooks', function () { - return new Hooks(); -}); + $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', ''), + ]); -$registry->set( - 'pools', - (function () { - $fallbackForDB = 'db_main=' . URL::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=' . URL::unparse([ - 'scheme' => 'redis', - 'host' => System::getEnv('_APP_REDIS_HOST', 'redis'), - 'port' => System::getEnv('_APP_REDIS_PORT', '6379'), - 'user' => System::getEnv('_APP_REDIS_USER', ''), - 'pass' => System::getEnv('_APP_REDIS_PASS', ''), - ]); + $connections = [ + 'console' => [ + 'type' => 'database', + 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), + 'multiple' => false, + 'schemes' => ['mariadb', 'mysql'], + ], + 'database' => [ + 'type' => 'database', + 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), + 'multiple' => true, + 'schemes' => ['mariadb', 'mysql'], + ], + 'queue' => [ + 'type' => 'queue', + 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'pubsub' => [ + 'type' => 'pubsub', + 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), + 'multiple' => false, + 'schemes' => ['redis'], + ], + 'cache' => [ + 'type' => 'cache', + 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), + 'multiple' => true, + 'schemes' => ['redis'], + ], + ]; - $connections = [ - 'console' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_CONSOLE', $fallbackForDB), - 'multiple' => false, - 'schemes' => ['mariadb', 'mysql'], - ], - 'database' => [ - 'type' => 'database', - 'dsns' => System::getEnv('_APP_CONNECTIONS_DB_PROJECT', $fallbackForDB), - 'multiple' => true, - 'schemes' => ['mariadb', 'mysql'], - ], - 'queue' => [ - 'type' => 'queue', - 'dsns' => System::getEnv('_APP_CONNECTIONS_QUEUE', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'pubsub' => [ - 'type' => 'pubsub', - 'dsns' => System::getEnv('_APP_CONNECTIONS_PUBSUB', $fallbackForRedis), - 'multiple' => false, - 'schemes' => ['redis'], - ], - 'cache' => [ - 'type' => 'cache', - 'dsns' => System::getEnv('_APP_CONNECTIONS_CACHE', $fallbackForRedis), - 'multiple' => true, - 'schemes' => ['redis'], - ], - ]; + $maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151); + $instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14); - $pools = []; - $poolSize = (int)System::getEnv('_APP_POOL_SIZE', 64); + $multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled'; - foreach ($connections as $key => $connection) { - $dsns = $connection['dsns'] ?? ''; - $multiple = $connection['multiple'] ?? false; - $schemes = $connection['schemes'] ?? []; - $dsns = explode(',', $connection['dsns'] ?? ''); - $config = []; + if ($multiprocessing) { + $workerCount = swoole_cpu_num() * intval(System::getEnv('_APP_WORKER_PER_CORE', 6)); + } else { + $workerCount = 1; + } - foreach ($dsns as &$dsn) { - $dsn = explode('=', $dsn); - $name = ($multiple) ? $key . '_' . $dsn[0] : $key; - $config[] = $name; - $dsn = $dsn[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); + } - if (empty($dsn)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}"); - } + $poolSize = (int)($instanceConnections / $workerCount); - $dsn = new DSN($dsn); - $dsnHost = $dsn->getHost(); - $dsnPort = $dsn->getPort(); - $dsnUser = $dsn->getUser(); - $dsnPass = $dsn->getPassword(); - $dsnScheme = $dsn->getScheme(); - $dsnDatabase = $dsn->getPath(); + 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; + } - if (!in_array($dsnScheme, $schemes)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); - } + $dsn = new DSN($dsn); + $dsnHost = $dsn->getHost(); + $dsnPort = $dsn->getPort(); + $dsnUser = $dsn->getUser(); + $dsnPass = $dsn->getPassword(); + $dsnScheme = $dsn->getScheme(); + $dsnDatabase = $dsn->getPath(); - /** - * Get Resource - * - * Creation could be reused accross connection types like database, cache, queue, etc. - * - * Resource assignment to an adapter will happen below. - */ - switch ($dsnScheme) { - case 'mysql': - case 'mariadb': - $pool = new PDOPool( - (new PDOConfig()) - ->withHost($dsnHost) - ->withPort($dsnPort) - ->withDbName($dsnDatabase) - ->withCharset('utf8mb4') - ->withUsername($dsnUser) - ->withPassword($dsnPass) - ->withOptions([ - // No need to set PDO::ATTR_ERRMODE it is overwitten in PDOProxy - // 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, - PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, + if (!in_array($dsnScheme, $schemes)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme"); + } - ]), - $poolSize - ); + /** + * Get Resource + * + * Creation could be reused across connection types like database, cache, queue, etc. + * + * Resource assignment to an adapter will happen below. + */ + $resource = match ($dsnScheme) { + 'mysql', + 'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { + return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) { + return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array( + PDO::ATTR_TIMEOUT => 3, // Seconds + PDO::ATTR_PERSISTENT => true, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => true, + PDO::ATTR_STRINGIFY_FETCHES => true + )); + }); + }, + 'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) { + $redis = new Redis(); + @$redis->pconnect($dsnHost, (int)$dsnPort); + if ($dsnPass) { + $redis->auth($dsnPass); + } + $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); + + return $redis; + }, + default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'), + }; + + $pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) { + // Get Adapter + switch ($type) { + case 'database': + $adapter = match ($dsn->getScheme()) { + 'mariadb' => new MariaDB($resource()), + 'mysql' => new MySQL($resource()), + default => null + }; + + $adapter->setDatabase($dsn->getPath()); break; - case 'redis': - $pool = new RedisPool( - (new RedisConfig()) - ->withHost($dsnHost) - ->withPort((int)$dsnPort) - ->withAuth($dsnPass ?? ''), - $poolSize - ); + case 'pubsub': + $adapter = $resource(); + break; + case 'queue': + $adapter = match ($dsn->getScheme()) { + 'redis' => new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()), + default => null + }; + break; + case 'cache': + $adapter = match ($dsn->getScheme()) { + 'redis' => new RedisCache($resource()), + default => null + }; break; default: - throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid scheme"); + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); } - $pools['pools-' . $key . '-' . $name] = [ - 'pool' => $pool, - 'dsn' => $dsn, - ]; - } + return $adapter; + }); - Config::setParam('pools-' . $key, $config); + $group->add($pool); } - return function () use ($pools): array { - return $pools; - }; - })() -); + Config::setParam('pools-' . $key, $config); + } -$registry->set('smtp', function () { + 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(); @@ -329,65 +1027,739 @@ $registry->set('smtp', function () { return $mail; }); - -$registry->set('promiseAdapter', function () { +$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; -$registry->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', ''); +$locales = Config::getParam('locale-codes', []); - return new PDO( - "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", - $dbUser, - $dbPass, - SQL::getPDOAttributes() - ); +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); +} + +\stream_context_set_default([ // Set global user agent and http settings + 'http' => [ + 'method' => 'GET', + 'user_agent' => \sprintf( + APP_USERAGENT, + System::getEnv('_APP_VERSION', 'UNKNOWN'), + System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)) + ), + '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', [])); }); -// Autoload -class_exists(JWT::class, true); -class_exists(DSN::class, true); -class_exists(Log::class, true); -class_exists(TOTP::class, true); -class_exists(Mail::class, true); -class_exists(Func::class, true); -class_exists(Cache::class, true); -class_exists(Abuse::class, true); -class_exists(MySQL::class, true); -class_exists(Event::class, true); -class_exists(Audit::class, true); -class_exists(Usage::class, true); -class_exists(Local::class, true); -class_exists(Build::class, true); -class_exists(Locale::class, true); -class_exists(Delete::class, true); -class_exists(GitHub::class, true); -class_exists(Schema::class, true); -class_exists(Domain::class, true); -class_exists(Console::class, true); -class_exists(Request::class, true); -class_exists(MariaDB::class, true); -class_exists(Document::class, true); -class_exists(Sharding::class, true); -class_exists(Database::class, true); -class_exists(Hostname::class, true); -class_exists(TimeLimit::class, true); -class_exists(Migration::class, true); -class_exists(Messaging::class, true); -class_exists(CacheRedis::class, true); -class_exists(Connections::class, true); -class_exists(Certificate::class, true); -class_exists(EventDatabase::class, true); -class_exists(Authorization::class, true); -class_exists(Authentication::class, true); -class_exists(Queue\Connection\Redis::class, true); +// Queues +App::setResource('queue', function (Group $pools) { + return $pools->get('queue')->pop()->getResource(); +}, ['pools']); +App::setResource('queueForMessaging', function (Connection $queue) { + return new Messaging($queue); +}, ['queue']); +App::setResource('queueForMails', function (Connection $queue) { + return new Mail($queue); +}, ['queue']); +App::setResource('queueForBuilds', function (Connection $queue) { + return new Build($queue); +}, ['queue']); +App::setResource('queueForDatabase', function (Connection $queue) { + return new EventDatabase($queue); +}, ['queue']); +App::setResource('queueForDeletes', function (Connection $queue) { + return new Delete($queue); +}, ['queue']); +App::setResource('queueForEvents', function (Connection $queue) { + return new Event($queue); +}, ['queue']); +App::setResource('queueForAudits', function (Connection $queue) { + return new Audit($queue); +}, ['queue']); +App::setResource('queueForFunctions', function (Connection $queue) { + return new Func($queue); +}, ['queue']); +App::setResource('queueForUsage', function (Connection $queue) { + return new Usage($queue); +}, ['queue']); +App::setResource('queueForCertificates', function (Connection $queue) { + return new Certificate($queue); +}, ['queue']); +App::setResource('queueForMigrations', function (Connection $queue) { + return new Migration($queue); +}, ['queue']); +App::setResource('clients', function ($request, $console, $project) { + $console->setAttribute('platforms', [ // Always allow current host + '$collection' => ID::custom('platforms'), + 'name' => 'Current Host', + 'type' => Origin::CLIENT_TYPE_WEB, + 'hostname' => $request->getHostname(), + ], Document::SET_TYPE_APPEND); -require_once __DIR__ . '/init/resources.php'; + $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); + } -Models::init(); + /** + * Get All verified client URLs for both console and current projects + * + Filter for duplicated entries + */ + $clientsConsole = \array_map( + fn ($node) => $node['hostname'], + \array_filter( + $console->getAttribute('platforms', []), + fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname'])) + ) + ); + + $clients = $clientsConsole; + $platforms = $project->getAttribute('platforms', []); + + foreach ($platforms as $node) { + if ( + isset($node['type']) && + ($node['type'] === Origin::CLIENT_TYPE_WEB || + $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && + !empty($node['hostname']) + ) { + $clients[] = $node['hostname']; + } + } + + return \array_unique($clients); +}, ['request', 'console', 'project']); + +App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) { + /** @var Appwrite\Utopia\Request $request */ + /** @var Appwrite\Utopia\Response $response */ + /** @var Utopia\Database\Document $project */ + /** @var Utopia\Database\Database $dbForProject */ + /** @var Utopia\Database\Database $dbForConsole */ + /** @var string $mode */ + + Authorization::setDefaultStatus(true); + + Auth::setCookieName('a_session_' . $project->getId()); + + if (APP_MODE_ADMIN === $mode) { + Auth::setCookieName('a_session_' . $console->getId()); + } + + $session = Auth::decodeSession( + $request->getCookie( + Auth::$cookieName, // Get sessions + $request->getCookie(Auth::$cookieName . '_legacy', '') + ) + ); + + // Get session from header for SSR clients + if (empty($session['id']) && empty($session['secret'])) { + $sessionHeader = $request->getHeader('x-appwrite-session', ''); + + if (!empty($sessionHeader)) { + $session = Auth::decodeSession($sessionHeader); + } + } + + // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies + if ($response) { + $response->addHeader('X-Debug-Fallback', 'false'); + } + + if (empty($session['id']) && empty($session['secret'])) { + if ($response) { + $response->addHeader('X-Debug-Fallback', 'true'); + } + $fallback = $request->getHeader('x-fallback-cookies', ''); + $fallback = \json_decode($fallback, true); + $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); + } + + Auth::$unique = $session['id'] ?? ''; + Auth::$secret = $session['secret'] ?? ''; + + if (APP_MODE_ADMIN !== $mode) { + if ($project->isEmpty()) { + $user = new Document([]); + } else { + if ($project->getId() === 'console') { + $user = $dbForConsole->getDocument('users', Auth::$unique); + } else { + $user = $dbForProject->getDocument('users', Auth::$unique); + } + } + } else { + $user = $dbForConsole->getDocument('users', Auth::$unique); + } + + if ( + $user->isEmpty() // Check a document has been found in the DB + || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) + ) { // Validate user has valid login token + $user = new Document([]); + } + + if (APP_MODE_ADMIN === $mode) { + if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { + Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. + } else { + $user = new Document([]); + } + } + + $authJWT = $request->getHeader('x-appwrite-jwt', ''); + + if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication + $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); + + try { + $payload = $jwt->decode($authJWT); + } catch (JWTException $error) { + throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); + } + + $jwtUserId = $payload['userId'] ?? ''; + if (!empty($jwtUserId)) { + $user = $dbForProject->getDocument('users', $jwtUserId); + } + + $jwtSessionId = $payload['sessionId'] ?? ''; + if (!empty($jwtSessionId)) { + if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token + $user = new Document([]); + } + } + } + + $dbForProject->setMetadata('user', $user->getId()); + $dbForConsole->setMetadata('user', $user->getId()); + + return $user; +}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']); + +App::setResource('project', function ($dbForConsole, $request, $console) { + /** @var Appwrite\Utopia\Request $request */ + /** @var Utopia\Database\Database $dbForConsole */ + /** @var Utopia\Database\Document $console */ + + $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); + + if (empty($projectId) || $projectId === 'console') { + return $console; + } + + $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + + return $project; +}, ['dbForConsole', 'request', 'console']); + +App::setResource('session', function (Document $user) { + if ($user->isEmpty()) { + return; + } + + $sessions = $user->getAttribute('sessions', []); + $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); + + if (!$sessionId) { + return; + } + + foreach ($sessions as $session) {/** @var Document $session */ + if ($sessionId === $session->getId()) { + return $session; + } + } + + return; +}, ['user']); + +App::setResource('console', function () { + return new Document([ + '$id' => ID::custom('console'), + '$internalId' => ID::custom('console'), + 'name' => 'Appwrite', + '$collection' => ID::custom('projects'), + 'description' => 'Appwrite core engine', + 'logo' => '', + 'teamId' => -1, + 'webhooks' => [], + 'keys' => [], + 'platforms' => [ + [ + '$collection' => ID::custom('platforms'), + 'name' => 'Localhost', + 'type' => Origin::CLIENT_TYPE_WEB, + 'hostname' => 'localhost', + ], // Current host is added on app init + ], + 'legalName' => '', + 'legalCountry' => '', + 'legalState' => '', + 'legalCity' => '', + 'legalAddress' => '', + 'legalTaxId' => '', + 'auths' => [ + 'mockNumbers' => [], + 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', + 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user + 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds + 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' + ], + 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], + 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], + 'oAuthProviders' => [ + 'githubEnabled' => true, + 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), + 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') + ], + ]); +}, []); + +App::setResource('dbForProject', function (Group $pools, Database $dbForConsole, Cache $cache, Document $project) { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + $dbAdapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); + + $database + ->setMetadata('host', \gethostname()) + ->setMetadata('project', $project->getId()) + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + return $database; +}, ['pools', 'dbForConsole', 'cache', 'project']); + +App::setResource('dbForConsole', function (Group $pools, Cache $cache) { + $dbAdapter = $pools + ->get('console') + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); + + $database + ->setNamespace('_console') + ->setMetadata('host', \gethostname()) + ->setMetadata('project', 'console') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + return $database; +}, ['pools', 'cache']); + +App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { + $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools + + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases) { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + $configure = (function (Database $database) use ($project, $dsn) { + $database + ->setMetadata('host', \gethostname()) + ->setMetadata('project', $project->getId()) + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); + + if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + }); + + if (isset($databases[$dsn->getHost()])) { + $database = $databases[$dsn->getHost()]; + $configure($database); + return $database; + } + + $dbAdapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + + $database = new Database($dbAdapter, $cache); + $databases[$dsn->getHost()] = $database; + $configure($database); + + return $database; + }; +}, ['pools', 'dbForConsole', 'cache']); + +App::setResource('cache', function (Group $pools) { + $list = Config::getParam('pools-cache', []); + $adapters = []; + + foreach ($list as $value) { + $adapters[] = $pools + ->get($value) + ->pop() + ->getResource() + ; + } + + return new Cache(new Sharding($adapters)); +}, ['pools']); + +App::setResource('deviceForLocal', function () { + return new Local(); +}); + +App::setResource('deviceForFiles', function ($project) { + return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); +}, ['project']); + +App::setResource('deviceForFunctions', function ($project) { + return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); +}, ['project']); + +App::setResource('deviceForBuilds', function ($project) { + return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); +}, ['project']); + +function getDevice($root): Device +{ + $connection = System::getEnv('_APP_CONNECTIONS_STORAGE', ''); + + if (!empty($connection)) { + $acl = 'private'; + $device = Storage::DEVICE_LOCAL; + $accessKey = ''; + $accessSecret = ''; + $bucket = ''; + $region = ''; + + try { + $dsn = new DSN($connection); + $device = $dsn->getScheme(); + $accessKey = $dsn->getUser() ?? ''; + $accessSecret = $dsn->getPassword() ?? ''; + $bucket = $dsn->getPath() ?? ''; + $region = $dsn->getParam('region'); + } catch (\Throwable $e) { + Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); + } + + switch ($device) { + case Storage::DEVICE_S3: + return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case STORAGE::DEVICE_DO_SPACES: + $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); + $device->setHttpVersion(S3::HTTP_VERSION_1_1); + return $device; + case Storage::DEVICE_BACKBLAZE: + return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_LINODE: + return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_WASABI: + return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); + case Storage::DEVICE_LOCAL: + default: + return new Local($root); + } + } else { + switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { + case Storage::DEVICE_LOCAL: + default: + return new Local($root); + case Storage::DEVICE_S3: + $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); + $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); + $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); + $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); + $s3Acl = 'private'; + return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); + case Storage::DEVICE_DO_SPACES: + $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); + $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); + $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); + $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); + $doSpacesAcl = 'private'; + $device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); + $device->setHttpVersion(S3::HTTP_VERSION_1_1); + return $device; + case Storage::DEVICE_BACKBLAZE: + $backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', ''); + $backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', ''); + $backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', ''); + $backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', ''); + $backblazeAcl = 'private'; + return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl); + case Storage::DEVICE_LINODE: + $linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', ''); + $linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', ''); + $linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', ''); + $linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', ''); + $linodeAcl = 'private'; + return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl); + case Storage::DEVICE_WASABI: + $wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', ''); + $wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', ''); + $wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', ''); + $wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', ''); + $wasabiAcl = 'private'; + return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl); + } + } +} + +App::setResource('mode', function ($request) { + /** @var Appwrite\Utopia\Request $request */ + + /** + * Defines the mode for the request: + * - 'default' => Requests for Client and Server Side + * - 'admin' => Request from the Console on non-console projects + */ + return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT)); +}, ['request']); + +App::setResource('geodb', function ($register) { + /** @var Utopia\Registry\Registry $register */ + return $register->get('geodb'); +}, ['register']); + +App::setResource('passwordsDictionary', function ($register) { + /** @var Utopia\Registry\Registry $register */ + return $register->get('passwordsDictionary'); +}, ['register']); + + +App::setResource('servers', function () { + $platforms = Config::getParam('platforms'); + $server = $platforms[APP_PLATFORM_SERVER]; + + $languages = array_map(function ($language) { + return strtolower($language['name']); + }, $server['sdks']); + + return $languages; +}); + +App::setResource('promiseAdapter', function ($register) { + return $register->get('promiseAdapter'); +}, ['register']); + +App::setResource('schema', function ($utopia, $dbForProject) { + + $complexity = function (int $complexity, array $args) { + $queries = Query::parseQueries($args['queries'] ?? []); + $query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null; + $limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT; + + return $complexity * $limit; + }; + + $attributes = function (int $limit, int $offset) use ($dbForProject) { + $attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [ + Query::limit($limit), + Query::offset($offset), + ])); + + return \array_map(function ($attr) { + return $attr->getArrayCopy(); + }, $attrs); + }; + + $urls = [ + 'list' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents"; + }, + 'create' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents"; + }, + 'read' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; + }, + 'update' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; + }, + 'delete' => function (string $databaseId, string $collectionId, array $args) { + return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}"; + }, + ]; + + $params = [ + 'list' => function (string $databaseId, string $collectionId, array $args) { + return [ 'queries' => $args['queries']]; + }, + 'create' => function (string $databaseId, string $collectionId, array $args) { + $id = $args['id'] ?? 'unique()'; + $permissions = $args['permissions'] ?? null; + + unset($args['id']); + unset($args['permissions']); + + // Order must be the same as the route params + return [ + 'databaseId' => $databaseId, + 'documentId' => $id, + 'collectionId' => $collectionId, + 'data' => $args, + 'permissions' => $permissions, + ]; + }, + 'update' => function (string $databaseId, string $collectionId, array $args) { + $documentId = $args['id']; + $permissions = $args['permissions'] ?? null; + + unset($args['id']); + unset($args['permissions']); + + // Order must be the same as the route params + return [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + 'documentId' => $documentId, + 'data' => $args, + 'permissions' => $permissions, + ]; + }, + ]; + + return Schema::build( + $utopia, + $complexity, + $attributes, + $urls, + $params, + ); +}, ['utopia', 'dbForProject']); + +App::setResource('contributors', function () { + $path = 'app/config/contributors.json'; + $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; + return $list; +}); + +App::setResource('employees', function () { + $path = 'app/config/employees.json'; + $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; + return $list; +}); + +App::setResource('heroes', function () { + $path = 'app/config/heroes.json'; + $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : []; + return $list; +}); + +App::setResource('gitHub', function (Cache $cache) { + return new VcsGitHub($cache); +}, ['cache']); + +App::setResource('requestTimestamp', function ($request) { + //TODO: Move this to the Request class itself + $timestampHeader = $request->getHeader('x-appwrite-timestamp'); + $requestTimestamp = null; + if (!empty($timestampHeader)) { + try { + $requestTimestamp = new \DateTime($timestampHeader); + } catch (\Throwable $e) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value'); + } + } + return $requestTimestamp; +}, ['request']); +App::setResource('plan', function (array $plan = []) { + return []; +}); diff --git a/app/init/config.php b/app/init/config.php deleted file mode 100644 index bc8333b5d9..0000000000 --- a/app/init/config.php +++ /dev/null @@ -1,37 +0,0 @@ - $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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 $database->getAuthorization()->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 = $database->getAuthorization()->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->find('targets', [ - Query::equal('$internalId', $targetIds) - ]); - } - return []; - } -); - -Database::addFilter( - 'providerSearch', - function (mixed $value, Document $provider) { - $searchValues = [ - $provider->getId(), - $provider->getAttribute('name', ''), - $provider->getAttribute('provider', ''), - $provider->getAttribute('type', '') - ]; - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'topicSearch', - function (mixed $value, Document $topic) { - $searchValues = [ - $topic->getId(), - $topic->getAttribute('name', ''), - $topic->getAttribute('description', ''), - ]; - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); - -Database::addFilter( - 'messageSearch', - function (mixed $value, Document $message) { - $searchValues = [ - $message->getId(), - $message->getAttribute('description', ''), - $message->getAttribute('status', ''), - ]; - - $data = \json_decode($message->getAttribute('data', []), true); - $providerType = $message->getAttribute('providerType', ''); - - if ($providerType === MESSAGE_TYPE_EMAIL) { - $searchValues = \array_merge($searchValues, [$data['subject'], MESSAGE_TYPE_EMAIL]); - } elseif ($providerType === MESSAGE_TYPE_SMS) { - $searchValues = \array_merge($searchValues, [$data['content'], MESSAGE_TYPE_SMS]); - } else { - $searchValues = \array_merge($searchValues, [$data['title'], MESSAGE_TYPE_PUSH]); - } - - $search = \implode(' ', \array_filter($searchValues)); - - return $search; - }, - function (mixed $value) { - return $value; - } -); diff --git a/app/init/database/formats.php b/app/init/database/formats.php deleted file mode 100644 index 88e46655ac..0000000000 --- a/app/init/database/formats.php +++ /dev/null @@ -1,43 +0,0 @@ -getScheme(); - $accessKey = $dsn->getUser() ?? ''; - $accessSecret = $dsn->getPassword() ?? ''; - $bucket = $dsn->getPath() ?? ''; - $region = $dsn->getParam('region'); - } catch (\Throwable $e) { - Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.'); - } - - switch ($device) { - case Storage::DEVICE_S3: - return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case STORAGE::DEVICE_DO_SPACES: - return new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_BACKBLAZE: - return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LINODE: - return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_WASABI: - return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl); - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - } - } else { - switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) { - case Storage::DEVICE_LOCAL: - default: - return new Local($root); - case Storage::DEVICE_S3: - $s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', ''); - $s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', ''); - $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); - $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); - $s3Acl = 'private'; - return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); - case Storage::DEVICE_DO_SPACES: - $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); - $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); - $doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', ''); - $doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', ''); - $doSpacesAcl = 'private'; - return new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl); - 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); - } - } -} - -$log = new Dependency(); -$mode = new Dependency(); -$user = new Dependency(); -$plan = new Dependency(); -$pools = new Dependency(); -$geodb = new Dependency(); -$cache = new Dependency(); -$pools = new Dependency(); -$queue = new Dependency(); -$hooks = new Dependency(); -$logger = new Dependency(); -$locale = new Dependency(); -$schema = new Dependency(); -$github = new Dependency(); -$session = new Dependency(); -$console = new Dependency(); -$project = new Dependency(); -$clients = new Dependency(); -$servers = new Dependency(); -$register = new Dependency(); -$connections = new Dependency(); -$localeCodes = new Dependency(); -$getConsoleDB = new Dependency(); -$getProjectDB = new Dependency(); -$dbForProject = new Dependency(); -$dbForConsole = new Dependency(); -$queueForUsage = new Dependency(); -$queueForMails = new Dependency(); -$authorization = new Dependency(); -$authentication = new Dependency(); -$queueForBuilds = new Dependency(); -$deviceForLocal = new Dependency(); -$deviceForFiles = new Dependency(); -$queueForEvents = new Dependency(); -$queueForAudits = new Dependency(); -$promiseAdapter = new Dependency(); -$schemaVariable = new Dependency(); -$deviceForBuilds = new Dependency(); -$queueForDeletes = new Dependency(); -$requestTimestamp = new Dependency(); -$queueForDatabase = new Dependency(); -$queueForMessaging = new Dependency(); -$queueForFunctions = new Dependency(); -$queueForMigrations = new Dependency(); -$deviceForFunctions = new Dependency(); -$passwordsDictionary = new Dependency(); -$queueForCertificates = new Dependency(); - - -$plan - ->setName('plan') - ->setCallback(fn () => []); - -$mode - ->setName('mode') - ->inject('request') - ->setCallback(function (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)); - }); - -$user - ->setName('user') - ->inject('mode') - ->inject('project') - ->inject('console') - ->inject('request') - ->inject('response') - ->inject('dbForProject') - ->inject('dbForConsole') - ->inject('authorization') - ->inject('authentication') - ->setCallback(function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Authorization $authorization, Authentication $authentication) { - $authorization->setDefaultStatus(true); - $authentication->setCookieName('a_session_' . $project->getId()); - - if (APP_MODE_ADMIN === $mode) { - $authentication->setCookieName('a_session_' . $console->getId()); - } - - $session = Auth::decodeSession( - $request->getCookie( - $authentication->getCookieName(), // Get sessions - $request->getCookie($authentication->getCookieName() . '_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[$authentication->getCookieName()])) ? $fallback[$authentication->getCookieName()] : '')); - } - - $authentication->setUnique($session['id'] ?? ''); - $authentication->setSecret($session['secret'] ?? ''); - - if (APP_MODE_ADMIN !== $mode) { - if ($project->isEmpty()) { - $user = new Document([]); - } else { - if ($project->getId() === 'console') { - $user = $dbForConsole->getDocument('users', $authentication->getUnique()); - } else { - $user = $dbForProject->getDocument('users', $authentication->getUnique()); - } - } - } else { - $user = $dbForConsole->getDocument('users', $authentication->getUnique()); - } - - if ( - $user->isEmpty() // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), $authentication->getSecret()) - ) { // Validate user has valid login token - $user = new Document([]); - } - - if (APP_MODE_ADMIN === $mode) { - if ($user->find('teamId', $project->getAttribute('teamId'), '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) { - $request->removeHeader('x-appwrite-jwt'); - 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([]); - } - } - } - - // Adds logs to database queries - $dbForProject->setMetadata('user', $user->getId()); - $dbForConsole->setMetadata('user', $user->getId()); - - return $user; - }); - - -$session - ->setName('session') - ->inject('user') - ->inject('project') - ->inject('authorization') - ->inject('authentication') - ->setCallback(function (Document $user, Document $project, Authorization $authorization, Authentication $authentication) { - if ($user->isEmpty()) { - return; - } - - $sessions = $user->getAttribute('sessions', []); - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; - $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), $authentication->getSecret(), $authDuration); - - if (!$sessionId) { - return; - } - - foreach ($sessions as $session) { - if ($sessionId === $session->getId()) { - return $session; - } - } - - return; - }); - -$console - ->setName('console') - ->setCallback(function () { - return new Document([ - '$id' => ID::custom('console'), - '$internalId' => ID::custom('console'), - 'name' => 'Appwrite', - '$collection' => ID::custom('projects'), - 'description' => 'Appwrite core engine', - 'logo' => '', - 'teamId' => -1, - 'webhooks' => [], - 'keys' => [], - 'platforms' => [ - [ - '$collection' => ID::custom('platforms'), - 'name' => 'Localhost', - 'type' => Origin::CLIENT_TYPE_WEB, - 'hostname' => 'localhost', - ], // Current host is added on app init - ], - 'legalName' => '', - 'legalCountry' => '', - 'legalState' => '', - 'legalCity' => '', - 'legalAddress' => '', - 'legalTaxId' => '', - 'auths' => [ - 'mockNumbers' => [], - 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', - 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds - 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled' - ], - 'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [], - 'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [], - 'oAuthProviders' => [ - 'githubEnabled' => true, - 'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''), - 'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '') - ], - ]); - }); - - -$project - ->setName('project') - ->inject('dbForConsole') - ->inject('request') - ->inject('console') - ->inject('authorization') - ->setCallback(function (Database $dbForConsole, Request $request, Document $console, Authorization $authorization) { - $projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', '')); - - if (empty($projectId) || $projectId === 'console') { - return $console; - } - - $project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - - return $project; - }); - -$pools - ->setName('pools') - ->inject('registry') - ->setCallback(function (Registry $registry) { - return $registry->get('pools'); - }); - -$dbForProject - ->setName('dbForProject') - ->inject('cache') - ->inject('pools') - ->inject('project') - ->inject('dbForConsole') - ->inject('authorization') - ->inject('connections') - ->setCallback(function (Cache $cache, array $pools, Document $project, Database $dbForConsole, Authorization $authorization, Connections $connections) { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; - $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($connectionDsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($connectionDsn->getPath()); - - $database = new Database($adapter, $cache); - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - $database->setAuthorization($authorization); - return $database; - }); - -$dbForConsole - ->setName('dbForConsole') - ->inject('getConsoleDB') - ->inject('connections') - ->setCallback(function (callable $getConsoleDB, Connections $connections): Database { - [$connection,$pool, $database] = $getConsoleDB(); - $connections->add($connection, $pool); - - return $database; - }); - -$cache - ->setName('cache') - ->inject('pools') - ->inject('connections') - ->setCallback(function (array $pools, Connections $connections) { - $adapters = []; - $databases = Config::getParam('pools-cache'); - - foreach ($databases as $database) { - $pool = $pools['pools-cache-' . $database]['pool']; - $dsn = $pools['pools-cache-' . $database]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapters[] = new CacheRedis($connection); - } - - return new Cache(new Sharding($adapters)); - }); - -$authorization - ->setName('authorization') - ->setCallback(function (): Authorization { - return new Authorization(); - }); - -$authentication - ->setName('authentication') - ->setCallback(function (): Authentication { - return new Authentication(); - }); - -$register - ->setName('registry') - ->setCallback(function () use (&$registry): Registry { - return $registry; - }); - -$pools - ->setName('pools') - ->inject('registry') - ->setCallback(function (Registry $registry) { - return $registry->get('pools'); - }); - -$logger - ->setName('logger') - ->inject('registry') - ->setCallback(function (Registry $registry) { - return $registry->get('logger'); - }); - -$log - ->setName('log') - ->setCallback(function () { - return new Log(); - }); - -$connections - ->setName('connections') - ->setCallback(function () { - return new Connections(); - }); - -$locale - ->setName('locale') - ->setCallback(fn () => new Locale(System::getEnv('_APP_LOCALE', 'en'))); - -$localeCodes - ->setName('localeCodes') - ->setCallback(fn () => array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []))); - -$queue - ->setName('queue') - ->inject('pools') - ->inject('connections') - ->setCallback(function (array $pools, Connections $connections) { - $pool = $pools['pools-queue-queue']['pool']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - return new Queue\Connection\Redis($connection); - }); - -$queueForMessaging - ->setName('queueForMessaging') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Messaging($queue); - }); - -$queueForMails - ->setName('queueForMails') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Mail($queue); - }); - -$queueForBuilds - ->setName('queueForBuilds') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Build($queue); - }); - -$queueForDatabase - ->setName('queueForDatabase') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new EventDatabase($queue); - }); - -$queueForDeletes - ->setName('queueForDeletes') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Delete($queue); - }); - -$queueForEvents - ->setName('queueForEvents') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Event($queue); - }); - -$queueForAudits - ->setName('queueForAudits') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Audit($queue); - }); - -$queueForFunctions - ->setName('queueForFunctions') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Func($queue); - }); - -$queueForUsage - ->setName('queueForUsage') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Usage($queue); - }); - -$queueForCertificates - ->setName('queueForCertificates') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Certificate($queue); - }); - -$queueForMigrations - ->setName('queueForMigrations') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new Migration($queue); - }); - -$deviceForLocal - ->setName('deviceForLocal') - ->setCallback(function () { - return new Local(); - }); - -$deviceForFiles - ->setName('deviceForFiles') - ->inject('project') - ->setCallback(function ($project) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); - }); - -$deviceForFunctions - ->setName('deviceForFunctions') - ->inject('project') - ->setCallback(function ($project) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); - }); - -$deviceForBuilds - ->setName('deviceForBuilds') - ->inject('project') - ->setCallback(function ($project) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); - }); - -$clients - ->setName('clients') - ->inject('request') - ->inject('console') - ->inject('project') - ->setCallback(function (Request $request, Document $console, Document $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) && isset($node['hostname']) && !empty($node['hostname'])) - ) - ); - - $clients = \array_unique( - \array_merge( - $clientsConsole, - \array_map( - fn ($node) => $node['hostname'], - \array_filter( - $project->getAttribute('platforms', []), - fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB || $node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) && isset($node['hostname']) && !empty($node['hostname'])) - ) - ) - ) - ); - - return $clients; - }); - -$servers - ->setName('servers') - ->setCallback(function () { - $platforms = Config::getParam('platforms'); - $server = $platforms[APP_PLATFORM_SERVER]; - - $languages = array_map(function ($language) { - return strtolower($language['name']); - }, $server['sdks']); - - return $languages; - }); - -$geodb - ->setName('geodb') - ->inject('registry') - ->setCallback(function (Registry $register) { - return $register->get('geodb'); - }); - -$passwordsDictionary - ->setName('passwordsDictionary') - ->setCallback(function () { - $content = file_get_contents(__DIR__ . '/../assets/security/10k-common-passwords'); - $content = explode("\n", $content); - $content = array_flip($content); - return $content; - }); - -$hooks - ->setName('hooks') - ->inject('registry') - ->setCallback(function (Registry $registry) { - return $registry->get('hooks'); - }); - -$github - ->setName('gitHub') - ->inject('cache') - ->setCallback(function (Cache $cache) { - return new GitHub($cache); - }); - -$requestTimestamp - ->setName('requestTimestamp') - ->inject('request') - ->setCallback(function ($request) { - $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; - }); -$getConsoleDB - ->setName('getConsoleDB') - ->inject('pools') - ->inject('cache') - ->inject('authorization') - ->inject('connections') - ->setCallback(function (array $pools, Cache $cache, Authorization $authorization) { - return function () use ($pools, $cache, $authorization): array { - $pool = $pools['pools-console-console']['pool']; - $dsn = $pools['pools-console-console']['dsn']; - $connection = $pool->get(); - - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - - $adapter->setDatabase($dsn->getPath()); - - $database = new Database($adapter, $cache); - $database->setAuthorization($authorization); - - $database - ->setNamespace('_console') - ->setMetadata('host', \gethostname()) - ->setMetadata('project', 'console') - ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS); - - return [$connection, $pool, $database]; - }; - }); - -$getProjectDB - ->setName('getProjectDB') - ->inject('pools') - ->inject('dbForConsole') - ->inject('cache') - ->inject('authorization') - ->inject('connections') - ->setCallback(function (array $pools, Database $dbForConsole, Cache $cache, Authorization $authorization, Connections $connections) { - $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools - - return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases, $authorization, $connections): Database { - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - try { - $dsn = new DSN($project->getAttribute('database')); - } catch (\InvalidArgumentException) { - // TODO: Temporary until all projects are using shared tables - $dsn = new DSN('mysql://' . $project->getAttribute('database')); - } - - if (isset($databases[$dsn->getHost()])) { - $database = $databases[$dsn->getHost()]; - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; - } - - $pool = $pools['pools-database-' . $dsn->getHost()]['pool']; - $connectionDsn = $pools['pools-database-' . $dsn->getHost()]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($connectionDsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - $adapter->setDatabase($connectionDsn->getPath()); - - $database = new Database($adapter, $cache); - $database->setAuthorization($authorization); - - $databases[$dsn->getHost()] = $database; - - if ($dsn->getHost() === DATABASE_SHARED_TABLES) { - $database - ->setSharedTables(true) - ->setTenant($project->getInternalId()) - ->setNamespace($dsn->getParam('namespace')); - } else { - $database - ->setSharedTables(false) - ->setTenant(null) - ->setNamespace('_' . $project->getInternalId()); - } - - return $database; - }; - }); - -$promiseAdapter - ->setName('promiseAdapter') - ->setCallback(function () use ($registry) { - return $registry->get('promiseAdapter'); - }); - -$schemaVariable - ->setName('schemaVariable') - ->setCallback(fn () => new Schema()); - -$schema - ->setName('schema') - ->inject('http') - ->inject('context') - ->inject('request') - ->inject('response') - ->inject('dbForProject') - ->inject('authorization') - ->inject('schemaVariable') - ->setCallback(function (Http $http, Container $context, Request $request, Response $response, Database $dbForProject, Authorization $authorization, $schemaVariable) { - $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, $authorization) { - $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']); - - 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']); - - return [ - 'databaseId' => $databaseId, - 'collectionId' => $collectionId, - 'documentId' => $documentId, - 'data' => $args, - 'permissions' => $permissions, - ]; - }, - ]; - - return $schemaVariable->build( - $http, - $request, - $response, - $context, - $complexity, - $attributes, - $urls, - $params, - ); - }); - -$container->set($log); -$container->set($mode); -$container->set($user); -$container->set($plan); -$container->set($cache); -$container->set($pools); -$container->set($queue); -$container->set($geodb); -$container->set($hooks); -$container->set($locale); -$container->set($schema); -$container->set($github); -$container->set($logger); -$container->set($session); -$container->set($console); -$container->set($project); -$container->set($clients); -$container->set($servers); -$container->set($register); -$container->set($connections); -$container->set($localeCodes); -$container->set($dbForProject); -$container->set($dbForConsole); -$container->set($getConsoleDB); -$container->set($getProjectDB); -$container->set($queueForUsage); -$container->set($queueForMails); -$container->set($authorization); -$container->set($authentication); -$container->set($schemaVariable); -$container->set($queueForBuilds); -$container->set($queueForEvents); -$container->set($queueForAudits); -$container->set($deviceForLocal); -$container->set($deviceForFiles); -$container->set($promiseAdapter); -$container->set($queueForDeletes); -$container->set($deviceForBuilds); -$container->set($queueForDatabase); -$container->set($requestTimestamp); -$container->set($queueForMessaging); -$container->set($queueForFunctions); -$container->set($queueForMigrations); -$container->set($deviceForFunctions); -$container->set($passwordsDictionary); -$container->set($queueForCertificates); diff --git a/app/realtime.php b/app/realtime.php index f4460e7a11..b8fdb2cf21 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -5,47 +5,136 @@ use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Network\Validator\Origin; -use Appwrite\Utopia\Queue\Connections; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; use Swoole\Http\Request as SwooleRequest; -use Swoole\Http\Response as SwooleHttpResponse; use Swoole\Http\Response as SwooleResponse; use Swoole\Runtime; use Swoole\Table; use Swoole\Timer; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\Database\TimeLimit; +use Utopia\App; +use Utopia\Cache\Adapter\Sharding; +use Utopia\Cache\Cache; use Utopia\CLI\Console; +use Utopia\Config\Config; +use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; -use Utopia\DI\Container; -use Utopia\DI\Dependency; -use Utopia\Http\Adapter\Swoole\Request as UtopiaRequest; -use Utopia\Http\Adapter\Swoole\Response as HttpResponse; -use Utopia\Http\Adapter\Swoole\Response as UtopiaResponse; -use Utopia\Http\Http; +use Utopia\Database\Validator\Authorization; +use Utopia\DSN\DSN; use Utopia\Logger\Log; -use Utopia\Pools\Connection; -use Utopia\Registry\Registry; use Utopia\System\System; use Utopia\WebSocket\Adapter; use Utopia\WebSocket\Server; /** - * @var Registry $registry - * @var Container $container + * @var \Utopia\Registry\Registry $register */ -global $registry, $container; - - require_once __DIR__ . '/init.php'; Runtime::enableCoroutine(SWOOLE_HOOK_ALL); +// Allows overriding +if (!function_exists("getConsoleDB")) { + function getConsoleDB(): Database + { + global $register; + + /** @var \Utopia\Pools\Group $pools */ + $pools = $register->get('pools'); + + $dbAdapter = $pools + ->get('console') + ->pop() + ->getResource() + ; + + $database = new Database($dbAdapter, getCache()); + + $database + ->setNamespace('_console') + ->setMetadata('host', \gethostname()) + ->setMetadata('project', '_console'); + + return $database; + } +} + +// Allows overriding +if (!function_exists("getProjectDB")) { + function getProjectDB(Document $project): Database + { + global $register; + + /** @var \Utopia\Pools\Group $pools */ + $pools = $register->get('pools'); + + if ($project->isEmpty() || $project->getId() === 'console') { + return getConsoleDB(); + } + + 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')); + } + + $adapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + + $database = new Database($adapter, getCache()); + + if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + $database + ->setMetadata('host', \gethostname()) + ->setMetadata('project', $project->getId()); + + return $database; + } +} + +// Allows overriding +if (!function_exists("getCache")) { + function getCache(): Cache + { + global $register; + + $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ + + $list = Config::getParam('pools-cache', []); + $adapters = []; + + foreach ($list as $value) { + $adapters[] = $pools + ->get($value) + ->pop() + ->getResource() + ; + } + + return new Cache(new Sharding($adapters)); + } +} + $realtime = new Realtime(); /** @@ -70,8 +159,8 @@ $adapter $server = new Server($adapter); -$logError = function (Throwable $error, string $action) use ($registry) { - $logger = $registry->get('logger'); +$logError = function (Throwable $error, string $action) use ($register) { + $logger = $register->get('logger'); if ($logger && !$error instanceof Exception) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); @@ -107,16 +196,16 @@ $logError = function (Throwable $error, string $action) use ($registry) { $server->error($logError); -$server->onStart(function () use ($stats, $container, $containerId, &$statsDocument, $logError) { +$server->onStart(function () use ($stats, $register, $containerId, &$statsDocument, $logError) { sleep(5); // wait for the initial database schema to be ready Console::success('Server started successfully'); - $authorization = $container->get('authorization'); + /** * Create document for this worker to share stats across Containers. */ - go(function () use ($container, $containerId, &$statsDocument) { + go(function () use ($register, $containerId, &$statsDocument) { $attempts = 0; - $database = $container->get('dbForConsole'); + $database = getConsoleDB(); do { try { @@ -130,15 +219,14 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum 'value' => '{}' ]); - $authorization = $container->get('authorization'); - $statsDocument = $authorization->skip(fn () => $database->createDocument('realtime', $document)); + $statsDocument = Authorization::skip(fn () => $database->createDocument('realtime', $document)); break; } catch (Throwable) { Console::warning("Collection not ready. Retrying connection ({$attempts})..."); sleep(DATABASE_RECONNECT_SLEEP); } } while (true); - ($container->get('connections'))->reclaim(); + $register->get('pools')->reclaim(); }); /** @@ -146,7 +234,7 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum */ // TODO: Remove this if check once it doesn't cause issues for cloud if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted') { - Timer::tick(5000, function () use ($container, $stats, &$statsDocument, $logError, $authorization) { + Timer::tick(5000, function () use ($register, $stats, &$statsDocument, $logError) { $payload = []; foreach ($stats as $projectId => $value) { $payload[$projectId] = $stats->get($projectId, 'connectionsTotal'); @@ -156,43 +244,40 @@ $server->onStart(function () use ($stats, $container, $containerId, &$statsDocum } try { - $database = $container->get('dbForConsole'); + $database = getConsoleDB(); $statsDocument ->setAttribute('timestamp', DateTime::now()) ->setAttribute('value', json_encode($payload)); - $authorization->skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); + Authorization::skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument)); } catch (Throwable $th) { call_user_func($logError, $th, "updateWorkerDocument"); } finally { - ($container->get('connections'))->reclaim(); - $container->refresh('dbForConsole'); + $register->get('pools')->reclaim(); } }); } }); -$server->onWorkerStart(function (int $workerId) use ($server, $container, $stats, $realtime, $logError) { +$server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime, $logError) { Console::success('Worker ' . $workerId . ' started successfully'); $attempts = 0; $start = time(); - $authorization = $container->get('authorization'); - - Timer::tick(5000, function () use ($server, $container, $realtime, $stats, $logError, $authorization) { + Timer::tick(5000, function () use ($server, $register, $realtime, $stats, $logError) { /** * Sending current connections to project channels on the console project every 5 seconds. */ // TODO: Remove this if check once it doesn't cause issues for cloud if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted') { if ($realtime->hasSubscriber('console', Role::users()->toString(), 'project')) { - $database = $container->get('dbForConsole'); + $database = getConsoleDB(); $payload = []; - $list = $authorization->skip(fn () => $database->find('realtime', [ + $list = Authorization::skip(fn () => $database->find('realtime', [ Query::greaterThan('timestamp', DateTime::addSeconds(new \DateTime(), -15)), ])); @@ -232,8 +317,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats 'data' => $event['data'] ])); } - ($container->get('connections'))->reclaim(); - $container->refresh('dbForConsole'); + + $register->get('pools')->reclaim(); } } /** @@ -263,26 +348,13 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats while ($attempts < 300) { try { if ($attempts > 0) { - Console::error( - 'Pub/sub connection lost (lasted ' . (time() - $start) . ' seconds, worker: ' . $workerId . '). - Attempting restart in 5 seconds (attempt #' . $attempts . ')' - ); + Console::error('Pub/sub connection lost (lasted ' . (time() - $start) . ' seconds, worker: ' . $workerId . '). + Attempting restart in 5 seconds (attempt #' . $attempts . ')'); sleep(5); // 5 sec delay between connection attempts } - $start = time(); - $pools = $container->get('pools'); - $pool = $pools['pools-pubsub-pubsub']['pool']; - - /** @var Connections $connections */ - $connections = $container->get('connections'); - $connection = $pool->get(); - $connections->add($connection, $pool); - - $redis = $connection; - - /** @var Redis $redis */ + $redis = $register->get('pools')->get('pubsub')->pop()->getResource(); /** @var Redis $redis */ $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); if ($redis->ping(true)) { @@ -292,7 +364,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats Console::error('Pub/sub failed (worker: ' . $workerId . ')'); } - $redis->subscribe(['realtime'], function (Redis $redis, string $channel, string $payload) use ($server, $workerId, $stats, $realtime, $authorization, $container) { + $redis->subscribe(['realtime'], function (Redis $redis, string $channel, string $payload) use ($server, $workerId, $stats, $register, $realtime) { $event = json_decode($payload, true); if ($event['permissionsChanged'] && isset($event['userId'])) { @@ -301,24 +373,25 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) { $connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId])); - $dbForConsole = $container->get('dbForConsole'); + $consoleDatabase = getConsoleDB(); + $project = Authorization::skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); + $database = getProjectDB($project); - $project = $authorization->skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - $dbForProject = $container->get('getProjectDB')($project); + $user = $database->getDocument('users', $userId); - $user = $dbForProject->getDocument('users', $userId); - - $roles = Auth::getRoles($user, $authorization); + $roles = Auth::getRoles($user); $channels = $realtime->connections[$connection]['channels']; $realtime->unsubscribe($connection); $realtime->subscribe($projectId, $connection, $roles, $channels); + + $register->get('pools')->reclaim(); } } $receivers = $realtime->getSubscribers($event); - if (Http::isDevelopment() && !empty($receivers)) { + if (App::isDevelopment() && !empty($receivers)) { Console::log("[Debug][Worker {$workerId}] Receivers: " . count($receivers)); Console::log("[Debug][Worker {$workerId}] Receivers Connection IDs: " . json_encode($receivers)); Console::log("[Debug][Worker {$workerId}] Event: " . $payload); @@ -344,40 +417,30 @@ $server->onWorkerStart(function (int $workerId) use ($server, $container, $stats sleep(DATABASE_RECONNECT_SLEEP); continue; } finally { - ($container->get('connections'))->reclaim(); - $container->refresh('dbForConsole'); + $register->get('pools')->reclaim(); } } Console::error('Failed to restart pub/sub...'); }); -$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $container, $stats, &$realtime, $logError) { - $authorization = $container->get('authorization'); - - $request = new Request(new UtopiaRequest($request)); - $response = new Response(new UtopiaResponse(new SwooleResponse())); - - $requestInjection = new Dependency(); - $responseInjection = new Dependency(); - - $requestInjection->setName('request')->setCallback(fn () => $request); - $responseInjection->setName('response')->setCallback(fn () => $response); - - $container->set($requestInjection); - $container->set($responseInjection); +$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime, $logError) { + $app = new App('UTC'); + $request = new Request($request); + $response = new Response(new SwooleResponse()); Console::info("Connection open (user: {$connection})"); + App::setResource('pools', fn () => $register->get('pools')); + App::setResource('request', fn () => $request); + App::setResource('response', fn () => $response); + try { - /** @var Document $project */ - $project = $container->refresh('project')->get('project'); - - $container->refresh('dbForProject'); + $project = $app->getResource('project'); /* - * Project Check + * Project Check */ if (empty($project->getId())) { throw new Exception(Exception::REALTIME_POLICY_VIOLATION, 'Missing or unknown project ID'); @@ -386,16 +449,15 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, if ( array_key_exists('realtime', $project->getAttribute('apis', [])) && !$project->getAttribute('apis', [])['realtime'] - && !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles())) + && !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles())) ) { throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } - $dbForProject = $container->get('getProjectDB')($project); - /** @var Document $console */ - $console = $container->get('console'); - /** @var Document $user */ - $user = $container->refresh('user')->get('user'); + $dbForProject = getProjectDB($project); + $console = $app->getResource('console'); /** @var Document $console */ + $user = $app->getResource('user'); /** @var Document $user */ + /* * Abuse Check * @@ -424,8 +486,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription()); } - $authorization = $container->get('authorization'); - $roles = Auth::getRoles($user, $authorization); + $roles = Auth::getRoles($user); $channels = Realtime::convertChannels($request->getQuery('channels', []), $user->getId()); @@ -474,33 +535,25 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $server->send([$connection], json_encode($response)); $server->close($connection, $code); - if (Http::isDevelopment()) { + if (App::isDevelopment()) { Console::error('[Error] Connection Error'); Console::error('[Error] Code: ' . $response['data']['code']); Console::error('[Error] Message: ' . $response['data']['message']); } } finally { - $connections = $container->get('connections'); - $connections->reclaim(); + $register->get('pools')->reclaim(); } }); -$server->onWorkerStop(function (int $workerId) use ($container) { - $connections = $container->get('connections'); - $connections->reclaim(); -}); - -$server->onMessage(function (int $connection, string $message) use ($server, $container, $realtime, $containerId) { +$server->onMessage(function (int $connection, string $message) use ($server, $register, $realtime, $containerId) { try { - $response = new Response(new HttpResponse(new SwooleHttpResponse())); + $response = new Response(new SwooleResponse()); $projectId = $realtime->connections[$connection]['projectId']; - $database = $container->get('dbForConsole'); - $authorization = $container->get('authorization'); - $authentication = $container->get('authentication'); + $database = getConsoleDB(); if ($projectId !== 'console') { - $project = $authorization->skip(fn () => $database->getDocument('projects', $projectId)); - $database = $container->get('getProjectDB')($project); + $project = Authorization::skip(fn () => $database->getDocument('projects', $projectId)); + $database = getProjectDB($project); } else { $project = null; } @@ -538,21 +591,20 @@ $server->onMessage(function (int $connection, string $message) use ($server, $co } $session = Auth::decodeSession($message['data']['session']); + Auth::$unique = $session['id'] ?? ''; + Auth::$secret = $session['secret'] ?? ''; - $authentication->setUnique($session['id'] ?? ''); - $authentication->setSecret($session['secret'] ?? ''); - - $user = $database->getDocument('users', $authentication->getUnique()); + $user = $database->getDocument('users', Auth::$unique); if ( empty($user->getId()) // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), $authentication->getSecret()) // Validate user has valid login token + || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) // Validate user has valid login token ) { // cookie not valid throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.'); } - $roles = Auth::getRoles($user, $authorization); + $roles = Auth::getRoles($user); $channels = Realtime::convertChannels(array_flip($realtime->connections[$connection]['channels']), $user->getId()); $realtime->subscribe($realtime->connections[$connection]['projectId'], $connection, $roles, $channels); @@ -586,8 +638,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $co $server->close($connection, $th->getCode()); } } finally { - ($container->get('connections'))->reclaim(); - $container->refresh('dbForConsole'); + $register->get('pools')->reclaim(); } }); diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 547a8758f4..8f83fed544 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -11,8 +11,7 @@ $httpsPort = $this->getParam('httpsPort', ''); $version = $this->getParam('version', ''); $organization = $this->getParam('organization', ''); $image = $this->getParam('image', ''); -?> -services: +?>services: traefik: image: traefik:2.11 container_name: appwrite-traefik @@ -852,7 +851,7 @@ services: - MYSQL_USER=${_APP_DB_USER} - MYSQL_PASSWORD=${_APP_DB_PASS} - MARIADB_AUTO_UPGRADE=1 - command: 'mysqld --innodb-flush-method=fsync --max_connections=5000' + command: 'mysqld --innodb-flush-method=fsync' redis: image: redis:7.2.4-alpine diff --git a/app/worker.php b/app/worker.php index 80d027fbc2..9bcdae78e6 100644 --- a/app/worker.php +++ b/app/worker.php @@ -2,107 +2,278 @@ require_once __DIR__ . '/init.php'; +use Appwrite\Event\Audit; +use Appwrite\Event\Build; +use Appwrite\Event\Certificate; +use Appwrite\Event\Database as EventDatabase; +use Appwrite\Event\Delete; +use Appwrite\Event\Event; +use Appwrite\Event\Func; +use Appwrite\Event\Mail; +use Appwrite\Event\Messaging; +use Appwrite\Event\Migration; +use Appwrite\Event\Usage; use Appwrite\Event\UsageDump; use Appwrite\Platform\Appwrite; -use Appwrite\Utopia\Queue\Connections; use Swoole\Runtime; +use Utopia\App; +use Utopia\Cache\Adapter\Sharding; +use Utopia\Cache\Cache; use Utopia\CLI\Console; +use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; -use Utopia\DI\Dependency; +use Utopia\DSN\DSN; use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Platform\Service; +use Utopia\Pools\Group; use Utopia\Queue\Connection; use Utopia\Queue\Message; -use Utopia\Queue\Worker; -use Utopia\Storage\Device\Local; +use Utopia\Queue\Server; +use Utopia\Registry\Registry; use Utopia\System\System; -global $registry, $container; - +Authorization::disable(); Runtime::enableCoroutine(SWOOLE_HOOK_ALL); -$project = new Dependency(); -$register = new Dependency(); -$dbForProject = new Dependency(); -$abuseRetention = new Dependency(); -$deviceForCache = new Dependency(); -$auditRetention = new Dependency(); -$queueForUsageDump = new Dependency(); -$executionRetention = new Dependency(); -$deviceForLocalFiles = new Dependency(); +Server::setResource('register', fn () => $register); -$register - ->setName('register') - ->setCallback(fn () => $registry); +Server::setResource('dbForConsole', function (Cache $cache, Registry $register) { + $pools = $register->get('pools'); + $database = $pools + ->get('console') + ->pop() + ->getResource(); -$project - ->setName('project') - ->inject('message') - ->inject('dbForConsole') - ->setCallback(function (Message $message, Database $dbForConsole) { - $payload = $message->getPayload() ?? []; - $project = new Document($payload['project'] ?? []); + $adapter = new Database($database, $cache); + $adapter->setNamespace('_console'); - if ($project->getId() === 'console') { - return $project; + return $adapter; +}, ['cache', 'register']); + +Server::setResource('project', function (Message $message, Database $dbForConsole) { + $payload = $message->getPayload() ?? []; + $project = new Document($payload['project'] ?? []); + + if ($project->getId() === 'console') { + return $project; + } + + return $dbForConsole->getDocument('projects', $project->getId()); +}, ['message', 'dbForConsole']); + +Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForConsole) { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + $pools = $register->get('pools'); + + 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')); + } + + $adapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); + + $database = new Database($adapter, $cache); + + try { + $dsn = new DSN($project->getAttribute('database')); + } catch (\InvalidArgumentException) { + // TODO: Temporary until all projects are using shared tables + $dsn = new DSN('mysql://' . $project->getAttribute('database')); + } + + if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + + return $database; +}, ['cache', 'register', 'message', 'project', 'dbForConsole']); + +Server::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { + $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools + + return function (Document $project) use ($pools, $dbForConsole, $cache, &$databases): Database { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; } - return $dbForConsole->getDocument('projects', $project->getId()); - }); + 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')); + } -$abuseRetention - ->setName('abuseRetention') - ->setCallback(function () { - return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400)); - }); + if (isset($databases[$dsn->getHost()])) { + $database = $databases[$dsn->getHost()]; -$auditRetention - ->setName('auditRetention') - ->setCallback(function () { - return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600)); - }); + if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } -$executionRetention - ->setName('executionRetention') - ->setCallback(function () { - return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); - }); + return $database; + } -$queueForUsageDump - ->setName('queueForUsageDump') - ->inject('queue') - ->setCallback(function (Connection $queue) { - return new UsageDump($queue); - }); + $dbAdapter = $pools + ->get($dsn->getHost()) + ->pop() + ->getResource(); -$deviceForCache - ->setName('deviceForCache') - ->inject('project') - ->setCallback(function (Document $project) { - return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId()); - }); + $database = new Database($dbAdapter, $cache); -$deviceForLocalFiles - ->setName('deviceForLocalFiles') - ->inject('project') - ->setCallback(function (Document $project) { - return new Local(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); - }); + $databases[$dsn->getHost()] = $database; -$container->set($project); -$container->set($register); -$container->set($dbForProject); -$container->set($abuseRetention); -$container->set($auditRetention); -$container->set($deviceForCache); -$container->set($queueForUsageDump); -$container->set($executionRetention); -$container->set($deviceForLocalFiles); + if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) { + $database + ->setSharedTables(true) + ->setTenant($project->getInternalId()) + ->setNamespace($dsn->getParam('namespace')); + } else { + $database + ->setSharedTables(false) + ->setTenant(null) + ->setNamespace('_' . $project->getInternalId()); + } + return $database; + }; +}, ['pools', 'dbForConsole', 'cache']); + +Server::setResource('abuseRetention', function () { + return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400)); +}); + +Server::setResource('auditRetention', function () { + return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600)); +}); + +Server::setResource('executionRetention', function () { + return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); +}); + +Server::setResource('cache', function (Registry $register) { + $pools = $register->get('pools'); + $list = Config::getParam('pools-cache', []); + $adapters = []; + + foreach ($list as $value) { + $adapters[] = $pools + ->get($value) + ->pop() + ->getResource() + ; + } + + return new Cache(new Sharding($adapters)); +}, ['register']); + +Server::setResource('log', fn () => new Log()); + +Server::setResource('queueForUsage', function (Connection $queue) { + return new Usage($queue); +}, ['queue']); + +Server::setResource('queueForUsageDump', function (Connection $queue) { + return new UsageDump($queue); +}, ['queue']); + +Server::setResource('queue', function (Group $pools) { + return $pools->get('queue')->pop()->getResource(); +}, ['pools']); + +Server::setResource('queueForDatabase', function (Connection $queue) { + return new EventDatabase($queue); +}, ['queue']); + +Server::setResource('queueForMessaging', function (Connection $queue) { + return new Messaging($queue); +}, ['queue']); + +Server::setResource('queueForMails', function (Connection $queue) { + return new Mail($queue); +}, ['queue']); + +Server::setResource('queueForBuilds', function (Connection $queue) { + return new Build($queue); +}, ['queue']); + +Server::setResource('queueForDeletes', function (Connection $queue) { + return new Delete($queue); +}, ['queue']); + +Server::setResource('queueForEvents', function (Connection $queue) { + return new Event($queue); +}, ['queue']); + +Server::setResource('queueForAudits', function (Connection $queue) { + return new Audit($queue); +}, ['queue']); + +Server::setResource('queueForFunctions', function (Connection $queue) { + return new Func($queue); +}, ['queue']); + +Server::setResource('queueForCertificates', function (Connection $queue) { + return new Certificate($queue); +}, ['queue']); + +Server::setResource('queueForMigrations', function (Connection $queue) { + return new Migration($queue); +}, ['queue']); + +Server::setResource('logger', function (Registry $register) { + return $register->get('logger'); +}, ['register']); + +Server::setResource('pools', function (Registry $register) { + return $register->get('pools'); +}, ['register']); + +Server::setResource('deviceForFunctions', function (Document $project) { + return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); +}, ['project']); + +Server::setResource('deviceForFiles', function (Document $project) { + return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); +}, ['project']); + +Server::setResource('deviceForBuilds', function (Document $project) { + return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); +}, ['project']); + +Server::setResource('deviceForCache', function (Document $project) { + return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId()); +}, ['project']); + + +$pools = $register->get('pools'); $platform = new Appwrite(); $args = $platform->getEnv('argv'); @@ -121,13 +292,6 @@ if (\str_starts_with($workerName, 'databases')) { } try { - $connection = new Connection\Redis( - System::getEnv('_APP_REDIS_HOST', 'redis'), - System::getEnv('_APP_REDIS_PORT', '6379'), - System::getEnv('_APP_REDIS_USER', ''), - System::getEnv('_APP_REDIS_PASS', '') - ); - /** * Any worker can be configured with the following env vars: * - _APP_WORKERS_NUM The total number of worker processes @@ -136,35 +300,32 @@ try { */ $platform->init(Service::TYPE_WORKER, [ 'workersNum' => System::getEnv('_APP_WORKERS_NUM', 1), - 'connection' => $connection, + 'connection' => $pools->get('queue')->pop()->getResource(), 'workerName' => strtolower($workerName) ?? null, 'queueName' => $queueName ]); } catch (\Throwable $e) { - Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); + Console::error($e->getMessage() . ', File: ' . $e->getFile() . ', Line: ' . $e->getLine()); } -Worker::init() - ->inject('authorization') - ->action(function (Authorization $authorization) { - $authorization->disable(); +$worker = $platform->getWorker(); + +$worker + ->shutdown() + ->inject('pools') + ->action(function (Group $pools) { + $pools->reclaim(); }); -Worker::shutdown() - ->inject('connections') - ->action(function (Connections $connections) { - $connections->reclaim(); - }); - -Worker::error() +$worker + ->error() ->inject('error') ->inject('logger') ->inject('log') - ->inject('connections') + ->inject('pools') ->inject('project') - ->inject('authorization') - ->action(function (Throwable $error, ?Logger $logger, Log $log, Connections $connections, Document $project, Authorization $authorization) use ($queueName) { - $connections->reclaim(); + ->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($queueName) { + $pools->reclaim(); $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); if ($logger) { @@ -180,7 +341,7 @@ Worker::error() $log->addExtra('file', $error->getFile()); $log->addExtra('line', $error->getLine()); $log->addExtra('trace', $error->getTraceAsString()); - $log->addExtra('roles', $authorization->getRoles()); + $log->addExtra('roles', Authorization::getRoles()); $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); @@ -195,7 +356,9 @@ Worker::error() Console::error('[Error] Line: ' . $error->getLine()); }); -$platform - ->getWorker() - ->setContainer($container) - ->start(); +$worker->workerStart() + ->action(function () use ($workerName) { + Console::info("Worker $workerName started"); + }); + +$worker->start(); diff --git a/composer.json b/composer.json index e23806adc9..91ff1eeb92 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,6 @@ "description": "End to end backend server for frontend and mobile apps.", "type": "project", "license": "BSD-3-Clause", - "minimum-stability": "stable", "authors": [ { "name": "Eldad Fux", @@ -15,8 +14,7 @@ "test": "vendor/bin/phpunit", "lint": "vendor/bin/pint --test", "format": "vendor/bin/pint", - "bench": "vendor/bin/phpbench run --report=benchmark", - "check": "./vendor/bin/phpstan analyse -c phpstan.neon --memory-limit 1G app src tests" + "bench": "vendor/bin/phpbench run --report=benchmark" }, "autoload": { "psr-4": { @@ -47,33 +45,33 @@ "ext-sockets": "*", "appwrite/php-runtimes": "0.15.*", "appwrite/php-clamav": "2.0.*", - "utopia-php/abuse": "0.44.*", - "utopia-php/analytics": "0.13.*", - "utopia-php/audit": "0.44.*", + "utopia-php/abuse": "0.43.0", + "utopia-php/analytics": "0.10.*", + "utopia-php/audit": "0.43.0", "utopia-php/cache": "0.10.*", - "utopia-php/cli": "0.19.*", + "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.54.*", - "utopia-php/domains": "0.6.*", - "utopia-php/dsn": "0.2.*", - "utopia-php/framework": "1.0.*", + "utopia-php/database": "0.53.*", + "utopia-php/domains": "0.5.*", + "utopia-php/dsn": "0.2.1", + "utopia-php/framework": "0.33.*", "utopia-php/fetch": "0.2.*", "utopia-php/image": "0.6.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.6.*", "utopia-php/messaging": "0.12.*", - "utopia-php/migration": "0.4.*", - "utopia-php/orchestration": "0.15.*", - "utopia-php/platform": "0.8.*", - "utopia-php/view": "0.2.*", + "utopia-php/migration": "0.5.*", + "utopia-php/orchestration": "0.9.*", + "utopia-php/platform": "0.7.*", "utopia-php/pools": "0.5.*", "utopia-php/preloader": "0.2.*", - "utopia-php/queue": "0.8.*", + "utopia-php/queue": "0.7.*", "utopia-php/registry": "0.5.*", - "utopia-php/storage": "0.19.*", + "utopia-php/storage": "0.18.*", + "utopia-php/swoole": "0.8.*", "utopia-php/system": "0.8.*", - "utopia-php/vcs": "0.9.*", - "utopia-php/websocket": "0.2.*", + "utopia-php/vcs": "0.8.*", + "utopia-php/websocket": "0.1.*", "matomo/device-detector": "6.1.*", "dragonmantank/cron-expression": "3.3.2", "phpmailer/phpmailer": "6.9.1", @@ -90,8 +88,7 @@ "swoole/ide-helper": "5.1.2", "textalk/websocket": "1.5.7", "laravel/pint": "^1.14", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "1.8.*" + "phpbench/phpbench": "^1.2" }, "provide": { "ext-phpiredis": "*" diff --git a/composer.lock b/composer.lock index cff13d28a1..147800df32 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": "6017f815da50b7d4dabad66386e013e3", + "content-hash": "b6820da26239716cf14a445697902a03", "packages": [ { "name": "adhocore/jwt", @@ -1429,16 +1429,16 @@ }, { "name": "utopia-php/abuse", - "version": "0.44.0", + "version": "0.43.0", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "cd37bba23778f3a0dc54b4ba6ffb36d4d91ed8f1" + "reference": "6346a3b4c5177a43160035a7289e30fdfb0790d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/cd37bba23778f3a0dc54b4ba6ffb36d4d91ed8f1", - "reference": "cd37bba23778f3a0dc54b4ba6ffb36d4d91ed8f1", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/6346a3b4c5177a43160035a7289e30fdfb0790d6", + "reference": "6346a3b4c5177a43160035a7289e30fdfb0790d6", "shasum": "" }, "require": { @@ -1446,7 +1446,7 @@ "ext-pdo": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "0.54.*" + "utopia-php/database": "0.53.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1474,28 +1474,27 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.44.0" + "source": "https://github.com/utopia-php/abuse/tree/0.43.0" }, - "time": "2024-09-05T16:09:32+00:00" + "time": "2024-08-30T05:17:23+00:00" }, { "name": "utopia-php/analytics", - "version": "0.13.0", + "version": "0.10.2", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "3dace02af5d4190623f88fb6e02f5559a99f14c4" + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/3dace02af5d4190623f88fb6e02f5559a99f14c4", - "reference": "3dace02af5d4190623f88fb6e02f5559a99f14c4", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.19.*", - "utopia-php/system": "0.8.*" + "utopia-php/cli": "^0.15.0" }, "require-dev": { "laravel/pint": "dev-main", @@ -1521,27 +1520,27 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.13.0" + "source": "https://github.com/utopia-php/analytics/tree/0.10.2" }, - "time": "2024-09-05T16:19:26+00:00" + "time": "2023-03-22T12:01:09+00:00" }, { "name": "utopia-php/audit", - "version": "0.44.0", + "version": "0.43.0", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "69eee24e4d6cb8fdae31235d80b9a46b18092139" + "reference": "cef22b5dc6a6d28fcd522f41c7bf7ded4a4dfd3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/69eee24e4d6cb8fdae31235d80b9a46b18092139", - "reference": "69eee24e4d6cb8fdae31235d80b9a46b18092139", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/cef22b5dc6a6d28fcd522f41c7bf7ded4a4dfd3e", + "reference": "cef22b5dc6a6d28fcd522f41c7bf7ded4a4dfd3e", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.54.*" + "utopia-php/database": "0.53.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1568,9 +1567,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.44.0" + "source": "https://github.com/utopia-php/audit/tree/0.43.0" }, - "time": "2024-09-05T16:12:41+00:00" + "time": "2024-08-30T05:17:36+00:00" }, { "name": "utopia-php/cache", @@ -1624,29 +1623,27 @@ }, { "name": "utopia-php/cli", - "version": "0.19.0", + "version": "0.15.0", "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "f8af1d6087f498bc1f0191750a118d357ded9948" + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/f8af1d6087f498bc1f0191750a118d357ded9948", - "reference": "f8af1d6087f498bc1f0191750a118d357ded9948", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", "shasum": "" }, "require": { "php": ">=7.4", - "utopia-php/di": "0.1.*", - "utopia-php/framework": "1.0.*" + "utopia-php/framework": "0.*.*" }, "require-dev": { "laravel/pint": "1.2.*", - "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.3", "squizlabs/php_codesniffer": "^3.6", - "swoole/ide-helper": "4.8.8" + "vimeo/psalm": "4.0.1" }, "type": "library", "autoload": { @@ -1669,9 +1666,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.19.0" + "source": "https://github.com/utopia-php/cli/tree/0.15.0" }, - "time": "2024-09-05T15:46:56+00:00" + "time": "2023-03-01T05:55:14+00:00" }, { "name": "utopia-php/config", @@ -1726,16 +1723,16 @@ }, { "name": "utopia-php/database", - "version": "0.54.1", + "version": "0.53.4", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "c32d6eab5992c927cbf6fb4aad51d76fc5f64946" + "reference": "36a0e89d983afc1368635282e04fa762220a1d2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/c32d6eab5992c927cbf6fb4aad51d76fc5f64946", - "reference": "c32d6eab5992c927cbf6fb4aad51d76fc5f64946", + "url": "https://api.github.com/repos/utopia-php/database/zipball/36a0e89d983afc1368635282e04fa762220a1d2a", + "reference": "36a0e89d983afc1368635282e04fa762220a1d2a", "shasum": "" }, "require": { @@ -1743,7 +1740,7 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.10.*", - "utopia-php/framework": "1.0.*", + "utopia-php/framework": "0.33.*", "utopia-php/mongo": "0.3.*" }, "require-dev": { @@ -1754,7 +1751,7 @@ "phpunit/phpunit": "9.6.*", "rregeer/phpunit-coverage-check": "0.3.*", "swoole/ide-helper": "5.1.3", - "utopia-php/cli": "0.19.*" + "utopia-php/cli": "0.14.*" }, "type": "library", "autoload": { @@ -1776,79 +1773,30 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.54.1" + "source": "https://github.com/utopia-php/database/tree/0.53.4" }, - "time": "2024-09-10T10:08:37+00:00" - }, - { - "name": "utopia-php/di", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/di.git", - "reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/di/zipball/22490c95f7ac3898ed1c33f1b1b5dd577305ee31", - "reference": "22490c95f7ac3898ed1c33f1b1b5dd577305ee31", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25", - "swoole/ide-helper": "4.8.3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\": "src/", - "Tests\\E2E\\": "tests/e2e" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple and lite library for managing dependency injections", - "keywords": [ - "framework", - "http", - "php", - "upf" - ], - "support": { - "issues": "https://github.com/utopia-php/di/issues", - "source": "https://github.com/utopia-php/di/tree/0.1.0" - }, - "time": "2024-08-08T14:35:19+00:00" + "time": "2024-09-10T10:19:57+00:00" }, { "name": "utopia-php/domains", - "version": "0.6.0", + "version": "0.5.0", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "5c70b0f524deeb1fccc3962ad1da650ae2c94cda" + "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/5c70b0f524deeb1fccc3962ad1da650ae2c94cda", - "reference": "5c70b0f524deeb1fccc3962ad1da650ae2c94cda", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/bf07f60326f8389f378ddf6fcde86217e5cfe18c", + "reference": "bf07f60326f8389f378ddf6fcde86217e5cfe18c", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/framework": "1.0.*" + "utopia-php/framework": "0.*.*" }, "require-dev": { "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.9.x-dev", "phpunit/phpunit": "^9.3" }, "type": "library", @@ -1885,9 +1833,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.6.0" + "source": "https://github.com/utopia-php/domains/tree/0.5.0" }, - "time": "2024-09-05T16:21:11+00:00" + "time": "2024-01-03T22:04:27+00:00" }, { "name": "utopia-php/dsn", @@ -1977,30 +1925,26 @@ }, { "name": "utopia-php/framework", - "version": "1.0.2", + "version": "0.33.8", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "fc63ec61c720190a5ea5bb484c615145850951e6" + "reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/fc63ec61c720190a5ea5bb484c615145850951e6", - "reference": "fc63ec61c720190a5ea5bb484c615145850951e6", + "url": "https://api.github.com/repos/utopia-php/http/zipball/a7f577540a25cb90896fef2b64767bf8d700f3c5", + "reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5", "shasum": "" }, "require": { - "ext-swoole": "*", - "php": ">=8.0", - "utopia-php/servers": "0.1.*" + "php": ">=8.0" }, "require-dev": { - "ext-xdebug": "*", "laravel/pint": "^1.2", "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25", - "swoole/ide-helper": "4.8.3" + "phpunit/phpunit": "^9.5.25" }, "type": "library", "autoload": { @@ -2012,18 +1956,17 @@ "license": [ "MIT" ], - "description": "A simple, light and advanced PHP HTTP framework", + "description": "A simple, light and advanced PHP framework", "keywords": [ "framework", - "http", "php", "upf" ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/1.0.2" + "source": "https://github.com/utopia-php/http/tree/0.33.8" }, - "time": "2024-09-10T09:04:19+00:00" + "time": "2024-08-15T14:10:09+00:00" }, { "name": "utopia-php/image", @@ -2231,16 +2174,16 @@ }, { "name": "utopia-php/migration", - "version": "0.4.4", + "version": "0.5.2", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33" + "reference": "f18d44d4459f78c292dac9edde856fd156fe497a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/a8a5d392bebf082faf289f4dfe09d9fd76844c33", - "reference": "a8a5d392bebf082faf289f4dfe09d9fd76844c33", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/f18d44d4459f78c292dac9edde856fd156fe497a", + "reference": "f18d44d4459f78c292dac9edde856fd156fe497a", "shasum": "" }, "require": { @@ -2250,6 +2193,7 @@ "require-dev": { "laravel/pint": "1.*", "phpunit/phpunit": "9.*", + "utopia-php/cli": "^0.18.0", "vlucas/phpdotenv": "5.*" }, "type": "library", @@ -2272,9 +2216,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.4.4" + "source": "https://github.com/utopia-php/migration/tree/0.5.2" }, - "time": "2024-05-17T05:25:31+00:00" + "time": "2024-07-22T09:27:07+00:00" }, { "name": "utopia-php/mongo", @@ -2338,26 +2282,26 @@ }, { "name": "utopia-php/orchestration", - "version": "0.15.0", + "version": "0.9.1", "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", - "reference": "cd55650ba5f13118c3580048e6dd86b604f9a5b3" + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/cd55650ba5f13118c3580048e6dd86b604f9a5b3", - "reference": "cd55650ba5f13118c3580048e6dd86b604f9a5b3", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.19.*" + "utopia-php/cli": "0.15.*" }, "require-dev": { "laravel/pint": "^1.2", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.3", + "vimeo/psalm": "4.0.1" }, "type": "library", "autoload": { @@ -2382,32 +2326,31 @@ ], "support": { "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.15.0" + "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" }, - "time": "2024-09-05T16:28:02+00:00" + "time": "2023-03-17T15:05:06+00:00" }, { "name": "utopia-php/platform", - "version": "0.8.1", + "version": "0.7.0", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "95d57f38a4001c7189a66885c485ac635d305234" + "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/95d57f38a4001c7189a66885c485ac635d305234", - "reference": "95d57f38a4001c7189a66885c485ac635d305234", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", + "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52", "shasum": "" }, "require": { "ext-json": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/cli": "0.19.*", - "utopia-php/framework": "1.0.*", - "utopia-php/queue": "0.8.*", - "utopia-php/servers": "0.1.*" + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.33.*", + "utopia-php/queue": "0.7.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2433,9 +2376,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.8.1" + "source": "https://github.com/utopia-php/platform/tree/0.7.0" }, - "time": "2024-09-06T02:33:27+00:00" + "time": "2024-05-08T17:00:55+00:00" }, { "name": "utopia-php/pools", @@ -2543,23 +2486,22 @@ }, { "name": "utopia-php/queue", - "version": "0.8.0", + "version": "0.7.0", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "a518b271f8c158d6e66e36972f767189111033c2" + "reference": "917565256eb94bcab7246f7a746b1a486813761b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/a518b271f8c158d6e66e36972f767189111033c2", - "reference": "a518b271f8c158d6e66e36972f767189111033c2", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/917565256eb94bcab7246f7a746b1a486813761b", + "reference": "917565256eb94bcab7246f7a746b1a486813761b", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.19.*", - "utopia-php/di": "0.1.*", - "utopia-php/servers": "0.1.*" + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.*.*" }, "require-dev": { "laravel/pint": "^0.2.3", @@ -2569,7 +2511,6 @@ "workerman/workerman": "^4.0" }, "suggest": { - "ext-redis": "Needed to support Redis connections", "ext-swoole": "Needed to support Swoole.", "workerman/workerman": "Needed to support Workerman." }, @@ -2600,9 +2541,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.8.0" + "source": "https://github.com/utopia-php/queue/tree/0.7.0" }, - "time": "2024-09-05T16:33:01+00:00" + "time": "2024-01-17T19:00:43+00:00" }, { "name": "utopia-php/registry", @@ -2656,71 +2597,18 @@ }, "time": "2021-03-10T10:45:22+00:00" }, - { - "name": "utopia-php/servers", - "version": "0.1.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/servers.git", - "reference": "fd5c8d32778f265256c1936372a071b944f5ba8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/servers/zipball/fd5c8d32778f265256c1936372a071b944f5ba8a", - "reference": "fd5c8d32778f265256c1936372a071b944f5ba8a", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/di": "0.1.*" - }, - "require-dev": { - "laravel/pint": "^0.2.3", - "phpstan/phpstan": "^1.8", - "phpunit/phpunit": "^9.5.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\Servers\\": "src/Servers" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Team Appwrite", - "email": "team@appwrite.io" - } - ], - "description": "A base library for building Utopia style servers.", - "keywords": [ - "framework", - "php", - "servers", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/servers/issues", - "source": "https://github.com/utopia-php/servers/tree/0.1.1" - }, - "time": "2024-09-06T02:25:56+00:00" - }, { "name": "utopia-php/storage", - "version": "0.19.0", + "version": "0.18.5", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "5013b894a776874d6010753fc9349df2225c69af" + "reference": "7d355c5e3ccc8ecebc0266f8ddd30088a43be919" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/5013b894a776874d6010753fc9349df2225c69af", - "reference": "5013b894a776874d6010753fc9349df2225c69af", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/7d355c5e3ccc8ecebc0266f8ddd30088a43be919", + "reference": "7d355c5e3ccc8ecebc0266f8ddd30088a43be919", "shasum": "" }, "require": { @@ -2732,8 +2620,8 @@ "ext-zlib": "*", "ext-zstd": "*", "php": ">=8.0", - "utopia-php/framework": "1.0.*", - "utopia-php/system": "0.8.*" + "utopia-php/framework": "0.*.*", + "utopia-php/system": "0.*.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2760,9 +2648,60 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.19.0" + "source": "https://github.com/utopia-php/storage/tree/0.18.5" }, - "time": "2024-09-05T17:00:24+00:00" + "time": "2024-09-04T08:57:27+00:00" + }, + { + "name": "utopia-php/swoole", + "version": "0.8.2", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/swoole.git", + "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", + "reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4", + "shasum": "" + }, + "require": { + "ext-swoole": "*", + "php": ">=8.0", + "utopia-php/framework": "0.33.*" + }, + "require-dev": { + "laravel/pint": "1.2.*", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.3", + "swoole/ide-helper": "5.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Swoole\\": "src/Swoole" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "An extension for Utopia Framework to work with PHP Swoole as a PHP FPM alternative", + "keywords": [ + "framework", + "http", + "php", + "server", + "swoole", + "upf", + "utopia" + ], + "support": { + "issues": "https://github.com/utopia-php/swoole/issues", + "source": "https://github.com/utopia-php/swoole/tree/0.8.2" + }, + "time": "2024-02-01T14:54:12+00:00" }, { "name": "utopia-php/system", @@ -2822,24 +2761,23 @@ }, { "name": "utopia-php/vcs", - "version": "0.9.0", + "version": "0.8.2", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "673abe2fef0750a841a4fa8fa6f99d4a602c68e7" + "reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/673abe2fef0750a841a4fa8fa6f99d4a602c68e7", - "reference": "673abe2fef0750a841a4fa8fa6f99d4a602c68e7", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18", + "reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18", "shasum": "" }, "require": { "adhocore/jwt": "^1.1", "php": ">=8.0", - "utopia-php/cache": "0.10.*", - "utopia-php/framework": "1.0.*", - "utopia-php/system": "0.8.*" + "utopia-php/cache": "^0.10.0", + "utopia-php/framework": "0.*.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -2866,76 +2804,32 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.9.0" + "source": "https://github.com/utopia-php/vcs/tree/0.8.2" }, - "time": "2024-09-05T16:44:48+00:00" - }, - { - "name": "utopia-php/view", - "version": "0.2.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/view.git", - "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/view/zipball/6ee55e83bc014c39ed6b69390f6d399116f65e88", - "reference": "6ee55e83bc014c39ed6b69390f6d399116f65e88", - "shasum": "" - }, - "require": { - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "^1.2", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\View\\": "src/View" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A simple, light and advanced PHP rendering engine", - "keywords": [ - "php", - "view" - ], - "support": { - "issues": "https://github.com/utopia-php/view/issues", - "source": "https://github.com/utopia-php/view/tree/0.2.0" - }, - "time": "2024-04-01T17:21:29+00:00" + "time": "2024-08-13T14:36:30+00:00" }, { "name": "utopia-php/websocket", - "version": "0.2.0", + "version": "0.1.0", "source": { "type": "git", "url": "https://github.com/utopia-php/websocket.git", - "reference": "e9d0919b321744a61f12563f5791c47ba9f57810" + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/websocket/zipball/e9d0919b321744a61f12563f5791c47ba9f57810", - "reference": "e9d0919b321744a61f12563f5791c47ba9f57810", + "url": "https://api.github.com/repos/utopia-php/websocket/zipball/51fcb86171400d8aa40d76c54593481fd273dab5", + "reference": "51fcb86171400d8aa40d76c54593481fd273dab5", "shasum": "" }, "require": { "php": ">=8.0" }, "require-dev": { - "laravel/pint": "^1.15", - "phpstan/phpstan": "^1.8", "phpunit/phpunit": "^9.5.5", - "swoole/ide-helper": "5.1.2", + "swoole/ide-helper": "4.6.6", "textalk/websocket": "1.5.2", + "vimeo/psalm": "^4.8.1", "workerman/workerman": "^4.0" }, "type": "library", @@ -2948,6 +2842,16 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Eldad Fux", + "email": "eldad@appwrite.io" + }, + { + "name": "Torsten Dittmann", + "email": "torsten@appwrite.io" + } + ], "description": "A simple abstraction for WebSocket servers.", "keywords": [ "framework", @@ -2958,9 +2862,9 @@ ], "support": { "issues": "https://github.com/utopia-php/websocket/issues", - "source": "https://github.com/utopia-php/websocket/tree/0.2.0" + "source": "https://github.com/utopia-php/websocket/tree/0.1.0" }, - "time": "2024-04-09T08:28:11+00:00" + "time": "2021-12-20T10:50:09+00:00" }, { "name": "webmozart/assert", @@ -4326,65 +4230,6 @@ }, "time": "2024-09-07T20:13:05+00:00" }, - { - "name": "phpstan/phpstan", - "version": "1.8.11", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "46e223dd68a620da18855c23046ddb00940b4014" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014", - "reference": "46e223dd68a620da18855c23046ddb00940b4014", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0" - }, - "conflict": { - "phpstan/phpstan-shim": "*" - }, - "bin": [ - "phpstan", - "phpstan.phar" - ], - "type": "library", - "autoload": { - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPStan - PHP Static Analysis Tool", - "keywords": [ - "dev", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.8.11" - }, - "funding": [ - { - "url": "https://github.com/ondrejmirtes", - "type": "github" - }, - { - "url": "https://github.com/phpstan", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" - } - ], - "time": "2022-10-24T15:45:13+00:00" - }, { "name": "phpunit/php-code-coverage", "version": "9.2.32", diff --git a/docker-compose.yml b/docker-compose.yml index c4bdffa1d0..6ecb0ecff8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -682,7 +682,6 @@ services: entrypoint: maintenance <<: *x-logging container_name: appwrite-task-maintenance - restart: unless-stopped image: appwrite-dev networks: - appwrite @@ -783,7 +782,6 @@ services: entrypoint: schedule-functions <<: *x-logging container_name: appwrite-task-scheduler-functions - restart: unless-stopped image: appwrite-dev networks: - appwrite @@ -812,7 +810,6 @@ services: entrypoint: schedule-executions <<: *x-logging container_name: appwrite-task-scheduler-executions - restart: unless-stopped image: appwrite-dev networks: - appwrite @@ -840,7 +837,6 @@ services: entrypoint: schedule-messages <<: *x-logging container_name: appwrite-task-scheduler-messages - restart: unless-stopped image: appwrite-dev networks: - appwrite @@ -960,7 +956,7 @@ services: - MYSQL_USER=${_APP_DB_USER} - MYSQL_PASSWORD=${_APP_DB_PASS} - MARIADB_AUTO_UPGRADE=1 - command: 'mysqld --innodb-flush-method=fsync --max_connections=5000' + command: "mysqld --innodb-flush-method=fsync" redis: image: redis:7.2.4-alpine diff --git a/docs/tutorials/add-route.md b/docs/tutorials/add-route.md index 0baa51b5c0..ac6fd40bdb 100644 --- a/docs/tutorials/add-route.md +++ b/docs/tutorials/add-route.md @@ -8,7 +8,7 @@ Setting an alias allows the route to be also accessible from the alias URL. The first parameter specifies the alias URL, the second parameter specifies default values for route parameters. ```php -Http::post('/v1/storage/buckets/:bucketId/files') +App::post('/v1/storage/buckets/:bucketId/files') ->alias('/v1/storage/files', ['bucketId' => 'default']) ``` @@ -17,7 +17,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') Used as an abstract description of the route. ```php -Http::post('/v1/storage/buckets/:bucketId/files') +App::post('/v1/storage/buckets/:bucketId/files') ->desc('Create File') ``` @@ -26,14 +26,14 @@ Http::post('/v1/storage/buckets/:bucketId/files') Groups array is used to group one or more routes with one or more hooks functionality. ```php -Http::post('/v1/storage/buckets/:bucketId/files') +App::post('/v1/storage/buckets/:bucketId/files') ->groups(['api']) ``` In the above example groups() is used to define the current route as part of the routes that shares a common init middleware hook. ```php -Http::init() +App::init() ->groups(['api']) ->action( some code..... @@ -52,7 +52,7 @@ Appwrite uses different labels to achieve different things, for example: - scope - Defines the route permissions scope. ```php -Http::post('/v1/storage/buckets/:bucketId/files') +App::post('/v1/storage/buckets/:bucketId/files') ->label('scope', 'files.write') ``` @@ -66,7 +66,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') - audits.resource - Signals the extraction part of the resource. ```php -Http::post('/v1/account/create') +App::post('/v1/account/create') ->label('audits.event', 'account.create') ->label('audits.resource', 'user/{response.$id}') ->label('audits.userId', '{response.$id}') @@ -84,7 +84,7 @@ Http::post('/v1/account/create') * sdk.offline.response.key - JSON property name that has the ID. Defaults to $id ```php -Http::post('/v1/account/jwt') +App::post('/v1/account/jwt') ->label('sdk.auth', [APP_AUTH_TYPE_SESSION]) ->label('sdk.namespace', 'account') ->label('sdk.method', 'createJWT') @@ -100,7 +100,7 @@ Http::post('/v1/account/jwt') - cache.resource - Identifies the cached resource. ```php -Http::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') +App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->label('cache', true) ->label('cache.resource', 'file/{request.fileId}') ``` @@ -115,7 +115,7 @@ When using the example below, we configure the abuse mechanism to allow this key constructed from the combination of the ip, http method, url, userId to hit the route maximum 60 times in 1 hour (60 seconds \* 60 minutes). ```php -Http::post('/v1/storage/buckets/:bucketId/files') +App::post('/v1/storage/buckets/:bucketId/files') ->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}') ->label('abuse-limit', 60) ->label('abuse-time', 3600) @@ -127,7 +127,7 @@ Http::post('/v1/storage/buckets/:bucketId/files') Placeholders marked as `[]` are parsed and replaced with their real values. ```php -Http::post('/v1/storage/buckets/:bucketId/files') +App::post('/v1/storage/buckets/:bucketId/files') ->label('event', 'buckets.[bucketId].files.[fileId].create') ``` @@ -145,7 +145,7 @@ As the name implies, `param()` is used to define a request parameter. - An array of injections ```php -Http::get('/v1/account/logs') +App::get('/v1/account/logs') ->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true) ``` @@ -154,14 +154,14 @@ Http::get('/v1/account/logs') inject is used to inject dependencies pre-bounded to the app. ```php -Http::post('/v1/storage/buckets/:bucketId/files') +App::post('/v1/storage/buckets/:bucketId/files') ->inject('user') ``` -In the example above, the user object is injected into the route pre-bounded using `Http::setResource()`. +In the example above, the user object is injected into the route pre-bounded using `App::setResource()`. ```php -Http::setResource('user', function() { +App::setResource('user', function() { some code... }); ``` @@ -170,7 +170,7 @@ some code... Action populates the actual route code and has to be very clear and understandable. A good route stays simple and doesn't contain complex logic. An action is where we describe our business needs in code, and combine different libraries to work together and tell our story. ```php -Http::post('/v1/account/sessions/anonymous') +App::post('/v1/account/sessions/anonymous') ->action(function (Request $request) { some code... }); diff --git a/phpstan.neon b/phpstan.neon deleted file mode 100644 index 25771ef17c..0000000000 --- a/phpstan.neon +++ /dev/null @@ -1,11 +0,0 @@ -parameters: - level: 0 - scanDirectories: - - vendor/swoole/ide-helper - excludePaths: - - tests/resources - ignoreErrors: - - '#Parameter \$geodb of anonymous function has invalid type MaxMind\\Db\\Reader\.#' - - '#Parameter \$geodb of function router\(\) has invalid type MaxMind\\Db\\Reader\.#' - - '#Instantiated class MaxMind\\Db\\Reader not found.#' - - '#Function scrypt not found\.#' diff --git a/src/Appwrite/Auth/Auth.php b/src/Appwrite/Auth/Auth.php index 0135020765..1e8109622e 100644 --- a/src/Appwrite/Auth/Auth.php +++ b/src/Appwrite/Auth/Auth.php @@ -91,6 +91,37 @@ class Auth */ public const MFA_RECENT_DURATION = 1800; // 30 mins + /** + * @var string + */ + public static $cookieName = 'a_session'; + + /** + * User Unique ID. + * + * @var string + */ + public static $unique = ''; + + /** + * User Secret Key. + * + * @var string + */ + public static $secret = ''; + + /** + * Set Cookie Name. + * + * @param $string + * + * @return string + */ + public static function setCookieName($string) + { + return self::$cookieName = $string; + } + /** * Encode Session. * @@ -408,14 +439,13 @@ class Auth * Returns all roles for a user. * * @param Document $user - * @param Authorization $auth * @return array */ - public static function getRoles(Document $user, Authorization $auth): array + public static function getRoles(Document $user): array { $roles = []; - if (!self::isPrivilegedUser($auth->getRoles()) && !self::isAppUser($auth->getRoles())) { + if (!self::isPrivilegedUser(Authorization::getRoles()) && !self::isAppUser(Authorization::getRoles())) { if ($user->getId()) { $roles[] = Role::user($user->getId())->toString(); $roles[] = Role::users()->toString(); diff --git a/src/Appwrite/Auth/Authentication.php b/src/Appwrite/Auth/Authentication.php deleted file mode 100644 index ef372309da..0000000000 --- a/src/Appwrite/Auth/Authentication.php +++ /dev/null @@ -1,57 +0,0 @@ -cookieName = $string; - } - - public function getCookieName(): string - { - return $this->cookieName; - } - - public function getUnique(): string - { - return $this->unique; - } - - public function setUnique(string $unique): void - { - $this->unique = $unique; - } - - public function getSecret(): string - { - return $this->secret; - } - - public function setSecret(string $secret): void - { - $this->secret = $secret; - } - -} diff --git a/src/Appwrite/Auth/Validator/MockNumber.php b/src/Appwrite/Auth/Validator/MockNumber.php index 8f0f14c9da..ac5ba89fc5 100644 --- a/src/Appwrite/Auth/Validator/MockNumber.php +++ b/src/Appwrite/Auth/Validator/MockNumber.php @@ -2,8 +2,8 @@ namespace Appwrite\Auth\Validator; -use Utopia\Http\Validator; -use Utopia\Http\Validator\Text; +use Utopia\Validator; +use Utopia\Validator\Text; /** * MockNumber. @@ -52,7 +52,6 @@ class MockNumber extends Validator return false; } - $this->message = ''; return true; } diff --git a/src/Appwrite/Auth/Validator/Password.php b/src/Appwrite/Auth/Validator/Password.php index 913701f7a3..bfe5577889 100644 --- a/src/Appwrite/Auth/Validator/Password.php +++ b/src/Appwrite/Auth/Validator/Password.php @@ -2,7 +2,7 @@ namespace Appwrite\Auth\Validator; -use Utopia\Http\Validator; +use Utopia\Validator; /** * Password. diff --git a/src/Appwrite/Auth/Validator/Phone.php b/src/Appwrite/Auth/Validator/Phone.php index d5f6df60c8..26aa687278 100644 --- a/src/Appwrite/Auth/Validator/Phone.php +++ b/src/Appwrite/Auth/Validator/Phone.php @@ -2,7 +2,7 @@ namespace Appwrite\Auth\Validator; -use Utopia\Http\Validator; +use Utopia\Validator; /** * Phone. diff --git a/src/Appwrite/Event/Func.php b/src/Appwrite/Event/Func.php index 2fbdb7e785..0cbaf17b60 100644 --- a/src/Appwrite/Event/Func.php +++ b/src/Appwrite/Event/Func.php @@ -175,9 +175,9 @@ class Func extends Event * * @return string */ - public function getBody(): string + public function getData(): string { - return $this->body; + return $this->data; } /** diff --git a/src/Appwrite/Event/Validator/Event.php b/src/Appwrite/Event/Validator/Event.php index 2d46bd7727..a3605e4df5 100644 --- a/src/Appwrite/Event/Validator/Event.php +++ b/src/Appwrite/Event/Validator/Event.php @@ -3,7 +3,7 @@ namespace Appwrite\Event\Validator; use Utopia\Config\Config; -use Utopia\Http\Validator; +use Utopia\Validator; class Event extends Validator { diff --git a/src/Appwrite/Functions/Validator/Headers.php b/src/Appwrite/Functions/Validator/Headers.php index 4c30a98045..04003d535b 100644 --- a/src/Appwrite/Functions/Validator/Headers.php +++ b/src/Appwrite/Functions/Validator/Headers.php @@ -2,7 +2,7 @@ namespace Appwrite\Functions\Validator; -use Utopia\Http\Validator; +use Utopia\Validator; /** * Headers. diff --git a/src/Appwrite/Functions/Validator/Payload.php b/src/Appwrite/Functions/Validator/Payload.php index 3b2ff4b918..acb461eabd 100644 --- a/src/Appwrite/Functions/Validator/Payload.php +++ b/src/Appwrite/Functions/Validator/Payload.php @@ -2,7 +2,7 @@ namespace Appwrite\Functions\Validator; -use Utopia\Http\Validator\Text; +use Utopia\Validator\Text; class Payload extends Text { diff --git a/src/Appwrite/Functions/Validator/RuntimeSpecification.php b/src/Appwrite/Functions/Validator/RuntimeSpecification.php index fa6efe90a4..22311838f6 100644 --- a/src/Appwrite/Functions/Validator/RuntimeSpecification.php +++ b/src/Appwrite/Functions/Validator/RuntimeSpecification.php @@ -2,7 +2,7 @@ namespace Appwrite\Functions\Validator; -use Utopia\Http\Validator; +use Utopia\Validator; class RuntimeSpecification extends Validator { diff --git a/src/Appwrite/GraphQL/Resolvers.php b/src/Appwrite/GraphQL/Resolvers.php index 49d7c421f7..8bc72af2f8 100644 --- a/src/Appwrite/GraphQL/Resolvers.php +++ b/src/Appwrite/GraphQL/Resolvers.php @@ -6,12 +6,9 @@ use Appwrite\GraphQL\Exception as GQLException; use Appwrite\Promises\Swoole; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; -use Utopia\DI\Container; +use Utopia\App; use Utopia\Exception; -use Utopia\Http\Http; -use Utopia\Http\Request as UtopiaHttpRequest; -use Utopia\Http\Response as UtopiaHttpResponse; -use Utopia\Http\Route; +use Utopia\Route; use Utopia\System\System; class Resolvers @@ -19,21 +16,24 @@ class Resolvers /** * Create a resolver for a given API {@see Route}. * - * @param Http $http + * @param App $utopia * @param ?Route $route * @return callable */ - public function api( - Http $http, + public static function api( + App $utopia, ?Route $route, - UtopiaHttpRequest $request, - UtopiaHttpResponse $response, - Container $container, ): callable { - $resolver = $this; + return static fn ($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($utopia, $route, $args, $context, $info) { + /** @var App $utopia */ + /** @var Response $response */ + /** @var Request $request */ + + $utopia = $utopia->getResource('utopia:graphql', true); + $request = $utopia->getResource('request', true); + $response = $utopia->getResource('response', true); - return fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $route, $args, $context, $container, $info, $request, $response, $resolver) { $path = $route->getPath(); foreach ($args as $key => $value) { if (\str_contains($path, '/:' . $key)) { @@ -46,13 +46,14 @@ class Resolvers switch ($route->getMethod()) { case 'GET': - $request->setQuery($args); + $request->setQueryString($args); break; default: $request->setPayload($args); break; } - $resolver->resolve($http, $request, $response, $container, $resolve, $reject); + + self::resolve($utopia, $request, $response, $resolve, $reject); } ); } @@ -60,20 +61,20 @@ class Resolvers /** * Create a resolver for a document in a specified database and collection with a specific method type. * - * @param Http $http + * @param App $utopia * @param string $databaseId * @param string $collectionId * @param string $methodType * @return callable */ - public function document( - Http $http, + public static function document( + App $utopia, string $databaseId, string $collectionId, string $methodType, ): callable { return [self::class, 'document' . \ucfirst($methodType)]( - $http, + $utopia, $databaseId, $collectionId ); @@ -82,28 +83,28 @@ class Resolvers /** * Create a resolver for getting a document in a specified database and collection. * - * @param Http $http + * @param App $utopia * @param string $databaseId * @param string $collectionId * @param callable $url * @return callable */ - public function documentGet( - Http $http, + public static function documentGet( + App $utopia, string $databaseId, string $collectionId, callable $url, - UtopiaHttpRequest $request, - UtopiaHttpResponse $response, - Container $container, ): callable { - $resolver = $this; - return fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) { + return static fn ($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $type, $args) { + $utopia = $utopia->getResource('utopia:graphql', true); + $request = $utopia->getResource('request', true); + $response = $utopia->getResource('response', true); + $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); - $resolver->resolve($http, $request, $response, $container, $resolve, $reject); + self::resolve($utopia, $request, $response, $resolve, $reject); } ); } @@ -111,35 +112,35 @@ class Resolvers /** * Create a resolver for listing documents in a specified database and collection. * - * @param Http $http + * @param App $utopia * @param string $databaseId * @param string $collectionId * @param callable $url * @param callable $params * @return callable */ - public function documentList( - Http $http, + public static function documentList( + App $utopia, string $databaseId, string $collectionId, callable $url, callable $params, - UtopiaHttpRequest $request, - UtopiaHttpResponse $response, - Container $container, ): callable { - $resolver = $this; - return fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { + return static fn ($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $type, $args) { + $utopia = $utopia->getResource('utopia:graphql', true); + $request = $utopia->getResource('request', true); + $response = $utopia->getResource('response', true); + $request->setMethod('GET'); $request->setURI($url($databaseId, $collectionId, $args)); - $request->setQuery($params($databaseId, $collectionId, $args)); + $request->setQueryString($params($databaseId, $collectionId, $args)); $beforeResolve = function ($payload) { return $payload['documents']; }; - $resolver->resolve($http, $request, $response, $container, $resolve, $reject, $beforeResolve); + self::resolve($utopia, $request, $response, $resolve, $reject, $beforeResolve); } ); } @@ -147,31 +148,31 @@ class Resolvers /** * Create a resolver for creating a document in a specified database and collection. * - * @param Http $http + * @param App $utopia * @param string $databaseId * @param string $collectionId * @param callable $url * @param callable $params * @return callable */ - public function documentCreate( - Http $http, + public static function documentCreate( + App $utopia, string $databaseId, string $collectionId, callable $url, callable $params, - UtopiaHttpRequest $request, - UtopiaHttpResponse $response, - Container $container, ): callable { - $resolver = $this; - return fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { + return static fn ($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $type, $args) { + $utopia = $utopia->getResource('utopia:graphql', true); + $request = $utopia->getResource('request', true); + $response = $utopia->getResource('response', true); + $request->setMethod('POST'); $request->setURI($url($databaseId, $collectionId, $args)); $request->setPayload($params($databaseId, $collectionId, $args)); - $resolver->resolve($http, $request, $response, $container, $resolve, $reject); + self::resolve($utopia, $request, $response, $resolve, $reject); } ); } @@ -179,31 +180,31 @@ class Resolvers /** * Create a resolver for updating a document in a specified database and collection. * - * @param Http $http + * @param App $utopia * @param string $databaseId * @param string $collectionId * @param callable $url * @param callable $params * @return callable */ - public function documentUpdate( - Http $http, + public static function documentUpdate( + App $utopia, string $databaseId, string $collectionId, callable $url, callable $params, - UtopiaHttpRequest $request, - UtopiaHttpResponse $response, - Container $container, ): callable { - $resolver = $this; - return fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $params, $type, $args, $container, $request, $response, $resolver) { + return static fn ($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $params, $type, $args) { + $utopia = $utopia->getResource('utopia:graphql', true); + $request = $utopia->getResource('request', true); + $response = $utopia->getResource('response', true); + $request->setMethod('PATCH'); $request->setURI($url($databaseId, $collectionId, $args)); $request->setPayload($params($databaseId, $collectionId, $args)); - $resolver->resolve($http, $request, $response, $container, $resolve, $reject); + self::resolve($utopia, $request, $response, $resolve, $reject); } ); } @@ -211,34 +212,34 @@ class Resolvers /** * Create a resolver for deleting a document in a specified database and collection. * - * @param Http $http + * @param App $utopia * @param string $databaseId * @param string $collectionId * @param callable $url * @return callable */ - public function documentDelete( - Http $http, + public static function documentDelete( + App $utopia, string $databaseId, string $collectionId, callable $url, - UtopiaHttpRequest $request, - UtopiaHttpResponse $response, - Container $container, ): callable { - $resolver = $this; - return fn ($type, $args, $context, $info) => new Swoole( - function (callable $resolve, callable $reject) use ($http, $databaseId, $collectionId, $url, $type, $args, $container, $request, $response, $resolver) { + return static fn ($type, $args, $context, $info) => new Swoole( + function (callable $resolve, callable $reject) use ($utopia, $databaseId, $collectionId, $url, $type, $args) { + $utopia = $utopia->getResource('utopia:graphql', true); + $request = $utopia->getResource('request', true); + $response = $utopia->getResource('response', true); + $request->setMethod('DELETE'); $request->setURI($url($databaseId, $collectionId, $args)); - $resolver->resolve($http, $request, $response, $container, $resolve, $reject); + self::resolve($utopia, $request, $response, $resolve, $reject); } ); } /** - * @param Http $http + * @param App $utopia * @param Request $request * @param Response $response * @param callable $resolve @@ -248,11 +249,10 @@ class Resolvers * @return void * @throws Exception */ - private function resolve( - Http $http, + private static function resolve( + App $utopia, Request $request, Response $response, - Container $context, callable $resolve, callable $reject, ?callable $beforeResolve = null, @@ -263,16 +263,14 @@ class Resolvers $request->removeHeader('content-type'); } + $request = clone $request; + $utopia->setResource('request', static fn () => $request); $response->setContentType(Response::CONTENT_TYPE_NULL); try { - $context - ->refresh('cache') - ->refresh('dbForProject') - ->refresh('dbForConsole') - ->refresh('getProjectDb'); + $route = $utopia->match($request, fresh: true); - $http->run(clone $context); + $utopia->execute($route, $request, $response); } catch (\Throwable $e) { if ($beforeReject) { $e = $beforeReject($e); @@ -287,12 +285,10 @@ class Resolvers if ($beforeReject) { $payload = $beforeReject($payload); } - $reject( - new GQLException( - message: $payload['message'], - code: $response->getStatusCode() - ) - ); + $reject(new GQLException( + message: $payload['message'], + code: $response->getStatusCode() + )); return; } diff --git a/src/Appwrite/GraphQL/Schema.php b/src/Appwrite/GraphQL/Schema.php index 2b05f08aee..833ea9d032 100644 --- a/src/Appwrite/GraphQL/Schema.php +++ b/src/Appwrite/GraphQL/Schema.php @@ -3,63 +3,54 @@ namespace Appwrite\GraphQL; use Appwrite\GraphQL\Types\Mapper; -use Appwrite\Utopia\Response\Models; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; use GraphQL\Type\Schema as GQLSchema; -use Utopia\DI\Container; +use Utopia\App; use Utopia\Exception; -use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse; -use Utopia\Http\Http; -use Utopia\Http\Request; -use Utopia\Http\Response as UtopiaHttpResponse; -use Utopia\Http\Route; +use Utopia\Route; class Schema { - protected ?GQLSchema $schema = null; - protected array $dirty = []; + protected static ?GQLSchema $schema = null; + protected static array $dirty = []; /** * - * @param Http $http - * @param callable $complexity Function to calculate complexity - * @param callable $attributes Function to get attributes - * @param array $urls Array of functions to get urls for specific method types - * @param array $params Array of functions to build parameters for specific method types + * @param App $utopia + * @param callable $complexity Function to calculate complexity + * @param callable $attributes Function to get attributes + * @param array $urls Array of functions to get urls for specific method types + * @param array $params Array of functions to build parameters for specific method types * @return GQLSchema * @throws Exception */ - public function build( - Http $http, - Request $request, - UtopiaHttpResponse $response, - Container $container, + public static function build( + App $utopia, callable $complexity, callable $attributes, array $urls, array $params, ): GQLSchema { - if (!empty($this->schema)) { - return $this->schema; + App::setResource('utopia:graphql', static function () use ($utopia) { + return $utopia; + }); + + if (!empty(self::$schema)) { + return self::$schema; } - $api = $this->api( - $http, - $request, - $response, - $container, + $api = static::api( + $utopia, $complexity ); - // $collections = $this->collections( - // $http, - // $complexity, - // $request, - // $response, - // $attributes, - // $urls, - // $params, - // ); + //$collections = static::collections( + // $utopia, + // $complexity, + // $attributes, + // $urls, + // $params, + //); $queries = \array_merge_recursive( $api['query'], @@ -73,7 +64,7 @@ class Schema \ksort($queries); \ksort($mutations); - return $this->schema = new GQLSchema([ + return static::$schema = new GQLSchema([ 'query' => new ObjectType([ 'name' => 'Query', 'fields' => $queries @@ -89,23 +80,21 @@ class Schema * This function iterates all API routes and builds a GraphQL * schema defining types and resolvers for all response models. * - * @param Http $http - * @param Request $request - * @param UtopiaSwooleResponse $response + * @param App $utopia * @param callable $complexity * @return array - * @throws \Exception + * @throws Exception */ - protected function api(Http $http, Request $request, UtopiaHttpResponse $response, Container $container, callable $complexity): array + protected static function api(App $utopia, callable $complexity): array { - Mapper::init(Models::getModels()); - - $mapper = new Mapper(); + Mapper::init($utopia + ->getResource('response') + ->getModels()); $queries = []; $mutations = []; - foreach ($http->getRoutes() as $routes) { + foreach ($utopia->getRoutes() as $routes) { foreach ($routes as $route) { /** @var Route $route */ @@ -117,7 +106,7 @@ class Schema continue; } - foreach ($mapper->route($http, $route, $request, $response, $container, $complexity) as $field) { + foreach (Mapper::route($utopia, $route, $complexity) as $field) { switch ($route->getMethod()) { case 'GET': $queries[$name] = $field; @@ -145,7 +134,7 @@ class Schema * Iterates all of a projects attributes and builds GraphQL * queries and mutations for the collections they make up. * - * @param Http $http + * @param App $utopia * @param callable $complexity * @param callable $attributes * @param array $urls @@ -154,7 +143,7 @@ class Schema * @throws \Exception */ protected static function collections( - Http $http, + App $utopia, callable $complexity, callable $attributes, array $urls, @@ -205,36 +194,36 @@ class Schema $queryFields[$collectionId . 'Get'] = [ 'type' => $objectType, 'args' => Mapper::args('id'), - /*'resolve' => Resolvers::documentGet( - $http, + 'resolve' => Resolvers::documentGet( + $utopia, $databaseId, $collectionId, $urls['get'], - )*/ + ) ]; $queryFields[$collectionId . 'List'] = [ 'type' => Type::listOf($objectType), 'args' => Mapper::args('list'), - /*'resolve' => Resolvers::documentList( - $http, + 'resolve' => Resolvers::documentList( + $utopia, $databaseId, $collectionId, $urls['list'], $params['list'], - ),*/ + ), 'complexity' => $complexity, ]; $mutationFields[$collectionId . 'Create'] = [ 'type' => $objectType, 'args' => $attributes, - /*'resolve' => Resolvers::documentCreate( - $http, + 'resolve' => Resolvers::documentCreate( + $utopia, $databaseId, $collectionId, $urls['create'], $params['create'], - )*/ + ) ]; $mutationFields[$collectionId . 'Update'] = [ 'type' => $objectType, @@ -245,23 +234,23 @@ class Schema $attributes ) ), - /*'resolve' => Resolvers::documentUpdate( - $http, + 'resolve' => Resolvers::documentUpdate( + $utopia, $databaseId, $collectionId, $urls['update'], $params['update'], - )*/ + ) ]; $mutationFields[$collectionId . 'Delete'] = [ 'type' => Mapper::model('none'), 'args' => Mapper::args('id'), - /*'resolve' => Resolvers::documentDelete( - $http, + 'resolve' => Resolvers::documentDelete( + $utopia, $databaseId, $collectionId, $urls['delete'], - )*/ + ) ]; } $offset += $limit; @@ -273,8 +262,8 @@ class Schema ]; } - public function setDirty(string $projectId): void + public static function setDirty(string $projectId): void { - $this->dirty[$projectId] = true; + self::$dirty[$projectId] = true; } } diff --git a/src/Appwrite/GraphQL/Types/Mapper.php b/src/Appwrite/GraphQL/Types/Mapper.php index a15c6aa475..36b246b28b 100644 --- a/src/Appwrite/GraphQL/Types/Mapper.php +++ b/src/Appwrite/GraphQL/Types/Mapper.php @@ -8,13 +8,10 @@ use Exception; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\UnionType; -use Utopia\DI\Container; -use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse; -use Utopia\Http\Http; -use Utopia\Http\Request; -use Utopia\Http\Route; -use Utopia\Http\Validator; -use Utopia\Http\Validator\Nullable; +use Utopia\App; +use Utopia\Route; +use Utopia\Validator; +use Utopia\Validator\Nullable; class Mapper { @@ -77,15 +74,12 @@ class Mapper return self::$args[$key] ?? []; } - public function route( - Http $http, + public static function route( + App $utopia, Route $route, - Request $request, - UtopiaSwooleResponse $response, - Container $container, callable $complexity ): iterable { - foreach (static::$blacklist as $blacklist) { + foreach (self::$blacklist as $blacklist) { if (\str_starts_with($route->getPath(), $blacklist)) { return; } @@ -107,7 +101,7 @@ class Mapper $list = true; } $parameterType = Mapper::param( - $container, + $utopia, $parameter['validator'], !$parameter['optional'], $parameter['injections'] @@ -122,7 +116,7 @@ class Mapper 'type' => $type, 'description' => $description, 'args' => $params, - 'resolve' => (new Resolvers())->api($http, $route, $request, $response, $container) + 'resolve' => Resolvers::api($utopia, $route) ]; if ($list) { @@ -211,7 +205,7 @@ class Mapper /** * Map a {@see Route} parameter to a GraphQL Type * - * @param Container $container + * @param App $utopia * @param Validator|callable $validator * @param bool $required * @param array $injections @@ -219,13 +213,13 @@ class Mapper * @throws Exception */ public static function param( - Container $container, + App $utopia, Validator|callable $validator, bool $required, array $injections ): Type { $validator = \is_callable($validator) - ? \call_user_func_array($validator, array_map(fn ($injection) => $container->get($injection), $injections)) + ? \call_user_func_array($validator, $utopia->getResources($injections)) : $validator; $isNullable = $validator instanceof Nullable; @@ -238,20 +232,20 @@ class Mapper case 'Appwrite\Network\Validator\CNAME': case 'Appwrite\Task\Validator\Cron': case 'Appwrite\Utopia\Database\Validator\CustomId': - case 'Utopia\Http\Validator\Domain': + case 'Utopia\Validator\Domain': case 'Appwrite\Network\Validator\Email': case 'Appwrite\Event\Validator\Event': case 'Appwrite\Event\Validator\FunctionEvent': - case 'Utopia\Http\Validator\HexColor': - case 'Utopia\Http\Validator\Host': - case 'Utopia\Http\Validator\IP': + case 'Utopia\Validator\HexColor': + case 'Utopia\Validator\Host': + case 'Utopia\Validator\IP': case 'Utopia\Database\Validator\Key': - case 'Utopia\Http\Validator\Origin': + case 'Utopia\Validator\Origin': case 'Appwrite\Auth\Validator\Password': - case 'Utopia\Http\Validator\Text': + case 'Utopia\Validator\Text': case 'Utopia\Database\Validator\UID': - case 'Utopia\Http\Validator\URL': - case 'Utopia\Http\Validator\WhiteList': + case 'Utopia\Validator\URL': + case 'Utopia\Validator\WhiteList': default: $type = Type::string(); break; @@ -279,29 +273,29 @@ class Mapper case 'Appwrite\Utopia\Database\Validator\Queries\Variables': $type = Type::listOf(Type::string()); break; - case 'Utopia\Http\Validator\Boolean': + case 'Utopia\Validator\Boolean': $type = Type::boolean(); break; - case 'Utopia\Http\Validator\ArrayList': + case 'Utopia\Validator\ArrayList': $type = Type::listOf(self::param( - $container, + $utopia, $validator->getValidator(), $required, $injections )); break; - case 'Utopia\Http\Validator\Integer': - case 'Utopia\Http\Validator\Numeric': - case 'Utopia\Http\Validator\Range': + case 'Utopia\Validator\Integer': + case 'Utopia\Validator\Numeric': + case 'Utopia\Validator\Range': $type = Type::int(); break; - case 'Utopia\Http\Validator\FloatValidator': + case 'Utopia\Validator\FloatValidator': $type = Type::float(); break; - case 'Utopia\Http\Validator\Assoc': + case 'Utopia\Validator\Assoc': $type = Types::assoc(); break; - case 'Utopia\Http\Validator\JSON': + case 'Utopia\Validator\JSON': $type = Types::json(); break; case 'Utopia\Storage\Validator\File': diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 5b215ec04f..cee1b2d263 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -98,10 +98,10 @@ abstract class Migration */ protected array $collections; - public function __construct(Authorization $auth) + public function __construct() { - $auth->disable(); - $auth->setDefaultStatus(false); + Authorization::disable(); + Authorization::setDefaultStatus(false); $this->collections = Config::getParam('collections', []); @@ -176,23 +176,25 @@ abstract class Migration Console::log('Migrating Collection ' . $collection['$id'] . ':'); foreach ($this->documentsIterator($collection['$id']) as $document) { - if (empty($document->getId()) || empty($document->getCollection())) { - return; - } + go(function (Document $document, callable $callback) { + if (empty($document->getId()) || empty($document->getCollection())) { + return; + } - $old = $document->getArrayCopy(); - $new = call_user_func($callback, $document); + $old = $document->getArrayCopy(); + $new = call_user_func($callback, $document); - if (is_null($new) || $new->getArrayCopy() == $old) { - return; - } + if (is_null($new) || $new->getArrayCopy() == $old) { + return; + } - try { - $this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document); - } catch (\Throwable $th) { - Console::error('Failed to update document: ' . $th->getMessage()); - return; - } + try { + $this->projectDB->updateDocument($document->getCollection(), $document->getId(), $document); + } catch (\Throwable $th) { + Console::error('Failed to update document: ' . $th->getMessage()); + return; + } + }, $document, $callback); } } } diff --git a/src/Appwrite/Network/Validator/CNAME.php b/src/Appwrite/Network/Validator/CNAME.php index e9e2b586a5..e1ae061c84 100644 --- a/src/Appwrite/Network/Validator/CNAME.php +++ b/src/Appwrite/Network/Validator/CNAME.php @@ -2,7 +2,7 @@ namespace Appwrite\Network\Validator; -use Utopia\Http\Validator; +use Utopia\Validator; class CNAME extends Validator { diff --git a/src/Appwrite/Network/Validator/Email.php b/src/Appwrite/Network/Validator/Email.php index bae0ff0bbf..3209a4aada 100644 --- a/src/Appwrite/Network/Validator/Email.php +++ b/src/Appwrite/Network/Validator/Email.php @@ -2,14 +2,14 @@ namespace Appwrite\Network\Validator; -use Utopia\Http\Validator; +use Utopia\Validator; /** * Email * * Validate that an variable is a valid email address * - * @package Utopia\Http\Validator + * @package Utopia\Validator */ class Email extends Validator { diff --git a/src/Appwrite/Network/Validator/Origin.php b/src/Appwrite/Network/Validator/Origin.php index 573a59b844..d41e9af2ad 100644 --- a/src/Appwrite/Network/Validator/Origin.php +++ b/src/Appwrite/Network/Validator/Origin.php @@ -2,8 +2,8 @@ namespace Appwrite\Network\Validator; -use Utopia\Http\Validator; -use Utopia\Http\Validator\Hostname; +use Utopia\Validator; +use Utopia\Validator\Hostname; class Origin extends Validator { diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index 3bf9e0d33b..82d1ca2d59 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -3,17 +3,13 @@ namespace Appwrite\Platform\Tasks; use Appwrite\ClamAV\Network; -use Appwrite\Utopia\Queue\Connections; +use Utopia\App; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\MariaDB; -use Utopia\Database\Adapter\MySQL; use Utopia\Domains\Domain; use Utopia\DSN\DSN; -use Utopia\Http\Http; use Utopia\Logger\Logger; use Utopia\Platform\Action; -use Utopia\Queue\Connection\Redis; use Utopia\Registry\Registry; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; @@ -31,11 +27,10 @@ class Doctor extends Action $this ->desc('Validate server health') ->inject('register') - ->inject('connections') - ->callback(fn (Registry $register, Connections $connections) => $this->action($register, $connections)); + ->callback(fn (Registry $register) => $this->action($register)); } - public function action(Registry $register, Connections $connections): void + public function action(Registry $register): void { Console::log(" __ ____ ____ _ _ ____ __ ____ ____ __ __ / _\ ( _ \( _ \/ )( \( _ \( )(_ _)( __) ( )/ \ @@ -130,73 +125,17 @@ class Doctor extends Action //throw $th; } - /** @var array $pools */ - $pools = $register->get('pools'); + $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ $configs = [ - 'Console.DB' => [ - 'prefix' => 'console', - 'databases' => Config::getParam('pools-console') - ], - 'Database.DB' => [ - 'prefix' => 'database', - 'databases' => Config::getParam('pools-database') - ], + 'Console.DB' => Config::getParam('pools-console'), + 'Projects.DB' => Config::getParam('pools-database'), ]; - foreach ($configs as $key => $config) { - foreach ($config['databases'] as $database) { + foreach ($config as $database) { try { - $pool = $pools['pools-' . $config['prefix'] . '-' . $database]['pool']; - $dsn = $pools['pools-' . $config['prefix'] . '-' . $database]['dsn']; - - $connection = $pool->get(); - $connections->add($connection, $pool); - $adapter = match ($dsn->getScheme()) { - 'mariadb' => new MariaDB($connection), - 'mysql' => new MySQL($connection), - default => null - }; - $adapter->setDatabase($dsn->getPath()); - - - if ($adapter->ping()) { - Console::success('🟢 ' . str_pad("$key({$database})", 50, '.') . 'connected'); - } else { - Console::error('🔴 ' . str_pad("$key({$database})", 47, '.') . 'disconnected'); - } - } catch (\Throwable $th) { - Console::error('🔴 ' . str_pad("{$key}.({$database})", 47, '.') . 'disconnected'); - } - } - } - - /** @var array $pools */ - $pools = $register->get('pools'); - $configs = [ - 'Cache' => [ - 'prefix' => 'cache', - 'databases' => Config::getParam('pools-cache') - ], - 'Queue' => [ - 'prefix' => 'queue', - 'databases' => Config::getParam('pools-queue') - ], - 'PubSub' => [ - 'prefix' => 'pubsub', - 'databases' => Config::getParam('pools-pubsub') - ], - ]; - foreach ($configs as $key => $config) { - foreach ($config['databases'] as $database) { - try { - $pool = $pools['pools-' . $config['prefix'] . '-' . $database]['pool']; - $dsn = $pools['pools-' . $config['prefix'] . '-' . $database]['dsn']; - $connection = $pool->get(); - $connections->add($connection, $pool); - - $adapter = new Redis($dsn->getHost(), $dsn->getPort()); + $adapter = $pools->get($database)->pop()->getResource(); if ($adapter->ping()) { Console::success('🟢 ' . str_pad("{$key}({$database})", 50, '.') . 'connected'); @@ -204,7 +143,30 @@ class Doctor extends Action Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected'); } } catch (\Throwable $th) { - Console::error('🔴 ' . str_pad("{$key}({$database})", 47, '.') . 'disconnected'); + Console::error('🔴 ' . str_pad("{$key}.({$database})", 47, '.') . 'disconnected'); + } + } + } + + $pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */ + $configs = [ + 'Cache' => Config::getParam('pools-cache'), + 'Queue' => Config::getParam('pools-queue'), + 'PubSub' => Config::getParam('pools-pubsub'), + ]; + + foreach ($configs as $key => $config) { + foreach ($config as $pool) { + try { + $adapter = $pools->get($pool)->pop()->getResource(); + + if ($adapter->ping()) { + Console::success('🟢 ' . str_pad("{$key}({$pool})", 50, '.') . 'connected'); + } else { + Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); + } + } catch (\Throwable $th) { + Console::error('🔴 ' . str_pad("{$key}({$pool})", 47, '.') . 'disconnected'); } } } @@ -296,7 +258,7 @@ class Doctor extends Action } try { - if (Http::isProduction()) { + if (App::isProduction()) { Console::log(''); $version = \json_decode(@\file_get_contents(System::getEnv('_APP_HOME', 'http://localhost') . '/version'), true); diff --git a/src/Appwrite/Platform/Tasks/Install.php b/src/Appwrite/Platform/Tasks/Install.php index a1a73e6385..4abd267684 100644 --- a/src/Appwrite/Platform/Tasks/Install.php +++ b/src/Appwrite/Platform/Tasks/Install.php @@ -8,9 +8,9 @@ use Appwrite\Docker\Env; use Appwrite\Utopia\View; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\Text; use Utopia\Platform\Action; +use Utopia\Validator\Boolean; +use Utopia\Validator\Text; class Install extends Action { @@ -213,7 +213,8 @@ class Install extends Action } $env = ''; - $output = ''; + $stdout = ''; + $stderr = ''; foreach ($input as $key => $value) { if ($value) { @@ -224,13 +225,13 @@ class Install extends Action $exit = 0; if (!$noStart) { Console::log("Running \"docker compose up -d --remove-orphans --renew-anon-volumes\""); - $exit = Console::execute("$env docker compose --project-directory $this->path up -d --remove-orphans --renew-anon-volumes", '', $output); + $exit = Console::execute("$env docker compose --project-directory $this->path up -d --remove-orphans --renew-anon-volumes", '', $stdout, $stderr); } if ($exit !== 0) { $message = 'Failed to install Appwrite dockers'; Console::error($message); - Console::error($output); + Console::error($stderr); Console::exit($exit); } else { $message = 'Appwrite installed successfully'; diff --git a/src/Appwrite/Platform/Tasks/Migrate.php b/src/Appwrite/Platform/Tasks/Migrate.php index 55be0b81c2..dcba59bb1d 100644 --- a/src/Appwrite/Platform/Tasks/Migrate.php +++ b/src/Appwrite/Platform/Tasks/Migrate.php @@ -10,10 +10,10 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use Utopia\Http\Validator\Text; use Utopia\Platform\Action; use Utopia\Registry\Registry; use Utopia\System\System; +use Utopia\Validator\Text; class Migrate extends Action { @@ -30,15 +30,12 @@ class Migrate extends Action ->desc('Migrate Appwrite to new version') /** @TODO APP_VERSION_STABLE needs to be defined */ ->param('version', APP_VERSION_STABLE, new Text(8), 'Version to migrate to.', true) - ->inject('cache') ->inject('dbForConsole') ->inject('getProjectDB') ->inject('register') - ->inject('authorization') - ->inject('console') - ->callback(function ($version, $dbForConsole, $getProjectDB, Registry $register, Authorization $authorization, Document $console) { - \Co\run(function () use ($version, $dbForConsole, $getProjectDB, $register, $authorization, $console) { - $this->action($version, $dbForConsole, $getProjectDB, $register, $authorization, $console); + ->callback(function ($version, $dbForConsole, $getProjectDB, Registry $register) { + \Co\run(function () use ($version, $dbForConsole, $getProjectDB, $register) { + $this->action($version, $dbForConsole, $getProjectDB, $register); }); }); } @@ -61,12 +58,13 @@ class Migrate extends Action } } - public function action(string $version, Database $dbForConsole, callable $getProjectDB, Registry $register, Authorization $auth, Document $console) + public function action(string $version, Database $dbForConsole, callable $getProjectDB, Registry $register) { - $auth->disable(); + Authorization::disable(); if (!array_key_exists($version, Migration::$versions)) { Console::error("Version {$version} not found."); Console::exit(1); + return; } @@ -79,8 +77,12 @@ class Migrate extends Action 10 ); + $app = new App('UTC'); + Console::success('Starting Data Migration to version ' . $version); + $console = $app->getResource('console'); + $limit = 30; $sum = 30; $offset = 0; @@ -99,7 +101,7 @@ class Migrate extends Action $class = 'Appwrite\\Migration\\Version\\' . Migration::$versions[$version]; /** @var Migration $migration */ - $migration = new $class($auth, ); + $migration = new $class(); while (!empty($projects)) { foreach ($projects as $project) { diff --git a/src/Appwrite/Platform/Tasks/QueueCount.php b/src/Appwrite/Platform/Tasks/QueueCount.php index 63f829073a..b02165c1d2 100644 --- a/src/Appwrite/Platform/Tasks/QueueCount.php +++ b/src/Appwrite/Platform/Tasks/QueueCount.php @@ -3,11 +3,11 @@ namespace Appwrite\Platform\Tasks; use Utopia\CLI\Console; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; use Utopia\Platform\Action; use Utopia\Queue\Client; use Utopia\Queue\Connection; +use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; class QueueCount extends Action { diff --git a/src/Appwrite/Platform/Tasks/QueueRetry.php b/src/Appwrite/Platform/Tasks/QueueRetry.php index 63f6c8e11e..b6139dc177 100644 --- a/src/Appwrite/Platform/Tasks/QueueRetry.php +++ b/src/Appwrite/Platform/Tasks/QueueRetry.php @@ -3,11 +3,11 @@ namespace Appwrite\Platform\Tasks; use Utopia\CLI\Console; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\Wildcard; use Utopia\Platform\Action; use Utopia\Queue\Client; use Utopia\Queue\Connection; +use Utopia\Validator\Text; +use Utopia\Validator\Wildcard; class QueueRetry extends Action { diff --git a/src/Appwrite/Platform/Tasks/SSL.php b/src/Appwrite/Platform/Tasks/SSL.php index ad4098d9ee..5af0cb6cd8 100644 --- a/src/Appwrite/Platform/Tasks/SSL.php +++ b/src/Appwrite/Platform/Tasks/SSL.php @@ -5,10 +5,10 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Certificate; use Utopia\CLI\Console; use Utopia\Database\Document; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\Hostname; use Utopia\Platform\Action; use Utopia\System\System; +use Utopia\Validator\Boolean; +use Utopia\Validator\Hostname; class SSL extends Action { diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index cb02ec60fd..e013220aa4 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -2,44 +2,44 @@ namespace Appwrite\Platform\Tasks; -use Appwrite\Utopia\Queue\Connections; use Swoole\Timer; use Utopia\CLI\Console; +use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception; use Utopia\Database\Query; use Utopia\Platform\Action; +use Utopia\Pools\Group; use Utopia\System\System; +use function Swoole\Coroutine\run; + abstract class ScheduleBase extends Action { protected const UPDATE_TIMER = 10; //seconds protected const ENQUEUE_TIMER = 60; //seconds protected array $schedules = []; - protected Connections $connections; abstract public static function getName(): string; - abstract public static function getSupportedResource(): string; abstract protected function enqueueResources( - array $pools, - callable $getConsoleDB + Group $pools, + Database $dbForConsole ); public function __construct() { - $this->connections = new Connections(); $type = static::getSupportedResource(); $this ->desc("Execute {$type}s scheduled in Appwrite") ->inject('pools') - ->inject('getConsoleDB') + ->inject('dbForConsole') ->inject('getProjectDB') - ->callback(fn (array $pools, callable $getConsoleDB, callable $getProjectDB) => $this->action($pools, $getConsoleDB, $getProjectDB)); + ->callback(fn (Group $pools, Database $dbForConsole, callable $getProjectDB) => $this->action($pools, $dbForConsole, $getProjectDB)); } /** @@ -47,12 +47,11 @@ abstract class ScheduleBase extends Action * 2. Create timer that sync all changes from 'schedules' collection to local copy. Only reading changes thanks to 'resourceUpdatedAt' attribute * 3. Create timer that prepares coroutines for soon-to-execute schedules. When it's ready, coroutine sleeps until exact time before sending request to worker. */ - public function action(array $pools, callable $getConsoleDB, callable $getProjectDB): void + public function action(Group $pools, Database $dbForConsole, callable $getProjectDB): void { Console::title(\ucfirst(static::getSupportedResource()) . ' scheduler V1'); Console::success(APP_NAME . ' ' . \ucfirst(static::getSupportedResource()) . ' scheduler v1 has started'); - [$_, $_, $dbForConsole] = $getConsoleDB(); /** * Extract only necessary attributes to lower memory used. * @@ -128,74 +127,76 @@ abstract class ScheduleBase extends Action $latestDocument = \end($results); } + $pools->reclaim(); Console::success("{$total} resources were loaded in " . (\microtime(true) - $loadStart) . " seconds"); Console::success("Starting timers at " . DateTime::now()); + run(function () use ($dbForConsole, &$lastSyncUpdate, $getSchedule, $pools) { + /** + * The timer synchronize $schedules copy with database collection. + */ + Timer::tick(static::UPDATE_TIMER * 1000, function () use ($dbForConsole, &$lastSyncUpdate, $getSchedule, $pools) { + $time = DateTime::now(); + $timerStart = \microtime(true); - Timer::tick(static::UPDATE_TIMER * 1000, function () use ($getConsoleDB, &$lastSyncUpdate, $getSchedule, $pools) { - [$connection,$pool, $dbForConsole] = $getConsoleDB(); - $connections = new Connections(); - $connections->add($connection, $pool); + $limit = 1000; + $sum = $limit; + $total = 0; + $latestDocument = null; - $time = DateTime::now(); - $timerStart = \microtime(true); + Console::log("Sync tick: Running at $time"); - $limit = 1000; - $sum = $limit; - $total = 0; - $latestDocument = null; + while ($sum === $limit) { + $paginationQueries = [Query::limit($limit)]; - Console::log("Sync tick: Running at $time"); - - while ($sum === $limit) { - $paginationQueries = [Query::limit($limit)]; - - if ($latestDocument) { - $paginationQueries[] = Query::cursorAfter($latestDocument); - } - - $results = $dbForConsole->find('schedules', \array_merge($paginationQueries, [ - Query::equal('region', [System::getEnv('_APP_REGION', 'default')]), - Query::equal('resourceType', [static::getSupportedResource()]), - Query::greaterThanEqual('resourceUpdatedAt', $lastSyncUpdate), - ])); - - $sum = count($results); - $total = $total + $sum; - - foreach ($results as $document) { - $localDocument = $schedules[$document['resourceId']] ?? null; - - // Check if resource has been updated since last sync - $org = $localDocument !== null ? \strtotime($localDocument['resourceUpdatedAt']) : null; - $new = \strtotime($document['resourceUpdatedAt']); - - if (!$document['active']) { - Console::info("Removing: {$document['resourceId']}"); - unset($this->schedules[$document->getInternalId()]); - } elseif ($new !== $org) { - Console::info("Updating: {$document['resourceId']}"); - $this->schedules[$document->getInternalId()] = $getSchedule($document); + if ($latestDocument) { + $paginationQueries[] = Query::cursorAfter($latestDocument); } + + $results = $dbForConsole->find('schedules', \array_merge($paginationQueries, [ + Query::equal('region', [System::getEnv('_APP_REGION', 'default')]), + Query::equal('resourceType', [static::getSupportedResource()]), + Query::greaterThanEqual('resourceUpdatedAt', $lastSyncUpdate), + ])); + + $sum = count($results); + $total = $total + $sum; + + foreach ($results as $document) { + $localDocument = $schedules[$document['resourceId']] ?? null; + + // Check if resource has been updated since last sync + $org = $localDocument !== null ? \strtotime($localDocument['resourceUpdatedAt']) : null; + $new = \strtotime($document['resourceUpdatedAt']); + + if (!$document['active']) { + Console::info("Removing: {$document['resourceId']}"); + unset($this->schedules[$document->getInternalId()]); + } elseif ($new !== $org) { + Console::info("Updating: {$document['resourceId']}"); + $this->schedules[$document->getInternalId()] = $getSchedule($document); + } + } + + $latestDocument = \end($results); } - $latestDocument = \end($results); - } + $lastSyncUpdate = $time; + $timerEnd = \microtime(true); - $lastSyncUpdate = $time; - $timerEnd = \microtime(true); + $pools->reclaim(); - $connections->reclaim(); - Console::log("Sync tick: {$total} schedules were updated in " . ($timerEnd - $timerStart) . " seconds"); + Console::log("Sync tick: {$total} schedules were updated in " . ($timerEnd - $timerStart) . " seconds"); + }); + + Timer::tick( + static::ENQUEUE_TIMER * 1000, + fn () => $this->enqueueResources($pools, $dbForConsole) + ); + + $this->enqueueResources($pools, $dbForConsole); }); - - Timer::tick( - static::ENQUEUE_TIMER * 1000, - fn () => $this->enqueueResources($pools, $getConsoleDB) - ); - - $this->enqueueResources($pools, $getConsoleDB); } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php index b67a892b2e..682d796585 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleExecutions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleExecutions.php @@ -4,7 +4,8 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Func; use Swoole\Coroutine as Co; -use Utopia\Queue\Connection\Redis; +use Utopia\Database\Database; +use Utopia\Pools\Group; class ScheduleExecutions extends ScheduleBase { @@ -21,16 +22,11 @@ class ScheduleExecutions extends ScheduleBase return 'execution'; } - protected function enqueueResources(array $pools, callable $getConsoleDB): void + protected function enqueueResources(Group $pools, Database $dbForConsole): void { - [$connection,$pool, $dbForConsole] = $getConsoleDB(); - $this->connections->add($connection, $pool); - - $queuePool = $pools['pools-queue-queue']['pool']; - $queueConnection = $queuePool->get(); - $this->connections->add($queueConnection, $queuePool); - - $queueForFunctions = new Func(new Redis($queueConnection)); + $queue = $pools->get('queue')->pop(); + $connection = $queue->getResource(); + $queueForFunctions = new Func($connection); $intervalEnd = (new \DateTime())->modify('+' . self::ENQUEUE_TIMER . ' seconds'); foreach ($this->schedules as $schedule) { @@ -56,7 +52,7 @@ class ScheduleExecutions extends ScheduleBase $delay = $scheduledAt->getTimestamp() - (new \DateTime())->getTimestamp(); - \go(function () use ($queueForFunctions, $schedule, $delay, $data, $dbForConsole) { + \go(function () use ($queueForFunctions, $schedule, $delay, $data) { Co::sleep($delay); $queueForFunctions->setType('schedule') @@ -71,16 +67,16 @@ class ScheduleExecutions extends ScheduleBase ->setProject($schedule['project']) ->setUserId($data['userId'] ?? '') ->trigger(); - - $dbForConsole->deleteDocument( - 'schedules', - $schedule['$id'], - ); }); + $dbForConsole->deleteDocument( + 'schedules', + $schedule['$id'], + ); + unset($this->schedules[$schedule['$internalId']]); } - $this->connections->reclaim(); + $queue->reclaim(); } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php index 450551400e..e2c278714f 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleFunctions.php +++ b/src/Appwrite/Platform/Tasks/ScheduleFunctions.php @@ -5,8 +5,9 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Func; use Cron\CronExpression; use Utopia\CLI\Console; +use Utopia\Database\Database; use Utopia\Database\DateTime; -use Utopia\Queue\Connection\Redis; +use Utopia\Pools\Group; class ScheduleFunctions extends ScheduleBase { @@ -25,7 +26,7 @@ class ScheduleFunctions extends ScheduleBase return 'function'; } - protected function enqueueResources(array $pools, callable $getConsoleDB): void + protected function enqueueResources(Group $pools, Database $dbForConsole): void { $timerStart = \microtime(true); $time = DateTime::now(); @@ -67,11 +68,8 @@ class ScheduleFunctions extends ScheduleBase \go(function () use ($delay, $scheduleKeys, $pools) { \sleep($delay); // in seconds - $pool = $pools['pools-queue-queue']['pool']; - $connection = $pool->get(); - $this->connections->add($connection, $pool); - - $queueConnection = new Redis($connection); + $queue = $pools->get('queue')->pop(); + $connection = $queue->getResource(); foreach ($scheduleKeys as $scheduleKey) { // Ensure schedule was not deleted @@ -81,7 +79,7 @@ class ScheduleFunctions extends ScheduleBase $schedule = $this->schedules[$scheduleKey]; - $queueForFunctions = new Func($queueConnection); + $queueForFunctions = new Func($connection); $queueForFunctions ->setType('schedule') @@ -92,8 +90,7 @@ class ScheduleFunctions extends ScheduleBase ->trigger(); } - $this->connections->reclaim(); - // $queue->reclaim(); // TODO: Do in try/catch/finally, or add to connectons resource + $queue->reclaim(); }); } diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index 72e1a5f786..167f1282ed 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -3,7 +3,8 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Messaging; -use Utopia\Queue\Connection\Redis; +use Utopia\Database\Database; +use Utopia\Pools\Group; class ScheduleMessages extends ScheduleBase { @@ -20,11 +21,8 @@ class ScheduleMessages extends ScheduleBase return 'message'; } - protected function enqueueResources(array $pools, callable $getConsoleDB): void + protected function enqueueResources(Group $pools, Database $dbForConsole): void { - [$connection,$pool, $dbForConsole] = $getConsoleDB(); - $this->connections->add($connection, $pool); - foreach ($this->schedules as $schedule) { if (!$schedule['active']) { continue; @@ -37,15 +35,10 @@ class ScheduleMessages extends ScheduleBase continue; } - \go(function () use ($now, $schedule, $pools, $dbForConsole) { - $pool = $pools['pools-queue-queue']['pool']; - $dsn = $pools['pools-queue-queue']['dsn']; - $connection = $pool->get(); - $this->connections->add($connection, $pool); - - $queueConnection = new Redis($dsn->getHost(), $dsn->getPort()); - - $queueForMessaging = new Messaging($queueConnection); + \go(function () use ($schedule, $pools, $dbForConsole) { + $queue = $pools->get('queue')->pop(); + $connection = $queue->getResource(); + $queueForMessaging = new Messaging($connection); $queueForMessaging ->setType(MESSAGE_SEND_TYPE_EXTERNAL) @@ -58,7 +51,8 @@ class ScheduleMessages extends ScheduleBase $schedule['$id'], ); - $this->connections->reclaim(); + $queue->reclaim(); + unset($this->schedules[$schedule['$internalId']]); }); } diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 6e69798d98..e171f2f405 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -6,26 +6,21 @@ use Appwrite\Specification\Format\OpenAPI3; use Appwrite\Specification\Format\Swagger2; use Appwrite\Specification\Specification; use Appwrite\Utopia\Response; -use Appwrite\Utopia\Response\Models; use Exception; -use Swoole\Http\Request as SwooleHttpRequest; -use Swoole\Http\Response as SwooleHttpResponse; +use Swoole\Http\Response as HttpResponse; +use Utopia\App; use Utopia\Cache\Adapter\None; use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Adapter\MySQL; use Utopia\Database\Database; -use Utopia\DI\Container; -use Utopia\DI\Dependency; -use Utopia\Http\Adapter\FPM\Server; -use Utopia\Http\Adapter\Swoole\Request; -use Utopia\Http\Adapter\Swoole\Response as HttpResponse; -use Utopia\Http\Http; -use Utopia\Http\Validator\Text; -use Utopia\Http\Validator\WhiteList; use Utopia\Platform\Action; +use Utopia\Registry\Registry; +use Utopia\Request; use Utopia\System\System; +use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; class Specs extends Action { @@ -40,32 +35,21 @@ class Specs extends Action ->desc('Generate Appwrite API specifications') ->param('version', 'latest', new Text(16), 'Spec version', true) ->param('mode', 'normal', new WhiteList(['normal', 'mocks']), 'Spec Mode', true) - ->inject('context') - ->callback(fn (string $version, string $mode, Container $context) => $this->action($version, $mode, $context)); + ->inject('register') + ->callback(fn (string $version, string $mode, Registry $register) => $this->action($version, $mode, $register)); } - public function action(string $version, string $mode, Container $container): void + public function action(string $version, string $mode, Registry $register): void { - $appRoutes = Http::getRoutes(); - $response = new Response(new HttpResponse(new SwooleHttpResponse())); + $appRoutes = App::getRoutes(); + $response = new Response(new HttpResponse()); $mocks = ($mode === 'mocks'); - $requestDependency = new Dependency(); - $responseDependency = new Dependency(); - $dbForConsole = new Dependency(); - $dbForProject = new Dependency(); - // Mock dependencies - $requestDependency->setName('request')->setCallback(fn () => new Request(new SwooleHttpRequest())); - $responseDependency->setName('response')->setCallback(fn () => $response); - $dbForConsole->setName('dbForConsole')->setCallback(fn () => new Database(new MySQL(''), new Cache(new None()))); - $dbForProject->setName('dbForProject')->setCallback(fn () => new Database(new MySQL(''), new Cache(new None()))); - - $container - ->set($requestDependency) - ->set($responseDependency) - ->set($dbForProject) - ->set($dbForConsole); + App::setResource('request', fn () => new Request()); + App::setResource('response', fn () => $response); + App::setResource('dbForConsole', fn () => new Database(new MySQL(''), new Cache(new None()))); + App::setResource('dbForProject', fn () => new Database(new MySQL(''), new Cache(new None()))); $platforms = [ 'client' => APP_PLATFORM_CLIENT, @@ -257,7 +241,7 @@ class Specs extends Action ]; } - $models = Models::getModels(); + $models = $response->getModels(); foreach ($models as $key => $value) { if ($platform !== APP_PLATFORM_CONSOLE && !$value->isPublic()) { @@ -265,7 +249,7 @@ class Specs extends Action } } - $arguments = [new Http(new Server(), $container, 'UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0]; + $arguments = [new App('UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0]; foreach (['swagger2', 'open-api3'] as $format) { $formatInstance = match ($format) { 'swagger2' => new Swagger2(...$arguments), diff --git a/src/Appwrite/Platform/Tasks/Upgrade.php b/src/Appwrite/Platform/Tasks/Upgrade.php index 608b924c06..341ce42fc4 100644 --- a/src/Appwrite/Platform/Tasks/Upgrade.php +++ b/src/Appwrite/Platform/Tasks/Upgrade.php @@ -3,8 +3,8 @@ namespace Appwrite\Platform\Tasks; use Utopia\CLI\Console; -use Utopia\Http\Validator\Boolean; -use Utopia\Http\Validator\Text; +use Utopia\Validator\Boolean; +use Utopia\Validator\Text; class Upgrade extends Install { diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index d21f1c267d..86ca59d3fd 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -9,7 +9,6 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization; use Utopia\Database\Exception\Structure; -use Utopia\Database\Validator\Authorization as ValidatorAuthorization; use Utopia\Platform\Action; use Utopia\Queue\Message; @@ -29,8 +28,7 @@ class Audits extends Action ->desc('Audits worker') ->inject('message') ->inject('dbForProject') - ->inject('authorization') - ->callback(fn ($message, $dbForProject, ValidatorAuthorization $authorization) => $this->action($message, $dbForProject, $authorization)); + ->callback(fn ($message, $dbForProject) => $this->action($message, $dbForProject)); } @@ -43,7 +41,7 @@ class Audits extends Action * @throws Authorization * @throws Structure */ - public function action(Message $message, Database $dbForProject, ValidatorAuthorization $auth): void + public function action(Message $message, Database $dbForProject): void { $payload = $message->getPayload() ?? []; diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 9274e7430c..5dd2f7f886 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -54,8 +54,7 @@ class Builds extends Action ->inject('dbForProject') ->inject('deviceForFunctions') ->inject('log') - ->inject('authorization') - ->callback(fn ($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log, Authorization $authorization) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $deviceForFunctions, $log, $authorization)); + ->callback(fn ($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $deviceForFunctions, $log)); } /** @@ -68,11 +67,10 @@ class Builds extends Action * @param Database $dbForProject * @param Device $deviceForFunctions * @param Log $log - * @param Authorization $auth * @return void * @throws \Utopia\Database\Exception */ - public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log, Authorization $auth): void + public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log): void { $payload = $message->getPayload() ?? []; @@ -94,7 +92,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, $queueForUsage, $dbForConsole, $dbForProject, $github, $project, $resource, $deployment, $template, $log, $auth); + $this->buildDeployment($deviceForFunctions, $queueForFunctions, $queueForEvents, $queueForUsage, $dbForConsole, $dbForProject, $github, $project, $resource, $deployment, $template, $log); break; default: @@ -115,12 +113,11 @@ class Builds extends Action * @param Document $deployment * @param Document $template * @param Log $log - * @param Authorization $auth * @return void * @throws \Utopia\Database\Exception * @throws Exception */ - protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, Log $log, Authorization $auth): void + protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, Log $log): void { $executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST')); @@ -224,18 +221,20 @@ class Builds extends Action $templateRootDirectory = \ltrim($templateRootDirectory, '/'); if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateVersion)) { - $output = ''; + $stdout = ''; + $stderr = ''; + // Clone template repo $tmpTemplateDirectory = '/tmp/builds/' . $buildId . '-template'; $gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory); - $exit = Console::execute($gitCloneCommandForTemplate, '', $output); + $exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr); if ($exit !== 0) { - throw new \Exception('Unable to clone code repository: ' . $output); + throw new \Exception('Unable to clone code repository: ' . $stderr); } // Ensure directories - Console::execute('mkdir -p ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory), '', $output); + Console::execute('mkdir -p ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory), '', $stdout, $stderr); $tmpPathFile = $tmpTemplateDirectory . '/code.tar.gz'; @@ -246,7 +245,7 @@ class Builds extends Action } $tarParamDirectory = \escapeshellarg($tmpTemplateDirectory . (empty($templateRootDirectory) ? '' : '/' . $templateRootDirectory)); - Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $output); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax + Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $stdout, $stderr); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax $source = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION)); $result = $localDevice->transfer($tmpPathFile, $source, $deviceForFunctions); @@ -255,7 +254,7 @@ class Builds extends Action throw new \Exception("Unable to move file"); } - Console::execute('rm -rf ' . \escapeshellarg($tmpTemplateDirectory), '', $output); + Console::execute('rm -rf ' . \escapeshellarg($tmpTemplateDirectory), '', $stdout, $stderr); $directorySize = $deviceForFunctions->getFileSize($source); $build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source)); @@ -277,7 +276,6 @@ class Builds extends Action $branchName = $deployment->getAttribute('providerBranch'); $commitHash = $deployment->getAttribute('providerCommitHash', ''); - $output = ''; $cloneVersion = $branchName; $cloneType = GitHub::CLONE_TYPE_BRANCH; @@ -285,18 +283,22 @@ class Builds extends Action $cloneVersion = $commitHash; $cloneType = GitHub::CLONE_TYPE_COMMIT; } + $gitCloneCommand = $github->generateCloneCommand($cloneOwner, $cloneRepository, $cloneVersion, $cloneType, $tmpDirectory, $rootDirectory); - Console::execute('mkdir -p ' . \escapeshellarg('/tmp/builds/' . $buildId), '', $output); + $stdout = ''; + $stderr = ''; + + Console::execute('mkdir -p ' . \escapeshellarg('/tmp/builds/' . $buildId), '', $stdout, $stderr); if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') { Console::info('Build has been canceled'); return; } - $exit = Console::execute($gitCloneCommand, '', $output); + $exit = Console::execute($gitCloneCommand, '', $stdout, $stderr); if ($exit !== 0) { - throw new \Exception('Unable to clone code repository: ' . $output); + throw new \Exception('Unable to clone code repository: ' . $stderr); } // Local refactoring for function folder with spaces @@ -304,10 +306,10 @@ class Builds extends Action $rootDirectoryWithoutSpaces = str_replace(' ', '', $rootDirectory); $from = $tmpDirectory . '/' . $rootDirectory; $to = $tmpDirectory . '/' . $rootDirectoryWithoutSpaces; - $exit = Console::execute('mv "' . \escapeshellarg($from) . '" "' . \escapeshellarg($to) . '"', '', $output); + $exit = Console::execute('mv "' . \escapeshellarg($from) . '" "' . \escapeshellarg($to) . '"', '', $stdout, $stderr); if ($exit !== 0) { - throw new \Exception('Unable to move function with spaces' . $output); + throw new \Exception('Unable to move function with spaces' . $stderr); } $rootDirectory = $rootDirectoryWithoutSpaces; } @@ -328,33 +330,33 @@ class Builds extends Action $tmpTemplateDirectory = '/tmp/builds/' . $buildId . '/template'; $gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory); - $exit = Console::execute($gitCloneCommandForTemplate, '', $output); + $exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr); if ($exit !== 0) { - throw new \Exception('Unable to clone code repository: ' . $output); + throw new \Exception('Unable to clone code repository: ' . $stderr); } // Ensure directories - Console::execute('mkdir -p ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory), '', $output); - Console::execute('mkdir -p ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $output); + Console::execute('mkdir -p ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory), '', $stdout, $stderr); + Console::execute('mkdir -p ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $stdout, $stderr); // Merge template into user repo - Console::execute('rsync -av --exclude \'.git\' ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory . '/') . ' ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $output); + Console::execute('rsync -av --exclude \'.git\' ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory . '/') . ' ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $stdout, $stderr); // Commit and push - $exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . \escapeshellarg($tmpDirectory) . ' && git add . && git commit -m "Create ' . \escapeshellarg($function->getAttribute('name', '')) . ' function" && git push origin ' . \escapeshellarg($branchName), '', $output); + $exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . \escapeshellarg($tmpDirectory) . ' && git add . && git commit -m "Create ' . \escapeshellarg($function->getAttribute('name', '')) . ' function" && git push origin ' . \escapeshellarg($branchName), '', $stdout, $stderr); if ($exit !== 0) { - throw new \Exception('Unable to push code repository: ' . $output); + throw new \Exception('Unable to push code repository: ' . $stderr); } - $exit = Console::execute('cd ' . \escapeshellarg($tmpDirectory) . ' && git rev-parse HEAD', '', $output); + $exit = Console::execute('cd ' . \escapeshellarg($tmpDirectory) . ' && git rev-parse HEAD', '', $stdout, $stderr); if ($exit !== 0) { - throw new \Exception('Unable to get vcs commit SHA: ' . $output); + throw new \Exception('Unable to get vcs commit SHA: ' . $stderr); } - $providerCommitHash = \trim($output); + $providerCommitHash = \trim($stdout); $authorUrl = "https://github.com/$cloneOwner"; $deployment->setAttribute('providerCommitHash', $providerCommitHash ?? ''); @@ -397,7 +399,7 @@ class Builds extends Action } $tarParamDirectory = '/tmp/builds/' . $buildId . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory); - Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $output); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax + Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $stdout, $stderr); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax $source = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION)); $result = $localDevice->transfer($tmpPathFile, $source, $deviceForFunctions); @@ -406,7 +408,7 @@ class Builds extends Action throw new \Exception("Unable to move file"); } - Console::execute('rm -rf ' . \escapeshellarg($tmpPath), '', $output); + Console::execute('rm -rf ' . \escapeshellarg($tmpPath), '', $stdout, $stderr); $build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source)); @@ -661,7 +663,7 @@ class Builds extends Action ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - $auth->skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); } catch (\Throwable $th) { if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') { Console::info('Build has been canceled'); diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index 4a8d928ba2..58dc1dd28a 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -11,6 +11,7 @@ use Appwrite\Template\Template; use Appwrite\Utopia\Response\Model\Rule; use Exception; use Throwable; +use Utopia\App; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\DateTime; @@ -21,7 +22,6 @@ use Utopia\Database\Exception\Structure; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Domains\Domain; -use Utopia\Http\Http; use Utopia\Locale\Locale; use Utopia\Logger\Log; use Utopia\Platform\Action; @@ -330,26 +330,30 @@ class Certificates extends Action * * @param string $folder Folder into which certificates should be generated * @param string $domain Domain to generate certificate for - * @return string output + * @return array Named array with keys 'stdout' and 'stderr', both string * @throws Exception */ - private function issueCertificate(string $folder, string $domain, string $email): string + private function issueCertificate(string $folder, string $domain, string $email): array { - $output = ''; + $stdout = ''; + $stderr = ''; - $staging = (Http::isProduction()) ? '' : ' --dry-run'; + $staging = (App::isProduction()) ? '' : ' --dry-run'; $exit = Console::execute("certbot certonly -v --webroot --noninteractive --agree-tos{$staging}" . " --email " . $email . " --cert-name " . $folder . " -w " . APP_STORAGE_CERTIFICATES - . " -d {$domain}", '', $output); + . " -d {$domain}", '', $stdout, $stderr); // Unexpected error, usually 5XX, API limits, ... if ($exit !== 0) { - throw new Exception('Failed to issue a certificate with message: ' . $output); + throw new Exception('Failed to issue a certificate with message: ' . $stderr); } - return $output; + return [ + 'stdout' => $stdout, + 'stderr' => $stderr + ]; } /** @@ -377,7 +381,7 @@ class Certificates extends Action * @return void * @throws Exception */ - private function applyCertificateFiles(string $folder, string $domain, string $letsEncryptData): void + private function applyCertificateFiles(string $folder, string $domain, array $letsEncryptData): void { // Prepare folder in storage for domain @@ -390,19 +394,19 @@ class Certificates extends Action // Move generated files if (!@\rename('/etc/letsencrypt/live/' . $folder . '/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) { - throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData); + throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); } if (!@\rename('/etc/letsencrypt/live/' . $folder . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) { - throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData); + throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); } if (!@\rename('/etc/letsencrypt/live/' . $folder . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) { - throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData); + throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); } if (!@\rename('/etc/letsencrypt/live/' . $folder . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) { - throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData); + throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']); } $config = \implode(PHP_EOL, [ diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index e0d3bc8405..c70d9ca11b 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -22,7 +22,6 @@ use Utopia\Database\Exception\Conflict; use Utopia\Database\Exception\Restricted; use Utopia\Database\Exception\Structure; use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization as ValidatorAuthorization; use Utopia\DSN\DSN; use Utopia\Logger\Log; use Utopia\Platform\Action; @@ -55,15 +54,14 @@ class Deletes extends Action ->inject('executionRetention') ->inject('auditRetention') ->inject('log') - ->inject('authorization') - ->callback(fn ($message, $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log, ValidatorAuthorization $authorization) => $this->action($message, $dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $abuseRetention, $executionRetention, $auditRetention, $log, $authorization)); + ->callback(fn ($message, $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log) => $this->action($message, $dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $abuseRetention, $executionRetention, $auditRetention, $log)); } /** * @throws Exception * @throws Throwable */ - public function action(Message $message, Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log, ValidatorAuthorization $auth): void + public function action(Message $message, Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log): void { $payload = $message->getPayload() ?? []; @@ -119,7 +117,7 @@ class Deletes extends Action break; case DELETE_TYPE_AUDIT: if (!$project->isEmpty()) { - $this->deleteAuditLogs($project, $getProjectDB, $auditRetention, $auth); + $this->deleteAuditLogs($project, $getProjectDB, $auditRetention); } if (!$document->isEmpty()) { @@ -127,7 +125,7 @@ class Deletes extends Action } break; case DELETE_TYPE_ABUSE: - $this->deleteAbuseLogs($project, $getProjectDB, $abuseRetention, $auth); + $this->deleteAbuseLogs($project, $getProjectDB, $abuseRetention); break; case DELETE_TYPE_REALTIME: $this->deleteRealtimeUsage($dbForConsole, $datetime); @@ -684,7 +682,7 @@ class Deletes extends Action * @return void * @throws Exception */ - private function deleteAbuseLogs(Document $project, callable $getProjectDB, string $abuseRetention, ValidatorAuthorization $auth): void + private function deleteAbuseLogs(Document $project, callable $getProjectDB, string $abuseRetention): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); @@ -705,7 +703,7 @@ class Deletes extends Action * @return void * @throws Exception */ - private function deleteAuditLogs(Document $project, callable $getProjectDB, string $auditRetention, ValidatorAuthorization $auth): void + private function deleteAuditLogs(Document $project, callable $getProjectDB, string $auditRetention): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); diff --git a/src/Appwrite/Platform/Workers/Mails.php b/src/Appwrite/Platform/Workers/Mails.php index 51bdb56cf0..e48f96ea90 100644 --- a/src/Appwrite/Platform/Workers/Mails.php +++ b/src/Appwrite/Platform/Workers/Mails.php @@ -84,13 +84,13 @@ class Mails extends Action $bodyTemplate = __DIR__ . '/../../../../app/config/locale/templates/email-base.tpl'; } $bodyTemplate = Template::fromFile($bodyTemplate); - $bodyTemplate->setParam('{{body}}', $body, escape: false); + $bodyTemplate->setParam('{{body}}', $body, escapeHtml: false); foreach ($variables as $key => $value) { // TODO: hotfix for redirect param - $bodyTemplate->setParam('{{' . $key . '}}', $value, escape: $key !== 'redirect'); + $bodyTemplate->setParam('{{' . $key . '}}', $value, escapeHtml: $key !== 'redirect'); } foreach ($this->richTextParams as $key => $value) { - $bodyTemplate->setParam('{{' . $key . '}}', $value, escape: false); + $bodyTemplate->setParam('{{' . $key . '}}', $value, escapeHtml: false); } $body = $bodyTemplate->render(); diff --git a/src/Appwrite/Promises/Promise.php b/src/Appwrite/Promises/Promise.php index f12590dfed..a6b1aa79d5 100644 --- a/src/Appwrite/Promises/Promise.php +++ b/src/Appwrite/Promises/Promise.php @@ -12,7 +12,7 @@ abstract class Promise private mixed $result; - final public function __construct(?callable $executor = null) + public function __construct(?callable $executor = null) { if (\is_null($executor)) { return; diff --git a/src/Appwrite/Promises/Swoole.php b/src/Appwrite/Promises/Swoole.php index 8cddd567f3..c258ef6a5e 100644 --- a/src/Appwrite/Promises/Swoole.php +++ b/src/Appwrite/Promises/Swoole.php @@ -6,16 +6,23 @@ use Swoole\Coroutine\Channel; class Swoole extends Promise { + public function __construct(?callable $executor = null) + { + parent::__construct($executor); + } + protected function execute( callable $executor, callable $resolve, callable $reject ): void { - try { - $executor($resolve, $reject); - } catch (\Throwable $exception) { - $reject($exception); - } + \go(function () use ($executor, $resolve, $reject) { + try { + $executor($resolve, $reject); + } catch (\Throwable $exception) { + $reject($exception); + } + }); } /** diff --git a/src/Appwrite/Specification/Format.php b/src/Appwrite/Specification/Format.php index e2048971be..30ce6470e1 100644 --- a/src/Appwrite/Specification/Format.php +++ b/src/Appwrite/Specification/Format.php @@ -3,13 +3,13 @@ namespace Appwrite\Specification; use Appwrite\Utopia\Response\Model; +use Utopia\App; use Utopia\Config\Config; -use Utopia\Http\Http; -use Utopia\Http\Route; +use Utopia\Route; abstract class Format { - protected Http $http; + protected App $app; /** * @var Route[] @@ -50,9 +50,9 @@ abstract class Format ] ]; - public function __construct(Http $http, array $services, array $routes, array $models, array $keys, int $authCount) + public function __construct(App $app, array $services, array $routes, array $models, array $keys, int $authCount) { - $this->http = $http; + $this->app = $app; $this->services = $services; $this->routes = $routes; $this->models = $models; diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index c9186ee0ac..3074d59b7c 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -7,11 +7,11 @@ use Appwrite\Template\Template; use Appwrite\Utopia\Response\Model; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; -use Utopia\Http\Validator; -use Utopia\Http\Validator\ArrayList; -use Utopia\Http\Validator\Nullable; -use Utopia\Http\Validator\Range; -use Utopia\Http\Validator\WhiteList; +use Utopia\Validator; +use Utopia\Validator\ArrayList; +use Utopia\Validator\Nullable; +use Utopia\Validator\Range; +use Utopia\Validator\WhiteList; class OpenAPI3 extends Format { @@ -238,11 +238,8 @@ class OpenAPI3 extends Format } if ($route->getLabel('sdk.response.code', 500) === 204) { - $labelCode = (string)$route->getLabel('sdk.response.code', '500'); - $temp['responses'][$labelCode]['description'] = 'No content'; - if (isset($temp['responses'][$labelCode]['schema'])) { - unset($temp['responses'][$labelCode]['schema']); - } + $temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['description'] = 'No content'; + unset($temp['responses'][(string)$route->getLabel('sdk.response.code', '500')]['schema']); } if ((!empty($scope))) { // && 'public' != $scope @@ -272,10 +269,10 @@ class OpenAPI3 extends Format $bodyRequired = []; foreach ($route->getParams() as $name => $param) { // Set params - $injections = array_map(fn ($injection) => $this->http->getContainer()->get($injection), $param['injections'] ?? []); - - /** @var Validator $validator */ - $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $injections) : $param['validator']; + /** + * @var \Utopia\Validator $validator + */ + $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator']; $node = [ 'name' => $name, @@ -292,11 +289,11 @@ class OpenAPI3 extends Format switch ((!empty($validator)) ? \get_class($validator) : '') { case 'Utopia\Database\Validator\UID': - case 'Utopia\Http\Validator\Text': + case 'Utopia\Validator\Text': $node['schema']['type'] = $validator->getType(); $node['schema']['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; break; - case 'Utopia\Http\Validator\Boolean': + case 'Utopia\Validator\Boolean': $node['schema']['type'] = $validator->getType(); $node['schema']['x-example'] = false; break; @@ -317,15 +314,15 @@ class OpenAPI3 extends Format $node['schema']['format'] = 'email'; $node['schema']['x-example'] = 'email@example.com'; break; - case 'Utopia\Http\Validator\Host': - case 'Utopia\Http\Validator\URL': + case 'Utopia\Validator\Host': + case 'Utopia\Validator\URL': $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'url'; $node['schema']['x-example'] = 'https://example.com'; break; - case 'Utopia\Http\Validator\JSON': - case 'Utopia\Http\Validator\Mock': - case 'Utopia\Http\Validator\Assoc': + case 'Utopia\Validator\JSON': + case 'Utopia\Validator\Mock': + case 'Utopia\Validator\Assoc': $param['default'] = (empty($param['default'])) ? new \stdClass() : $param['default']; $node['schema']['type'] = 'object'; $node['schema']['x-example'] = '{}'; @@ -335,7 +332,7 @@ class OpenAPI3 extends Format $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'binary'; break; - case 'Utopia\Http\Validator\ArrayList': + case 'Utopia\Validator\ArrayList': /** @var ArrayList $validator */ $node['schema']['type'] = 'array'; $node['schema']['items'] = [ @@ -397,25 +394,25 @@ class OpenAPI3 extends Format $node['schema']['format'] = 'phone'; $node['schema']['x-example'] = '+12065550100'; // In the US, 555 is reserved like example.com break; - case 'Utopia\Http\Validator\Range': + case 'Utopia\Validator\Range': /** @var Range $validator */ $node['schema']['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType(); $node['schema']['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float'; $node['schema']['x-example'] = $validator->getMin(); break; - case 'Utopia\Http\Validator\Numeric': - case 'Utopia\Http\Validator\Integer': + case 'Utopia\Validator\Numeric': + case 'Utopia\Validator\Integer': $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'int32'; break; - case 'Utopia\Http\Validator\FloatValidator': + case 'Utopia\Validator\FloatValidator': $node['schema']['type'] = 'number'; $node['schema']['format'] = 'float'; break; - case 'Utopia\Http\Validator\Length': + case 'Utopia\Validator\Length': $node['schema']['type'] = $validator->getType(); break; - case 'Utopia\Http\Validator\WhiteList': + case 'Utopia\Validator\WhiteList': /** @var WhiteList $validator */ $node['schema']['type'] = $validator->getType(); $node['schema']['x-example'] = $validator->getList()[0]; diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 81e0f2c41d..2eab7807b3 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -7,10 +7,10 @@ use Appwrite\Template\Template; use Appwrite\Utopia\Response\Model; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; -use Utopia\Http\Validator; -use Utopia\Http\Validator\ArrayList; -use Utopia\Http\Validator\Nullable; -use Utopia\Http\Validator\Range; +use Utopia\Validator; +use Utopia\Validator\ArrayList; +use Utopia\Validator\Nullable; +use Utopia\Validator\Range; class Swagger2 extends Format { @@ -270,10 +270,8 @@ class Swagger2 extends Format ); foreach ($parameters as $name => $param) { // Set params - $injections = array_map(fn ($injection) => $this->http->getContainer()->get($injection), $param['injections'] ?? []); - /** @var Validator $validator */ - $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $injections) : $param['validator']; + $validator = (\is_callable($param['validator'])) ? call_user_func_array($param['validator'], $this->app->getResources($param['injections'])) : $param['validator']; $node = [ 'name' => $name, @@ -289,18 +287,18 @@ class Swagger2 extends Format } $validatorClass = (!empty($validator)) ? \get_class($validator) : ''; - if ($validatorClass === 'Utopia\Http\Validator\AnyOf') { + if ($validatorClass === 'Utopia\Validator\AnyOf') { $validator = $param['validator']->getValidators()[0]; $validatorClass = \get_class($validator); } switch ($validatorClass) { - case 'Utopia\Http\Validator\Text': + case 'Utopia\Validator\Text': case 'Utopia\Database\Validator\UID': $node['type'] = $validator->getType(); $node['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; break; - case 'Utopia\Http\Validator\Boolean': + case 'Utopia\Validator\Boolean': $node['type'] = $validator->getType(); $node['x-example'] = false; break; @@ -321,13 +319,13 @@ class Swagger2 extends Format $node['format'] = 'email'; $node['x-example'] = 'email@example.com'; break; - case 'Utopia\Http\Validator\Host': - case 'Utopia\Http\Validator\URL': + case 'Utopia\Validator\Host': + case 'Utopia\Validator\URL': $node['type'] = $validator->getType(); $node['format'] = 'url'; $node['x-example'] = 'https://example.com'; break; - case 'Utopia\Http\Validator\ArrayList': + case 'Utopia\Validator\ArrayList': /** @var ArrayList $validator */ $node['type'] = 'array'; $node['collectionFormat'] = 'multi'; @@ -335,9 +333,9 @@ class Swagger2 extends Format 'type' => $validator->getValidator()->getType(), ]; break; - case 'Utopia\Http\Validator\JSON': - case 'Utopia\Http\Validator\Mock': - case 'Utopia\Http\Validator\Assoc': + case 'Utopia\Validator\JSON': + case 'Utopia\Validator\Mock': + case 'Utopia\Validator\Assoc': $node['type'] = 'object'; $node['default'] = (empty($param['default'])) ? new \stdClass() : $param['default']; $node['x-example'] = '{}'; @@ -408,26 +406,26 @@ class Swagger2 extends Format $node['format'] = 'phone'; $node['x-example'] = '+12065550100'; break; - case 'Utopia\Http\Validator\Range': + case 'Utopia\Validator\Range': /** @var Range $validator */ $node['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType(); $node['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float'; $node['x-example'] = $validator->getMin(); break; - case 'Utopia\Http\Validator\Numeric': - case 'Utopia\Http\Validator\Integer': + case 'Utopia\Validator\Numeric': + case 'Utopia\Validator\Integer': $node['type'] = $validator->getType(); $node['format'] = 'int32'; break; - case 'Utopia\Http\Validator\FloatValidator': + case 'Utopia\Validator\FloatValidator': $node['type'] = 'number'; $node['format'] = 'float'; break; - case 'Utopia\Http\Validator\Length': + case 'Utopia\Validator\Length': $node['type'] = $validator->getType(); break; - case 'Utopia\Http\Validator\WhiteList': - /** @var \Utopia\Http\Validator\WhiteList $validator */ + case 'Utopia\Validator\WhiteList': + /** @var \Utopia\Validator\WhiteList $validator */ $node['type'] = $validator->getType(); $node['x-example'] = $validator->getList()[0]; diff --git a/src/Appwrite/Task/Validator/Cron.php b/src/Appwrite/Task/Validator/Cron.php index afa19c50a6..03bd1c5220 100644 --- a/src/Appwrite/Task/Validator/Cron.php +++ b/src/Appwrite/Task/Validator/Cron.php @@ -3,7 +3,7 @@ namespace Appwrite\Task\Validator; use Cron\CronExpression; -use Utopia\Http\Validator; +use Utopia\Validator; class Cron extends Validator { diff --git a/src/Appwrite/Utopia/Database/Validator/CompoundUID.php b/src/Appwrite/Utopia/Database/Validator/CompoundUID.php index b851d8ba85..3f23500952 100644 --- a/src/Appwrite/Utopia/Database/Validator/CompoundUID.php +++ b/src/Appwrite/Utopia/Database/Validator/CompoundUID.php @@ -3,7 +3,7 @@ namespace Appwrite\Utopia\Database\Validator; use Utopia\Database\Validator\UID; -use Utopia\Http\Validator; +use Utopia\Validator; class CompoundUID extends Validator { diff --git a/src/Appwrite/Utopia/Database/Validator/ProjectId.php b/src/Appwrite/Utopia/Database/Validator/ProjectId.php index 28abe176fe..46b0cdf53e 100644 --- a/src/Appwrite/Utopia/Database/Validator/ProjectId.php +++ b/src/Appwrite/Utopia/Database/Validator/ProjectId.php @@ -2,7 +2,7 @@ namespace Appwrite\Utopia\Database\Validator; -use Utopia\Http\Validator; +use Utopia\Validator; class ProjectId extends Validator { diff --git a/src/Appwrite/Utopia/Queue/Connections.php b/src/Appwrite/Utopia/Queue/Connections.php deleted file mode 100644 index e873373566..0000000000 --- a/src/Appwrite/Utopia/Queue/Connections.php +++ /dev/null @@ -1,36 +0,0 @@ -connections[] = ['connection' => $connection, 'pool' => $pool]; - return $this; - } - - /** - * @return self - */ - public function reclaim(): self - { - foreach ($this->connections as $id => $resource) { - $pool = $resource['pool']; - $connection = $resource['connection']; - $pool->put($connection); - unset($this->connections[$id]); - } - - return $this; - } -} diff --git a/src/Appwrite/Utopia/Request.php b/src/Appwrite/Utopia/Request.php index 6be9701baa..3f0a196d5e 100644 --- a/src/Appwrite/Utopia/Request.php +++ b/src/Appwrite/Utopia/Request.php @@ -3,10 +3,11 @@ namespace Appwrite\Utopia; use Appwrite\Utopia\Request\Filter; -use Utopia\Http\Adapter\Swoole\Request as HttpRequest; -use Utopia\Http\Route; +use Swoole\Http\Request as SwooleRequest; +use Utopia\Route; +use Utopia\Swoole\Request as UtopiaRequest; -class Request extends HttpRequest +class Request extends UtopiaRequest { /** * @var array @@ -14,12 +15,9 @@ class Request extends HttpRequest private array $filters = []; private static ?Route $route = null; - /** - * Request constructor. - */ - public function __construct(HttpRequest $request) + public function __construct(SwooleRequest $request) { - parent::__construct($request->swoole); + parent::__construct($request); } /** @@ -115,16 +113,6 @@ class Request extends HttpRequest return self::$route !== null; } - - public function removeHeader(string $key): static - { - if (isset($this->headers[$key])) { - unset($this->headers[$key]); - } - - return parent::removeHeader($key); - } - /** * Get headers * @@ -134,14 +122,10 @@ class Request extends HttpRequest */ public function getHeaders(): array { - if ($this->headers !== null) { - return $this->headers; - } - - $this->headers = $this->generateHeaders(); + $headers = $this->generateHeaders(); if (empty($this->swoole->cookie)) { - return $this->headers; + return $headers; } $cookieHeaders = []; @@ -150,10 +134,10 @@ class Request extends HttpRequest } if (!empty($cookieHeaders)) { - $this->headers['cookie'] = \implode('; ', $cookieHeaders); + $headers['cookie'] = \implode('; ', $cookieHeaders); } - return $this->headers; + return $headers; } /** diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 3808b1e9f8..a2c07d34b0 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -4,20 +4,122 @@ namespace Appwrite\Utopia; use Appwrite\Utopia\Fetch\BodyMultipart; use Appwrite\Utopia\Response\Filter; -use Appwrite\Utopia\Response\Models; +use Appwrite\Utopia\Response\Model; +use Appwrite\Utopia\Response\Model\Account; +use Appwrite\Utopia\Response\Model\AlgoArgon2; +use Appwrite\Utopia\Response\Model\AlgoBcrypt; +use Appwrite\Utopia\Response\Model\AlgoMd5; +use Appwrite\Utopia\Response\Model\AlgoPhpass; +use Appwrite\Utopia\Response\Model\AlgoScrypt; +use Appwrite\Utopia\Response\Model\AlgoScryptModified; +use Appwrite\Utopia\Response\Model\AlgoSha; +use Appwrite\Utopia\Response\Model\Any; +use Appwrite\Utopia\Response\Model\Attribute; +use Appwrite\Utopia\Response\Model\AttributeBoolean; +use Appwrite\Utopia\Response\Model\AttributeDatetime; +use Appwrite\Utopia\Response\Model\AttributeEmail; +use Appwrite\Utopia\Response\Model\AttributeEnum; +use Appwrite\Utopia\Response\Model\AttributeFloat; +use Appwrite\Utopia\Response\Model\AttributeInteger; +use Appwrite\Utopia\Response\Model\AttributeIP; +use Appwrite\Utopia\Response\Model\AttributeList; +use Appwrite\Utopia\Response\Model\AttributeRelationship; +use Appwrite\Utopia\Response\Model\AttributeString; +use Appwrite\Utopia\Response\Model\AttributeURL; +use Appwrite\Utopia\Response\Model\AuthProvider; +use Appwrite\Utopia\Response\Model\BaseList; +use Appwrite\Utopia\Response\Model\Branch; +use Appwrite\Utopia\Response\Model\Bucket; +use Appwrite\Utopia\Response\Model\Build; +use Appwrite\Utopia\Response\Model\Collection; +use Appwrite\Utopia\Response\Model\ConsoleVariables; +use Appwrite\Utopia\Response\Model\Continent; +use Appwrite\Utopia\Response\Model\Country; +use Appwrite\Utopia\Response\Model\Currency; +use Appwrite\Utopia\Response\Model\Database; +use Appwrite\Utopia\Response\Model\Deployment; +use Appwrite\Utopia\Response\Model\Detection; +use Appwrite\Utopia\Response\Model\Document as ModelDocument; +use Appwrite\Utopia\Response\Model\Error; +use Appwrite\Utopia\Response\Model\ErrorDev; +use Appwrite\Utopia\Response\Model\Execution; +use Appwrite\Utopia\Response\Model\File; +use Appwrite\Utopia\Response\Model\Func; +use Appwrite\Utopia\Response\Model\Headers; +use Appwrite\Utopia\Response\Model\HealthAntivirus; +use Appwrite\Utopia\Response\Model\HealthCertificate; +use Appwrite\Utopia\Response\Model\HealthQueue; +use Appwrite\Utopia\Response\Model\HealthStatus; +use Appwrite\Utopia\Response\Model\HealthTime; +use Appwrite\Utopia\Response\Model\HealthVersion; +use Appwrite\Utopia\Response\Model\Identity; +use Appwrite\Utopia\Response\Model\Index; +use Appwrite\Utopia\Response\Model\Installation; +use Appwrite\Utopia\Response\Model\JWT; +use Appwrite\Utopia\Response\Model\Key; +use Appwrite\Utopia\Response\Model\Language; +use Appwrite\Utopia\Response\Model\Locale; +use Appwrite\Utopia\Response\Model\LocaleCode; +use Appwrite\Utopia\Response\Model\Log; +use Appwrite\Utopia\Response\Model\Membership; +use Appwrite\Utopia\Response\Model\Message; +use Appwrite\Utopia\Response\Model\Metric; +use Appwrite\Utopia\Response\Model\MetricBreakdown; +use Appwrite\Utopia\Response\Model\MFAChallenge; +use Appwrite\Utopia\Response\Model\MFAFactors; +use Appwrite\Utopia\Response\Model\MFARecoveryCodes; +use Appwrite\Utopia\Response\Model\MFAType; +use Appwrite\Utopia\Response\Model\Migration; +use Appwrite\Utopia\Response\Model\MigrationFirebaseProject; +use Appwrite\Utopia\Response\Model\MigrationReport; +use Appwrite\Utopia\Response\Model\Mock; +use Appwrite\Utopia\Response\Model\MockNumber; +use Appwrite\Utopia\Response\Model\None; +use Appwrite\Utopia\Response\Model\Phone; +use Appwrite\Utopia\Response\Model\Platform; +use Appwrite\Utopia\Response\Model\Preferences; +use Appwrite\Utopia\Response\Model\Project; +use Appwrite\Utopia\Response\Model\Provider; +use Appwrite\Utopia\Response\Model\ProviderRepository; +use Appwrite\Utopia\Response\Model\Rule; +use Appwrite\Utopia\Response\Model\Runtime; +use Appwrite\Utopia\Response\Model\Session; +use Appwrite\Utopia\Response\Model\Specification; +use Appwrite\Utopia\Response\Model\Subscriber; +use Appwrite\Utopia\Response\Model\Target; +use Appwrite\Utopia\Response\Model\Team; +use Appwrite\Utopia\Response\Model\TemplateEmail; +use Appwrite\Utopia\Response\Model\TemplateFunction; +use Appwrite\Utopia\Response\Model\TemplateRuntime; +use Appwrite\Utopia\Response\Model\TemplateSMS; +use Appwrite\Utopia\Response\Model\TemplateVariable; +use Appwrite\Utopia\Response\Model\Token; +use Appwrite\Utopia\Response\Model\Topic; +use Appwrite\Utopia\Response\Model\UsageBuckets; +use Appwrite\Utopia\Response\Model\UsageCollection; +use Appwrite\Utopia\Response\Model\UsageDatabase; +use Appwrite\Utopia\Response\Model\UsageDatabases; +use Appwrite\Utopia\Response\Model\UsageFunction; +use Appwrite\Utopia\Response\Model\UsageFunctions; +use Appwrite\Utopia\Response\Model\UsageProject; +use Appwrite\Utopia\Response\Model\UsageStorage; +use Appwrite\Utopia\Response\Model\UsageUsers; +use Appwrite\Utopia\Response\Model\User; +use Appwrite\Utopia\Response\Model\Variable; +use Appwrite\Utopia\Response\Model\VcsContent; +use Appwrite\Utopia\Response\Model\Webhook; use Exception; use JsonException; +use Swoole\Http\Response as SwooleHTTPResponse; // Keep last use Utopia\Database\Document; -use Utopia\Http\Adapter\Swoole\Response as HttpResponse; - -// Keep last +use Utopia\Swoole\Response as SwooleResponse; /** * @method int getStatusCode() * @method Response setStatusCode(int $code = 200) */ -class Response extends HttpResponse +class Response extends SwooleResponse { // General public const MODEL_NONE = 'none'; @@ -228,9 +330,162 @@ class Response extends HttpResponse * * @param float $time */ - public function __construct(HttpResponse $response) + public function __construct(SwooleHTTPResponse $response) { - parent::__construct($response->swoole); + $this + // General + ->setModel(new None()) + ->setModel(new Any()) + ->setModel(new Error()) + ->setModel(new ErrorDev()) + // Lists + ->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT)) + ->setModel(new BaseList('Collections List', self::MODEL_COLLECTION_LIST, 'collections', self::MODEL_COLLECTION)) + ->setModel(new BaseList('Databases List', self::MODEL_DATABASE_LIST, 'databases', self::MODEL_DATABASE)) + ->setModel(new BaseList('Indexes List', self::MODEL_INDEX_LIST, 'indexes', self::MODEL_INDEX)) + ->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER)) + ->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION)) + ->setModel(new BaseList('Identities List', self::MODEL_IDENTITY_LIST, 'identities', self::MODEL_IDENTITY)) + ->setModel(new BaseList('Logs List', self::MODEL_LOG_LIST, 'logs', self::MODEL_LOG)) + ->setModel(new BaseList('Files List', self::MODEL_FILE_LIST, 'files', self::MODEL_FILE)) + ->setModel(new BaseList('Buckets List', self::MODEL_BUCKET_LIST, 'buckets', self::MODEL_BUCKET)) + ->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM)) + ->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP)) + ->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION)) + ->setModel(new BaseList('Function Templates List', self::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', self::MODEL_TEMPLATE_FUNCTION)) + ->setModel(new BaseList('Installations List', self::MODEL_INSTALLATION_LIST, 'installations', self::MODEL_INSTALLATION)) + ->setModel(new BaseList('Provider Repositories List', self::MODEL_PROVIDER_REPOSITORY_LIST, 'providerRepositories', self::MODEL_PROVIDER_REPOSITORY)) + ->setModel(new BaseList('Branches List', self::MODEL_BRANCH_LIST, 'branches', self::MODEL_BRANCH)) + ->setModel(new BaseList('Runtimes List', self::MODEL_RUNTIME_LIST, 'runtimes', self::MODEL_RUNTIME)) + ->setModel(new BaseList('Deployments List', self::MODEL_DEPLOYMENT_LIST, 'deployments', self::MODEL_DEPLOYMENT)) + ->setModel(new BaseList('Executions List', self::MODEL_EXECUTION_LIST, 'executions', self::MODEL_EXECUTION)) + ->setModel(new BaseList('Builds List', self::MODEL_BUILD_LIST, 'builds', self::MODEL_BUILD)) // Not used anywhere yet + ->setModel(new BaseList('Projects List', self::MODEL_PROJECT_LIST, 'projects', self::MODEL_PROJECT, true, false)) + ->setModel(new BaseList('Webhooks List', self::MODEL_WEBHOOK_LIST, 'webhooks', self::MODEL_WEBHOOK, true, false)) + ->setModel(new BaseList('API Keys List', self::MODEL_KEY_LIST, 'keys', self::MODEL_KEY, true, false)) + ->setModel(new BaseList('Auth Providers List', self::MODEL_AUTH_PROVIDER_LIST, 'platforms', self::MODEL_AUTH_PROVIDER, true, false)) + ->setModel(new BaseList('Platforms List', self::MODEL_PLATFORM_LIST, 'platforms', self::MODEL_PLATFORM, true, false)) + ->setModel(new BaseList('Countries List', self::MODEL_COUNTRY_LIST, 'countries', self::MODEL_COUNTRY)) + ->setModel(new BaseList('Continents List', self::MODEL_CONTINENT_LIST, 'continents', self::MODEL_CONTINENT)) + ->setModel(new BaseList('Languages List', self::MODEL_LANGUAGE_LIST, 'languages', self::MODEL_LANGUAGE)) + ->setModel(new BaseList('Currencies List', self::MODEL_CURRENCY_LIST, 'currencies', self::MODEL_CURRENCY)) + ->setModel(new BaseList('Phones List', self::MODEL_PHONE_LIST, 'phones', self::MODEL_PHONE)) + ->setModel(new BaseList('Metric List', self::MODEL_METRIC_LIST, 'metrics', self::MODEL_METRIC, true, false)) + ->setModel(new BaseList('Variables List', self::MODEL_VARIABLE_LIST, 'variables', self::MODEL_VARIABLE)) + ->setModel(new BaseList('Status List', self::MODEL_HEALTH_STATUS_LIST, 'statuses', self::MODEL_HEALTH_STATUS)) + ->setModel(new BaseList('Rule List', self::MODEL_PROXY_RULE_LIST, 'rules', self::MODEL_PROXY_RULE)) + ->setModel(new BaseList('Locale codes list', self::MODEL_LOCALE_CODE_LIST, 'localeCodes', self::MODEL_LOCALE_CODE)) + ->setModel(new BaseList('Provider list', self::MODEL_PROVIDER_LIST, 'providers', self::MODEL_PROVIDER)) + ->setModel(new BaseList('Message list', self::MODEL_MESSAGE_LIST, 'messages', self::MODEL_MESSAGE)) + ->setModel(new BaseList('Topic list', self::MODEL_TOPIC_LIST, 'topics', self::MODEL_TOPIC)) + ->setModel(new BaseList('Subscriber list', self::MODEL_SUBSCRIBER_LIST, 'subscribers', self::MODEL_SUBSCRIBER)) + ->setModel(new BaseList('Target list', self::MODEL_TARGET_LIST, 'targets', self::MODEL_TARGET)) + ->setModel(new BaseList('Migrations List', self::MODEL_MIGRATION_LIST, 'migrations', self::MODEL_MIGRATION)) + ->setModel(new BaseList('Migrations Firebase Projects List', self::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', self::MODEL_MIGRATION_FIREBASE_PROJECT)) + ->setModel(new BaseList('Specifications List', self::MODEL_SPECIFICATION_LIST, 'specifications', self::MODEL_SPECIFICATION)) + ->setModel(new BaseList('VCS Content List', self::MODEL_VCS_CONTENT_LIST, 'contents', self::MODEL_VCS_CONTENT)) + // Entities + ->setModel(new Database()) + ->setModel(new Collection()) + ->setModel(new Attribute()) + ->setModel(new AttributeList()) + ->setModel(new AttributeString()) + ->setModel(new AttributeInteger()) + ->setModel(new AttributeFloat()) + ->setModel(new AttributeBoolean()) + ->setModel(new AttributeEmail()) + ->setModel(new AttributeEnum()) + ->setModel(new AttributeIP()) + ->setModel(new AttributeURL()) + ->setModel(new AttributeDatetime()) + ->setModel(new AttributeRelationship()) + ->setModel(new Index()) + ->setModel(new ModelDocument()) + ->setModel(new Log()) + ->setModel(new User()) + ->setModel(new AlgoMd5()) + ->setModel(new AlgoSha()) + ->setModel(new AlgoPhpass()) + ->setModel(new AlgoBcrypt()) + ->setModel(new AlgoScrypt()) + ->setModel(new AlgoScryptModified()) + ->setModel(new AlgoArgon2()) + ->setModel(new Account()) + ->setModel(new Preferences()) + ->setModel(new Session()) + ->setModel(new Identity()) + ->setModel(new Token()) + ->setModel(new JWT()) + ->setModel(new Locale()) + ->setModel(new LocaleCode()) + ->setModel(new File()) + ->setModel(new Bucket()) + ->setModel(new Team()) + ->setModel(new Membership()) + ->setModel(new Func()) + ->setModel(new TemplateFunction()) + ->setModel(new TemplateRuntime()) + ->setModel(new TemplateVariable()) + ->setModel(new Installation()) + ->setModel(new ProviderRepository()) + ->setModel(new Detection()) + ->setModel(new VcsContent()) + ->setModel(new Branch()) + ->setModel(new Runtime()) + ->setModel(new Deployment()) + ->setModel(new Execution()) + ->setModel(new Build()) + ->setModel(new Project()) + ->setModel(new Webhook()) + ->setModel(new Key()) + ->setModel(new MockNumber()) + ->setModel(new AuthProvider()) + ->setModel(new Platform()) + ->setModel(new Variable()) + ->setModel(new Country()) + ->setModel(new Continent()) + ->setModel(new Language()) + ->setModel(new Currency()) + ->setModel(new Phone()) + ->setModel(new HealthAntivirus()) + ->setModel(new HealthQueue()) + ->setModel(new HealthStatus()) + ->setModel(new HealthCertificate()) + ->setModel(new HealthTime()) + ->setModel(new HealthVersion()) + ->setModel(new Metric()) + ->setModel(new MetricBreakdown()) + ->setModel(new UsageDatabases()) + ->setModel(new UsageDatabase()) + ->setModel(new UsageCollection()) + ->setModel(new UsageUsers()) + ->setModel(new UsageStorage()) + ->setModel(new UsageBuckets()) + ->setModel(new UsageFunctions()) + ->setModel(new UsageFunction()) + ->setModel(new UsageProject()) + ->setModel(new Headers()) + ->setModel(new Specification()) + ->setModel(new Rule()) + ->setModel(new TemplateSMS()) + ->setModel(new TemplateEmail()) + ->setModel(new ConsoleVariables()) + ->setModel(new MFAChallenge()) + ->setModel(new MFARecoveryCodes()) + ->setModel(new MFAType()) + ->setModel(new MFAFactors()) + ->setModel(new Provider()) + ->setModel(new Message()) + ->setModel(new Topic()) + ->setModel(new Subscriber()) + ->setModel(new Target()) + ->setModel(new Migration()) + ->setModel(new MigrationReport()) + ->setModel(new MigrationFirebaseProject()) + // Tests (keep last) + ->setModel(new Mock()); + + parent::__construct($response); } /** @@ -240,6 +495,49 @@ class Response extends HttpResponse public const CONTENT_TYPE_NULL = 'null'; public const CONTENT_TYPE_MULTIPART = 'multipart/form-data'; + /** + * List of defined output objects + */ + protected $models = []; + + /** + * Set Model Object + * + * @return self + */ + public function setModel(Model $instance) + { + $this->models[$instance->getType()] = $instance; + + return $this; + } + + /** + * Get Model Object + * + * @param string $key + * @return Model + * @throws Exception + */ + public function getModel(string $key): Model + { + if (!isset($this->models[$key])) { + throw new Exception('Undefined model: ' . $key); + } + + return $this->models[$key]; + } + + /** + * Get Models List + * + * @return Model[] + */ + public function getModels(): array + { + return $this->models; + } + public function applyFilters(array $data, string $model): array { foreach ($this->filters as $filter) { @@ -307,7 +605,7 @@ class Response extends HttpResponse public function output(Document $document, string $model): array { $data = clone $document; - $model = Models::getModel($model); + $model = $this->getModel($model); $output = []; $data = $model->filter($data); @@ -337,7 +635,7 @@ class Response extends HttpResponse if (\is_array($rule['type'])) { foreach ($rule['type'] as $type) { $condition = false; - foreach (Models::getModel($type)->conditions as $attribute => $val) { + foreach ($this->getModel($type)->conditions as $attribute => $val) { $condition = $item->getAttribute($attribute) === $val; if (!$condition) { break; @@ -352,7 +650,7 @@ class Response extends HttpResponse $ruleType = $rule['type']; } - if (!array_key_exists($ruleType, Models::getModels())) { + if (!array_key_exists($ruleType, $this->models)) { throw new Exception('Missing model for rule: ' . $ruleType); } diff --git a/src/Appwrite/Utopia/Response/Models.php b/src/Appwrite/Utopia/Response/Models.php deleted file mode 100644 index 2a0393321c..0000000000 --- a/src/Appwrite/Utopia/Response/Models.php +++ /dev/null @@ -1,304 +0,0 @@ -getType()] = $instance; - } - - /** - * Get Model Object - * - * @param string $key - * @return Model - * @throws Exception - */ - public static function getModel(string $key): Model - { - if (!isset(self::$models[$key])) { - throw new Exception('Undefined model: ' . $key); - } - - return self::$models[$key]; - } - - public static function getModels(): array - { - return self::$models; - } -} diff --git a/src/Appwrite/Utopia/View.php b/src/Appwrite/Utopia/View.php index 60cafb1ca8..e4ed8164c9 100644 --- a/src/Appwrite/Utopia/View.php +++ b/src/Appwrite/Utopia/View.php @@ -2,7 +2,7 @@ namespace Appwrite\Utopia; -use Utopia\View\View as OldView; +use Utopia\View as OldView; class View extends OldView { diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index 28c3227979..04f2dbd8c8 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -12,7 +12,7 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Datetime as DatetimeValidator; -use Utopia\Http\Validator\JSON; +use Utopia\Validator\JSON; trait DatabasesBase { @@ -2869,7 +2869,7 @@ trait DatabasesBase $this->assertEquals('Invalid document structure: Attribute "floatRange" has invalid format. Value must be a valid range between 1 and 1', $badFloatRange['body']['message']); $this->assertEquals('Invalid document structure: Attribute "probability" has invalid format. Value must be a valid range between 0 and 1', $badProbability['body']['message']); $this->assertEquals('Invalid document structure: Attribute "upperBound" has invalid format. Value must be a valid range between -9,223,372,036,854,775,808 and 10', $tooHigh['body']['message']); - $this->assertEquals('Invalid document structure: Attribute "lowerBound" has invalid format. Value must be a valid range between 5 and '.\number_format(PHP_INT_MAX), $tooLow['body']['message']); + $this->assertEquals('Invalid document structure: Attribute "lowerBound" has invalid format. Value must be a valid range between 5 and 9,223,372,036,854,775,807', $tooLow['body']['message']); } /** diff --git a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php index 9b7be063db..8484996058 100644 --- a/tests/e2e/Services/Databases/DatabasesCustomClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesCustomClientTest.php @@ -64,10 +64,9 @@ class DatabasesCustomClientTest extends Scope 'required' => true, ]); - $this->assertEquals(202, $response['headers']['status-code']); - sleep(1); + $this->assertEquals(202, $response['headers']['status-code']); // Document aliases write to update, delete $document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', array_merge([ @@ -83,7 +82,6 @@ class DatabasesCustomClientTest extends Scope ] ]); - $this->assertEquals(201, $document1['headers']['status-code']); $this->assertNotContains(Permission::create(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']); $this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']); $this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $document1['body']['$permissions']); diff --git a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php b/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php index 4288b92613..ca8753f374 100644 --- a/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php +++ b/tests/e2e/Services/Databases/DatabasesPermissionsGuestTest.php @@ -17,14 +17,6 @@ class DatabasesPermissionsGuestTest extends Scope use SideClient; use DatabasesPermissionsScope; - protected Authorization $auth; - - public function setUp(): void - { - parent::setUp(); - $this->auth = new Authorization(); - } - public function createCollection(): array { $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ @@ -119,8 +111,8 @@ class DatabasesPermissionsGuestTest extends Scope $this->assertEquals(201, $publicResponse['headers']['status-code']); $this->assertEquals(201, $privateResponse['headers']['status-code']); - $roles = $this->auth->getRoles(); - $this->auth->cleanRoles(); + $roles = Authorization::getRoles(); + Authorization::cleanRoles(); $publicDocuments = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', [ 'content-type' => 'application/json', @@ -142,7 +134,7 @@ class DatabasesPermissionsGuestTest extends Scope } foreach ($roles as $role) { - $this->auth->addRole($role); + Authorization::setRole($role); } } @@ -153,8 +145,8 @@ class DatabasesPermissionsGuestTest extends Scope $privateCollectionId = $data['privateCollectionId']; $databaseId = $data['databaseId']; - $roles = $this->auth->getRoles(); - $this->auth->cleanRoles(); + $roles = Authorization::getRoles(); + Authorization::cleanRoles(); $publicResponse = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $publicCollectionId . '/documents', [ 'content-type' => 'application/json', @@ -230,7 +222,7 @@ class DatabasesPermissionsGuestTest extends Scope $this->assertEquals(401, $privateDocument['headers']['status-code']); foreach ($roles as $role) { - $this->auth->addRole($role); + Authorization::setRole($role); } } diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index f9898757f7..2d94b9f0e3 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -7,11 +7,12 @@ use Utopia\CLI\Console; trait FunctionsBase { - protected string $output = ''; + protected string $stdout = ''; + protected string $stderr = ''; protected function packageCode($folder) { - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->output); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); } protected function awaitDeploymentIsBuilt($functionId, $deploymentId, $checkForSuccess = true): void diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index dccbf2e544..2958e6cb5f 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -10,12 +10,12 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; +use Utopia\App; use Utopia\Database\Document; 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 { @@ -476,6 +476,7 @@ class FunctionsCustomServerTest extends Scope * and ensure variable works as expected in execution. */ $this->assertEmpty($template['body']['variables']); + // Create function using settings from template. // Deployment is automatically created from template inside endpoint $function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([ @@ -503,6 +504,7 @@ class FunctionsCustomServerTest extends Scope $this->assertNotEmpty($function['body']['$id']); $functionId = $function['body']['$id']; + // List deployments so we can await deployment build $deployments = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments', [ 'content-type' => 'application/json', @@ -2462,7 +2464,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($cookie, $response['body']); // Await Aggregation - sleep(System::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', 30)); + sleep(App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', 30)); $tries = 0; while (true) { diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index b77f006bf8..735e4eced5 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -2498,6 +2498,6 @@ trait Base protected function packageCode($folder): void { - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); } } diff --git a/tests/e2e/Services/GraphQL/StorageClientTest.php b/tests/e2e/Services/GraphQL/StorageClientTest.php index 7d6d617c87..9896598c2d 100644 --- a/tests/e2e/Services/GraphQL/StorageClientTest.php +++ b/tests/e2e/Services/GraphQL/StorageClientTest.php @@ -183,6 +183,7 @@ class StorageClientTest extends Scope /** * @depends testCreateFile * @param $file + * @return array * @throws \Exception */ public function testGetFileDownload($file) diff --git a/tests/e2e/Services/GraphQL/StorageServerTest.php b/tests/e2e/Services/GraphQL/StorageServerTest.php index 6be103629e..7fea895b1c 100644 --- a/tests/e2e/Services/GraphQL/StorageServerTest.php +++ b/tests/e2e/Services/GraphQL/StorageServerTest.php @@ -232,6 +232,7 @@ class StorageServerTest extends Scope /** * @depends testCreateFile * @param $file + * @return array * @throws \Exception */ public function testGetFileDownload($file) diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 9fc862e85b..c41d861f1d 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -1691,7 +1691,7 @@ class ProjectsConsoleClientTest extends Scope ]); $this->assertEquals(400, $response['headers']['status-code']); - $this->assertEquals('Invalid `numbers` param: Value must a valid array no longer than 10 items', $response['body']['message']); + $this->assertEquals('Invalid `numbers` param: Value must a valid array no longer than 10 items and Phone number must start with a \'+\' can have a maximum of fifteen digits.', $response['body']['message']); /** * Test for success diff --git a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php index 86aaee2ab0..c3372b98c5 100644 --- a/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php +++ b/tests/e2e/Services/Realtime/RealtimeCustomClientTest.php @@ -1272,10 +1272,11 @@ class RealtimeCustomClientTest extends Scope $this->assertNotEmpty($function['body']['$id']); $folder = 'timeout'; - $output = ''; + $stderr = ''; + $stdout = ''; $code = realpath(__DIR__ . '/../../../resources/functions') . "/{$folder}/code.tar.gz"; - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/{$folder} && tar --exclude code.tar.gz -czf code.tar.gz .", '', $output); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/{$folder} && tar --exclude code.tar.gz -czf code.tar.gz .", '', $stdout, $stderr); $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([ 'content-type' => 'multipart/form-data', diff --git a/tests/e2e/Services/Storage/StorageCustomClientTest.php b/tests/e2e/Services/Storage/StorageCustomClientTest.php index 55340ab849..c723fba50a 100644 --- a/tests/e2e/Services/Storage/StorageCustomClientTest.php +++ b/tests/e2e/Services/Storage/StorageCustomClientTest.php @@ -1089,7 +1089,7 @@ class StorageCustomClientTest extends Scope $this->assertEquals(200, $file['headers']['status-code']); - // Team 2 view success + // Team 1 view success $file = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/view', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1112,8 +1112,6 @@ class StorageCustomClientTest extends Scope 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/logo.png'), 'image/png', 'permissions.png'), ]); - $this->assertEquals($file['headers']['status-code'], 401); - // Team 2 create failure $file = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', [ 'content-type' => 'multipart/form-data', diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php index 54d55e5e68..d2f132e960 100644 --- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php +++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php @@ -486,10 +486,11 @@ class WebhooksCustomServerTest extends Scope /** * Test for SUCCESS */ - $output = ''; + $stderr = ''; + $stdout = ''; $folder = 'timeout'; $code = realpath(__DIR__ . '/../../../resources/functions') . "/{$folder}/code.tar.gz"; - Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/{$folder} && tar --exclude code.tar.gz -czf code.tar.gz .", '', $output); + Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/{$folder} && tar --exclude code.tar.gz -czf code.tar.gz .", '', $stdout, $stderr); $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge([ 'content-type' => 'multipart/form-data', diff --git a/tests/resources/docker/docker-compose.yml b/tests/resources/docker/docker-compose.yml index 8f86fb8374..a34b4fcf88 100644 --- a/tests/resources/docker/docker-compose.yml +++ b/tests/resources/docker/docker-compose.yml @@ -324,7 +324,7 @@ services: - MYSQL_USER=user - MYSQL_PASSWORD=password - MARIADB_AUTO_UPGRADE=1 - command: 'mysqld --innodb-flush-method=fsync --max_connections=5000' + command: 'mysqld --innodb-flush-method=fsync' maildev: image: djfarrelly/maildev diff --git a/tests/unit/Auth/AuthTest.php b/tests/unit/Auth/AuthTest.php index 1ff5850402..705da42879 100644 --- a/tests/unit/Auth/AuthTest.php +++ b/tests/unit/Auth/AuthTest.php @@ -3,7 +3,6 @@ namespace Tests\Unit\Auth; use Appwrite\Auth\Auth; -use Appwrite\Auth\Authentication; use PHPUnit\Framework\TestCase; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -14,31 +13,21 @@ use Utopia\Database\Validator\Roles; class AuthTest extends TestCase { - protected Authorization $auth; - protected Authentication $authentication; - - public function setUp(): void - { - parent::setUp(); - $this->auth = new Authorization(); - $this->authentication = new Authentication(); - } - /** * Reset Roles */ public function tearDown(): void { - $this->auth->cleanRoles(); - $this->auth->addRole(Role::any()->toString()); + Authorization::cleanRoles(); + Authorization::setRole(Role::any()->toString()); } public function testCookieName(): void { $name = 'cookie-name'; - $this->assertEquals($this->authentication->setCookieName($name), $name); - $this->assertEquals($this->authentication->getCookieName(), $name); + $this->assertEquals(Auth::setCookieName($name), $name); + $this->assertEquals(Auth::$cookieName, $name); } public function testEncodeDecodeSession(): void @@ -358,7 +347,7 @@ class AuthTest extends TestCase '$id' => '' ]); - $roles = Auth::getRoles($user, $this->auth); + $roles = Auth::getRoles($user); $this->assertCount(1, $roles); $this->assertContains(Role::guests()->toString(), $roles); } @@ -394,7 +383,7 @@ class AuthTest extends TestCase ] ]); - $roles = Auth::getRoles($user, $this->auth); + $roles = Auth::getRoles($user); $this->assertCount(13, $roles); $this->assertContains(Role::users()->toString(), $roles); @@ -415,21 +404,21 @@ class AuthTest extends TestCase $user['emailVerification'] = false; $user['phoneVerification'] = false; - $roles = Auth::getRoles($user, $this->auth); + $roles = Auth::getRoles($user); $this->assertContains(Role::users(Roles::DIMENSION_UNVERIFIED)->toString(), $roles); $this->assertContains(Role::user(ID::custom('123'), Roles::DIMENSION_UNVERIFIED)->toString(), $roles); // Enable single verification type $user['emailVerification'] = true; - $roles = Auth::getRoles($user, $this->auth); + $roles = Auth::getRoles($user); $this->assertContains(Role::users(Roles::DIMENSION_VERIFIED)->toString(), $roles); $this->assertContains(Role::user(ID::custom('123'), Roles::DIMENSION_VERIFIED)->toString(), $roles); } public function testPrivilegedUserRoles(): void { - $this->auth->addRole(Auth::USER_ROLE_OWNER); + Authorization::setRole(Auth::USER_ROLE_OWNER); $user = new Document([ '$id' => ID::custom('123'), 'emailVerification' => true, @@ -455,7 +444,7 @@ class AuthTest extends TestCase ] ]); - $roles = Auth::getRoles($user, $this->auth); + $roles = Auth::getRoles($user); $this->assertCount(7, $roles); $this->assertNotContains(Role::users()->toString(), $roles); @@ -473,7 +462,7 @@ class AuthTest extends TestCase public function testAppUserRoles(): void { - $this->auth->addRole(Auth::USER_ROLE_APPS); + Authorization::setRole(Auth::USER_ROLE_APPS); $user = new Document([ '$id' => ID::custom('123'), 'memberships' => [ @@ -497,7 +486,7 @@ class AuthTest extends TestCase ] ]); - $roles = Auth::getRoles($user, $this->auth); + $roles = Auth::getRoles($user); $this->assertCount(7, $roles); $this->assertNotContains(Role::users()->toString(), $roles); diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index 38534b9b16..dd9833378f 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -66,13 +66,9 @@ class EventTest extends TestCase $this->assertEquals('eventValue1', $this->object->getParam('eventKey1')); $this->assertEquals('eventValue2', $this->object->getParam('eventKey2')); $this->assertEquals(null, $this->object->getParam('eventKey3')); - - global $registry; - $pools = $registry->get('pools'); - $dsn = $pools['pools-queue-queue']['dsn']; - $queue = new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort()); - - $client = new Client($this->object->getQueue(), $queue); + global $register; + $pools = $register->get('pools'); + $client = new Client($this->object->getQueue(), $pools->get('queue')->pop()->getResource()); $this->assertEquals($client->getQueueSize(), 1); } diff --git a/tests/unit/GraphQL/BuilderTest.php b/tests/unit/GraphQL/BuilderTest.php index f11045f318..d79a104c90 100644 --- a/tests/unit/GraphQL/BuilderTest.php +++ b/tests/unit/GraphQL/BuilderTest.php @@ -4,10 +4,8 @@ namespace Tests\Unit\GraphQL; use Appwrite\GraphQL\Types\Mapper; use Appwrite\Utopia\Response; -use Appwrite\Utopia\Response\Models; use PHPUnit\Framework\TestCase; use Swoole\Http\Response as SwooleResponse; -use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse; class BuilderTest extends TestCase { @@ -15,9 +13,8 @@ class BuilderTest extends TestCase public function setUp(): void { - Models::init(); - $this->response = new Response(new UtopiaSwooleResponse(new SwooleResponse())); - Mapper::init(Models::getModels()); + $this->response = new Response(new SwooleResponse()); + Mapper::init($this->response->getModels()); } /** @@ -25,7 +22,7 @@ class BuilderTest extends TestCase */ public function testCreateTypeMapping() { - $model = Models::getModel(Response::MODEL_COLLECTION); + $model = $this->response->getModel(Response::MODEL_COLLECTION); $type = Mapper::model(\ucfirst($model->getType())); $this->assertEquals('Collection', $type->name); } diff --git a/tests/unit/Messaging/MessagingChannelsTest.php b/tests/unit/Messaging/MessagingChannelsTest.php index 77b3cee7d6..8ba0374093 100644 --- a/tests/unit/Messaging/MessagingChannelsTest.php +++ b/tests/unit/Messaging/MessagingChannelsTest.php @@ -8,7 +8,6 @@ use PHPUnit\Framework\TestCase; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; -use Utopia\Database\Validator\Authorization as ValidatorAuthorization; class MessagingChannelsTest extends TestCase { @@ -35,12 +34,8 @@ class MessagingChannelsTest extends TestCase 'functions.1', ]; - protected ValidatorAuthorization $auth; - public function setUp(): void { - $this->auth = new ValidatorAuthorization(); - /** * Setup global Counts */ @@ -71,7 +66,7 @@ class MessagingChannelsTest extends TestCase ] ]); - $roles = Auth::getRoles($user, $this->auth); + $roles = Auth::getRoles($user); $parsedChannels = Realtime::convertChannels([0 => $channel], $user->getId()); @@ -95,7 +90,7 @@ class MessagingChannelsTest extends TestCase '$id' => '' ]); - $roles = Auth::getRoles($user, $this->auth); + $roles = Auth::getRoles($user); $parsedChannels = Realtime::convertChannels([0 => $channel], $user->getId()); diff --git a/tests/unit/Migration/MigrationTest.php b/tests/unit/Migration/MigrationTest.php index 8043ec9f94..536278d55b 100644 --- a/tests/unit/Migration/MigrationTest.php +++ b/tests/unit/Migration/MigrationTest.php @@ -36,7 +36,7 @@ abstract class MigrationTest extends TestCase */ public function testMigrationVersions(): void { - //require_once __DIR__ . '/../../../app/init.php'; + require_once __DIR__ . '/../../../app/init.php'; foreach (Migration::$versions as $class) { $this->assertTrue(class_exists('Appwrite\\Migration\\Version\\' . $class)); diff --git a/tests/unit/Utopia/RequestTest.php b/tests/unit/Utopia/RequestTest.php index 6124a7a0c1..73daaa88bc 100644 --- a/tests/unit/Utopia/RequestTest.php +++ b/tests/unit/Utopia/RequestTest.php @@ -7,8 +7,7 @@ use PHPUnit\Framework\TestCase; use Swoole\Http\Request as SwooleRequest; use Tests\Unit\Utopia\Request\Filters\First; use Tests\Unit\Utopia\Request\Filters\Second; -use Utopia\Http\Adapter\Swoole\Request as UtopiaSwooleRequest; -use Utopia\Http\Route; +use Utopia\Route; class RequestTest extends TestCase { @@ -16,7 +15,7 @@ class RequestTest extends TestCase public function setUp(): void { - $this->request = new Request(new UtopiaSwooleRequest(new SwooleRequest())); + $this->request = new Request(new SwooleRequest()); } public function testFilters(): void @@ -37,7 +36,7 @@ class RequestTest extends TestCase // set test header to prevent header populaten inside the request class $this->request->addHeader('EXAMPLE', 'VALUE'); $this->request->setRoute($route); - $this->request->setQuery([ + $this->request->setQueryString([ 'initial' => true, 'first' => false ]); diff --git a/tests/unit/Utopia/ResponseTest.php b/tests/unit/Utopia/ResponseTest.php index 1cd02beb2c..cd111ec22c 100644 --- a/tests/unit/Utopia/ResponseTest.php +++ b/tests/unit/Utopia/ResponseTest.php @@ -3,14 +3,12 @@ namespace Tests\Unit\Utopia; use Appwrite\Utopia\Response; -use Appwrite\Utopia\Response\Models; use Exception; use PHPUnit\Framework\TestCase; use Swoole\Http\Response as SwooleResponse; use Tests\Unit\Utopia\Response\Filters\First; use Tests\Unit\Utopia\Response\Filters\Second; use Utopia\Database\Document; -use Utopia\Http\Adapter\Swoole\Response as UtopiaSwooleResponse; class ResponseTest extends TestCase { @@ -18,10 +16,10 @@ class ResponseTest extends TestCase public function setUp(): void { - $this->response = new Response(new UtopiaSwooleResponse(new SwooleResponse())); - Models::setModel(new Single()); - Models::setModel(new Lists()); - Models::setModel(new Nested()); + $this->response = new Response(new SwooleResponse()); + $this->response->setModel(new Single()); + $this->response->setModel(new Lists()); + $this->response->setModel(new Nested()); } public function testFilters(): void From 5ffca0f6fad75092746b0823bf423013940da3c1 Mon Sep 17 00:00:00 2001 From: Binyamin Yawitz <316103+byawitz@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:37:45 -0400 Subject: [PATCH 192/195] fix: typo in scheduler base --- src/Appwrite/Platform/Tasks/ScheduleBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index e013220aa4..79a05dcd13 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -165,7 +165,7 @@ abstract class ScheduleBase extends Action $total = $total + $sum; foreach ($results as $document) { - $localDocument = $schedules[$document['resourceId']] ?? null; + $localDocument = $this->schedules[$document->getInternalId()] ?? null; // Check if resource has been updated since last sync $org = $localDocument !== null ? \strtotime($localDocument['resourceUpdatedAt']) : null; From 8ae99cb3736e61b92eb6bf6ac89280aebceabf0b Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Fri, 20 Sep 2024 20:00:59 +0100 Subject: [PATCH 193/195] feat: make create execution async loose --- app/controllers/api/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 4792343a3e..f8675253b3 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1728,7 +1728,7 @@ Http::post('/v1/functions/:functionId/executions') ->label('sdk.request.type', Response::CONTENT_TYPE_JSON) ->param('functionId', '', new UID(), 'Function ID.') ->param('body', '', new Payload(10485760, 0), 'HTTP body of execution. Default value is empty string.', true) - ->param('async', false, new Boolean(), 'Execute code in the background. Default value is false.', true) + ->param('async', false, new Boolean(true), 'Execute code in the background. Default value is false.', true) ->param('path', '/', new Text(2048), 'HTTP path of execution. Path can include query params. Default value is /', true) ->param('method', 'POST', new Whitelist(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], true), 'HTTP method of execution. Default value is GET.', true) ->param('headers', [], new AnyOf([new Assoc(), new Text(65535)], AnyOf::TYPE_MIXED), 'HTTP headers of execution. Defaults to empty.', true) From 711dc7bd08ccff940d3c295506a831dfd660a069 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Wed, 25 Sep 2024 09:59:51 +0100 Subject: [PATCH 194/195] feat: add logic --- app/controllers/api/functions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index ce8ba861fe..9641f0477f 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1734,6 +1734,7 @@ App::post('/v1/functions/:functionId/executions') ->inject('queueForFunctions') ->inject('geodb') ->action(function (string $functionId, string $body, bool $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) { + $async = \strval($async) === 'true' || \strval($async) === '1'; if (!$async && !is_null($scheduledAt)) { throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Scheduled executions must run asynchronously. Set scheduledAt to a future date, or set async to true.'); From bff846480a71e8830695ea7accd521b781b3cdd9 Mon Sep 17 00:00:00 2001 From: "Luke B. Silver" <22452787+loks0n@users.noreply.github.com> Date: Wed, 25 Sep 2024 10:01:12 +0100 Subject: [PATCH 195/195] Update app/controllers/api/functions.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matej Bačo --- app/controllers/api/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 9641f0477f..9c3e6782b4 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1733,7 +1733,7 @@ App::post('/v1/functions/:functionId/executions') ->inject('queueForUsage') ->inject('queueForFunctions') ->inject('geodb') - ->action(function (string $functionId, string $body, bool $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) { + ->action(function (string $functionId, string $body, mixed $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) { $async = \strval($async) === 'true' || \strval($async) === '1'; if (!$async && !is_null($scheduledAt)) {