diff --git a/.env b/.env index b8610c71ca..1bac2f1137 100644 --- a/.env +++ b/.env @@ -56,7 +56,7 @@ _APP_SMTP_PORT=1025 _APP_SMTP_SECURE= _APP_SMTP_USERNAME= _APP_SMTP_PASSWORD= -_APP_SMS_PROVIDER=sms://mock +_APP_SMS_PROVIDER=sms://username:password@mock _APP_SMS_FROM=+123456789 _APP_STORAGE_LIMIT=30000000 _APP_STORAGE_PREVIEW_LIMIT=20000000 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5f75c1d8fa..d99c29999a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,7 +4,7 @@ on: [pull_request] jobs: tests: name: Unit & E2E - runs-on: self-hosted + runs-on: ubuntu-latest steps: - name: Checkout repository @@ -23,13 +23,14 @@ jobs: # Upstream bug causes buildkit pulls to fail so prefetch base images # https://github.com/moby/moby/issues/41864 run: | - echo "_APP_FUNCTIONS_RUNTIMES=php-8.0" >> .env + export COMPOSE_INTERACTIVE_NO_CLI + export DOCKER_BUILDKIT=1 + export COMPOSE_DOCKER_CLI_BUILD=1 + export BUILDKIT_PROGRESS=plain docker pull composer:2.0 - docker pull php:8.0-cli-alpine - docker compose build --progress=plain - docker compose up -d - sleep 15 - + docker compose -f docker-compose.yml -f docker-compose.ci.yml build appwrite + docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d + sleep 30 - name: Doctor run: docker compose exec -T appwrite doctor @@ -38,10 +39,3 @@ jobs: - name: Run Tests run: docker compose exec -T appwrite test --debug - - - name: Teardown - if: always() - run: | - docker ps -aq | xargs docker rm --force || true - docker volume prune --force || true - docker network prune --force || true \ No newline at end of file diff --git a/CHANGES.md b/CHANGES.md index 68ca4abd17..df303e8555 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,18 @@ +# Version 1.0.2 +## Bugs +- Fixed nullable values in functions variables [#3885](https://github.com/appwrite/appwrite/pull/3885) +- Fixed migration for audit by migrating the `time` attribute [#4038](https://github.com/appwrite/appwrite/pull/4038) +- Fixed default value for creating Boolean Attribute [#4040](https://github.com/appwrite/appwrite/pull/4040) +- Fixed phone authentication code to be hashed in the internal database [#3906](https://github.com/appwrite/appwrite/pull/3906) +- Fixed `/v1/teams/:teamId/memberships/:membershipId` response [#3883](https://github.com/appwrite/appwrite/pull/3883) +- Fixed removing variables when function is deleted [#3884](https://github.com/appwrite/appwrite/pull/3884) +- Fixed scheduled function not being triggered [#3908](https://github.com/appwrite/appwrite/pull/3908) +- Fixed Phone Provider configuration [#3862](https://github.com/appwrite/appwrite/pull/3883) +- Fixed Queries with `0` values [utopia-php/database#194](https://github.com/utopia-php/database/pull/194) + # Version 1.0.1 ## Bugs -- Fixed migration for abuse by migrating the `time` attribute [3839](https://github.com/appwrite/appwrite/pull/3839) +- Fixed migration for abuse by migrating the `time` attribute [#3839](https://github.com/appwrite/appwrite/pull/3839) # Version 1.0.0 ## BREAKING CHANGES diff --git a/README-CN.md b/README-CN.md index 82908f1425..4258552da7 100644 --- a/README-CN.md +++ b/README-CN.md @@ -8,13 +8,16 @@

