From 1ed3cf0e59717d8dc508b3c0f13deb591aca281b Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Tue, 25 Nov 2025 19:24:30 +0530 Subject: [PATCH 01/11] Support query limit and offset in list repos API --- app/controllers/api/vcs.php | 19 ++- composer.json | 10 +- composer.lock | 239 ++++++++++++++++++++++++++++-------- 3 files changed, 209 insertions(+), 59 deletions(-) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 204957f485..fc68a631f1 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -31,7 +31,10 @@ use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; +use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Cursor; +use Utopia\Database\Validator\Query\Limit; +use Utopia\Database\Validator\Query\Offset; use Utopia\Detector\Detection\Framework\Analog; use Utopia\Detector\Detection\Framework\Angular; use Utopia\Detector\Detection\Framework\Astro; @@ -1033,10 +1036,11 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories') ->param('installationId', '', new Text(256), 'Installation Id') ->param('type', '', new WhiteList(['runtime', 'framework']), 'Detector type. Must be one of the following: runtime, framework') ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) + ->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) ->inject('gitHub') ->inject('response') ->inject('dbForPlatform') - ->action(function (string $installationId, string $type, string $search, GitHub $github, Response $response, Database $dbForPlatform) { + ->action(function (string $installationId, string $type, string $search, array $queries, GitHub $github, Response $response, Database $dbForPlatform) { if (empty($search)) { $search = ""; } @@ -1052,11 +1056,16 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories') $githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID'); $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); - $page = 1; - $perPage = 4; + $queries = Query::parseQueries($queries); + $limitQuery = array_filter($queries, fn ($query) => $query->getMethod() === Query::TYPE_LIMIT); + $offsetQuery = array_filter($queries, fn ($query) => $query->getMethod() === Query::TYPE_OFFSET); + + $limit = !empty($limitQuery) ? $limitQuery[0]->getValue() : 4; + $offset = !empty($offsetQuery) ? $offsetQuery[0]->getValue() : 0; + $page = ($offset / $limit) + 1; $owner = $github->getOwnerName($providerInstallationId); - $repos = $github->searchRepositories($owner, $page, $perPage, $search); + ['items' => $repos, 'total' => $total] = $github->searchRepositories($owner, $page, $limit, $search); $repos = \array_map(function ($repo) use ($installation) { $repo['id'] = \strval($repo['id'] ?? ''); @@ -1228,7 +1237,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories') $response->dynamic(new Document([ $type === 'framework' ? 'frameworkProviderRepositories' : 'runtimeProviderRepositories' => $repos, - 'total' => \count($repos), + 'total' => $total, ]), ($type === 'framework') ? Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST : Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST); }); diff --git a/composer.json b/composer.json index 684beb870a..faaa0b95b8 100644 --- a/composer.json +++ b/composer.json @@ -74,7 +74,7 @@ "utopia-php/swoole": "0.8.*", "utopia-php/system": "0.9.*", "utopia-php/telemetry": "0.1.*", - "utopia-php/vcs": "0.12.*", + "utopia-php/vcs": "dev-ser-504 as 0.12.99", "utopia-php/websocket": "0.3.*", "matomo/device-detector": "6.4.*", "dragonmantank/cron-expression": "3.4.*", @@ -107,5 +107,11 @@ "php-http/discovery": true, "tbachert/spi": true } - } + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/utopia-php/vcs" + } + ] } diff --git a/composer.lock b/composer.lock index 96a54d09c8..397b43b4d4 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": "1d3d4b19a835b3be79a63146fcdd389b", + "content-hash": "c5355d781be40fb38cc8eb0a265116ad", "packages": [ { "name": "adhocore/jwt", @@ -1236,16 +1236,16 @@ }, { "name": "open-telemetry/api", - "version": "1.7.0", + "version": "1.7.1", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/api.git", - "reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522" + "reference": "45bda7efa8fcdd9bdb0daa2f26c8e31f062f49d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/610b79ad9d6d97e8368bcb6c4d42394fbb87b522", - "reference": "610b79ad9d6d97e8368bcb6c4d42394fbb87b522", + "url": "https://api.github.com/repos/opentelemetry-php/api/zipball/45bda7efa8fcdd9bdb0daa2f26c8e31f062f49d4", + "reference": "45bda7efa8fcdd9bdb0daa2f26c8e31f062f49d4", "shasum": "" }, "require": { @@ -1265,7 +1265,7 @@ ] }, "branch-alias": { - "dev-main": "1.7.x-dev" + "dev-main": "1.8.x-dev" } }, "autoload": { @@ -1298,11 +1298,11 @@ ], "support": { "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", - "docs": "https://opentelemetry.io/docs/php", + "docs": "https://opentelemetry.io/docs/languages/php", "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-10-02T23:44:28+00:00" + "time": "2025-10-19T10:49:48+00:00" }, { "name": "open-telemetry/context", @@ -1365,16 +1365,16 @@ }, { "name": "open-telemetry/exporter-otlp", - "version": "1.3.2", + "version": "1.3.3", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/exporter-otlp.git", - "reference": "196f3a1dbce3b2c0f8110d164232c11ac00ddbb2" + "reference": "07b02bc71838463f6edcc78d3485c04b48fb263d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/196f3a1dbce3b2c0f8110d164232c11ac00ddbb2", - "reference": "196f3a1dbce3b2c0f8110d164232c11ac00ddbb2", + "url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/07b02bc71838463f6edcc78d3485c04b48fb263d", + "reference": "07b02bc71838463f6edcc78d3485c04b48fb263d", "shasum": "" }, "require": { @@ -1421,11 +1421,11 @@ ], "support": { "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", - "docs": "https://opentelemetry.io/docs/php", + "docs": "https://opentelemetry.io/docs/languages/php", "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-06-16T00:24:51+00:00" + "time": "2025-11-13T08:04:37+00:00" }, { "name": "open-telemetry/gen-otlp-protobuf", @@ -1492,16 +1492,16 @@ }, { "name": "open-telemetry/sdk", - "version": "1.9.0", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/opentelemetry-php/sdk.git", - "reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e" + "reference": "3dfc3d1ad729ec7eb25f1b9a4ae39fe779affa99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e", - "reference": "8986bcbcbea79cb1ba9e91c1d621541ad63d6b3e", + "url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/3dfc3d1ad729ec7eb25f1b9a4ae39fe779affa99", + "reference": "3dfc3d1ad729ec7eb25f1b9a4ae39fe779affa99", "shasum": "" }, "require": { @@ -1581,11 +1581,11 @@ ], "support": { "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V", - "docs": "https://opentelemetry.io/docs/php", + "docs": "https://opentelemetry.io/docs/languages/php", "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", "source": "https://github.com/open-telemetry/opentelemetry-php" }, - "time": "2025-10-02T23:44:28+00:00" + "time": "2025-11-25T10:59:15+00:00" }, { "name": "open-telemetry/sem-conv", @@ -3941,6 +3941,54 @@ }, "time": "2025-11-24T15:52:51+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" + }, { "name": "utopia-php/dns", "version": "1.1.3", @@ -4208,28 +4256,33 @@ }, { "name": "utopia-php/framework", - "version": "0.33.30", + "version": "0.33.31", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "07cf699a7c47bd1a03b4da1812f1719a66b3c924" + "reference": "cb01940ffe620b3d09f934dc2b8c72c2406309e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/07cf699a7c47bd1a03b4da1812f1719a66b3c924", - "reference": "07cf699a7c47bd1a03b4da1812f1719a66b3c924", + "url": "https://api.github.com/repos/utopia-php/http/zipball/cb01940ffe620b3d09f934dc2b8c72c2406309e7", + "reference": "cb01940ffe620b3d09f934dc2b8c72c2406309e7", "shasum": "" }, "require": { + "ext-swoole": "*", "php": ">=8.1", "utopia-php/compression": "0.1.*", - "utopia-php/telemetry": "0.1.*" + "utopia-php/servers": "0.2.*", + "utopia-php/telemetry": "0.1.*", + "utopia-php/validators": "0.0.*" }, "require-dev": { + "ext-xdebug": "*", "laravel/pint": "^1.2", "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5.25" + "phpstan/phpstan": "1.*", + "phpunit/phpunit": "^9.5.25", + "swoole/ide-helper": "4.8.3" }, "type": "library", "autoload": { @@ -4241,17 +4294,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.30" + "source": "https://github.com/utopia-php/http/tree/1.2.0" }, - "time": "2025-11-18T12:18:00+00:00" + "time": "2025-10-21T10:53:55+00:00" }, { "name": "utopia-php/image", @@ -4456,16 +4510,16 @@ }, { "name": "utopia-php/migration", - "version": "1.3.3", + "version": "1.3.6", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "731b3a963c58c30e0b2368695d57a7e8fcb7455c" + "reference": "4abe70cc242bbffebfa377e4126ee2a4a1c9ef7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/731b3a963c58c30e0b2368695d57a7e8fcb7455c", - "reference": "731b3a963c58c30e0b2368695d57a7e8fcb7455c", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/4abe70cc242bbffebfa377e4126ee2a4a1c9ef7e", + "reference": "4abe70cc242bbffebfa377e4126ee2a4a1c9ef7e", "shasum": "" }, "require": { @@ -4505,9 +4559,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.3.3" + "source": "https://github.com/utopia-php/migration/tree/1.3.6" }, - "time": "2025-10-28T04:02:08+00:00" + "time": "2025-11-25T11:36:57+00:00" }, { "name": "utopia-php/mongo", @@ -4893,6 +4947,60 @@ }, "time": "2021-03-10T10:45:22+00:00" }, + { + "name": "utopia-php/servers", + "version": "0.2.1", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/servers.git", + "reference": "c6ec23b0597f657020a75d4a2f8ef03177d7edaa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/servers/zipball/c6ec23b0597f657020a75d4a2f8ef03177d7edaa", + "reference": "c6ec23b0597f657020a75d4a2f8ef03177d7edaa", + "shasum": "" + }, + "require": { + "php": ">=8.0", + "utopia-php/di": "0.1.*", + "utopia-php/validators": "0.0.*" + }, + "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.2.1" + }, + "time": "2025-10-21T10:08:19+00:00" + }, { "name": "utopia-php/storage", "version": "0.18.14", @@ -5156,16 +5264,16 @@ }, { "name": "utopia-php/vcs", - "version": "0.12.0", + "version": "dev-ser-504", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "28457cf347972c4ec95d3ca77776a4921364a665" + "reference": "089a340a1b0e58d589b86b3935d013108db10ccd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/28457cf347972c4ec95d3ca77776a4921364a665", - "reference": "28457cf347972c4ec95d3ca77776a4921364a665", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/089a340a1b0e58d589b86b3935d013108db10ccd", + "reference": "089a340a1b0e58d589b86b3935d013108db10ccd", "shasum": "" }, "require": { @@ -5186,7 +5294,25 @@ "Utopia\\VCS\\": "src/VCS" } }, - "notification-url": "https://packagist.org/downloads/", + "autoload-dev": { + "psr-4": { + "Utopia\\Tests\\": "tests/VCS" + } + }, + "scripts": { + "lint": [ + "./vendor/bin/pint --test --config pint.json" + ], + "format": [ + "./vendor/bin/pint --config pint.json" + ], + "check": [ + "./vendor/bin/phpstan analyse --level 8 -c phpstan.neon src tests" + ], + "test": [ + "./vendor/bin/phpunit --configuration phpunit.xml --debug" + ] + }, "license": [ "MIT" ], @@ -5198,10 +5324,10 @@ "vcs" ], "support": { - "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.12.0" + "source": "https://github.com/utopia-php/vcs/tree/ser-504", + "issues": "https://github.com/utopia-php/vcs/issues" }, - "time": "2025-10-22T12:58:29+00:00" + "time": "2025-11-25T13:52:52+00:00" }, { "name": "utopia-php/websocket", @@ -5379,16 +5505,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.5.8", + "version": "1.5.9", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "05367bc4a4c3e020e9aca114ae875b626ce8fc55" + "reference": "ee434aa00a9185380b9a39bb46bf86d7104d3a93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/05367bc4a4c3e020e9aca114ae875b626ce8fc55", - "reference": "05367bc4a4c3e020e9aca114ae875b626ce8fc55", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/ee434aa00a9185380b9a39bb46bf86d7104d3a93", + "reference": "ee434aa00a9185380b9a39bb46bf86d7104d3a93", "shasum": "" }, "require": { @@ -5424,9 +5550,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/1.5.8" + "source": "https://github.com/appwrite/sdk-generator/tree/1.5.9" }, - "time": "2025-11-20T11:00:34+00:00" + "time": "2025-11-25T05:22:25+00:00" }, { "name": "doctrine/annotations", @@ -8891,9 +9017,18 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/vcs", + "version": "dev-ser-504", + "alias": "0.12.99", + "alias_normalized": "0.12.99.0" + } + ], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "utopia-php/vcs": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From fd466dfceab86d2a266aeb4a2426782a79a02941 Mon Sep 17 00:00:00 2001 From: Hemachandar <132386067+hmacr@users.noreply.github.com> Date: Thu, 27 Nov 2025 16:31:14 +0530 Subject: [PATCH 02/11] Revert "Update deploymentId in sites rule for first deployment" --- app/controllers/api/vcs.php | 3 - .../Platform/Modules/Compute/Base.php | 37 +---------- .../Functions/Http/Deployments/Create.php | 7 +- .../Http/Deployments/Template/Create.php | 4 -- .../Functions/Http/Deployments/Vcs/Create.php | 1 - .../Functions/Http/Functions/Create.php | 2 - .../Functions/Http/Functions/Update.php | 2 +- .../Modules/Sites/Http/Deployments/Create.php | 5 +- .../Http/Deployments/Template/Create.php | 2 - .../Modules/Sites/Http/Sites/Update.php | 2 +- .../e2e/Services/Functions/FunctionsBase.php | 23 ------- .../Functions/FunctionsCustomServerTest.php | 65 ++----------------- tests/e2e/Services/Sites/SitesBase.php | 23 ------- .../Services/Sites/SitesCustomServerTest.php | 52 ++++----------- 14 files changed, 24 insertions(+), 204 deletions(-) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index ab8e9195b3..204957f485 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -5,7 +5,6 @@ use Appwrite\Event\Build; use Appwrite\Event\Delete; use Appwrite\Extend\Exception; use Appwrite\Network\Validator\Redirect; -use Appwrite\Platform\Modules\Compute\Base as ComputeBase; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -434,8 +433,6 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId } } - ComputeBase::updateEmptyManualRule($project, $resource, $deployment, $dbForPlatform); - if ($resource->getCollection() === 'sites' && !empty($latestCommentId) && !empty($previewRuleId)) { $retries = 0; $lockAcquired = false; diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index cc9b403925..a538eb1497 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -13,7 +13,6 @@ use Utopia\Database\Exception\Duplicate; 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\Swoole\Request; use Utopia\System\System; @@ -53,7 +52,7 @@ class Base extends Action return $allowedSpecifications[0] ?? APP_COMPUTE_SPECIFICATION_DEFAULT; } - public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, string $referenceType = 'branch', string $reference = ''): Document + public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, string $referenceType = 'branch', string $reference = ''): Document { $deploymentId = ID::unique(); $entrypoint = $function->getAttribute('entrypoint', ''); @@ -134,8 +133,6 @@ class Base extends Action ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); - $this->updateEmptyManualRule($project, $function, $deployment, $dbForPlatform); - $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) @@ -330,8 +327,6 @@ class Base extends Action } } - $this->updateEmptyManualRule($project, $site, $deployment, $dbForPlatform); - $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($site) @@ -340,34 +335,4 @@ class Base extends Action return $deployment; } - - /** - * Update empty manual rule for deployment. - * In case of first deployment, deployment ID will be empty in the rules, so we need to update it here. - * - * @param \Utopia\Database\Document $project - * @param \Utopia\Database\Document $resource - * @param \Utopia\Database\Document $deployment - * @param \Utopia\Database\Database $dbForPlatform - * @return void - */ - public static function updateEmptyManualRule(Document $project, Document $resource, Document $deployment, Database $dbForPlatform) - { - $resourceType = $resource->getCollection() === 'sites' ? 'site' : 'function'; - - $queries = [ - Query::equal('projectInternalId', [$project->getSequence()]), - Query::equal('deploymentResourceInternalId', [$resource->getSequence()]), - Query::equal('deploymentResourceType', [$resourceType]), - Query::equal('deploymentId', ['']), - Query::equal('type', ['deployment']), - Query::equal('trigger', ['manual']), - ]; - $dbForPlatform->forEach('rules', function (Document $rule) use ($deployment, $dbForPlatform) { - Authorization::skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([ - 'deploymentId' => $deployment->getId(), - 'deploymentInternalId' => $deployment->getSequence(), - ]))); - }, $queries); - } } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php index d56b0d4d8f..d53324b27f 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Functions\Http\Deployments; use Appwrite\Event\Build; use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\Platform\Modules\Compute\Base; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -32,7 +31,7 @@ use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; use Utopia\Validator\Text; -class Create extends Base +class Create extends Action { use HTTP; @@ -83,7 +82,6 @@ class Create extends Base ->inject('request') ->inject('response') ->inject('dbForProject') - ->inject('dbForPlatform') ->inject('queueForEvents') ->inject('project') ->inject('deviceForFunctions') @@ -102,7 +100,6 @@ class Create extends Base Request $request, Response $response, Database $dbForProject, - Database $dbForPlatform, Event $queueForEvents, Document $project, Device $deviceForFunctions, @@ -304,8 +301,6 @@ class Create extends Base } } - $this->updateEmptyManualRule($project, $function, $deployment, $dbForPlatform); - $metadata = null; $queueForEvents diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php index 19e1653dcb..00c29d6bba 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php @@ -124,7 +124,6 @@ class Create extends Base project: $project, installation: $installation, dbForProject: $dbForProject, - dbForPlatform: $dbForPlatform, queueForBuilds: $queueForBuilds, template: $template, github: $github, @@ -171,9 +170,6 @@ class Create extends Base ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); - - $this->updateEmptyManualRule($project, $function, $deployment, $dbForPlatform); - $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php index 79e2e215ad..0ad9852722 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php @@ -105,7 +105,6 @@ class Create extends Base project: $project, installation: $installation, dbForProject: $dbForProject, - dbForPlatform: $dbForPlatform, queueForBuilds: $queueForBuilds, template: $template, github: $github, diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index b22710253e..ec2a4baac5 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -305,14 +305,12 @@ class Create extends Base $template = new Document(); $installation = $dbForPlatform->getDocument('installations', $function->getAttribute('installationId')); - // TODO: Is this still needed? Can this be removed? $deployment = $this->redeployVcsFunction( request: $request, function: $function, project: $project, installation: $installation, dbForProject: $dbForProject, - dbForPlatform: $dbForPlatform, queueForBuilds: $queueForBuilds, template: $template, github: $github, diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php index 4bc3dca613..318c2a2032 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php @@ -273,7 +273,7 @@ class Update extends Base // Redeploy logic if (!$isConnected && !empty($providerRepositoryId)) { - $this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $dbForPlatform, $queueForBuilds, new Document(), $github, true); + $this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $queueForBuilds, new Document(), $github, true); } // Inform scheduler if function is still active diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php index 0662f09b80..aa622d8d84 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php @@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Sites\Http\Deployments; use Appwrite\Event\Build; use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\Platform\Modules\Compute\Base; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -33,7 +32,7 @@ use Utopia\Validator\Boolean; use Utopia\Validator\Nullable; use Utopia\Validator\Text; -class Create extends Base +class Create extends Action { use HTTP; @@ -366,8 +365,6 @@ class Create extends Base } } - $this->updateEmptyManualRule($project, $site, $deployment, $dbForPlatform); - $metadata = null; $queueForEvents diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php index f5d7d5a873..dc7d4c4ace 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php @@ -208,8 +208,6 @@ class Create extends Base ])) ); - $this->updateEmptyManualRule($project, $site, $deployment, $dbForPlatform); - $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($site) diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php index 61d59d4da0..72ec04a2a5 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php @@ -272,7 +272,7 @@ class Update extends Base // Redeploy logic if (!$isConnected && !empty($providerRepositoryId)) { - $this->redeployVcsFunction($request, $site, $project, $installation, $dbForProject, $dbForPlatform, $queueForBuilds, new Document(), $github, true); + $this->redeployVcsFunction($request, $site, $project, $installation, $dbForProject, $queueForBuilds, new Document(), $github, true); } $queueForEvents->setParam('siteId', $site->getId()); diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 492226b01a..7403b23a73 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -432,27 +432,4 @@ trait FunctionsBase return $specifications; } - - protected function createFunctionRule(string $functionId, string $domain): mixed - { - $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/function', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'functionId' => $functionId, - 'domain' => $domain, - ]); - - return $rule; - } - - protected function getFunctionRule(string $ruleId): mixed - { - $rule = $this->client->call(Client::METHOD_GET, '/proxy/rules/' . $ruleId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - return $rule; - } } diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 74b6ed3e8d..8cc986b072 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -14,7 +14,6 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Datetime as DatetimeValidator; -use Utopia\System\System; class FunctionsCustomServerTest extends Scope { @@ -393,10 +392,6 @@ class FunctionsCustomServerTest extends Scope $functionId = $function['body']['$id'] ?? ''; - $domain = ID::unique() . '.' . System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $rule = $this->createFunctionRule($functionId, $domain); - $this->assertEquals(201, $rule['headers']['status-code']); - $deployment = $this->createTemplateDeployment( $functionId, [ @@ -413,20 +408,6 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); - $rule = $this->getFunctionRule($rule['body']['$id']); - $this->assertEquals(200, $rule['headers']['status-code']); - $this->assertEquals($deployment['body']['$id'], $rule['body']['deploymentId']); - - $proxyClient = new Client(); - $proxyClient->setEndpoint('http://' . $domain); - - $response = $proxyClient->call(Client::METHOD_GET, '/'); - $this->assertEquals(400, $response['headers']['status-code']); - $this->assertStringContainsString("Deployment is still building", $response['body']); - $this->assertStringContainsString("The page will update after the build completes.", $response['body']); - - $deployment = $this->getDeployment($functionId, $deployment['body']['$id']); - $this->assertEquals(200, $deployment['headers']['status-code']); // Wait for deployment to be ready $deploymentId = $deployment['body']['$id']; $this->assertEventually(function () use ($functionId, $deploymentId) { @@ -436,6 +417,7 @@ class FunctionsCustomServerTest extends Scope // Verify deployment sizes $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertGreaterThan(0, $deployment['body']['sourceSize']); $this->assertGreaterThan(0, $deployment['body']['buildSize']); $totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize']; @@ -680,10 +662,6 @@ class FunctionsCustomServerTest extends Scope */ $functionId = $data['functionId']; - $domain = ID::unique() . '.' . System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $rule = $this->createFunctionRule($functionId, $domain); - $this->assertEquals(201, $rule['headers']['status-code']); - $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('basic'), 'activate' => true @@ -695,18 +673,6 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt'])); $this->assertEquals('index.js', $deployment['body']['entrypoint']); - $rule = $this->getFunctionRule($rule['body']['$id']); - $this->assertEquals(200, $rule['headers']['status-code']); - $this->assertEquals($deployment['body']['$id'], $rule['body']['deploymentId']); - - $proxyClient = new Client(); - $proxyClient->setEndpoint('http://' . $domain); - - $response = $proxyClient->call(Client::METHOD_GET, '/'); - $this->assertEquals(400, $response['headers']['status-code']); - $this->assertStringContainsString("Deployment is still building", $response['body']); - $this->assertStringContainsString('The page will update after the build completes.', $response['body']); - $deploymentIdActive = $deployment['body']['$id'] ?? ''; $this->assertEventually(function () use ($functionId, $deploymentIdActive) { @@ -2426,12 +2392,6 @@ class FunctionsCustomServerTest extends Scope $domain = $this->setupFunctionDomain($functionId); $proxyClient->setEndpoint('http://' . $domain); - $response = $proxyClient->call(Client::METHOD_GET, '/'); - - $this->assertEquals(404, $response['headers']['status-code']); - $this->assertStringContainsString('No active deployments', $response['body']); - $this->assertStringContainsString('View deployments', $response['body']); - $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('basic'), 'activate' => true @@ -2444,22 +2404,11 @@ class FunctionsCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ])); - $this->assertEquals(400, $response['headers']['status-code']); - $this->assertStringContainsString('Deployment is still building', $response['body']); - $this->assertStringContainsString('The page will update after the build completes.', $response['body']); + $this->assertEquals(404, $response['headers']['status-code']); + $this->assertStringContainsString('No active deployments', $response['body']); + $this->assertStringContainsString('View deployments', $response['body']); // canceled deployment - $functionId = $this->setupFunction([ - 'functionId' => ID::unique(), - 'name' => 'Test Error Pages', - 'runtime' => 'node-22', - 'entrypoint' => 'index.js', - 'timeout' => 15, - 'commands' => 'cd non-existing-directory', - 'execute' => ['any'] - ]); - $domain = $this->setupFunctionDomain($functionId); - $proxyClient->setEndpoint('http://' . $domain); $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('basic'), 'activate' => true @@ -2477,9 +2426,9 @@ class FunctionsCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ])); - $this->assertEquals(400, $response['headers']['status-code']); - $this->assertStringContainsString('Deployment build canceled', $response['body']); - $this->assertStringContainsString('This build was canceled and won\'t be deployed.', $response['body']); + $this->assertEquals(404, $response['headers']['status-code']); + $this->assertStringContainsString('No active deployments', $response['body']); + $this->assertStringContainsString('View deployments', $response['body']); $this->cleanupFunction($functionId); } diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index 6abe1b38d9..7eb5d9699c 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -474,27 +474,4 @@ trait SitesBase return $specifications; } - - protected function createSiteRule(string $siteId, string $domain): mixed - { - $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/site', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'siteId' => $siteId, - 'domain' => $domain, - ]); - - return $rule; - } - - protected function getSiteRule(string $ruleId): mixed - { - $rule = $this->client->call(Client::METHOD_GET, '/proxy/rules/' . $ruleId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - return $rule; - } } diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index 413fea289f..b7dc9e7334 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -861,10 +861,6 @@ class SitesCustomServerTest extends Scope $this->assertNotNull($siteId); - $domain = ID::unique() . '.' . System::getEnv('_APP_DOMAIN_SITES', ''); - $rule = $this->createSiteRule($siteId, $domain); - $this->assertEquals(201, $rule['headers']['status-code']); - $deployment = $this->createDeployment($siteId, [ 'siteId' => $siteId, 'code' => $this->packageSite('static-single-file'), @@ -876,18 +872,6 @@ class SitesCustomServerTest extends Scope $this->assertEquals('waiting', $deployment['body']['status']); $this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt'])); - $rule = $this->getSiteRule($rule['body']['$id']); - $this->assertEquals(200, $rule['headers']['status-code']); - $this->assertEquals($deployment['body']['$id'], $rule['body']['deploymentId']); - - $proxyClient = new Client(); - $proxyClient->setEndpoint('http://' . $domain); - - $response = $proxyClient->call(Client::METHOD_GET, '/'); - $this->assertEquals(400, $response['headers']['status-code']); - $this->assertStringContainsString("Deployment is still building", $response['body']); - $this->assertStringContainsString('The page will update after the build completes.', $response['body']); - $deploymentIdActive = $deployment['body']['$id'] ?? ''; $this->assertEventually(function () use ($siteId, $deploymentIdActive) { @@ -1579,10 +1563,6 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); - $domain = ID::unique() . '.' . System::getEnv('_APP_DOMAIN_SITES', ''); - $rule = $this->createSiteRule($siteId, $domain); - $this->assertEquals(201, $rule['headers']['status-code']); - $deployment = $this->createTemplateDeployment($siteId, [ 'repository' => $template['providerRepositoryId'], 'owner' => $template['providerOwner'], @@ -1595,18 +1575,6 @@ class SitesCustomServerTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); - $rule = $this->getSiteRule($rule['body']['$id']); - $this->assertEquals(200, $rule['headers']['status-code']); - $this->assertEquals($deployment['body']['$id'], $rule['body']['deploymentId']); - - $proxyClient = new Client(); - $proxyClient->setEndpoint('http://' . $domain); - - $response = $proxyClient->call(Client::METHOD_GET, '/'); - $this->assertEquals(400, $response['headers']['status-code']); - $this->assertStringContainsString("Deployment is still building", $response['body']); - $this->assertStringContainsString('The page will update after the build completes.', $response['body']); - $deployment = $this->getDeployment($siteId, $deployment['body']['$id']); $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals(0, $deployment['body']['sourceSize']); @@ -1618,6 +1586,10 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($site['body']['deploymentId']); }, 50000, 500); + $domain = $this->setupSiteDomain($siteId); + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + $response = $proxyClient->call(Client::METHOD_GET, '/'); $this->assertEquals(200, $response['headers']['status-code']); @@ -2612,8 +2584,7 @@ class SitesCustomServerTest extends Scope }, 100000, 500); $response = $proxyClient->call(Client::METHOD_GET, '/'); - $this->assertEquals(400, $response['headers']['status-code']); - $this->assertStringContainsString('Deployment build failed', $response['body']); + $this->assertStringContainsString('This page is empty, activate a deployment to make it live.', $response['body']); $this->cleanupSite($siteId); } @@ -2708,12 +2679,6 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); $domain = $this->setupSiteDomain($siteId); - $proxyClient->setEndpoint('http://' . $domain); - $response = $proxyClient->call(Client::METHOD_GET, '/'); - - $this->assertEquals(404, $response['headers']['status-code']); - $this->assertStringContainsString('No active deployments', $response['body']); - $this->assertStringContainsString('View deployments', $response['body']); // test canceled deployment error page $deployment = $this->createDeployment($siteId, [ @@ -2749,6 +2714,13 @@ class SitesCustomServerTest extends Scope $this->assertStringContainsString("Deployment build canceled", $response['body']); $this->assertStringContainsString("View deployments", $response['body']); + // check site domain for no active deployments + $proxyClient->setEndpoint('http://' . $domain); + $response = $proxyClient->call(Client::METHOD_GET, '/'); + $this->assertEquals(404, $response['headers']['status-code']); + $this->assertStringContainsString('No active deployments', $response['body']); + $this->assertStringContainsString('View deployments', $response['body']); + $deployment = $this->createDeployment($siteId, [ 'code' => $this->packageSite('static-single-file'), 'activate' => 'true' From 2fdfbf6e61c08ee42bd449aab88b7a3a5d6e54c4 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 27 Nov 2025 11:48:32 +0000 Subject: [PATCH 03/11] Fix type --- app/controllers/shared/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 05477d679a..16d44481b6 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -856,7 +856,7 @@ App::shutdown() * * Therefore, we consider this an anonymous request and create a relevant user. */ - $user = new Document([ + $user = new User([ '$id' => '', 'status' => true, 'type' => ACTIVITY_TYPE_GUEST, From c09f7d5cf9e613479d0460d38986952ea757b3f4 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Thu, 27 Nov 2025 19:40:09 +0530 Subject: [PATCH 04/11] Fix file token expiry --- app/init/resources.php | 3 +- .../Utopia/Response/Model/ResourceToken.php | 6 +- .../Tokens/TokensConsoleClientTest.php | 67 +++++++++++-------- 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/app/init/resources.php b/app/init/resources.php index 0c05b30a83..5d05c49ca6 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -1003,7 +1003,8 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) { $tokenJWT = $request->getParam('token'); if (!empty($tokenJWT) && !$project->isEmpty()) { // JWT authentication - $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. + // Use a large but reasonable maxAge to avoid auto-exp when token has no expiry + $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 86400 * 365 * 10, 10); // Instantiate with key, algo, maxAge and leeway. try { $payload = $jwt->decode($tokenJWT); diff --git a/src/Appwrite/Utopia/Response/Model/ResourceToken.php b/src/Appwrite/Utopia/Response/Model/ResourceToken.php index 87ab66ab5d..e5ff1a645b 100644 --- a/src/Appwrite/Utopia/Response/Model/ResourceToken.php +++ b/src/Appwrite/Utopia/Response/Model/ResourceToken.php @@ -73,13 +73,13 @@ class ResourceToken extends Model 'resourceInternalId' => $document->getAttribute('resourceInternalId'), ]; + $createdDate = new \DateTime($document->getCreatedAt()); + $payload['iat'] = $createdDate->getTimestamp(); + // Set explicit expiration in JWT payload if we have an expiry date if ($expire !== null) { $expiryDate = new \DateTime($expire); $payload['exp'] = $expiryDate->getTimestamp(); - } else { - // For infinite expiry, set 'iat' to prevent JWT library from auto-adding 'exp' - $payload['iat'] = time(); } $secret = $jwt->encode($payload); diff --git a/tests/e2e/Services/Tokens/TokensConsoleClientTest.php b/tests/e2e/Services/Tokens/TokensConsoleClientTest.php index f1480faba0..7a9181b1dc 100644 --- a/tests/e2e/Services/Tokens/TokensConsoleClientTest.php +++ b/tests/e2e/Services/Tokens/TokensConsoleClientTest.php @@ -72,37 +72,45 @@ class TokensConsoleClientTest extends Scope $this->assertEquals(400, $token['headers']['status-code']); $this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']); - // Success case: No expire date - $token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'] - ], $this->getHeaders()), [ - 'expire' => null, - ]); + // Success cases: With & without expiry + $expireList = [null, date('Y-m-d', strtotime("tomorrow"))]; + foreach ($expireList as $expire) { + $token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'expire' => $expire, + ]); - $this->assertEquals(201, $token['headers']['status-code']); - $this->assertEquals('files', $token['body']['resourceType']); - $this->assertNotEmpty($token['body']['$id']); - $this->assertNotEmpty($token['body']['secret']); + $this->assertEquals(201, $token['headers']['status-code']); + $this->assertEquals('files', $token['body']['resourceType']); + $this->assertNotEmpty($token['body']['$id']); + $this->assertNotEmpty($token['body']['secret']); - // Verify the generated token JWT contains correct resource information - $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 86400 * 365 * 10, 10); // 10 years maxAge - try { - $payload = $jwt->decode($token['body']['secret']); - $this->assertIsArray($payload, 'JWT payload should decode to an array'); - $this->assertArrayHasKey('tokenId', $payload, 'JWT payload should contain tokenId'); - $this->assertArrayHasKey('resourceId', $payload, 'JWT payload should contain resourceId'); - $this->assertArrayHasKey('resourceType', $payload, 'JWT payload should contain resourceType'); - $this->assertArrayHasKey('resourceInternalId', $payload, 'JWT payload should contain resourceInternalId'); + // Verify the generated token JWT contains correct resource information + $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 86400 * 365 * 10, 10); // 10 years maxAge + try { + $payload = $jwt->decode($token['body']['secret']); + $this->assertIsArray($payload, 'JWT payload should decode to an array'); + $this->assertArrayHasKey('tokenId', $payload, 'JWT payload should contain tokenId'); + $this->assertArrayHasKey('resourceId', $payload, 'JWT payload should contain resourceId'); + $this->assertArrayHasKey('resourceType', $payload, 'JWT payload should contain resourceType'); + $this->assertArrayHasKey('resourceInternalId', $payload, 'JWT payload should contain resourceInternalId'); + $this->assertArrayHasKey('iat', $payload, 'JWT payload should contain iat'); - $this->assertEquals($token['body']['$id'], $payload['tokenId'], 'JWT tokenId should match token ID'); - $this->assertEquals($bucketId . ':' . $fileId, $payload['resourceId'], 'JWT resourceId should match bucketId:fileId format'); - $this->assertEquals('files', $payload['resourceType'], 'JWT resourceType should be files'); + if (!empty($expire)) { + $this->assertArrayHasKey('exp', $payload, 'JWT payload should contain exp'); + } else { + $this->assertArrayNotHasKey('exp', $payload, 'JWT payload should not contain exp field for tokens without expiry'); + } - // For newly created tokens without expiry, should not have exp field - $this->assertArrayNotHasKey('exp', $payload, 'JWT payload should not contain exp field for tokens without expiry'); - } catch (JWTException $e) { - $this->fail('Failed to decode JWT: ' . $e->getMessage()); + $this->assertEquals($token['body']['$id'], $payload['tokenId'], 'JWT tokenId should match token ID'); + $this->assertEquals($bucketId . ':' . $fileId, $payload['resourceId'], 'JWT resourceId should match bucketId:fileId format'); + $this->assertEquals('files', $payload['resourceType'], 'JWT resourceType should be files'); + + } catch (JWTException $e) { + $this->fail('Failed to decode JWT: ' . $e->getMessage()); + } } return [ @@ -218,6 +226,11 @@ class TokensConsoleClientTest extends Scope $this->assertArrayHasKey('resourceId', $payload, 'JWT payload should contain resourceId'); $this->assertArrayHasKey('resourceType', $payload, 'JWT payload should contain resourceType'); $this->assertArrayHasKey('resourceInternalId', $payload, 'JWT payload should contain resourceInternalId'); + $this->assertArrayHasKey('iat', $payload, 'JWT payload should contain iat'); + + if (!empty($token['expire'])) { + $this->assertArrayHasKey('exp', $payload, 'JWT payload should contain exp'); + } $this->assertEquals($token['$id'], $payload['tokenId'], 'JWT tokenId should match token ID'); $this->assertEquals($data['bucketId'] . ':' . $data['fileId'], $payload['resourceId'], 'JWT resourceId should match bucketId:fileId format'); From adc5426ce915926332afb913138cc12a921fb827 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Thu, 27 Nov 2025 20:02:39 +0530 Subject: [PATCH 05/11] feedback --- app/init/constants.php | 7 +++++++ app/init/resources.php | 2 +- src/Appwrite/Utopia/Response/Model/ResourceToken.php | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/init/constants.php b/app/init/constants.php index 0cec24b749..ea5c0fb2c5 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -93,6 +93,13 @@ const APP_VCS_GITHUB_EMAIL = 'team@appwrite.io'; const APP_VCS_GITHUB_URL = 'https://github.com/TeamAppwrite'; const APP_BRANDED_EMAIL_BASE_TEMPLATE = 'email-base-styled'; +/** + * JWT for Resource Tokens. + */ +const RESOURCE_TOKEN_ALGORITHM = 'HS256'; +const RESOURCE_TOKEN_MAX_AGE = 86400 * 365 * 10; /* 10 years */ +const RESOURCE_TOKEN_LEEWAY = 10; // 10 seconds + /** * Token Expiration times. */ diff --git a/app/init/resources.php b/app/init/resources.php index 5d05c49ca6..98162d3a2b 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -1004,7 +1004,7 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) { if (!empty($tokenJWT) && !$project->isEmpty()) { // JWT authentication // Use a large but reasonable maxAge to avoid auto-exp when token has no expiry - $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 86400 * 365 * 10, 10); // Instantiate with key, algo, maxAge and leeway. + $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), RESOURCE_TOKEN_ALGORITHM, RESOURCE_TOKEN_MAX_AGE, RESOURCE_TOKEN_LEEWAY); // Instantiate with key, algo, maxAge and leeway. try { $payload = $jwt->decode($tokenJWT); diff --git a/src/Appwrite/Utopia/Response/Model/ResourceToken.php b/src/Appwrite/Utopia/Response/Model/ResourceToken.php index e5ff1a645b..c2b3deb56b 100644 --- a/src/Appwrite/Utopia/Response/Model/ResourceToken.php +++ b/src/Appwrite/Utopia/Response/Model/ResourceToken.php @@ -64,7 +64,7 @@ class ResourceToken extends Model $expire = $document->getAttribute('expire'); // Use a large but reasonable maxAge to avoid auto-exp when we set explicit exp - $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 86400 * 365 * 10, 10); // 10 years + $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), RESOURCE_TOKEN_ALGORITHM, RESOURCE_TOKEN_MAX_AGE, RESOURCE_TOKEN_LEEWAY); // 10 years $payload = [ 'tokenId' => $document->getId(), From 67ae0b088ad4c7957c625ee56d9c92e79062d794 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Thu, 27 Nov 2025 20:12:22 +0530 Subject: [PATCH 06/11] update composer.lock --- composer.lock | 388 ++++++++++++++++++-------------------------------- 1 file changed, 137 insertions(+), 251 deletions(-) diff --git a/composer.lock b/composer.lock index 397b43b4d4..e0ab04eda6 100644 --- a/composer.lock +++ b/composer.lock @@ -891,16 +891,16 @@ }, { "name": "matomo/device-detector", - "version": "6.4.7", + "version": "6.4.8", "source": { "type": "git", "url": "https://github.com/matomo-org/device-detector.git", - "reference": "e53eed31bb1530851feebe52bd64c3451da19e77" + "reference": "56baf981af4f192e15a4f369d4975af847a81ccb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/e53eed31bb1530851feebe52bd64c3451da19e77", - "reference": "e53eed31bb1530851feebe52bd64c3451da19e77", + "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/56baf981af4f192e15a4f369d4975af847a81ccb", + "reference": "56baf981af4f192e15a4f369d4975af847a81ccb", "shasum": "" }, "require": { @@ -957,7 +957,7 @@ "source": "https://github.com/matomo-org/matomo", "wiki": "https://dev.matomo.org/" }, - "time": "2025-08-20T17:20:16+00:00" + "time": "2025-11-26T16:02:47+00:00" }, { "name": "mongodb/mongodb", @@ -2673,16 +2673,16 @@ }, { "name": "symfony/http-client", - "version": "v7.3.6", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de" + "reference": "ee5e0e0139ab506f6063a230e631bed677c650a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de", - "reference": "3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de", + "url": "https://api.github.com/repos/symfony/http-client/zipball/ee5e0e0139ab506f6063a230e631bed677c650a4", + "reference": "ee5e0e0139ab506f6063a230e631bed677c650a4", "shasum": "" }, "require": { @@ -2713,12 +2713,13 @@ "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", "symfony/amphp-http-client-meta": "^1.0|^2.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -2749,7 +2750,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.6" + "source": "https://github.com/symfony/http-client/tree/v7.4.0" }, "funding": [ { @@ -2769,7 +2770,7 @@ "type": "tidelift" } ], - "time": "2025-11-05T17:41:46+00:00" + "time": "2025-11-20T12:32:50+00:00" }, { "name": "symfony/http-client-contracts", @@ -3941,66 +3942,18 @@ }, "time": "2025-11-24T15:52:51+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" - }, { "name": "utopia-php/dns", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/utopia-php/dns.git", - "reference": "1e6b4bac735329c9e5ec69a6a5d899ec2d050707" + "reference": "eea6b9299a1420ae6c574f16eb1e9da8689ac56b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dns/zipball/1e6b4bac735329c9e5ec69a6a5d899ec2d050707", - "reference": "1e6b4bac735329c9e5ec69a6a5d899ec2d050707", + "url": "https://api.github.com/repos/utopia-php/dns/zipball/eea6b9299a1420ae6c574f16eb1e9da8689ac56b", + "reference": "eea6b9299a1420ae6c574f16eb1e9da8689ac56b", "shasum": "" }, "require": { @@ -4008,7 +3961,7 @@ "utopia-php/console": "0.0.*", "utopia-php/domains": "0.9.*", "utopia-php/telemetry": "0.1.*", - "utopia-php/validators": "^0.0.2" + "utopia-php/validators": "0.*" }, "require-dev": { "laravel/pint": "1.25.*", @@ -4042,32 +3995,32 @@ ], "support": { "issues": "https://github.com/utopia-php/dns/issues", - "source": "https://github.com/utopia-php/dns/tree/1.1.3" + "source": "https://github.com/utopia-php/dns/tree/1.1.4" }, - "time": "2025-11-06T19:08:29+00:00" + "time": "2025-11-26T13:38:10+00:00" }, { "name": "utopia-php/domains", - "version": "0.9.1", + "version": "0.9.2", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "99b4ec95d5d6b7a5c990a66c56412212d9af37e7" + "reference": "52b654f8a0e170bfa2e54cb47755b256822477c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/99b4ec95d5d6b7a5c990a66c56412212d9af37e7", - "reference": "99b4ec95d5d6b7a5c990a66c56412212d9af37e7", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/52b654f8a0e170bfa2e54cb47755b256822477c7", + "reference": "52b654f8a0e170bfa2e54cb47755b256822477c7", "shasum": "" }, "require": { - "php": ">=8.0", + "php": ">=8.2", "utopia-php/cache": "0.13.*", - "utopia-php/validators": "0.0.*" + "utopia-php/validators": "0.*" }, "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.9.x-dev", + "laravel/pint": "^1.18", + "phpstan/phpstan": "^1.12", "phpunit/phpunit": "^9.3" }, "type": "library", @@ -4104,9 +4057,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.9.1" + "source": "https://github.com/utopia-php/domains/tree/0.9.2" }, - "time": "2025-10-21T14:52:27+00:00" + "time": "2025-11-26T12:16:36+00:00" }, { "name": "utopia-php/dsn", @@ -4157,16 +4110,16 @@ }, { "name": "utopia-php/emails", - "version": "0.6.2", + "version": "0.6.3", "source": { "type": "git", "url": "https://github.com/utopia-php/emails.git", - "reference": "9c4c40cf7c03c2e9e21364566f9b192d03ea93c9" + "reference": "9524d7f7bd1651a06fef8a3d964f774b04fe2918" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/emails/zipball/9c4c40cf7c03c2e9e21364566f9b192d03ea93c9", - "reference": "9c4c40cf7c03c2e9e21364566f9b192d03ea93c9", + "url": "https://api.github.com/repos/utopia-php/emails/zipball/9524d7f7bd1651a06fef8a3d964f774b04fe2918", + "reference": "9524d7f7bd1651a06fef8a3d964f774b04fe2918", "shasum": "" }, "require": { @@ -4174,7 +4127,7 @@ "utopia-php/cli": "^0.15", "utopia-php/domains": "^0.9", "utopia-php/fetch": "^0.4", - "utopia-php/validators": "^0.0.2" + "utopia-php/validators": "0.*" }, "require-dev": { "laravel/pint": "1.25.*", @@ -4211,9 +4164,9 @@ ], "support": { "issues": "https://github.com/utopia-php/emails/issues", - "source": "https://github.com/utopia-php/emails/tree/0.6.2" + "source": "https://github.com/utopia-php/emails/tree/0.6.3" }, - "time": "2025-10-28T16:08:17+00:00" + "time": "2025-11-26T12:27:47+00:00" }, { "name": "utopia-php/fetch", @@ -4256,33 +4209,29 @@ }, { "name": "utopia-php/framework", - "version": "0.33.31", + "version": "0.33.33", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "cb01940ffe620b3d09f934dc2b8c72c2406309e7" + "reference": "838e3a28276e73187bc34a314f014096dc92191b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/cb01940ffe620b3d09f934dc2b8c72c2406309e7", - "reference": "cb01940ffe620b3d09f934dc2b8c72c2406309e7", + "url": "https://api.github.com/repos/utopia-php/http/zipball/838e3a28276e73187bc34a314f014096dc92191b", + "reference": "838e3a28276e73187bc34a314f014096dc92191b", "shasum": "" }, "require": { - "ext-swoole": "*", "php": ">=8.1", "utopia-php/compression": "0.1.*", - "utopia-php/servers": "0.2.*", "utopia-php/telemetry": "0.1.*", - "utopia-php/validators": "0.0.*" + "utopia-php/validators": "0.1.*" }, "require-dev": { - "ext-xdebug": "*", "laravel/pint": "^1.2", "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "1.*", - "phpunit/phpunit": "^9.5.25", - "swoole/ide-helper": "4.8.3" + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.25" }, "type": "library", "autoload": { @@ -4294,18 +4243,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.2.0" + "source": "https://github.com/utopia-php/http/tree/0.33.33" }, - "time": "2025-10-21T10:53:55+00:00" + "time": "2025-11-25T10:21:13+00:00" }, { "name": "utopia-php/image", @@ -4947,60 +4895,6 @@ }, "time": "2021-03-10T10:45:22+00:00" }, - { - "name": "utopia-php/servers", - "version": "0.2.1", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/servers.git", - "reference": "c6ec23b0597f657020a75d4a2f8ef03177d7edaa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/servers/zipball/c6ec23b0597f657020a75d4a2f8ef03177d7edaa", - "reference": "c6ec23b0597f657020a75d4a2f8ef03177d7edaa", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/di": "0.1.*", - "utopia-php/validators": "0.0.*" - }, - "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.2.1" - }, - "time": "2025-10-21T10:08:19+00:00" - }, { "name": "utopia-php/storage", "version": "0.18.14", @@ -5218,26 +5112,25 @@ }, { "name": "utopia-php/validators", - "version": "0.0.2", + "version": "0.1.0", "source": { "type": "git", "url": "https://github.com/utopia-php/validators.git", - "reference": "894210695c5d35fa248fb65f7fe7237b6ff4fb0b" + "reference": "5c57d5b6cf964f8981807c1d3ea8df620c869080" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/validators/zipball/894210695c5d35fa248fb65f7fe7237b6ff4fb0b", - "reference": "894210695c5d35fa248fb65f7fe7237b6ff4fb0b", + "url": "https://api.github.com/repos/utopia-php/validators/zipball/5c57d5b6cf964f8981807c1d3ea8df620c869080", + "reference": "5c57d5b6cf964f8981807c1d3ea8df620c869080", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.0" }, "require-dev": { - "ext-xdebug": "*", - "laravel/pint": "^1.2", + "laravel/pint": "1.*", "phpstan/phpstan": "1.*", - "phpunit/phpunit": "^9.5.25" + "phpunit/phpunit": "11.*" }, "type": "library", "autoload": { @@ -5258,9 +5151,9 @@ ], "support": { "issues": "https://github.com/utopia-php/validators/issues", - "source": "https://github.com/utopia-php/validators/tree/0.0.2" + "source": "https://github.com/utopia-php/validators/tree/0.1.0" }, - "time": "2025-10-20T21:52:28+00:00" + "time": "2025-11-18T11:05:46+00:00" }, { "name": "utopia-php/vcs", @@ -5780,16 +5673,16 @@ }, { "name": "laravel/pint", - "version": "v1.25.1", + "version": "v1.26.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9" + "reference": "69dcca060ecb15e4b564af63d1f642c81a241d6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/5016e263f95d97670d71b9a987bd8996ade6d8d9", - "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9", + "url": "https://api.github.com/repos/laravel/pint/zipball/69dcca060ecb15e4b564af63d1f642c81a241d6f", + "reference": "69dcca060ecb15e4b564af63d1f642c81a241d6f", "shasum": "" }, "require": { @@ -5800,13 +5693,13 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.87.2", - "illuminate/view": "^11.46.0", - "larastan/larastan": "^3.7.1", - "laravel-zero/framework": "^11.45.0", + "friendsofphp/php-cs-fixer": "^3.90.0", + "illuminate/view": "^12.40.1", + "larastan/larastan": "^3.8.0", + "laravel-zero/framework": "^12.0.4", "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^2.3.1", - "pestphp/pest": "^2.36.0" + "nunomaduro/termwind": "^2.3.3", + "pestphp/pest": "^3.8.4" }, "bin": [ "builds/pint" @@ -5832,6 +5725,7 @@ "description": "An opinionated code formatter for PHP.", "homepage": "https://laravel.com", "keywords": [ + "dev", "format", "formatter", "lint", @@ -5842,7 +5736,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-09-19T02:57:12+00:00" + "time": "2025-11-25T21:15:52+00:00" }, { "name": "matthiasmullie/minify", @@ -7999,47 +7893,39 @@ }, { "name": "symfony/console", - "version": "v7.3.6", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a" + "reference": "307d3cf852f5ead3618ac60ecbedbdd512c348b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", - "reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a", + "url": "https://api.github.com/repos/symfony/console/zipball/307d3cf852f5ead3618ac60ecbedbdd512c348b1", + "reference": "307d3cf852f5ead3618ac60ecbedbdd512c348b1", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" + "symfony/string": "^7.4|^8.0" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8073,7 +7959,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.6" + "source": "https://github.com/symfony/console/tree/v8.0.0" }, "funding": [ { @@ -8093,29 +7979,29 @@ "type": "tidelift" } ], - "time": "2025-11-04T01:21:42+00:00" + "time": "2025-11-21T13:19:49+00:00" }, { "name": "symfony/filesystem", - "version": "v7.3.6", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "e9bcfd7837928ab656276fe00464092cc9e1826a" + "reference": "7fc96ae83372620eaba3826874f46e26295768ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/e9bcfd7837928ab656276fe00464092cc9e1826a", - "reference": "e9bcfd7837928ab656276fe00464092cc9e1826a", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/7fc96ae83372620eaba3826874f46e26295768ca", + "reference": "7fc96ae83372620eaba3826874f46e26295768ca", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^6.4|^7.0" + "symfony/process": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8143,7 +8029,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.3.6" + "source": "https://github.com/symfony/filesystem/tree/v8.0.0" }, "funding": [ { @@ -8163,27 +8049,27 @@ "type": "tidelift" } ], - "time": "2025-11-05T09:52:27+00:00" + "time": "2025-11-05T14:36:47+00:00" }, { "name": "symfony/finder", - "version": "v7.3.5", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "9f696d2f1e340484b4683f7853b273abff94421f" + "reference": "7598dd5770580fa3517ec83e8da0c9b9e01f4291" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/9f696d2f1e340484b4683f7853b273abff94421f", - "reference": "9f696d2f1e340484b4683f7853b273abff94421f", + "url": "https://api.github.com/repos/symfony/finder/zipball/7598dd5770580fa3517ec83e8da0c9b9e01f4291", + "reference": "7598dd5770580fa3517ec83e8da0c9b9e01f4291", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "symfony/filesystem": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8211,7 +8097,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.5" + "source": "https://github.com/symfony/finder/tree/v8.0.0" }, "funding": [ { @@ -8231,24 +8117,24 @@ "type": "tidelift" } ], - "time": "2025-10-15T18:45:57+00:00" + "time": "2025-11-05T14:36:47+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.3.3", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d" + "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d", - "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/d2b592535ffa6600c265a3893a7f7fd2bad82dd7", + "reference": "d2b592535ffa6600c265a3893a7f7fd2bad82dd7", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -8282,7 +8168,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.3" + "source": "https://github.com/symfony/options-resolver/tree/v8.0.0" }, "funding": [ { @@ -8302,7 +8188,7 @@ "type": "tidelift" } ], - "time": "2025-08-05T10:16:07+00:00" + "time": "2025-11-12T15:55:31+00:00" }, { "name": "symfony/polyfill-ctype", @@ -8636,20 +8522,20 @@ }, { "name": "symfony/process", - "version": "v7.3.4", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" + "reference": "a0a750500c4ce900d69ba4e9faf16f82c10ee149" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", + "url": "https://api.github.com/repos/symfony/process/zipball/a0a750500c4ce900d69ba4e9faf16f82c10ee149", + "reference": "a0a750500c4ce900d69ba4e9faf16f82c10ee149", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "type": "library", "autoload": { @@ -8677,7 +8563,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.3.4" + "source": "https://github.com/symfony/process/tree/v8.0.0" }, "funding": [ { @@ -8697,38 +8583,38 @@ "type": "tidelift" } ], - "time": "2025-09-11T10:12:26+00:00" + "time": "2025-10-16T16:25:44+00:00" }, { "name": "symfony/string", - "version": "v7.3.4", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f96476035142921000338bad71e5247fbc138872" + "reference": "f929eccf09531078c243df72398560e32fa4cf4f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", - "reference": "f96476035142921000338bad71e5247fbc138872", + "url": "https://api.github.com/repos/symfony/string/zipball/f929eccf09531078c243df72398560e32fa4cf4f", + "reference": "f929eccf09531078c243df72398560e32fa4cf4f", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.1", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -8767,7 +8653,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.4" + "source": "https://github.com/symfony/string/tree/v8.0.0" }, "funding": [ { @@ -8787,7 +8673,7 @@ "type": "tidelift" } ], - "time": "2025-09-11T14:36:48+00:00" + "time": "2025-09-11T14:37:55+00:00" }, { "name": "textalk/websocket", From 6f0c4d6c4ed4558a05885d93a2bc6e5174d871aa Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Fri, 28 Nov 2025 12:20:13 +0530 Subject: [PATCH 07/11] fix limit & offset computation --- app/controllers/api/vcs.php | 8 ++-- .../e2e/Services/VCS/VCSConsoleClientTest.php | 37 +++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index fc68a631f1..8d2f7832cb 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -1057,11 +1057,11 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories') $github->initializeVariables($providerInstallationId, $privateKey, $githubAppId); $queries = Query::parseQueries($queries); - $limitQuery = array_filter($queries, fn ($query) => $query->getMethod() === Query::TYPE_LIMIT); - $offsetQuery = array_filter($queries, fn ($query) => $query->getMethod() === Query::TYPE_OFFSET); + $limitQuery = current(array_filter($queries, fn ($query) => $query->getMethod() === Query::TYPE_LIMIT)); + $offsetQuery = current(array_filter($queries, fn ($query) => $query->getMethod() === Query::TYPE_OFFSET)); - $limit = !empty($limitQuery) ? $limitQuery[0]->getValue() : 4; - $offset = !empty($offsetQuery) ? $offsetQuery[0]->getValue() : 0; + $limit = !empty($limitQuery) ? $limitQuery->getValue() : 4; + $offset = !empty($offsetQuery) ? $offsetQuery->getValue() : 0; $page = ($offset / $limit) + 1; $owner = $github->getOwnerName($providerInstallationId); diff --git a/tests/e2e/Services/VCS/VCSConsoleClientTest.php b/tests/e2e/Services/VCS/VCSConsoleClientTest.php index 13c3ddb251..55c8559c25 100644 --- a/tests/e2e/Services/VCS/VCSConsoleClientTest.php +++ b/tests/e2e/Services/VCS/VCSConsoleClientTest.php @@ -316,6 +316,43 @@ class VCSConsoleClientTest extends Scope $this->assertEquals($searchedRepositories['body']['runtimeProviderRepositories'][0]['name'], 'appwrite'); $this->assertEquals($searchedRepositories['body']['runtimeProviderRepositories'][0]['runtime'], 'other'); + // with limit and offset + $repositories = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'type' => 'runtime', + 'limit' => 1, + 'offset' => 0 + ]); + $this->assertSame(200, $repositories['headers']['status-code']); + $this->assertSame(4, $repositories['body']['total']); + $this->assertSame(1, \count($repositories['body']['runtimeProviderRepositories'])); + $this->assertSame('starter-for-svelte', $repositories['body']['runtimeProviderRepositories'][0]['name']); + + $repositories = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'type' => 'runtime', + 'limit' => 2, + 'offset' => 1 + ]); + $this->assertSame(200, $repositories['headers']['status-code']); + $this->assertSame(4, $repositories['body']['total']); + $this->assertSame(2, \count($repositories['body']['runtimeProviderRepositories'])); + $this->assertSame('templates-for-functions', $repositories['body']['runtimeProviderRepositories'][0]['name']); + $this->assertSame('appwrite', $repositories['body']['runtimeProviderRepositories'][1]['name']); + + $repositories = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'type' => 'runtime', + 'limit' => 2, + 'offset' => 100 + ]); + $this->assertSame(200, $repositories['headers']['status-code']); + $this->assertSame(4, $repositories['body']['total']); + $this->assertSame(0, \count($repositories['body']['runtimeProviderRepositories'])); + // TODO: If you are about to add another check, rewrite this to @provideScenarios /** From 128478ed000f9d68cf1d687a7e88158e88bc875f Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Fri, 28 Nov 2025 12:54:34 +0530 Subject: [PATCH 08/11] coderabbit feedback --- .../e2e/Services/VCS/VCSConsoleClientTest.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/e2e/Services/VCS/VCSConsoleClientTest.php b/tests/e2e/Services/VCS/VCSConsoleClientTest.php index 55c8559c25..4e076356e8 100644 --- a/tests/e2e/Services/VCS/VCSConsoleClientTest.php +++ b/tests/e2e/Services/VCS/VCSConsoleClientTest.php @@ -10,6 +10,7 @@ use Utopia\Cache\Adapter\None; use Utopia\Cache\Cache; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; +use Utopia\Database\Query; use Utopia\System\System; use Utopia\VCS\Adapter\Git\GitHub; @@ -321,24 +322,24 @@ class VCSConsoleClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'type' => 'runtime', - 'limit' => 1, - 'offset' => 0 + 'limit' => Query::limit(1)->toString(), + 'offset' => Query::offset(0)->toString() ]); $this->assertSame(200, $repositories['headers']['status-code']); $this->assertSame(4, $repositories['body']['total']); - $this->assertSame(1, \count($repositories['body']['runtimeProviderRepositories'])); + $this->assertCount(1, $repositories['body']['runtimeProviderRepositories']); $this->assertSame('starter-for-svelte', $repositories['body']['runtimeProviderRepositories'][0]['name']); $repositories = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories', array_merge([ 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'type' => 'runtime', - 'limit' => 2, - 'offset' => 1 + 'limit' => Query::limit(2)->toString(), + 'offset' => Query::offset(1)->toString() ]); $this->assertSame(200, $repositories['headers']['status-code']); $this->assertSame(4, $repositories['body']['total']); - $this->assertSame(2, \count($repositories['body']['runtimeProviderRepositories'])); + $this->assertCount(2, $repositories['body']['runtimeProviderRepositories']); $this->assertSame('templates-for-functions', $repositories['body']['runtimeProviderRepositories'][0]['name']); $this->assertSame('appwrite', $repositories['body']['runtimeProviderRepositories'][1]['name']); @@ -346,12 +347,12 @@ class VCSConsoleClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'type' => 'runtime', - 'limit' => 2, - 'offset' => 100 + 'limit' => Query::limit(2)->toString(), + 'offset' => Query::offset(100)->toString() ]); $this->assertSame(200, $repositories['headers']['status-code']); $this->assertSame(4, $repositories['body']['total']); - $this->assertSame(0, \count($repositories['body']['runtimeProviderRepositories'])); + $this->assertCount(0, $repositories['body']['runtimeProviderRepositories']); // TODO: If you are about to add another check, rewrite this to @provideScenarios From e204419b8b9d7970ddebc6e8b89430fc65719955 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Fri, 28 Nov 2025 15:33:32 +0530 Subject: [PATCH 09/11] handle invalid offset --- app/controllers/api/vcs.php | 6 +++++- tests/e2e/Services/VCS/VCSConsoleClientTest.php | 17 ++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 8d2f7832cb..01a0817b55 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -1062,8 +1062,12 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories') $limit = !empty($limitQuery) ? $limitQuery->getValue() : 4; $offset = !empty($offsetQuery) ? $offsetQuery->getValue() : 0; - $page = ($offset / $limit) + 1; + if ($offset % $limit !== 0) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'offset must be a multiple of the limit'); + } + + $page = ($offset / $limit) + 1; $owner = $github->getOwnerName($providerInstallationId); ['items' => $repos, 'total' => $total] = $github->searchRepositories($owner, $page, $limit, $search); diff --git a/tests/e2e/Services/VCS/VCSConsoleClientTest.php b/tests/e2e/Services/VCS/VCSConsoleClientTest.php index 4e076356e8..963aa5a84b 100644 --- a/tests/e2e/Services/VCS/VCSConsoleClientTest.php +++ b/tests/e2e/Services/VCS/VCSConsoleClientTest.php @@ -335,13 +335,13 @@ class VCSConsoleClientTest extends Scope ], $this->getHeaders()), [ 'type' => 'runtime', 'limit' => Query::limit(2)->toString(), - 'offset' => Query::offset(1)->toString() + 'offset' => Query::offset(2)->toString() ]); $this->assertSame(200, $repositories['headers']['status-code']); $this->assertSame(4, $repositories['body']['total']); $this->assertCount(2, $repositories['body']['runtimeProviderRepositories']); - $this->assertSame('templates-for-functions', $repositories['body']['runtimeProviderRepositories'][0]['name']); - $this->assertSame('appwrite', $repositories['body']['runtimeProviderRepositories'][1]['name']); + $this->assertSame('appwrite', $repositories['body']['runtimeProviderRepositories'][0]['name']); + $this->assertSame('ruby-starter', $repositories['body']['runtimeProviderRepositories'][1]['name']); $repositories = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories', array_merge([ 'x-appwrite-project' => $this->getProject()['$id'], @@ -376,6 +376,17 @@ class VCSConsoleClientTest extends Scope $this->assertEquals(400, $repositories['headers']['status-code']); + // invalid offset + $repositories = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories', array_merge([ + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'type' => 'runtime', + 'limit' => Query::limit(2)->toString(), + 'offset' => Query::offset(1)->toString() + ]); + $this->assertEquals(400, $repositories['headers']['status-code']); + $this->assertEquals('offset must be a multiple of the limit', $repositories['body']['message']); + $repositories = $this->client->call(Client::METHOD_GET, '/vcs/github/installations/' . $installationId . '/providerRepositories', array_merge([ 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ From 0b5d67a77aad0986da04436f5b6a3497fd99289d Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Fri, 28 Nov 2025 15:43:31 +0530 Subject: [PATCH 10/11] update vcs version --- composer.json | 10 ++-------- composer.lock | 49 +++++++++++-------------------------------------- 2 files changed, 13 insertions(+), 46 deletions(-) diff --git a/composer.json b/composer.json index e5ecbd205f..25d3c06c8d 100644 --- a/composer.json +++ b/composer.json @@ -75,7 +75,7 @@ "utopia-php/swoole": "0.8.*", "utopia-php/system": "0.9.*", "utopia-php/telemetry": "0.1.*", - "utopia-php/vcs": "dev-ser-504 as 0.12.99", + "utopia-php/vcs": "0.13.*", "utopia-php/websocket": "0.3.*", "matomo/device-detector": "6.4.*", "dragonmantank/cron-expression": "3.4.*", @@ -108,11 +108,5 @@ "php-http/discovery": true, "tbachert/spi": true } - }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/utopia-php/vcs" - } - ] + } } diff --git a/composer.lock b/composer.lock index bfa5684972..4dccb29a1a 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": "ef3686f6ddeabf05dab9f1322b6e754f", + "content-hash": "0cad126c9b41c0d496462ba03ff36d7b", "packages": [ { "name": "adhocore/jwt", @@ -5212,16 +5212,16 @@ }, { "name": "utopia-php/vcs", - "version": "dev-ser-504", + "version": "0.13.0", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "089a340a1b0e58d589b86b3935d013108db10ccd" + "reference": "c59e21db5ca42014fe2071fec3c2f814efcc86dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/089a340a1b0e58d589b86b3935d013108db10ccd", - "reference": "089a340a1b0e58d589b86b3935d013108db10ccd", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/c59e21db5ca42014fe2071fec3c2f814efcc86dd", + "reference": "c59e21db5ca42014fe2071fec3c2f814efcc86dd", "shasum": "" }, "require": { @@ -5242,25 +5242,7 @@ "Utopia\\VCS\\": "src/VCS" } }, - "autoload-dev": { - "psr-4": { - "Utopia\\Tests\\": "tests/VCS" - } - }, - "scripts": { - "lint": [ - "./vendor/bin/pint --test --config pint.json" - ], - "format": [ - "./vendor/bin/pint --config pint.json" - ], - "check": [ - "./vendor/bin/phpstan analyse --level 8 -c phpstan.neon src tests" - ], - "test": [ - "./vendor/bin/phpunit --configuration phpunit.xml --debug" - ] - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -5272,10 +5254,10 @@ "vcs" ], "support": { - "source": "https://github.com/utopia-php/vcs/tree/ser-504", - "issues": "https://github.com/utopia-php/vcs/issues" + "issues": "https://github.com/utopia-php/vcs/issues", + "source": "https://github.com/utopia-php/vcs/tree/0.13.0" }, - "time": "2025-11-25T13:52:52+00:00" + "time": "2025-11-28T08:42:31+00:00" }, { "name": "utopia-php/websocket", @@ -8958,18 +8940,9 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [ - { - "package": "utopia-php/vcs", - "version": "dev-ser-504", - "alias": "0.12.99", - "alias_normalized": "0.12.99.0" - } - ], + "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "utopia-php/vcs": 20 - }, + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { From 27b8b23c07543185587192535a911dd292721090 Mon Sep 17 00:00:00 2001 From: Darshan Date: Sat, 29 Nov 2025 17:49:52 +0530 Subject: [PATCH 11/11] fix: wrong path. --- .../Modules/Account/Http/Account/MFA/Challenges/Create.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php index 08d5e05454..10230df7af 100644 --- a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php +++ b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php @@ -144,7 +144,8 @@ class Create extends Action $challenge = $dbForProject->createDocument('challenges', $challenge); - $templatesPath = \dirname(__DIR__, 7) . '/app/config/locale/templates'; + // 9 levels up to project root + $templatesPath = \dirname(__DIR__, 9) . '/app/config/locale/templates'; switch ($factor) { case Type::PHONE: