diff --git a/.gitmodules b/.gitmodules index fc1a92efee..ff2e0a6aab 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 3.2.5 + branch = 3.2.6 diff --git a/CHANGES.md b/CHANGES.md index 0363f28683..e8f0c08c1f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,22 @@ +# Version 1.4.11 + +## Miscellaneous + +* Update database by @abnegate in [#7111](https://github.com/appwrite/appwrite/pull/7111) + +# Version 1.4.10 + +## Bug fixes +* Handle cases where password history could contain NULLs [#7092](https://github.com/appwrite/appwrite/pull/7092) +* Missing functionId error on create execution [#7091](https://github.com/appwrite/appwrite/pull/7091) +* Ensure usage endpoints don't throw 500 when usage is disabled [#7087](https://github.com/appwrite/appwrite/pull/7087) +* Missing sessionId error when deleting all user sessions [#7085](https://github.com/appwrite/appwrite/pull/7085) +* Domain validation in Create Proxy rule results in 500 error [#7084](https://github.com/appwrite/appwrite/pull/7084) +* Fix optional services [#7078](https://github.com/appwrite/appwrite/pull/7078) +* Fix regression from worker refactor [#7074](https://github.com/appwrite/appwrite/pull/7074) +* Use getQueueSize() in the Health service's get X queue endpoints [#7073](https://github.com/appwrite/appwrite/pull/7073) +* Delete linked VCS repos and comments [#7066](https://github.com/appwrite/appwrite/pull/7066) + # Version 1.4.9 ## Bug fixes diff --git a/README-CN.md b/README-CN.md index 4a2f9edd65..4e45c423c4 100644 --- a/README-CN.md +++ b/README-CN.md @@ -66,7 +66,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.4.9 + appwrite/appwrite:1.4.11 ``` ### Windows @@ -78,7 +78,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.4.9 + appwrite/appwrite:1.4.11 ``` #### PowerShell @@ -88,7 +88,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.4.9 + appwrite/appwrite:1.4.11 ``` 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 diff --git a/README.md b/README.md index 9b4ba2bb58..88615a355a 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.4.9 + appwrite/appwrite:1.4.11 ``` ### Windows @@ -88,7 +88,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.4.9 + appwrite/appwrite:1.4.11 ``` #### PowerShell @@ -98,7 +98,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.4.9 + appwrite/appwrite:1.4.11 ``` Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation. diff --git a/app/config/services.php b/app/config/services.php index 3700af659a..5c2233dfc6 100644 --- a/app/config/services.php +++ b/app/config/services.php @@ -170,7 +170,7 @@ return [ 'docs' => false, 'docsUrl' => '', 'tests' => false, - 'optional' => true, + 'optional' => false, 'icon' => '', ], 'functions' => [ @@ -196,7 +196,7 @@ return [ 'docs' => true, 'docsUrl' => 'https://appwrite.io/docs/proxy', 'tests' => false, - 'optional' => true, + 'optional' => false, 'icon' => '/images/services/proxy.png', ], 'mock' => [ @@ -248,7 +248,7 @@ return [ 'docs' => true, 'docsUrl' => 'https://appwrite.io/docs/migrations', 'tests' => true, - 'optional' => true, + 'optional' => false, 'icon' => '/images/services/migrations.png', ], ]; diff --git a/app/console b/app/console index 9810ce8581..f7c34a1b37 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 9810ce85812ca26c95b7d35196848c92e8ba813d +Subproject commit f7c34a1b37d53dd5f28c83b4e12a4e68fcd9b484 diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index aaf30e00c3..f47e3f8265 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -3632,7 +3632,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu App::get('/v1/databases/usage') ->desc('Get usage stats for the database') - ->groups(['api', 'database']) + ->groups(['api', 'database', 'usage']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'databases') @@ -3750,7 +3750,7 @@ App::get('/v1/databases/usage') App::get('/v1/databases/:databaseId/usage') ->desc('Get usage stats for the database') - ->groups(['api', 'database']) + ->groups(['api', 'database', 'usage']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'databases') @@ -3860,7 +3860,7 @@ App::get('/v1/databases/:databaseId/usage') App::get('/v1/databases/:databaseId/collections/:collectionId/usage') ->alias('/v1/database/:collectionId/usage', ['databaseId' => 'default']) ->desc('Get usage stats for a collection') - ->groups(['api', 'database']) + ->groups(['api', 'database', 'usage']) ->label('scope', 'collections.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'databases') diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 96827beb3c..268acc0692 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -1658,6 +1658,8 @@ App::post('/v1/functions/:functionId/executions') ->setJWT($jwt) ->setProject($project) ->setUser($user) + ->setParam('functionId', $function->getId()) + ->setParam('executionId', $execution->getId()) ->trigger(); return $response diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 9eb190310b..e0e26781cf 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -348,7 +348,7 @@ App::get('/v1/health/queue/webhooks') ->inject('response') ->action(function (Connection $queue, Response $response) { $client = new Client(Event::WEBHOOK_QUEUE_NAME, $queue); - $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/logs') @@ -366,7 +366,7 @@ App::get('/v1/health/queue/logs') ->inject('response') ->action(function (Connection $queue, Response $response) { $client = new Client(Event::AUDITS_QUEUE_NAME, $queue); - $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/certificates') @@ -384,7 +384,7 @@ App::get('/v1/health/queue/certificates') ->inject('response') ->action(function (Connection $queue, Response $response) { $client = new Client(Event::CERTIFICATES_QUEUE_NAME, $queue); - $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/builds') @@ -402,7 +402,7 @@ App::get('/v1/health/queue/builds') ->inject('response') ->action(function (Connection $queue, Response $response) { $client = new Client(Event::BUILDS_QUEUE_NAME, $queue); - $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/databases') @@ -421,7 +421,7 @@ App::get('/v1/health/queue/databases') ->inject('response') ->action(function (string $name, Connection $queue, Response $response) { $client = new Client($name, $queue); - $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/deletes') @@ -439,7 +439,7 @@ App::get('/v1/health/queue/deletes') ->inject('response') ->action(function (Connection $queue, Response $response) { $client = new Client(Event::DELETE_QUEUE_NAME, $queue); - $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/mails') @@ -457,7 +457,7 @@ App::get('/v1/health/queue/mails') ->inject('response') ->action(function (Connection $queue, Response $response) { $client = new Client(Event::MAILS_QUEUE_NAME, $queue); - $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/messaging') @@ -475,7 +475,7 @@ App::get('/v1/health/queue/messaging') ->inject('response') ->action(function (Connection $queue, Response $response) { $client = new Client(Event::MESSAGING_QUEUE_NAME, $queue); - $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/migrations') @@ -493,7 +493,7 @@ App::get('/v1/health/queue/migrations') ->inject('response') ->action(function (Connection $queue, Response $response) { $client = new Client(Event::MIGRATIONS_QUEUE_NAME, $queue); - $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/queue/functions') @@ -511,7 +511,7 @@ App::get('/v1/health/queue/functions') ->inject('response') ->action(function (Connection $queue, Response $response) { $client = new Client(Event::FUNCTIONS_QUEUE_NAME, $queue); - $response->dynamic(new Document([ 'size' => $client->sumProcessingJobs() ]), Response::MODEL_HEALTH_QUEUE); + $response->dynamic(new Document([ 'size' => $client->getQueueSize() ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); App::get('/v1/health/storage/local') diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php index f9621fb05a..23916a114c 100644 --- a/app/controllers/api/proxy.php +++ b/app/controllers/api/proxy.php @@ -87,7 +87,11 @@ App::post('/v1/proxy/rules') $resourceInternalId = $function->getInternalId(); } - $domain = new Domain($domain); + try { + $domain = new Domain($domain); + } catch (\Exception) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.'); + } $ruleId = ID::unique(); $rule = new Document([ diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index fcaa2ff641..0869453cc9 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -85,7 +85,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e 'status' => true, 'labels' => [], 'password' => $password, - 'passwordHistory' => is_null($password) && $passwordHistory === 0 ? [] : [$password], + 'passwordHistory' => is_null($password) || $passwordHistory === 0 ? [] : [$password], 'passwordUpdate' => (!empty($password)) ? DateTime::now() : null, 'hash' => $hash === 'plaintext' ? Auth::DEFAULT_ALGO : $hash, 'hashOptions' => $hash === 'plaintext' ? Auth::DEFAULT_ALGO_OPTIONS : $hashOptionsObject + ['type' => $hash], @@ -1127,7 +1127,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId') App::delete('/v1/users/:userId/sessions') ->desc('Delete user sessions') ->groups(['api', 'users']) - ->label('event', 'users.[userId].sessions.[sessionId].delete') + ->label('event', 'users.[userId].sessions.delete') ->label('scope', 'users.write') ->label('audits.event', 'session.delete') ->label('audits.resource', 'user/{user.$id}') diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 388851faef..b37d76a816 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -554,3 +554,11 @@ App::shutdown() ->submit(); } }); + +App::init() + ->groups(['usage']) + ->action(function () { + if (App::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') { + throw new Exception(Exception::GENERAL_USAGE_DISABLED); + } + }); diff --git a/app/init.php b/app/init.php index e4b9e7573d..be6b440498 100644 --- a/app/init.php +++ b/app/init.php @@ -109,8 +109,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 = 515; -const APP_VERSION_STABLE = '1.4.9'; +const APP_CACHE_BUSTER = 516; +const APP_VERSION_STABLE = '1.4.11'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; diff --git a/composer.json b/composer.json index 4ce175abea..a24feca83b 100644 --- a/composer.json +++ b/composer.json @@ -43,13 +43,13 @@ "ext-sockets": "*", "appwrite/php-runtimes": "0.13.*", "appwrite/php-clamav": "2.0.*", - "utopia-php/abuse": "0.32.*", + "utopia-php/abuse": "0.33.*", "utopia-php/analytics": "0.10.*", - "utopia-php/audit": "0.34.*", + "utopia-php/audit": "0.35.*", "utopia-php/cache": "0.8.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.44.*", + "utopia-php/database": "0.45.*", "utopia-php/domains": "0.3.*", "utopia-php/dsn": "0.1.*", "utopia-php/framework": "0.31.0", diff --git a/composer.lock b/composer.lock index e8334179ff..25f0c3964d 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": "9afc62ce9c6ba587b9c028e11494e026", + "content-hash": "69bc2e21a65b78344393706b39d789b4", "packages": [ { "name": "adhocore/jwt", @@ -1615,23 +1615,23 @@ }, { "name": "utopia-php/abuse", - "version": "0.32.0", + "version": "0.33.0", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "9717ffb2d7711f3fd621bb6df3edf5724c08ea78" + "reference": "1ba8d5f2793885cbf779e3b5b9d886968af43d2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/9717ffb2d7711f3fd621bb6df3edf5724c08ea78", - "reference": "9717ffb2d7711f3fd621bb6df3edf5724c08ea78", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/1ba8d5f2793885cbf779e3b5b9d886968af43d2c", + "reference": "1ba8d5f2793885cbf779e3b5b9d886968af43d2c", "shasum": "" }, "require": { "ext-curl": "*", "ext-pdo": "*", "php": ">=8.0", - "utopia-php/database": "0.44.*" + "utopia-php/database": "0.45.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1658,9 +1658,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.32.0" + "source": "https://github.com/utopia-php/abuse/tree/0.33.0" }, - "time": "2023-10-18T07:28:55+00:00" + "time": "2023-11-01T08:51:33+00:00" }, { "name": "utopia-php/analytics", @@ -1710,21 +1710,21 @@ }, { "name": "utopia-php/audit", - "version": "0.34.0", + "version": "0.35.0", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "cf34cc3f9f20da4e574a9be4517e1a11025a858f" + "reference": "ed9366ef05556da040de7a8b570f4160c7d8ea4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/cf34cc3f9f20da4e574a9be4517e1a11025a858f", - "reference": "cf34cc3f9f20da4e574a9be4517e1a11025a858f", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/ed9366ef05556da040de7a8b570f4160c7d8ea4a", + "reference": "ed9366ef05556da040de7a8b570f4160c7d8ea4a", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.44.*" + "utopia-php/database": "0.45.*" }, "require-dev": { "laravel/pint": "1.5.*", @@ -1751,9 +1751,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.34.0" + "source": "https://github.com/utopia-php/audit/tree/0.35.0" }, - "time": "2023-10-18T07:43:25+00:00" + "time": "2023-11-01T08:51:29+00:00" }, { "name": "utopia-php/cache", @@ -1906,16 +1906,16 @@ }, { "name": "utopia-php/database", - "version": "0.44.4", + "version": "0.45.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "b0c3fd8ecfedc3646d7780f2d6b38955a66baf48" + "reference": "0e76f996439b80794ab73c2fffdb51ebd6676e4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/b0c3fd8ecfedc3646d7780f2d6b38955a66baf48", - "reference": "b0c3fd8ecfedc3646d7780f2d6b38955a66baf48", + "url": "https://api.github.com/repos/utopia-php/database/zipball/0e76f996439b80794ab73c2fffdb51ebd6676e4b", + "reference": "0e76f996439b80794ab73c2fffdb51ebd6676e4b", "shasum": "" }, "require": { @@ -1956,9 +1956,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.44.4" + "source": "https://github.com/utopia-php/database/tree/0.45.1" }, - "time": "2023-10-26T07:08:12+00:00" + "time": "2023-11-01T08:30:19+00:00" }, { "name": "utopia-php/domains", @@ -2318,16 +2318,16 @@ }, { "name": "utopia-php/migration", - "version": "0.3.5", + "version": "0.3.6", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83" + "reference": "f78273b38bade23db5866e5c7cb5f55427ba82af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", - "reference": "b2fd3a8310296f4e44ff0e85b0eb0230ad9a2f83", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/f78273b38bade23db5866e5c7cb5f55427ba82af", + "reference": "f78273b38bade23db5866e5c7cb5f55427ba82af", "shasum": "" }, "require": { @@ -2360,9 +2360,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.3.5" + "source": "https://github.com/utopia-php/migration/tree/0.3.6" }, - "time": "2023-09-25T16:51:47+00:00" + "time": "2023-11-02T15:13:03+00:00" }, { "name": "utopia-php/mongo", @@ -2904,16 +2904,16 @@ }, { "name": "utopia-php/vcs", - "version": "0.6.1", + "version": "0.6.2", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "d161d1156ef336d197a8d45384b531e5ec31243d" + "reference": "f135291b87cb45335fc6608722e7f89894bc33ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/d161d1156ef336d197a8d45384b531e5ec31243d", - "reference": "d161d1156ef336d197a8d45384b531e5ec31243d", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/f135291b87cb45335fc6608722e7f89894bc33ee", + "reference": "f135291b87cb45335fc6608722e7f89894bc33ee", "shasum": "" }, "require": { @@ -2947,9 +2947,9 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.6.1" + "source": "https://github.com/utopia-php/vcs/tree/0.6.2" }, - "time": "2023-10-19T07:43:31+00:00" + "time": "2023-11-08T15:36:03+00:00" }, { "name": "utopia-php/websocket", @@ -5822,5 +5822,5 @@ "platform-overrides": { "php": "8.0" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/src/Appwrite/Auth/Validator/PasswordHistory.php b/src/Appwrite/Auth/Validator/PasswordHistory.php index 8cfabf4666..fb73ea6c9f 100644 --- a/src/Appwrite/Auth/Validator/PasswordHistory.php +++ b/src/Appwrite/Auth/Validator/PasswordHistory.php @@ -44,7 +44,7 @@ class PasswordHistory extends Password public function isValid($value): bool { foreach ($this->history as $hash) { - if (Auth::passwordVerify($value, $hash, $this->algo, $this->algoOptions)) { + if (!empty($hash) && Auth::passwordVerify($value, $hash, $this->algo, $this->algoOptions)) { return false; } } diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index efa0fad8fb..406acae7df 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -74,6 +74,8 @@ abstract class Migration '1.4.7' => 'V19', '1.4.8' => 'V19', '1.4.9' => 'V19', + '1.4.10' => 'V19', + '1.4.11' => 'V19', ]; /** diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 2130499257..6bb8636695 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -45,7 +45,7 @@ class Deletes extends Action ->inject('getFunctionsDevice') ->inject('getBuildsDevice') ->inject('getCacheDevice') - ->callback(fn($message, $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice) => $this->action($message, $dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice)); + ->callback(fn ($message, $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice) => $this->action($message, $dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice)); } /** @@ -178,7 +178,8 @@ class Deletes extends Action $project = $dbForConsole->getDocument('projects', $document->getAttribute('projectId')); if ($project->isEmpty()) { - Console::warning('Unable to delete schedule for function ' . $document->getAttribute('resourceId')); + $dbForConsole->deleteDocument('schedules', $document->getId()); + Console::success('Deleted schedule for deleted project ' . $document->getAttribute('projectId')); return; } @@ -450,6 +451,21 @@ class Deletes extends Action Query::equal('projectInternalId', [$projectInternalId]) ], $dbForConsole); + // Delete VCS Installations + $this->deleteByGroup('installations', [ + Query::equal('projectInternalId', [$projectInternalId]) + ], $dbForConsole); + + // Delete VCS Repositories + $this->deleteByGroup('repositories', [ + Query::equal('projectInternalId', [$projectInternalId]), + ], $dbForConsole); + + // Delete VCS commments + $this->deleteByGroup('vcsComments', [ + Query::equal('projectInternalId', [$projectInternalId]), + ], $dbForConsole); + // Delete metadata tables try { $dbForProject->deleteCollection('_metadata'); @@ -688,7 +704,7 @@ class Deletes extends Action $this->deleteDeploymentFiles($functionsStorage, $document); }); - /** + /** * Delete builds */ Console::info("Deleting builds for function " . $functionId); @@ -709,6 +725,22 @@ class Deletes extends Action Query::equal('functionInternalId', [$functionInternalId]) ], $dbForProject); + /** + * Delete VCS Repositories and VCS Comments + */ + Console::info("Deleting VCS repositories and comments linked to function " . $functionId); + $this->deleteByGroup('repositories', [ + Query::equal('resourceInternalId', [$functionInternalId]), + Query::equal('resourceType', ['function']), + ], $dbForConsole, function (Document $document) use ($dbForConsole) { + $providerRepositoryId = $document->getAttribute('providerRepositoryId', ''); + $projectId = $document->getAttribute('projectId', ''); + $this->deleteByGroup('vcsComments', [ + Query::equal('providerRepositoryId', [$providerRepositoryId]), + Query::equal('projectId', [$projectId]), + ], $dbForConsole); + }); + /** * Request executor to delete all deployment containers */ @@ -920,17 +952,28 @@ class Deletes extends Action $count = 0; $chunk = 0; $limit = 50; + $results = []; $sum = $limit; + $cursor = null; $executionStart = \microtime(true); while ($sum === $limit) { $chunk++; - $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); + $mergedQueries = \array_merge([Query::limit($limit)], $queries); + if ($cursor instanceof Document) { + $mergedQueries[] = Query::cursorAfter($cursor); + } + + $results = $database->find($collection, $mergedQueries); $sum = count($results); + if ($sum > 0) { + $cursor = $results[$sum - 1]; + } + foreach ($results as $document) { if (is_callable($callback)) { $callback($document); diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index dd7b92bf5e..fa25145a13 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -25,7 +25,7 @@ class Webhooks extends Action $this ->desc('Webhooks worker') ->inject('message') - ->callback(fn($message) => $this->action($message)); + ->callback(fn ($message) => $this->action($message)); } /** @@ -48,7 +48,7 @@ class Webhooks extends Action foreach ($project->getAttribute('webhooks', []) as $webhook) { if (array_intersect($webhook->getAttribute('events', []), $events)) { - $this->execute($events, $webhookPayload, $webhook, $user, $project); + $this->execute($events, $webhookPayload, $webhook, $user, $project); } } @@ -78,7 +78,9 @@ class Webhooks extends Action \curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); \curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); \curl_setopt($ch, CURLOPT_HEADER, 0); - \curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + \curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0); + \curl_setopt($ch, CURLOPT_TIMEOUT, 15); + \curl_setopt($ch, CURLOPT_MAXFILESIZE, 5242880); \curl_setopt($ch, CURLOPT_USERAGENT, \sprintf( APP_USERAGENT, App::getEnv('_APP_VERSION', 'UNKNOWN'), @@ -88,16 +90,17 @@ class Webhooks extends Action $ch, CURLOPT_HTTPHEADER, [ - 'Content-Type: application/json', - 'Content-Length: ' . \strlen($payload), - 'X-' . APP_NAME . '-Webhook-Id: ' . $webhook->getId(), - 'X-' . APP_NAME . '-Webhook-Events: ' . implode(',', $events), - 'X-' . APP_NAME . '-Webhook-Name: ' . $webhook->getAttribute('name', ''), - 'X-' . APP_NAME . '-Webhook-User-Id: ' . $user->getId(), - 'X-' . APP_NAME . '-Webhook-Project-Id: ' . $project->getId(), - 'X-' . APP_NAME . '-Webhook-Signature: ' . $signature, + 'Content-Type: application/json', + 'Content-Length: ' . \strlen($payload), + 'X-' . APP_NAME . '-Webhook-Id: ' . $webhook->getId(), + 'X-' . APP_NAME . '-Webhook-Events: ' . implode(',', $events), + 'X-' . APP_NAME . '-Webhook-Name: ' . $webhook->getAttribute('name', ''), + 'X-' . APP_NAME . '-Webhook-User-Id: ' . $user->getId(), + 'X-' . APP_NAME . '-Webhook-Project-Id: ' . $project->getId(), + 'X-' . APP_NAME . '-Webhook-Signature: ' . $signature, ] ); + curl_setopt($ch, CURLOPT_MAXREDIRS, 5); if (!$webhook->getAttribute('security', true)) { \curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index ff3f8e8e94..f90a04f290 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -1606,6 +1606,100 @@ class ProjectsConsoleClientTest extends Scope $this->assertEquals(false, $response['body']['authPersonalDataCheck']); } + public function testUpdateProjectServicesAll(): void + { + $team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'teamId' => ID::unique(), + 'name' => 'Project Test', + ]); + + $this->assertEquals(201, $team['headers']['status-code']); + $this->assertNotEmpty($team['body']['$id']); + + $project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'projectId' => ID::unique(), + 'name' => 'Project Test', + 'teamId' => $team['body']['$id'], + 'region' => 'default' + ]); + + $this->assertEquals(201, $project['headers']['status-code']); + $this->assertNotEmpty($project['body']['$id']); + + $id = $project['body']['$id']; + + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service/all', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'status' => false, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + + $matches = []; + $pattern = '/serviceStatusFor.*/'; + + foreach ($response['body'] as $key => $value) { + if (\preg_match($pattern, $key)) { + \var_dump('Matched key: ' . $key); + $matches[$key] = $value; + } + } + + foreach ($matches as $value) { + $this->assertFalse($value); + } + + $response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $id . '/service/all', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ]), [ + 'status' => true, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + + $response = $this->client->call(Client::METHOD_GET, '/projects/' . $id, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_console=' . $this->getRoot()['session'], + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + + $matches = []; + foreach ($response['body'] as $key => $value) { + if (\preg_match($pattern, $key)) { + $matches[$key] = $value; + } + } + + foreach ($matches as $value) { + $this->assertTrue($value); + } + } public function testUpdateProjectServiceStatusAdmin(): array {