- + + +[![Hacktoberfest](https://img.shields.io/static/v1?label=hacktoberfest&message=friendly&color=191120&style=flat-square)](https://hacktoberfest.appwrite.io) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord?r=Github) -[![Docker Pulls](https://img.shields.io/docker/pulls/appwrite/appwrite?color=f02e65&style=flat-square)](https://hub.docker.com/r/appwrite/appwrite) -[![Build Status](https://img.shields.io/travis/com/appwrite/appwrite?style=flat-square)](https://travis-ci.com/appwrite/appwrite) +[![Build Status](https://img.shields.io/github/workflow/status/appwrite/appwrite/Tests?label=tests&style=flat-square)](https://github.com/appwrite/appwrite/actions) [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) -[![翻译](https://img.shields.io/badge/translate-f02e65?style=flat-square)](docs/tutorials/add-translations.md) -[![周边商店](https://img.shields.io/badge/swag%20store-f02e65?style=flat-square)](https://store.appwrite.io) + + + + [English](README.md) | 简体中文 @@ -59,7 +62,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.0.1 + appwrite/appwrite:1.0.2 ``` ### Windows @@ -71,7 +74,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.0.1 + appwrite/appwrite:1.0.2 ``` #### PowerShell @@ -81,7 +84,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.0.1 + appwrite/appwrite:1.0.2 ``` 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 @@ -128,7 +131,7 @@ docker run -it --rm , #### 服务器 * ✅   [NodeJS](https://github.com/appwrite/sdk-for-node) (由 Appwrite 团队维护) -* ✅   [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwr实验 团队维护) +* ✅   [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwrite 团队维护) * ✅   [Dart](https://github.com/appwrite/sdk-for-dart) - (由 Appwrite 团队维护) * ✅   [Deno](https://github.com/appwrite/sdk-for-deno) - **公测** (由 Appwrite 团队维护) * ✅   [Ruby](https://github.com/appwrite/sdk-for-ruby) (由 Appwrite 团队维护) diff --git a/README.md b/README.md index e4c8b4ca3b..1bd62d61b9 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,17 @@

- + +[![Hacktoberfest](https://img.shields.io/static/v1?label=hacktoberfest&message=ready&color=191120&style=flat-square)](https://hacktoberfest.appwrite.io) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord?r=Github) -[![Docker Pulls](https://img.shields.io/docker/pulls/appwrite/appwrite?color=f02e65&style=flat-square)](https://hub.docker.com/r/appwrite/appwrite) [![Build Status](https://img.shields.io/github/workflow/status/appwrite/appwrite/Tests?label=tests&style=flat-square)](https://github.com/appwrite/appwrite/actions) [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) -[![Translate](https://img.shields.io/badge/translate-f02e65?style=flat-square)](docs/tutorials/add-translations.md) -[![Swag Store](https://img.shields.io/badge/swag%20store-f02e65?style=flat-square)](https://store.appwrite.io) + + + + English | [简体中文](README-CN.md) @@ -31,7 +33,7 @@ Using Appwrite, you can easily integrate your app with user authentication & mul


- Appwrite - 100% open source alternative for Firebase | Product Hunt + Appwrite - 100% open source alternative for Firebase | Product Hunt

@@ -73,7 +75,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.0.1 + appwrite/appwrite:1.0.2 ``` ### Windows @@ -85,17 +87,17 @@ 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.0.1 + appwrite/appwrite:1.0.2 ``` #### PowerShell ```powershell -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.0.1 +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.0.2 ``` Once the Docker installation completes, 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 installation completes. diff --git a/app/config/events.php b/app/config/events.php index 0a7d819142..6fe030d4bf 100644 --- a/app/config/events.php +++ b/app/config/events.php @@ -1,7 +1,7 @@ 'web', 'name' => 'Web', - 'version' => '10.0.0', + 'version' => '10.1.0', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -63,7 +63,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '8.0.0', + 'version' => '8.1.0', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -81,7 +81,7 @@ return [ [ 'key' => 'apple', 'name' => 'Apple', - 'version' => '1.0.0', + 'version' => '1.1.0', 'url' => 'https://github.com/appwrite/sdk-for-apple', 'package' => 'https://github.com/appwrite/sdk-for-apple', 'enabled' => true, @@ -116,7 +116,7 @@ return [ [ 'key' => 'android', 'name' => 'Android', - 'version' => '1.0.0', + 'version' => '1.1.0', 'url' => 'https://github.com/appwrite/sdk-for-android', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android', 'enabled' => true, @@ -162,7 +162,7 @@ return [ [ 'key' => 'web', 'name' => 'Console', - 'version' => '7.0.0', + 'version' => '7.1.0', 'url' => 'https://github.com/appwrite/sdk-for-console', 'package' => '', 'enabled' => true, @@ -180,7 +180,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '1.0.0', + 'version' => '1.1.1', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://www.npmjs.com/package/appwrite-cli', 'enabled' => true, @@ -208,7 +208,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '8.0.0', + 'version' => '8.1.0', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, @@ -226,7 +226,7 @@ return [ [ 'key' => 'deno', 'name' => 'Deno', - 'version' => '6.0.0', + 'version' => '6.1.0', 'url' => 'https://github.com/appwrite/sdk-for-deno', 'package' => 'https://deno.land/x/appwrite', 'enabled' => true, @@ -244,7 +244,7 @@ return [ [ 'key' => 'php', 'name' => 'PHP', - 'version' => '7.0.0', + 'version' => '7.1.0', 'url' => 'https://github.com/appwrite/sdk-for-php', 'package' => 'https://packagist.org/packages/appwrite/appwrite', 'enabled' => true, @@ -262,7 +262,7 @@ return [ [ 'key' => 'python', 'name' => 'Python', - 'version' => '1.0.0', + 'version' => '1.1.0', 'url' => 'https://github.com/appwrite/sdk-for-python', 'package' => 'https://pypi.org/project/appwrite/', 'enabled' => true, @@ -280,7 +280,7 @@ return [ [ 'key' => 'ruby', 'name' => 'Ruby', - 'version' => '7.0.0', + 'version' => '7.1.0', 'url' => 'https://github.com/appwrite/sdk-for-ruby', 'package' => 'https://rubygems.org/gems/appwrite', 'enabled' => true, @@ -298,7 +298,7 @@ return [ [ 'key' => 'go', 'name' => 'Go', - 'version' => '1.0.0', + 'version' => '1.1.0', 'url' => 'https://github.com/appwrite/sdk-for-go', 'package' => '', 'enabled' => false, @@ -316,7 +316,7 @@ return [ [ 'key' => 'java', 'name' => 'Java', - 'version' => '1.0.0', + 'version' => '1.1.0', 'url' => 'https://github.com/appwrite/sdk-for-java', 'package' => '', 'enabled' => false, @@ -334,7 +334,7 @@ return [ [ 'key' => 'dotnet', 'name' => '.NET', - 'version' => '1.0.0', + 'version' => '1.1.0', 'url' => 'https://github.com/appwrite/sdk-for-dotnet', 'package' => 'https://www.nuget.org/packages/Appwrite', 'enabled' => false, @@ -352,7 +352,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '7.0.0', + 'version' => '7.1.0', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, @@ -370,7 +370,7 @@ return [ [ 'key' => 'kotlin', 'name' => 'Kotlin', - 'version' => '1.0.0', + 'version' => '1.1.0', 'url' => 'https://github.com/appwrite/sdk-for-kotlin', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin', 'enabled' => true, @@ -392,7 +392,7 @@ return [ [ 'key' => 'swift', 'name' => 'Swift', - 'version' => '1.0.0', + 'version' => '1.1.0', 'url' => 'https://github.com/appwrite/sdk-for-swift', 'package' => 'https://github.com/appwrite/sdk-for-swift', 'enabled' => true, diff --git a/app/config/providers.php b/app/config/providers.php index 98ad93b30c..62430faae1 100644 --- a/app/config/providers.php +++ b/app/config/providers.php @@ -17,7 +17,7 @@ return [ // Ordered by ABC. 'icon' => 'icon-apple', 'enabled' => true, 'sandbox' => false, - 'form' => 'apple.phtml', // Perperation for adding ability to customized OAuth UI forms, currently handled hardcoded. + 'form' => 'apple.phtml', // Preparation for adding ability to customized OAuth UI forms, currently handled hardcoded. 'beta' => true, 'mock' => false, ], diff --git a/app/config/variables.php b/app/config/variables.php index cba5cdcf6d..09f9ff8dec 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -413,7 +413,7 @@ return [ 'variables' => [ [ 'name' => '_APP_SMS_PROVIDER', - 'description' => "Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'. \n\nAvailable providers are twilio, text-magic and telesign.", + 'description' => "Provider used for delivering SMS for Phone authentication. Use the following format: 'sms://[USER]:[SECRET]@[PROVIDER]'. \n\nAvailable providers are twilio, text-magic, telesign, msg91, and vonage.", 'introduction' => '0.15.0', 'default' => '', 'required' => false, diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index ef94b251c8..fb91a4517d 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -2,7 +2,6 @@ use Ahc\Jwt\JWT; use Appwrite\Auth\Auth; -use Appwrite\SMS\Adapter\Mock; use Appwrite\Auth\Validator\Password; use Appwrite\Auth\Validator\Phone; use Appwrite\Detector\Detector; @@ -930,7 +929,7 @@ App::post('/v1/account/sessions/phone') ]))); } - $secret = (App::getEnv('_APP_SMS_PROVIDER') === 'sms://mock') ? Mock::$digits : Auth::codeGenerator(); + $secret = Auth::codeGenerator(); $expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_PHONE); $token = new Document([ @@ -938,7 +937,7 @@ App::post('/v1/account/sessions/phone') 'userId' => $user->getId(), 'userInternalId' => $user->getInternalId(), 'type' => Auth::TOKEN_TYPE_PHONE, - 'secret' => $secret, + 'secret' => Auth::hash($secret), 'expire' => $expire, 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), @@ -1418,7 +1417,7 @@ App::get('/v1/account/sessions/:sessionId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_SESSION) - ->param('sessionId', null, new UID(), 'Session ID. Use the string \'current\' to get the current device session.') + ->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to get the current device session.') ->inject('response') ->inject('user') ->inject('locale') @@ -1696,7 +1695,7 @@ App::delete('/v1/account/sessions/:sessionId') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) ->label('abuse-limit', 100) - ->param('sessionId', null, new UID(), 'Session ID. Use the string \'current\' to delete the current device session.') + ->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to delete the current device session.') ->inject('request') ->inject('response') ->inject('user') @@ -1769,7 +1768,7 @@ App::patch('/v1/account/sessions/:sessionId') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_SESSION) ->label('abuse-limit', 10) - ->param('sessionId', null, new UID(), 'Session ID. Use the string \'current\' to update the current device session.') + ->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to update the current device session.') ->inject('request') ->inject('response') ->inject('user') @@ -2258,7 +2257,7 @@ App::post('/v1/account/verification/phone') $isPrivilegedUser = Auth::isPrivilegedUser($roles); $isAppUser = Auth::isAppUser($roles); $verificationSecret = Auth::tokenGenerator(); - $secret = (App::getEnv('_APP_SMS_PROVIDER') === 'sms://mock') ? Mock::$digits : Auth::codeGenerator(); + $secret = Auth::codeGenerator(); $expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM); $verification = new Document([ @@ -2266,7 +2265,7 @@ App::post('/v1/account/verification/phone') 'userId' => $user->getId(), 'userInternalId' => $user->getInternalId(), 'type' => Auth::TOKEN_TYPE_PHONE, - 'secret' => $secret, + 'secret' => Auth::hash($secret), 'expire' => $expire, 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index a2ddf9a5bc..ffe6c10b84 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -88,11 +88,11 @@ function createAttribute(string $databaseId, string $collectionId, Document $att } // Must throw here since dbForProject->createAttribute is performed by db worker - if ($required && $default) { + if ($required && isset($default)) { throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required attribute'); } - if ($array && $default) { + if ($array && isset($default)) { throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array attributes'); } @@ -1781,7 +1781,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('key', '', new Key(), 'Index Key.') ->inject('response') ->inject('dbForProject') @@ -1854,7 +1854,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->label('sdk.response.model', Response::MODEL_DOCUMENT) ->param('databaseId', '', new UID(), 'Database ID.') ->param('documentId', '', new CustomId(), 'Document ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents.') ->param('data', [], new JSON(), 'Document data as JSON object.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default the current user is granted with all permissions. [Learn more about permissions](/docs/permissions).', true) ->inject('response') @@ -2072,8 +2072,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_DOCUMENT) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('documentId', null, new UID(), 'Document ID.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('documentId', '', new UID(), 'Document ID.') ->inject('response') ->inject('dbForProject') ->inject('mode') @@ -2135,7 +2135,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ->label('sdk.response.model', Response::MODEL_LOG_LIST) ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID.') - ->param('documentId', null, new UID(), 'Document ID.') + ->param('documentId', '', new UID(), 'Document ID.') ->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/databases#querying-documents). Only supported methods are limit and offset', true) ->inject('response') ->inject('dbForProject') @@ -2241,8 +2241,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_DOCUMENT) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', null, new UID(), 'Collection ID.') - ->param('documentId', null, new UID(), 'Document ID.') + ->param('collectionId', '', new UID(), 'Collection ID.') + ->param('documentId', '', new UID(), 'Document ID.') ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default the current permissions are inherited. [Learn more about permissions](/docs/permissions).', true) ->inject('response') @@ -2374,8 +2374,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) ->param('databaseId', '', new UID(), 'Database ID.') - ->param('collectionId', null, new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') - ->param('documentId', null, new UID(), 'Document ID.') + ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') + ->param('documentId', '', new UID(), 'Document ID.') ->inject('response') ->inject('dbForProject') ->inject('events') @@ -2532,7 +2532,7 @@ App::get('/v1/databases/usage') }; $stats[$metric][] = [ 'value' => 0, - 'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), ]; $backfill--; } @@ -2647,7 +2647,7 @@ App::get('/v1/databases/:databaseId/usage') }; $stats[$metric][] = [ 'value' => 0, - 'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), ]; $backfill--; } @@ -2762,7 +2762,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage') }; $stats[$metric][] = [ 'value' => 0, - 'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), ]; $backfill--; } diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index f42d1059e0..d3a26b414e 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -71,8 +71,13 @@ App::post('/v1/functions') ->param('enabled', true, new Boolean(), 'Is function enabled?', true) ->inject('response') ->inject('dbForProject') + ->inject('project') + ->inject('user') ->inject('events') - ->action(function (string $functionId, string $name, array $execute, string $runtime, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Event $eventsInstance) { + ->action(function (string $functionId, string $name, array $execute, string $runtime, array $events, string $schedule, int $timeout, bool $enabled, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance) { + + $cron = !empty($schedule) ? new CronExpression($schedule) : null; + $next = !empty($schedule) ? DateTime::format($cron->getNextRunDate()) : null; $functionId = ($functionId == 'unique()') ? ID::unique() : $functionId; $function = $dbForProject->createDocument('functions', new Document([ @@ -86,11 +91,22 @@ App::post('/v1/functions') 'schedule' => $schedule, 'scheduleUpdatedAt' => DateTime::now(), 'schedulePrevious' => null, - 'scheduleNext' => null, + 'scheduleNext' => $next, 'timeout' => $timeout, 'search' => implode(' ', [$functionId, $name, $runtime]) ])); + if ($next) { + // Async task reschedule + $functionEvent = new Func(); + $functionEvent + ->setFunction($function) + ->setType('schedule') + ->setUser($user) + ->setProject($project) + ->schedule(new \DateTime($next)); + } + $eventsInstance->setParam('functionId', $function->getId()); $response @@ -281,7 +297,7 @@ App::get('/v1/functions/:functionId/usage') }; $stats[$metric][] = [ 'value' => 0, - 'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), ]; $backfill--; } @@ -384,7 +400,7 @@ App::get('/v1/functions/usage') }; $stats[$metric][] = [ 'value' => 0, - 'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), ]; $backfill--; } @@ -442,11 +458,9 @@ App::put('/v1/functions/:functionId') throw new Exception(Exception::FUNCTION_NOT_FOUND); } - $original = $function->getAttribute('schedule', ''); - $cron = (!empty($function->getAttribute('deployment')) && !empty($schedule)) ? new CronExpression($schedule) : null; - $next = (!empty($function->getAttribute('deployment')) && !empty($schedule)) ? DateTime::format($cron->getNextRunDate()) : null; + $cron = !empty($schedule) ? new CronExpression($schedule) : null; + $next = !empty($schedule) ? DateTime::format($cron->getNextRunDate()) : null; - $scheduleUpdatedAt = $schedule !== $original ? DateTime::now() : $function->getAttribute('scheduleUpdatedAt'); $enabled ??= $function->getAttribute('enabled', true); $function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [ @@ -454,14 +468,14 @@ App::put('/v1/functions/:functionId') 'name' => $name, 'events' => $events, 'schedule' => $schedule, - 'scheduleUpdatedAt' => $scheduleUpdatedAt, + 'scheduleUpdatedAt' => DateTime::now(), 'scheduleNext' => $next, 'timeout' => $timeout, 'enabled' => $enabled, 'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]), ]))); - if ($next && $schedule !== $original) { + if ($next) { // Async task reschedule $functionEvent = new Func(); $functionEvent @@ -519,24 +533,10 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId') throw new Exception(Exception::BUILD_NOT_READY); } - $schedule = $function->getAttribute('schedule', ''); - $cron = (empty($function->getAttribute('deployment')) && !empty($schedule)) ? new CronExpression($schedule) : null; - $next = (empty($function->getAttribute('deployment')) && !empty($schedule)) ? DateTime::format($cron->getNextRunDate()) : null; - $function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [ - 'deployment' => $deployment->getId(), - 'scheduleNext' => $next, + 'deployment' => $deployment->getId() ]))); - if ($next) { // Init first schedule - $functionEvent = new Func(); - $functionEvent - ->setType('schedule') - ->setFunction($function) - ->setProject($project) - ->schedule(new \DateTime($next)); - } - $events ->setParam('functionId', $function->getId()) ->setParam('deploymentId', $deployment->getId()); @@ -935,6 +935,67 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId') $response->noContent(); }); +App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') + ->groups(['api', 'functions']) + ->desc('Create Build') + ->label('scope', 'functions.write') + ->label('event', 'functions.[functionId].deployments.[deploymentId].update') + ->label('audits.event', 'deployment.update') + ->label('audits.resource', 'function/{request.functionId}') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'functions') + ->label('sdk.method', 'createBuild') + ->label('sdk.description', '/docs/references/functions/create-build.md') + ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) + ->label('sdk.response.model', Response::MODEL_NONE) + ->param('functionId', '', new UID(), 'Function ID.') + ->param('deploymentId', '', new UID(), 'Deployment ID.') + ->param('buildId', '', new UID(), 'Build unique ID.') + ->inject('response') + ->inject('dbForProject') + ->inject('project') + ->inject('events') + ->action(function (string $functionId, string $deploymentId, string $buildId, Response $response, Database $dbForProject, Document $project, Event $events) { + + $function = $dbForProject->getDocument('functions', $functionId); + $deployment = $dbForProject->getDocument('deployments', $deploymentId); + + if ($function->isEmpty()) { + throw new Exception(Exception::FUNCTION_NOT_FOUND); + } + + if ($deployment->isEmpty()) { + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); + } + + $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $buildId)); + + if ($build->isEmpty()) { + throw new Exception(Exception::BUILD_NOT_FOUND); + } + + if ($build->getAttribute('status') !== 'failed') { + throw new Exception(Exception::BUILD_IN_PROGRESS, 'Build not failed'); + } + + $events + ->setParam('functionId', $function->getId()) + ->setParam('deploymentId', $deployment->getId()); + + // Retry the build + $buildEvent = new Build(); + $buildEvent + ->setType(BUILD_TYPE_RETRY) + ->setResource($function) + ->setDeployment($deployment) + ->setProject($project) + ->trigger(); + + $response->noContent(); + }); + + + App::post('/v1/functions/:functionId/executions') ->groups(['api', 'functions']) ->desc('Create Execution') @@ -1066,21 +1127,21 @@ App::post('/v1/functions/:functionId/executions') } $vars = array_reduce($function['vars'] ?? [], function (array $carry, Document $var) { - $carry[$var->getAttribute('key')] = $var->getAttribute('value'); + $carry[$var->getAttribute('key')] = $var->getAttribute('value') ?? ''; return $carry; }, []); $vars = \array_merge($vars, [ 'APPWRITE_FUNCTION_ID' => $function->getId(), - 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name', ''), + 'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'), 'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(), - 'APPWRITE_FUNCTION_TRIGGER' => 'http', - 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'], - 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'], - 'APPWRITE_FUNCTION_DATA' => $data, 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), - 'APPWRITE_FUNCTION_USER_ID' => $user->getId(), - 'APPWRITE_FUNCTION_JWT' => $jwt, + 'APPWRITE_FUNCTION_TRIGGER' => 'http', + 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', + 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', + 'APPWRITE_FUNCTION_DATA' => $data ?? '', + 'APPWRITE_FUNCTION_USER_ID' => $user->getId() ?? '', + 'APPWRITE_FUNCTION_JWT' => $jwt ?? '', ]); /** Execute function */ @@ -1258,65 +1319,6 @@ App::get('/v1/functions/:functionId/executions/:executionId') $response->dynamic($execution, Response::MODEL_EXECUTION); }); -App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') - ->groups(['api', 'functions']) - ->desc('Retry Build') - ->label('scope', 'functions.write') - ->label('event', 'functions.[functionId].deployments.[deploymentId].update') - ->label('audits.event', 'deployment.update') - ->label('audits.resource', 'function/{request.functionId}') - ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) - ->label('sdk.namespace', 'functions') - ->label('sdk.method', 'retryBuild') - ->label('sdk.description', '/docs/references/functions/retry-build.md') - ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) - ->label('sdk.response.model', Response::MODEL_NONE) - ->param('functionId', '', new UID(), 'Function ID.') - ->param('deploymentId', '', new UID(), 'Deployment ID.') - ->param('buildId', '', new UID(), 'Build unique ID.') - ->inject('response') - ->inject('dbForProject') - ->inject('project') - ->inject('events') - ->action(function (string $functionId, string $deploymentId, string $buildId, Response $response, Database $dbForProject, Document $project, Event $events) { - - $function = $dbForProject->getDocument('functions', $functionId); - $deployment = $dbForProject->getDocument('deployments', $deploymentId); - - if ($function->isEmpty()) { - throw new Exception(Exception::FUNCTION_NOT_FOUND); - } - - if ($deployment->isEmpty()) { - throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); - } - - $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $buildId)); - - if ($build->isEmpty()) { - throw new Exception(Exception::BUILD_NOT_FOUND); - } - - if ($build->getAttribute('status') !== 'failed') { - throw new Exception(Exception::BUILD_IN_PROGRESS, 'Build not failed'); - } - - $events - ->setParam('functionId', $function->getId()) - ->setParam('deploymentId', $deployment->getId()); - - // Retry the build - $buildEvent = new Build(); - $buildEvent - ->setType(BUILD_TYPE_RETRY) - ->setResource($function) - ->setDeployment($deployment) - ->setProject($project) - ->trigger(); - - $response->noContent(); - }); - // Variables App::post('/v1/functions/:functionId/variables') @@ -1332,7 +1334,7 @@ App::post('/v1/functions/:functionId/variables') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_VARIABLE) - ->param('functionId', null, new UID(), 'Function unique ID.', false) + ->param('functionId', '', new UID(), 'Function unique ID.', false) ->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false) ->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', false) ->inject('response') @@ -1384,7 +1386,7 @@ App::get('/v1/functions/:functionId/variables') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_VARIABLE_LIST) - ->param('functionId', null, new UID(), 'Function unique ID.', false) + ->param('functionId', '', new UID(), 'Function unique ID.', false) ->inject('response') ->inject('dbForProject') ->action(function (string $functionId, Response $response, Database $dbForProject) { @@ -1411,8 +1413,8 @@ App::get('/v1/functions/:functionId/variables/:variableId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_VARIABLE) - ->param('functionId', null, new UID(), 'Function unique ID.', false) - ->param('variableId', null, new UID(), 'Variable unique ID.', false) + ->param('functionId', '', new UID(), 'Function unique ID.', false) + ->param('variableId', '', new UID(), 'Variable unique ID.', false) ->inject('response') ->inject('dbForProject') ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject) { @@ -1447,8 +1449,8 @@ App::put('/v1/functions/:functionId/variables/:variableId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_VARIABLE) - ->param('functionId', null, new UID(), 'Function unique ID.', false) - ->param('variableId', null, new UID(), 'Variable unique ID.', false) + ->param('functionId', '', new UID(), 'Function unique ID.', false) + ->param('variableId', '', new UID(), 'Variable unique ID.', false) ->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false) ->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', true) ->inject('response') @@ -1499,8 +1501,8 @@ App::delete('/v1/functions/:functionId/variables/:variableId') ->label('sdk.description', '/docs/references/functions/delete-variable.md') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) - ->param('functionId', null, new UID(), 'Function unique ID.', false) - ->param('variableId', null, new UID(), 'Variable unique ID.', false) + ->param('functionId', '', new UID(), 'Function unique ID.', false) + ->param('variableId', '', new UID(), 'Variable unique ID.', false) ->inject('response') ->inject('dbForProject') ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject) { diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 5c8ba8a268..bfea863d29 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -330,7 +330,7 @@ App::get('/v1/projects/:projectId/usage') }; $stats[$metric][] = [ 'value' => 0, - 'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), ]; $backfill--; } @@ -584,7 +584,7 @@ App::post('/v1/projects/:projectId/webhooks') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_WEBHOOK) - ->param('projectId', null, new UID(), 'Project unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') ->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.') ->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') ->param('url', null, new URL(['http', 'https']), 'Webhook URL.') @@ -672,8 +672,8 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_WEBHOOK) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('webhookId', null, new UID(), 'Webhook unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('webhookId', '', new UID(), 'Webhook unique ID.') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { @@ -706,8 +706,8 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_WEBHOOK) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('webhookId', null, new UID(), 'Webhook unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('webhookId', '', new UID(), 'Webhook unique ID.') ->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.') ->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') ->param('url', null, new URL(['http', 'https']), 'Webhook URL.') @@ -760,8 +760,8 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_WEBHOOK) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('webhookId', null, new UID(), 'Webhook unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('webhookId', '', new UID(), 'Webhook unique ID.') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { @@ -798,8 +798,8 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') ->label('sdk.method', 'deleteWebhook') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('webhookId', null, new UID(), 'Webhook unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('webhookId', '', new UID(), 'Webhook unique ID.') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { @@ -838,7 +838,7 @@ App::post('/v1/projects/:projectId/keys') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_KEY) - ->param('projectId', null, new UID(), 'Project unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') ->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.') ->param('expire', null, new DatetimeValidator(), 'Expiration time in ISO 8601 format. Use null for unlimited expiration.', true) @@ -888,7 +888,7 @@ App::get('/v1/projects/:projectId/keys') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_KEY_LIST) - ->param('projectId', null, new UID(), 'Project unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, Response $response, Database $dbForConsole) { @@ -920,8 +920,8 @@ App::get('/v1/projects/:projectId/keys/:keyId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_KEY) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('keyId', null, new UID(), 'Key unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('keyId', '', new UID(), 'Key unique ID.') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { @@ -954,8 +954,8 @@ App::put('/v1/projects/:projectId/keys/:keyId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_KEY) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('keyId', null, new UID(), 'Key unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('keyId', '', new UID(), 'Key unique ID.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') ->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') ->param('expire', null, new DatetimeValidator(), 'Expiration time in ISO 8601 format. Use null for unlimited expiration.', true) @@ -1000,8 +1000,8 @@ App::delete('/v1/projects/:projectId/keys/:keyId') ->label('sdk.method', 'deleteKey') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('keyId', null, new UID(), 'Key unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('keyId', '', new UID(), 'Key unique ID.') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { @@ -1040,7 +1040,7 @@ App::post('/v1/projects/:projectId/platforms') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PLATFORM) - ->param('projectId', null, new UID(), 'Project unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') ->param('type', null, new WhiteList([Origin::CLIENT_TYPE_WEB, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_LINUX, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_APPLE_IOS, Origin::CLIENT_TYPE_APPLE_MACOS, Origin::CLIENT_TYPE_APPLE_WATCHOS, Origin::CLIENT_TYPE_APPLE_TVOS, Origin::CLIENT_TYPE_ANDROID, Origin::CLIENT_TYPE_UNITY], true), 'Platform type.') ->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.') ->param('key', '', new Text(256), 'Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.', true) @@ -1122,8 +1122,8 @@ App::get('/v1/projects/:projectId/platforms/:platformId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PLATFORM) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('platformId', null, new UID(), 'Platform unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('platformId', '', new UID(), 'Platform unique ID.') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) { @@ -1156,8 +1156,8 @@ App::put('/v1/projects/:projectId/platforms/:platformId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PLATFORM) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('platformId', null, new UID(), 'Platform unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('platformId', '', new UID(), 'Platform unique ID.') ->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.') ->param('key', '', new Text(256), 'Package name for android or bundle ID for iOS. Max length: 256 chars.', true) ->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true) @@ -1203,8 +1203,8 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') ->label('sdk.method', 'deletePlatform') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('platformId', null, new UID(), 'Platform unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('platformId', '', new UID(), 'Platform unique ID.') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) { @@ -1243,7 +1243,7 @@ App::post('/v1/projects/:projectId/domains') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_DOMAIN) - ->param('projectId', null, new UID(), 'Project unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') ->param('domain', null, new DomainValidator(), 'Domain name.') ->inject('response') ->inject('dbForConsole') @@ -1340,8 +1340,8 @@ App::get('/v1/projects/:projectId/domains/:domainId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_DOMAIN) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('domainId', null, new UID(), 'Domain unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('domainId', '', new UID(), 'Domain unique ID.') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) { @@ -1374,8 +1374,8 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_DOMAIN) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('domainId', null, new UID(), 'Domain unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('domainId', '', new UID(), 'Domain unique ID.') ->inject('response') ->inject('dbForConsole') ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) { @@ -1433,8 +1433,8 @@ App::delete('/v1/projects/:projectId/domains/:domainId') ->label('sdk.method', 'deleteDomain') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) - ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('domainId', null, new UID(), 'Domain unique ID.') + ->param('projectId', '', new UID(), 'Project unique ID.') + ->param('domainId', '', new UID(), 'Domain unique ID.') ->inject('response') ->inject('dbForConsole') ->inject('deletes') diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 1134887fb6..f236285749 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -346,7 +346,7 @@ App::post('/v1/storage/buckets/:bucketId/files') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_FILE) - ->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') + ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('fileId', '', new CustomId(), 'File ID. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('file', [], new File(), 'Binary file.', false) ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission strings. By default the current user is granted with all permissions. [Learn more about permissions](/docs/permissions).', true) @@ -667,7 +667,7 @@ App::get('/v1/storage/buckets/:bucketId/files') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_FILE_LIST) - ->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') + ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('queries', [], new Files(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Files::ALLOWED_ATTRIBUTES), true) ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->inject('response') @@ -744,7 +744,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_FILE) - ->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') + ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('fileId', '', new UID(), 'File ID.') ->inject('response') ->inject('dbForProject') @@ -793,7 +793,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE) ->label('sdk.methodType', 'location') - ->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') + ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('fileId', '', new UID(), 'File ID') ->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true) ->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true) @@ -959,7 +959,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', '*/*') ->label('sdk.methodType', 'location') - ->param('bucketId', null, new UID(), 'Storage bucket ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') + ->param('bucketId', '', new UID(), 'Storage bucket ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('fileId', '', new UID(), 'File ID.') ->inject('request') ->inject('response') @@ -1099,7 +1099,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', '*/*') ->label('sdk.methodType', 'location') - ->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') + ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('fileId', '', new UID(), 'File ID.') ->inject('response') ->inject('request') @@ -1256,7 +1256,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_FILE) - ->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') + ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('fileId', '', new UID(), 'File unique ID.') ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission string. By default the current permissions are inherited. [Learn more about permissions](/docs/permissions).', true) ->inject('response') @@ -1358,7 +1358,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->label('sdk.description', '/docs/references/storage/delete-file.md') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) - ->param('bucketId', null, new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') + ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).') ->param('fileId', '', new UID(), 'File ID.') ->inject('response') ->inject('dbForProject') @@ -1518,7 +1518,7 @@ App::get('/v1/storage/usage') }; $stats[$metric][] = [ 'value' => 0, - 'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), ]; $backfill--; } @@ -1629,7 +1629,7 @@ App::get('/v1/storage/:bucketId/usage') }; $stats[$metric][] = [ 'value' => 0, - 'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), ]; $backfill--; } diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 801c647a29..a42b9d317e 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -865,7 +865,7 @@ App::get('/v1/teams/:teamId/logs') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_LOG_LIST) - ->param('teamId', null, new UID(), 'Team ID.') + ->param('teamId', '', new UID(), 'Team ID.') ->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/databases#querying-documents). Only supported methods are limit and offset', true) ->inject('response') ->inject('dbForProject') diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index c83f03d96e..407bc1eaab 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -290,10 +290,10 @@ App::post('/v1/users/scrypt') ->param('email', '', new Email(), 'User email.') ->param('password', '', new Password(), 'User password hashed using Scrypt.') ->param('passwordSalt', '', new Text(128), 'Optional salt used to hash password.') - ->param('passwordCpu', '', new Integer(), 'Optional CPU cost used to hash password.') - ->param('passwordMemory', '', new Integer(), 'Optional memory cost used to hash password.') - ->param('passwordParallel', '', new Integer(), 'Optional parallelization cost used to hash password.') - ->param('passwordLength', '', new Integer(), 'Optional hash length used to hash password.') + ->param('passwordCpu', 8, new Integer(), 'Optional CPU cost used to hash password.') + ->param('passwordMemory', 14, new Integer(), 'Optional memory cost used to hash password.') + ->param('passwordParallel', 1, new Integer(), 'Optional parallelization cost used to hash password.') + ->param('passwordLength', 64, new Integer(), 'Optional hash length used to hash password.') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') ->inject('dbForProject') @@ -981,7 +981,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) ->param('userId', '', new UID(), 'User ID.') - ->param('sessionId', null, new UID(), 'Session ID.') + ->param('sessionId', '', new UID(), 'Session ID.') ->inject('response') ->inject('dbForProject') ->inject('events') @@ -1176,7 +1176,7 @@ App::get('/v1/users/usage') }; $stats[$metric][] = [ 'value' => 0, - 'date' => DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff), + 'date' => DateTime::formatTz(DateTime::addSeconds(new \DateTime($stats[$metric][$last]['date'] ?? null), -1 * $diff)), ]; $backfill--; } diff --git a/app/init.php b/app/init.php index f4f5ade9a2..cbb8e2db7a 100644 --- a/app/init.php +++ b/app/init.php @@ -97,8 +97,8 @@ const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate pe const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours -const APP_CACHE_BUSTER = 500; -const APP_VERSION_STABLE = '1.0.1'; +const APP_CACHE_BUSTER = 501; +const APP_VERSION_STABLE = '1.0.2'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; @@ -1034,7 +1034,7 @@ App::setResource('sms', function () { $secret = $dsn->getPassword(); return match ($dsn->getHost()) { - 'mock' => new Mock('', ''), // used for tests + 'mock' => new Mock($user, $secret), // used for tests 'twilio' => new Twilio($user, $secret), 'text-magic' => new TextMagic($user, $secret), 'telesign' => new Telesign($user, $secret), diff --git a/app/views/console/comps/permissions-matrix.phtml b/app/views/console/comps/permissions-matrix.phtml index 836b0843ad..e912f26629 100644 --- a/app/views/console/comps/permissions-matrix.phtml +++ b/app/views/console/comps/permissions-matrix.phtml @@ -81,6 +81,8 @@ $escapedPermissions = \array_map(function ($perm) { list="types" type="text" x-model="permission.role" + @keydown.enter="prevent($event)" + @keydown="clearPermission(index)" @keyup="updatePermission(index)"/> diff --git a/app/views/console/functions/function.phtml b/app/views/console/functions/function.phtml index 80806fed56..c98e092a0e 100644 --- a/app/views/console/functions/function.phtml +++ b/app/views/console/functions/function.phtml @@ -391,9 +391,9 @@ sort($patterns); Created - Status - Trigger - Runtime + Status + Trigger + Runtime @@ -422,11 +422,11 @@ sort($patterns);
- + - +