diff --git a/.env b/.env index 4d7c038a6b..960405111a 100644 --- a/.env +++ b/.env @@ -103,6 +103,7 @@ _APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000 _APP_MAINTENANCE_RETENTION_SCHEDULES=86400 _APP_USAGE_STATS=enabled _APP_LOGGING_CONFIG= +_APP_LOGGING_CONFIG_REALTIME= _APP_GRAPHQL_MAX_BATCH_SIZE=10 _APP_GRAPHQL_MAX_COMPLEXITY=250 _APP_GRAPHQL_MAX_DEPTH=4 diff --git a/Dockerfile b/Dockerfile index d097edf0ca..d2cb1af9da 100755 --- a/Dockerfile +++ b/Dockerfile @@ -24,9 +24,9 @@ ENV _APP_VERSION=$VERSION \ _APP_HOME=https://appwrite.io RUN \ - if [ "$DEBUG" == "true" ]; then \ + if [ "$DEBUG" == "true" ]; then \ apk add boost boost-dev; \ - fi + fi WORKDIR /usr/src/code diff --git a/app/assets/dbip/dbip-country-lite-2024-09.mmdb b/app/assets/dbip/dbip-country-lite-2024-09.mmdb deleted file mode 100644 index 43d6bcdeea..0000000000 Binary files a/app/assets/dbip/dbip-country-lite-2024-09.mmdb and /dev/null differ diff --git a/app/assets/dbip/dbip-country-lite-2025-12.mmdb b/app/assets/dbip/dbip-country-lite-2025-12.mmdb new file mode 100644 index 0000000000..4ecabbf735 Binary files /dev/null and b/app/assets/dbip/dbip-country-lite-2025-12.mmdb differ diff --git a/app/cli.php b/app/cli.php index 0900926346..7b65ab8fa5 100644 --- a/app/cli.php +++ b/app/cli.php @@ -9,6 +9,7 @@ use Appwrite\Event\StatsResources; use Appwrite\Event\StatsUsage; use Appwrite\Platform\Appwrite; use Appwrite\Runtimes\Runtimes; +use Appwrite\Utopia\Database\Documents\User; use Executor\Executor; use Swoole\Runtime; use Swoole\Timer; @@ -81,6 +82,7 @@ CLI::setResource('dbForPlatform', function ($pools, $cache, $authorization) { ->setNamespace('_console') ->setMetadata('host', \gethostname()) ->setMetadata('project', 'console'); + $dbForPlatform->setDocumentType('users', User::class); // Ensure tables exist $collections = Config::getParam('collections', [])['console']; diff --git a/app/config/collections/common.php b/app/config/collections/common.php index 7bccfb19ce..a364a0a866 100644 --- a/app/config/collections/common.php +++ b/app/config/collections/common.php @@ -1,6 +1,6 @@ 256, 'signed' => true, 'required' => false, - 'default' => Auth::DEFAULT_ALGO, + 'default' => (new Argon2())->getName(), 'array' => false, 'filters' => [], ], @@ -184,7 +184,7 @@ return [ 'size' => 65535, 'signed' => true, 'required' => false, - 'default' => Auth::DEFAULT_ALGO_OPTIONS, + 'default' => (new Argon2())->getOptions(), 'array' => false, 'filters' => ['json'], ], diff --git a/app/config/console.php b/app/config/console.php index f8f68a8039..5c4bf87614 100644 --- a/app/config/console.php +++ b/app/config/console.php @@ -4,7 +4,6 @@ * Initializes console project document. */ -use Appwrite\Auth\Auth; use Appwrite\Network\Platform; use Utopia\Database\Helpers\ID; use Utopia\System\System; @@ -38,7 +37,7 @@ $console = [ 'mockNumbers' => [], 'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled', 'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds + 'duration' => TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds 'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled', 'invalidateSessions' => true ], diff --git a/app/config/frameworks.php b/app/config/frameworks.php index 47e26ac91e..6078c53c63 100644 --- a/app/config/frameworks.php +++ b/app/config/frameworks.php @@ -8,20 +8,13 @@ use Utopia\Config\Config; $templateRuntimes = Config::getParam('template-runtimes'); -function getVersions(array $versions, string $prefix) -{ - return array_map(function ($version) use ($prefix) { - return $prefix . '-' . $version; - }, $versions); -} - return [ 'analog' => [ 'key' => 'analog', 'name' => 'Analog', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'bundleCommand' => 'bash /usr/local/server/helpers/analog/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/analog/env.sh', 'adapters' => [ @@ -47,7 +40,7 @@ return [ 'name' => 'Angular', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'bundleCommand' => 'bash /usr/local/server/helpers/angular/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/angular/env.sh', 'adapters' => [ @@ -73,7 +66,7 @@ return [ 'name' => 'Next.js', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'bundleCommand' => 'bash /usr/local/server/helpers/next-js/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/next-js/env.sh', 'adapters' => [ @@ -98,7 +91,7 @@ return [ 'name' => 'React', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'adapters' => [ 'static' => [ 'key' => 'static', @@ -115,7 +108,7 @@ return [ 'name' => 'Nuxt', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'bundleCommand' => 'bash /usr/local/server/helpers/nuxt/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/nuxt/env.sh', 'adapters' => [ @@ -140,7 +133,7 @@ return [ 'name' => 'Vue.js', 'screenshotSleep' => 5000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'adapters' => [ 'static' => [ 'key' => 'static', @@ -157,7 +150,7 @@ return [ 'name' => 'SvelteKit', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'bundleCommand' => 'bash /usr/local/server/helpers/sveltekit/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/sveltekit/env.sh', 'adapters' => [ @@ -182,7 +175,7 @@ return [ 'name' => 'Astro', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'bundleCommand' => 'bash /usr/local/server/helpers/astro/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/astro/env.sh', 'adapters' => [ @@ -207,7 +200,7 @@ return [ 'name' => 'TanStack Start', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'bundleCommand' => 'bash /usr/local/server/helpers/tanstack-start/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/tanstack-start/env.sh', 'adapters' => [ @@ -215,7 +208,7 @@ return [ 'key' => 'ssr', 'buildCommand' => 'npm run build', 'installCommand' => 'npm install', - 'outputDirectory' => './dist', + 'outputDirectory' => './.output', 'startCommand' => 'bash helpers/tanstack-start/server.sh', ], 'static' => [ @@ -232,7 +225,7 @@ return [ 'name' => 'Remix', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'bundleCommand' => 'bash /usr/local/server/helpers/remix/bundle.sh', 'envCommand' => 'source /usr/local/server/helpers/remix/env.sh', 'adapters' => [ @@ -257,7 +250,7 @@ return [ 'name' => 'Lynx', 'screenshotSleep' => 5000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'adapters' => [ 'static' => [ 'key' => 'static', @@ -274,7 +267,7 @@ return [ 'name' => 'Flutter', 'screenshotSleep' => 5000, 'buildRuntime' => 'flutter-3.35', - 'runtimes' => getVersions($templateRuntimes['FLUTTER']['versions'], 'flutter'), + 'runtimes' => $templateRuntimes['FLUTTER'], 'adapters' => [ 'static' => [ 'key' => 'static', @@ -291,7 +284,7 @@ return [ 'name' => 'React Native', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'adapters' => [ 'static' => [ 'key' => 'static', @@ -308,7 +301,7 @@ return [ 'name' => 'Vite', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'adapters' => [ 'static' => [ 'key' => 'static', @@ -324,7 +317,7 @@ return [ 'name' => 'Other', 'screenshotSleep' => 3000, 'buildRuntime' => 'node-22', - 'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'), + 'runtimes' => $templateRuntimes['NODE'], 'adapters' => [ 'static' => [ 'key' => 'static', diff --git a/app/config/platforms.php b/app/config/platforms.php index 2b5c107648..b8b66efa39 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '21.4.0', + 'version' => '21.5.0', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -60,7 +60,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '20.3.1', + 'version' => '20.3.2', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -79,7 +79,7 @@ return [ [ 'key' => 'apple', 'name' => 'Apple', - 'version' => '13.4.0', + 'version' => '13.5.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' => '11.3.0', + 'version' => '11.4.0', 'url' => 'https://github.com/appwrite/sdk-for-android', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android', 'enabled' => true, @@ -139,7 +139,7 @@ return [ [ 'key' => 'react-native', 'name' => 'React Native', - 'version' => '0.18.0', + 'version' => '0.19.0', 'url' => 'https://github.com/appwrite/sdk-for-react-native', 'package' => 'https://npmjs.com/package/react-native-appwrite', 'enabled' => true, @@ -226,7 +226,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '11.1.1', + 'version' => '12.0.1', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://www.npmjs.com/package/appwrite-cli', 'enabled' => true, @@ -262,7 +262,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '20.3.0', + 'version' => '21.0.0', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, @@ -281,7 +281,7 @@ return [ [ 'key' => 'php', 'name' => 'PHP', - 'version' => '18.0.1', + 'version' => '19.0.0', 'url' => 'https://github.com/appwrite/sdk-for-php', 'package' => 'https://packagist.org/packages/appwrite/appwrite', 'enabled' => true, @@ -300,7 +300,7 @@ return [ [ 'key' => 'python', 'name' => 'Python', - 'version' => '13.6.1', + 'version' => '14.0.0', 'url' => 'https://github.com/appwrite/sdk-for-python', 'package' => 'https://pypi.org/project/appwrite/', 'enabled' => true, @@ -319,7 +319,7 @@ return [ [ 'key' => 'ruby', 'name' => 'Ruby', - 'version' => '19.3.0', + 'version' => '20.0.0', 'url' => 'https://github.com/appwrite/sdk-for-ruby', 'package' => 'https://rubygems.org/gems/appwrite', 'enabled' => true, @@ -338,7 +338,7 @@ return [ [ 'key' => 'go', 'name' => 'Go', - 'version' => 'v0.14.0', + 'version' => 'v0.15.0', 'url' => 'https://github.com/appwrite/sdk-for-go', 'package' => 'https://github.com/appwrite/sdk-for-go', 'enabled' => true, @@ -357,7 +357,7 @@ return [ [ 'key' => 'dotnet', 'name' => '.NET', - 'version' => '0.22.0', + 'version' => '0.23.0', 'url' => 'https://github.com/appwrite/sdk-for-dotnet', 'package' => 'https://www.nuget.org/packages/Appwrite', 'enabled' => true, @@ -376,7 +376,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '19.4.0', + 'version' => '20.0.0', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, @@ -395,7 +395,7 @@ return [ [ 'key' => 'kotlin', 'name' => 'Kotlin', - 'version' => '12.3.0', + 'version' => '13.0.0', 'url' => 'https://github.com/appwrite/sdk-for-kotlin', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin', 'enabled' => true, @@ -418,7 +418,7 @@ return [ [ 'key' => 'swift', 'name' => 'Swift', - 'version' => '13.3.0', + 'version' => '14.0.0', 'url' => 'https://github.com/appwrite/sdk-for-swift', 'package' => 'https://github.com/appwrite/sdk-for-swift', 'enabled' => true, diff --git a/app/config/roles.php b/app/config/roles.php index 0f0945a2b4..3bf2297550 100644 --- a/app/config/roles.php +++ b/app/config/roles.php @@ -1,6 +1,6 @@ [ + User::ROLE_GUESTS => [ 'label' => 'Guests', 'scopes' => [ 'global', @@ -112,23 +112,23 @@ return [ 'execution.write', ], ], - Auth::USER_ROLE_USERS => [ + User::ROLE_USERS => [ 'label' => 'Users', 'scopes' => \array_merge($member), ], - Auth::USER_ROLE_ADMIN => [ + User::ROLE_ADMIN => [ 'label' => 'Admin', 'scopes' => \array_merge($admins), ], - Auth::USER_ROLE_DEVELOPER => [ + User::ROLE_DEVELOPER => [ 'label' => 'Developer', 'scopes' => \array_merge($admins), ], - Auth::USER_ROLE_OWNER => [ + User::ROLE_OWNER => [ 'label' => 'Owner', 'scopes' => \array_merge($member, $admins), ], - Auth::USER_ROLE_APPS => [ + User::ROLE_APPS => [ 'label' => 'Applications', 'scopes' => ['global', 'health.read', 'graphql'], ], diff --git a/app/config/scopes.php b/app/config/scopes.php index d90ca2eabf..a6ad98e4e0 100644 --- a/app/config/scopes.php +++ b/app/config/scopes.php @@ -47,10 +47,10 @@ return [ // List of publicly visible scopes 'description' => 'Access to create, update, and delete your project\'s database table\'s columns', ], 'indexes.read' => [ - 'description' => 'Access to read your project\'s database collection\'s indexes', + 'description' => 'Access to read your project\'s database table\'s indexes', ], 'indexes.write' => [ - 'description' => 'Access to create, update, and delete your project\'s database collection\'s indexes', + 'description' => 'Access to create, update, and delete your project\'s database table\'s indexes', ], 'documents.read' => [ 'description' => 'Access to read your project\'s database documents', diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index 9adb21d960..5d645ac86e 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -258,7 +258,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 58, + "weight": 48, "cookies": false, "type": "", "demo": "account\/list-identities.md", @@ -328,7 +328,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 59, + "weight": 49, "cookies": false, "type": "", "demo": "account\/delete-identity.md", @@ -517,7 +517,7 @@ "x-appwrite": { "method": "updateMFA", "group": "mfa", - "weight": 45, + "weight": 306, "cookies": false, "type": "", "demo": "account\/update-mfa.md", @@ -587,7 +587,7 @@ "x-appwrite": { "method": "createMfaAuthenticator", "group": "mfa", - "weight": 47, + "weight": 308, "cookies": false, "type": "", "demo": "account\/create-mfa-authenticator.md", @@ -707,7 +707,7 @@ "x-appwrite": { "method": "updateMfaAuthenticator", "group": "mfa", - "weight": 48, + "weight": 309, "cookies": false, "type": "", "demo": "account\/update-mfa-authenticator.md", @@ -843,7 +843,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 52, + "weight": 310, "cookies": false, "type": "", "demo": "account\/delete-mfa-authenticator.md", @@ -939,7 +939,7 @@ ] } }, - "\/account\/mfa\/challenge": { + "\/account\/mfa\/challenges": { "post": { "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", @@ -963,7 +963,7 @@ "x-appwrite": { "method": "createMfaChallenge", "group": "mfa", - "weight": 53, + "weight": 314, "cookies": false, "type": "", "demo": "account\/create-mfa-challenge.md", @@ -1091,7 +1091,7 @@ "x-appwrite": { "method": "updateMfaChallenge", "group": "mfa", - "weight": 54, + "weight": 315, "cookies": false, "type": "", "demo": "account\/update-mfa-challenge.md", @@ -1225,7 +1225,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 46, + "weight": 307, "cookies": false, "type": "", "demo": "account\/list-mfa-factors.md", @@ -1322,7 +1322,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 51, + "weight": 313, "cookies": false, "type": "", "demo": "account\/get-mfa-recovery-codes.md", @@ -1417,7 +1417,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 49, + "weight": 311, "cookies": false, "type": "", "demo": "account\/create-mfa-recovery-codes.md", @@ -1512,7 +1512,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 50, + "weight": 312, "cookies": false, "type": "", "demo": "account\/update-mfa-recovery-codes.md", @@ -2918,7 +2918,7 @@ "x-appwrite": { "method": "createPushTarget", "group": "pushTargets", - "weight": 55, + "weight": 45, "cookies": false, "type": "", "demo": "account\/create-push-target.md", @@ -2997,7 +2997,7 @@ "x-appwrite": { "method": "updatePushTarget", "group": "pushTargets", - "weight": 56, + "weight": 46, "cookies": false, "type": "", "demo": "account\/update-push-target.md", @@ -3068,7 +3068,7 @@ "x-appwrite": { "method": "deletePushTarget", "group": "pushTargets", - "weight": 57, + "weight": 47, "cookies": false, "type": "", "demo": "account\/delete-push-target.md", @@ -3879,7 +3879,7 @@ "x-appwrite": { "method": "getBrowser", "group": null, - "weight": 61, + "weight": 51, "cookies": false, "type": "location", "demo": "avatars\/get-browser.md", @@ -4005,7 +4005,7 @@ "x-appwrite": { "method": "getCreditCard", "group": null, - "weight": 60, + "weight": 50, "cookies": false, "type": "location", "demo": "avatars\/get-credit-card.md", @@ -4137,7 +4137,7 @@ "x-appwrite": { "method": "getFavicon", "group": null, - "weight": 64, + "weight": 54, "cookies": false, "type": "location", "demo": "avatars\/get-favicon.md", @@ -4195,7 +4195,7 @@ "x-appwrite": { "method": "getFlag", "group": null, - "weight": 62, + "weight": 52, "cookies": false, "type": "location", "demo": "avatars\/get-flag.md", @@ -4683,7 +4683,7 @@ "x-appwrite": { "method": "getImage", "group": null, - "weight": 63, + "weight": 53, "cookies": false, "type": "location", "demo": "avatars\/get-image.md", @@ -4765,7 +4765,7 @@ "x-appwrite": { "method": "getInitials", "group": null, - "weight": 66, + "weight": 56, "cookies": false, "type": "location", "demo": "avatars\/get-initials.md", @@ -4857,7 +4857,7 @@ "x-appwrite": { "method": "getQR", "group": null, - "weight": 65, + "weight": 55, "cookies": false, "type": "location", "demo": "avatars\/get-qr.md", @@ -4949,7 +4949,7 @@ "x-appwrite": { "method": "getScreenshot", "group": null, - "weight": 67, + "weight": 57, "cookies": false, "type": "location", "demo": "avatars\/get-screenshot.md", @@ -4993,7 +4993,7 @@ "required": false, "schema": { "type": "object", - "x-example": "{}", + "x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}", "default": {} }, "in": "query" @@ -5005,7 +5005,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1920", "default": 1280 }, "in": "query" @@ -5017,7 +5017,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1080", "default": 720 }, "in": "query" @@ -5029,7 +5029,7 @@ "schema": { "type": "number", "format": "float", - "x-example": 0.1, + "x-example": "2", "default": 1 }, "in": "query" @@ -5040,7 +5040,7 @@ "required": false, "schema": { "type": "string", - "x-example": "light", + "x-example": "dark", "enum": [ "light", "dark" @@ -5057,7 +5057,7 @@ "required": false, "schema": { "type": "string", - "x-example": "", + "x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15", "default": "" }, "in": "query" @@ -5068,7 +5068,7 @@ "required": false, "schema": { "type": "boolean", - "x-example": false, + "x-example": "true", "default": false }, "in": "query" @@ -5079,7 +5079,7 @@ "required": false, "schema": { "type": "string", - "x-example": "", + "x-example": "en-US", "default": "" }, "in": "query" @@ -5090,7 +5090,7 @@ "required": false, "schema": { "type": "string", - "x-example": "africa\/abidjan", + "x-example": "america\/new_york", "enum": [ "africa\/abidjan", "africa\/accra", @@ -5525,7 +5525,7 @@ "schema": { "type": "number", "format": "float", - "x-example": -90, + "x-example": "37.7749", "default": 0 }, "in": "query" @@ -5537,7 +5537,7 @@ "schema": { "type": "number", "format": "float", - "x-example": -180, + "x-example": "-122.4194", "default": 0 }, "in": "query" @@ -5549,7 +5549,7 @@ "schema": { "type": "number", "format": "float", - "x-example": 0, + "x-example": "100", "default": 0 }, "in": "query" @@ -5560,7 +5560,7 @@ "required": false, "schema": { "type": "boolean", - "x-example": false, + "x-example": "true", "default": false }, "in": "query" @@ -5572,8 +5572,33 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "geolocation", + "camera", + "microphone", + "notifications", + "midi", + "push", + "clipboard-read", + "clipboard-write", + "payment-handler", + "usb", + "bluetooth", + "accelerometer", + "gyroscope", + "magnetometer", + "ambient-light-sensor", + "background-sync", + "persistent-storage", + "screen-wake-lock", + "web-share", + "xr-spatial-tracking" + ], + "x-enum-name": "BrowserPermission", + "x-enum-keys": [] }, + "x-example": "[\"geolocation\",\"notifications\"]", "default": [] }, "in": "query" @@ -5585,7 +5610,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "3", "default": 0 }, "in": "query" @@ -5597,7 +5622,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "800", "default": 0 }, "in": "query" @@ -5609,7 +5634,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "600", "default": 0 }, "in": "query" @@ -5621,7 +5646,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": -1, + "x-example": "85", "default": -1 }, "in": "query" @@ -5632,7 +5657,7 @@ "required": false, "schema": { "type": "string", - "x-example": "jpg", + "x-example": "jpeg", "enum": [ "jpg", "jpeg", @@ -7370,7 +7395,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 251, + "weight": 241, "cookies": false, "type": "graphql", "demo": "graphql\/query.md", @@ -7422,7 +7447,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 250, + "weight": 240, "cookies": false, "type": "graphql", "demo": "graphql\/mutation.md", @@ -7474,7 +7499,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 71, + "weight": 61, "cookies": false, "type": "", "demo": "locale\/get.md", @@ -7526,7 +7551,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 72, + "weight": 62, "cookies": false, "type": "", "demo": "locale\/list-codes.md", @@ -7578,7 +7603,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 76, + "weight": 66, "cookies": false, "type": "", "demo": "locale\/list-continents.md", @@ -7630,7 +7655,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 73, + "weight": 63, "cookies": false, "type": "", "demo": "locale\/list-countries.md", @@ -7682,7 +7707,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 74, + "weight": 64, "cookies": false, "type": "", "demo": "locale\/list-countries-eu.md", @@ -7734,7 +7759,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 75, + "weight": 65, "cookies": false, "type": "", "demo": "locale\/list-countries-phones.md", @@ -7786,7 +7811,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 77, + "weight": 67, "cookies": false, "type": "", "demo": "locale\/list-currencies.md", @@ -7838,7 +7863,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 78, + "weight": 68, "cookies": false, "type": "", "demo": "locale\/list-languages.md", @@ -7890,7 +7915,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 300, + "weight": 290, "cookies": false, "type": "", "demo": "messaging\/create-subscriber.md", @@ -7973,7 +7998,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 304, + "weight": 294, "cookies": false, "type": "", "demo": "messaging\/delete-subscriber.md", @@ -8048,7 +8073,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 161, + "weight": 151, "cookies": false, "type": "", "demo": "storage\/list-files.md", @@ -8145,7 +8170,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 160, + "weight": 150, "cookies": false, "type": "upload", "demo": "storage\/create-file.md", @@ -8244,7 +8269,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 162, + "weight": 152, "cookies": false, "type": "", "demo": "storage\/get-file.md", @@ -8316,7 +8341,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 167, + "weight": 157, "cookies": false, "type": "", "demo": "storage\/update-file.md", @@ -8407,7 +8432,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 168, + "weight": 158, "cookies": false, "type": "", "demo": "storage\/delete-file.md", @@ -8474,7 +8499,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 164, + "weight": 154, "cookies": false, "type": "location", "demo": "storage\/get-file-download.md", @@ -8552,7 +8577,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 163, + "weight": 153, "cookies": false, "type": "location", "demo": "storage\/get-file-preview.md", @@ -8780,7 +8805,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 165, + "weight": 155, "cookies": false, "type": "location", "demo": "storage\/get-file-view.md", @@ -10281,7 +10306,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 172, + "weight": 162, "cookies": false, "type": "", "demo": "teams\/list.md", @@ -10368,7 +10393,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 171, + "weight": 161, "cookies": false, "type": "", "demo": "teams\/create.md", @@ -10453,7 +10478,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 173, + "weight": 163, "cookies": false, "type": "", "demo": "teams\/get.md", @@ -10515,7 +10540,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 175, + "weight": 165, "cookies": false, "type": "", "demo": "teams\/update-name.md", @@ -10589,7 +10614,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 177, + "weight": 167, "cookies": false, "type": "", "demo": "teams\/delete.md", @@ -10653,7 +10678,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 179, + "weight": 169, "cookies": false, "type": "", "demo": "teams\/list-memberships.md", @@ -10750,7 +10775,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 178, + "weight": 168, "cookies": false, "type": "", "demo": "teams\/create-membership.md", @@ -10814,7 +10839,14 @@ "description": "Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions). Maximum of 100 roles are allowed, each 32 characters long.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "url": { @@ -10861,7 +10893,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 180, + "weight": 170, "cookies": false, "type": "", "demo": "teams\/get-membership.md", @@ -10933,7 +10965,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 181, + "weight": 171, "cookies": false, "type": "", "demo": "teams\/update-membership.md", @@ -10992,7 +11024,14 @@ "description": "An array of strings. Use this param to set the user's roles in the team. A role can be any string. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions). Maximum of 100 roles are allowed, each 32 characters long.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } } }, @@ -11020,7 +11059,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 183, + "weight": 173, "cookies": false, "type": "", "demo": "teams\/delete-membership.md", @@ -11094,7 +11133,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 182, + "weight": 172, "cookies": false, "type": "", "demo": "teams\/update-membership-status.md", @@ -11192,7 +11231,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 174, + "weight": 164, "cookies": false, "type": "", "demo": "teams\/get-prefs.md", @@ -11253,7 +11292,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 176, + "weight": 166, "cookies": false, "type": "", "demo": "teams\/update-prefs.md", diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index fded6c46b0..e6a6bdfee5 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -295,7 +295,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 58, + "weight": 48, "cookies": false, "type": "", "demo": "account\/list-identities.md", @@ -364,7 +364,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 59, + "weight": 49, "cookies": false, "type": "", "demo": "account\/delete-identity.md", @@ -551,7 +551,7 @@ "x-appwrite": { "method": "updateMFA", "group": "mfa", - "weight": 45, + "weight": 306, "cookies": false, "type": "", "demo": "account\/update-mfa.md", @@ -620,7 +620,7 @@ "x-appwrite": { "method": "createMfaAuthenticator", "group": "mfa", - "weight": 47, + "weight": 308, "cookies": false, "type": "", "demo": "account\/create-mfa-authenticator.md", @@ -739,7 +739,7 @@ "x-appwrite": { "method": "updateMfaAuthenticator", "group": "mfa", - "weight": 48, + "weight": 309, "cookies": false, "type": "", "demo": "account\/update-mfa-authenticator.md", @@ -874,7 +874,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 52, + "weight": 310, "cookies": false, "type": "", "demo": "account\/delete-mfa-authenticator.md", @@ -969,7 +969,7 @@ ] } }, - "\/account\/mfa\/challenge": { + "\/account\/mfa\/challenges": { "post": { "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", @@ -993,7 +993,7 @@ "x-appwrite": { "method": "createMfaChallenge", "group": "mfa", - "weight": 53, + "weight": 314, "cookies": false, "type": "", "demo": "account\/create-mfa-challenge.md", @@ -1121,7 +1121,7 @@ "x-appwrite": { "method": "updateMfaChallenge", "group": "mfa", - "weight": 54, + "weight": 315, "cookies": false, "type": "", "demo": "account\/update-mfa-challenge.md", @@ -1254,7 +1254,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 46, + "weight": 307, "cookies": false, "type": "", "demo": "account\/list-mfa-factors.md", @@ -1350,7 +1350,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 51, + "weight": 313, "cookies": false, "type": "", "demo": "account\/get-mfa-recovery-codes.md", @@ -1444,7 +1444,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 49, + "weight": 311, "cookies": false, "type": "", "demo": "account\/create-mfa-recovery-codes.md", @@ -1538,7 +1538,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 50, + "weight": 312, "cookies": false, "type": "", "demo": "account\/update-mfa-recovery-codes.md", @@ -2930,7 +2930,7 @@ "x-appwrite": { "method": "createPushTarget", "group": "pushTargets", - "weight": 55, + "weight": 45, "cookies": false, "type": "", "demo": "account\/create-push-target.md", @@ -3008,7 +3008,7 @@ "x-appwrite": { "method": "updatePushTarget", "group": "pushTargets", - "weight": 56, + "weight": 46, "cookies": false, "type": "", "demo": "account\/update-push-target.md", @@ -3078,7 +3078,7 @@ "x-appwrite": { "method": "deletePushTarget", "group": "pushTargets", - "weight": 57, + "weight": 47, "cookies": false, "type": "", "demo": "account\/delete-push-target.md", @@ -3884,7 +3884,7 @@ "x-appwrite": { "method": "getBrowser", "group": null, - "weight": 61, + "weight": 51, "cookies": false, "type": "location", "demo": "avatars\/get-browser.md", @@ -4010,7 +4010,7 @@ "x-appwrite": { "method": "getCreditCard", "group": null, - "weight": 60, + "weight": 50, "cookies": false, "type": "location", "demo": "avatars\/get-credit-card.md", @@ -4142,7 +4142,7 @@ "x-appwrite": { "method": "getFavicon", "group": null, - "weight": 64, + "weight": 54, "cookies": false, "type": "location", "demo": "avatars\/get-favicon.md", @@ -4200,7 +4200,7 @@ "x-appwrite": { "method": "getFlag", "group": null, - "weight": 62, + "weight": 52, "cookies": false, "type": "location", "demo": "avatars\/get-flag.md", @@ -4688,7 +4688,7 @@ "x-appwrite": { "method": "getImage", "group": null, - "weight": 63, + "weight": 53, "cookies": false, "type": "location", "demo": "avatars\/get-image.md", @@ -4770,7 +4770,7 @@ "x-appwrite": { "method": "getInitials", "group": null, - "weight": 66, + "weight": 56, "cookies": false, "type": "location", "demo": "avatars\/get-initials.md", @@ -4862,7 +4862,7 @@ "x-appwrite": { "method": "getQR", "group": null, - "weight": 65, + "weight": 55, "cookies": false, "type": "location", "demo": "avatars\/get-qr.md", @@ -4954,7 +4954,7 @@ "x-appwrite": { "method": "getScreenshot", "group": null, - "weight": 67, + "weight": 57, "cookies": false, "type": "location", "demo": "avatars\/get-screenshot.md", @@ -4998,7 +4998,7 @@ "required": false, "schema": { "type": "object", - "x-example": "{}", + "x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}", "default": {} }, "in": "query" @@ -5010,7 +5010,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1920", "default": 1280 }, "in": "query" @@ -5022,7 +5022,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1080", "default": 720 }, "in": "query" @@ -5034,7 +5034,7 @@ "schema": { "type": "number", "format": "float", - "x-example": 0.1, + "x-example": "2", "default": 1 }, "in": "query" @@ -5045,7 +5045,7 @@ "required": false, "schema": { "type": "string", - "x-example": "light", + "x-example": "dark", "enum": [ "light", "dark" @@ -5062,7 +5062,7 @@ "required": false, "schema": { "type": "string", - "x-example": "", + "x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15", "default": "" }, "in": "query" @@ -5073,7 +5073,7 @@ "required": false, "schema": { "type": "boolean", - "x-example": false, + "x-example": "true", "default": false }, "in": "query" @@ -5084,7 +5084,7 @@ "required": false, "schema": { "type": "string", - "x-example": "", + "x-example": "en-US", "default": "" }, "in": "query" @@ -5095,7 +5095,7 @@ "required": false, "schema": { "type": "string", - "x-example": "africa\/abidjan", + "x-example": "america\/new_york", "enum": [ "africa\/abidjan", "africa\/accra", @@ -5530,7 +5530,7 @@ "schema": { "type": "number", "format": "float", - "x-example": -90, + "x-example": "37.7749", "default": 0 }, "in": "query" @@ -5542,7 +5542,7 @@ "schema": { "type": "number", "format": "float", - "x-example": -180, + "x-example": "-122.4194", "default": 0 }, "in": "query" @@ -5554,7 +5554,7 @@ "schema": { "type": "number", "format": "float", - "x-example": 0, + "x-example": "100", "default": 0 }, "in": "query" @@ -5565,7 +5565,7 @@ "required": false, "schema": { "type": "boolean", - "x-example": false, + "x-example": "true", "default": false }, "in": "query" @@ -5577,8 +5577,33 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "geolocation", + "camera", + "microphone", + "notifications", + "midi", + "push", + "clipboard-read", + "clipboard-write", + "payment-handler", + "usb", + "bluetooth", + "accelerometer", + "gyroscope", + "magnetometer", + "ambient-light-sensor", + "background-sync", + "persistent-storage", + "screen-wake-lock", + "web-share", + "xr-spatial-tracking" + ], + "x-enum-name": "BrowserPermission", + "x-enum-keys": [] }, + "x-example": "[\"geolocation\",\"notifications\"]", "default": [] }, "in": "query" @@ -5590,7 +5615,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "3", "default": 0 }, "in": "query" @@ -5602,7 +5627,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "800", "default": 0 }, "in": "query" @@ -5614,7 +5639,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "600", "default": 0 }, "in": "query" @@ -5626,7 +5651,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": -1, + "x-example": "85", "default": -1 }, "in": "query" @@ -5637,7 +5662,7 @@ "required": false, "schema": { "type": "string", - "x-example": "jpg", + "x-example": "jpeg", "enum": [ "jpg", "jpeg", @@ -5673,7 +5698,7 @@ "x-appwrite": { "method": "chat", "group": "console", - "weight": 253, + "weight": 243, "cookies": false, "type": "", "demo": "assistant\/chat.md", @@ -5808,7 +5833,7 @@ "x-appwrite": { "method": "variables", "group": "console", - "weight": 252, + "weight": 242, "cookies": false, "type": "", "demo": "console\/variables.md", @@ -7071,6 +7096,22 @@ "type": "boolean", "description": "Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.", "x-example": false + }, + "attributes": { + "type": "array", + "description": "Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of attribute keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -12379,7 +12420,13 @@ "description": "Array of index orders. Maximum of 100 orders are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -13239,7 +13286,66 @@ "description": "List of scopes allowed for API key auto-generated for every execution. Maximum of 100 scopes are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -13437,7 +13543,78 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "node-14.5", + "node-16.0", + "node-18.0", + "node-19.0", + "node-20.0", + "node-21.0", + "node-22", + "php-8.0", + "php-8.1", + "php-8.2", + "php-8.3", + "ruby-3.0", + "ruby-3.1", + "ruby-3.2", + "ruby-3.3", + "python-3.8", + "python-3.9", + "python-3.10", + "python-3.11", + "python-3.12", + "python-ml-3.11", + "python-ml-3.12", + "deno-1.21", + "deno-1.24", + "deno-1.35", + "deno-1.40", + "deno-1.46", + "deno-2.0", + "dart-2.15", + "dart-2.16", + "dart-2.17", + "dart-2.18", + "dart-2.19", + "dart-3.0", + "dart-3.1", + "dart-3.3", + "dart-3.5", + "dart-3.8", + "dart-3.9", + "dotnet-6.0", + "dotnet-7.0", + "dotnet-8.0", + "java-8.0", + "java-11.0", + "java-17.0", + "java-18.0", + "java-21.0", + "java-22", + "swift-5.5", + "swift-5.8", + "swift-5.9", + "swift-5.10", + "kotlin-1.6", + "kotlin-1.8", + "kotlin-1.9", + "kotlin-2.0", + "cpp-17", + "cpp-20", + "bun-1.0", + "bun-1.1", + "go-1.23", + "static-1", + "flutter-3.24", + "flutter-3.27", + "flutter-3.29", + "flutter-3.32", + "flutter-3.35" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [] }, @@ -13450,7 +13627,17 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "dev-tools", + "starter", + "databases", + "ai", + "messaging", + "utilities" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [] }, @@ -13882,7 +14069,66 @@ "description": "List of scopes allowed for API Key auto-generated for every execution. Maximum of 100 scopes are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -14424,7 +14670,7 @@ "branch", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -14524,7 +14770,7 @@ "branch", "commit" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -15663,7 +15909,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 251, + "weight": 241, "cookies": false, "type": "graphql", "demo": "graphql\/query.md", @@ -15715,7 +15961,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 250, + "weight": 240, "cookies": false, "type": "graphql", "demo": "graphql\/mutation.md", @@ -15767,7 +16013,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 79, + "weight": 69, "cookies": false, "type": "", "demo": "health\/get.md", @@ -15816,7 +16062,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 100, + "weight": 90, "cookies": false, "type": "", "demo": "health\/get-antivirus.md", @@ -15865,7 +16111,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 82, + "weight": 72, "cookies": false, "type": "", "demo": "health\/get-cache.md", @@ -15914,7 +16160,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 87, + "weight": 77, "cookies": false, "type": "", "demo": "health\/get-certificate.md", @@ -15974,7 +16220,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 81, + "weight": 71, "cookies": false, "type": "", "demo": "health\/get-db.md", @@ -16023,7 +16269,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 83, + "weight": 73, "cookies": false, "type": "", "demo": "health\/get-pub-sub.md", @@ -16072,7 +16318,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 89, + "weight": 79, "cookies": false, "type": "", "demo": "health\/get-queue-builds.md", @@ -16134,7 +16380,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 88, + "weight": 78, "cookies": false, "type": "", "demo": "health\/get-queue-certificates.md", @@ -16196,7 +16442,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 90, + "weight": 80, "cookies": false, "type": "", "demo": "health\/get-queue-databases.md", @@ -16269,7 +16515,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 91, + "weight": 81, "cookies": false, "type": "", "demo": "health\/get-queue-deletes.md", @@ -16331,7 +16577,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 101, + "weight": 91, "cookies": false, "type": "", "demo": "health\/get-failed-jobs.md", @@ -16419,7 +16665,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 95, + "weight": 85, "cookies": false, "type": "", "demo": "health\/get-queue-functions.md", @@ -16481,7 +16727,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 86, + "weight": 76, "cookies": false, "type": "", "demo": "health\/get-queue-logs.md", @@ -16543,7 +16789,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 92, + "weight": 82, "cookies": false, "type": "", "demo": "health\/get-queue-mails.md", @@ -16605,7 +16851,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 93, + "weight": 83, "cookies": false, "type": "", "demo": "health\/get-queue-messaging.md", @@ -16667,7 +16913,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 94, + "weight": 84, "cookies": false, "type": "", "demo": "health\/get-queue-migrations.md", @@ -16729,7 +16975,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 96, + "weight": 86, "cookies": false, "type": "", "demo": "health\/get-queue-stats-resources.md", @@ -16791,7 +17037,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 97, + "weight": 87, "cookies": false, "type": "", "demo": "health\/get-queue-usage.md", @@ -16853,7 +17099,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 85, + "weight": 75, "cookies": false, "type": "", "demo": "health\/get-queue-webhooks.md", @@ -16915,7 +17161,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 99, + "weight": 89, "cookies": false, "type": "", "demo": "health\/get-storage.md", @@ -16964,7 +17210,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 98, + "weight": 88, "cookies": false, "type": "", "demo": "health\/get-storage-local.md", @@ -17013,7 +17259,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 84, + "weight": 74, "cookies": false, "type": "", "demo": "health\/get-time.md", @@ -17062,7 +17308,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 71, + "weight": 61, "cookies": false, "type": "", "demo": "locale\/get.md", @@ -17114,7 +17360,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 72, + "weight": 62, "cookies": false, "type": "", "demo": "locale\/list-codes.md", @@ -17166,7 +17412,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 76, + "weight": 66, "cookies": false, "type": "", "demo": "locale\/list-continents.md", @@ -17218,7 +17464,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 73, + "weight": 63, "cookies": false, "type": "", "demo": "locale\/list-countries.md", @@ -17270,7 +17516,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 74, + "weight": 64, "cookies": false, "type": "", "demo": "locale\/list-countries-eu.md", @@ -17322,7 +17568,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 75, + "weight": 65, "cookies": false, "type": "", "demo": "locale\/list-countries-phones.md", @@ -17374,7 +17620,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 77, + "weight": 67, "cookies": false, "type": "", "demo": "locale\/list-currencies.md", @@ -17426,7 +17672,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 78, + "weight": 68, "cookies": false, "type": "", "demo": "locale\/list-languages.md", @@ -17478,7 +17724,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 308, + "weight": 298, "cookies": false, "type": "", "demo": "messaging\/list-messages.md", @@ -17565,7 +17811,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 305, + "weight": 295, "cookies": false, "type": "", "demo": "messaging\/create-email.md", @@ -17710,7 +17956,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 312, + "weight": 302, "cookies": false, "type": "", "demo": "messaging\/update-email.md", @@ -17867,7 +18113,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 307, + "weight": 297, "cookies": false, "type": "", "demo": "messaging\/create-push.md", @@ -18043,7 +18289,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 314, + "weight": 304, "cookies": false, "type": "", "demo": "messaging\/update-push.md", @@ -18239,7 +18485,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 306, + "weight": 296, "cookies": false, "type": "", "demo": "messaging\/create-sms.md", @@ -18417,7 +18663,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 313, + "weight": 303, "cookies": false, "type": "", "demo": "messaging\/update-sms.md", @@ -18601,7 +18847,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 311, + "weight": 301, "cookies": false, "type": "", "demo": "messaging\/get-message.md", @@ -18654,7 +18900,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 315, + "weight": 305, "cookies": false, "type": "", "demo": "messaging\/delete.md", @@ -18716,7 +18962,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 309, + "weight": 299, "cookies": false, "type": "", "demo": "messaging\/list-message-logs.md", @@ -18802,7 +19048,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 310, + "weight": 300, "cookies": false, "type": "", "demo": "messaging\/list-targets.md", @@ -18888,7 +19134,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 279, + "weight": 269, "cookies": false, "type": "", "demo": "messaging\/list-providers.md", @@ -18975,7 +19221,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 278, + "weight": 268, "cookies": false, "type": "", "demo": "messaging\/create-apns-provider.md", @@ -19151,7 +19397,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 292, + "weight": 282, "cookies": false, "type": "", "demo": "messaging\/update-apns-provider.md", @@ -19329,7 +19575,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 277, + "weight": 267, "cookies": false, "type": "", "demo": "messaging\/create-fcm-provider.md", @@ -19478,7 +19724,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 291, + "weight": 281, "cookies": false, "type": "", "demo": "messaging\/update-fcm-provider.md", @@ -19628,7 +19874,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 268, + "weight": 258, "cookies": false, "type": "", "demo": "messaging\/create-mailgun-provider.md", @@ -19745,7 +19991,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 282, + "weight": 272, "cookies": false, "type": "", "demo": "messaging\/update-mailgun-provider.md", @@ -19865,7 +20111,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 272, + "weight": 262, "cookies": false, "type": "", "demo": "messaging\/create-msg-91-provider.md", @@ -19961,7 +20207,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 286, + "weight": 276, "cookies": false, "type": "", "demo": "messaging\/update-msg-91-provider.md", @@ -20060,7 +20306,7 @@ "x-appwrite": { "method": "createResendProvider", "group": "providers", - "weight": 270, + "weight": 260, "cookies": false, "type": "", "demo": "messaging\/create-resend-provider.md", @@ -20166,7 +20412,7 @@ "x-appwrite": { "method": "updateResendProvider", "group": "providers", - "weight": 284, + "weight": 274, "cookies": false, "type": "", "demo": "messaging\/update-resend-provider.md", @@ -20275,7 +20521,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 269, + "weight": 259, "cookies": false, "type": "", "demo": "messaging\/create-sendgrid-provider.md", @@ -20381,7 +20627,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 283, + "weight": 273, "cookies": false, "type": "", "demo": "messaging\/update-sendgrid-provider.md", @@ -20490,7 +20736,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 271, + "weight": 261, "cookies": false, "type": "", "demo": "messaging\/create-smtp-provider.md", @@ -20718,7 +20964,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 285, + "weight": 275, "cookies": false, "type": "", "demo": "messaging\/update-smtp-provider.md", @@ -20946,7 +21192,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 273, + "weight": 263, "cookies": false, "type": "", "demo": "messaging\/create-telesign-provider.md", @@ -21042,7 +21288,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 287, + "weight": 277, "cookies": false, "type": "", "demo": "messaging\/update-telesign-provider.md", @@ -21141,7 +21387,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 274, + "weight": 264, "cookies": false, "type": "", "demo": "messaging\/create-textmagic-provider.md", @@ -21237,7 +21483,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 288, + "weight": 278, "cookies": false, "type": "", "demo": "messaging\/update-textmagic-provider.md", @@ -21336,7 +21582,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 275, + "weight": 265, "cookies": false, "type": "", "demo": "messaging\/create-twilio-provider.md", @@ -21432,7 +21678,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 289, + "weight": 279, "cookies": false, "type": "", "demo": "messaging\/update-twilio-provider.md", @@ -21531,7 +21777,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 276, + "weight": 266, "cookies": false, "type": "", "demo": "messaging\/create-vonage-provider.md", @@ -21627,7 +21873,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 290, + "weight": 280, "cookies": false, "type": "", "demo": "messaging\/update-vonage-provider.md", @@ -21726,7 +21972,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 281, + "weight": 271, "cookies": false, "type": "", "demo": "messaging\/get-provider.md", @@ -21779,7 +22025,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 293, + "weight": 283, "cookies": false, "type": "", "demo": "messaging\/delete-provider.md", @@ -21841,7 +22087,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 280, + "weight": 270, "cookies": false, "type": "", "demo": "messaging\/list-provider-logs.md", @@ -21927,7 +22173,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 302, + "weight": 292, "cookies": false, "type": "", "demo": "messaging\/list-subscriber-logs.md", @@ -22013,7 +22259,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 295, + "weight": 285, "cookies": false, "type": "", "demo": "messaging\/list-topics.md", @@ -22098,7 +22344,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 294, + "weight": 284, "cookies": false, "type": "", "demo": "messaging\/create-topic.md", @@ -22181,7 +22427,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 297, + "weight": 287, "cookies": false, "type": "", "demo": "messaging\/get-topic.md", @@ -22241,7 +22487,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 298, + "weight": 288, "cookies": false, "type": "", "demo": "messaging\/update-topic.md", @@ -22320,7 +22566,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 299, + "weight": 289, "cookies": false, "type": "", "demo": "messaging\/delete-topic.md", @@ -22382,7 +22628,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 296, + "weight": 286, "cookies": false, "type": "", "demo": "messaging\/list-topic-logs.md", @@ -22468,7 +22714,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 301, + "weight": 291, "cookies": false, "type": "", "demo": "messaging\/list-subscribers.md", @@ -22563,7 +22809,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 300, + "weight": 290, "cookies": false, "type": "", "demo": "messaging\/create-subscriber.md", @@ -22653,7 +22899,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 303, + "weight": 293, "cookies": false, "type": "", "demo": "messaging\/get-subscriber.md", @@ -22716,7 +22962,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 304, + "weight": 294, "cookies": false, "type": "", "demo": "messaging\/delete-subscriber.md", @@ -22791,7 +23037,7 @@ "x-appwrite": { "method": "list", "group": null, - "weight": 260, + "weight": 250, "cookies": false, "type": "", "demo": "migrations\/list.md", @@ -22876,7 +23122,7 @@ "x-appwrite": { "method": "createAppwriteMigration", "group": null, - "weight": 254, + "weight": 244, "cookies": false, "type": "", "demo": "migrations\/create-appwrite-migration.md", @@ -22909,7 +23155,27 @@ "description": "List of resources to migrate", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "team", + "membership", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file", + "function", + "deployment", + "environment-variable" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "endpoint": { @@ -22964,7 +23230,7 @@ "x-appwrite": { "method": "getAppwriteReport", "group": null, - "weight": 262, + "weight": 252, "cookies": false, "type": "", "demo": "migrations\/get-appwrite-report.md", @@ -22994,7 +23260,27 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "team", + "membership", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file", + "function", + "deployment", + "environment-variable" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "in": "query" @@ -23057,7 +23343,7 @@ "x-appwrite": { "method": "createCSVExport", "group": null, - "weight": 259, + "weight": 249, "cookies": false, "type": "", "demo": "migrations\/create-csv-export.md", @@ -23171,7 +23457,7 @@ "x-appwrite": { "method": "createCSVImport", "group": null, - "weight": 258, + "weight": 248, "cookies": false, "type": "", "demo": "migrations\/create-csv-import.md", @@ -23255,7 +23541,7 @@ "x-appwrite": { "method": "createFirebaseMigration", "group": null, - "weight": 255, + "weight": 245, "cookies": false, "type": "", "demo": "migrations\/create-firebase-migration.md", @@ -23288,7 +23574,21 @@ "description": "List of resources to migrate", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "serviceAccount": { @@ -23331,7 +23631,7 @@ "x-appwrite": { "method": "getFirebaseReport", "group": null, - "weight": 263, + "weight": 253, "cookies": false, "type": "", "demo": "migrations\/get-firebase-report.md", @@ -23361,7 +23661,21 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "in": "query" @@ -23403,7 +23717,7 @@ "x-appwrite": { "method": "createNHostMigration", "group": null, - "weight": 257, + "weight": 247, "cookies": false, "type": "", "demo": "migrations\/create-n-host-migration.md", @@ -23436,7 +23750,22 @@ "description": "List of resources to migrate", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "subdomain": { @@ -23514,7 +23843,7 @@ "x-appwrite": { "method": "getNHostReport", "group": null, - "weight": 265, + "weight": 255, "cookies": false, "type": "", "demo": "migrations\/get-n-host-report.md", @@ -23544,7 +23873,22 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "in": "query" @@ -23647,7 +23991,7 @@ "x-appwrite": { "method": "createSupabaseMigration", "group": null, - "weight": 256, + "weight": 246, "cookies": false, "type": "", "demo": "migrations\/create-supabase-migration.md", @@ -23680,7 +24024,22 @@ "description": "List of resources to migrate", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "endpoint": { @@ -23752,7 +24111,7 @@ "x-appwrite": { "method": "getSupabaseReport", "group": null, - "weight": 264, + "weight": 254, "cookies": false, "type": "", "demo": "migrations\/get-supabase-report.md", @@ -23782,7 +24141,22 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "in": "query" @@ -23876,7 +24250,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 261, + "weight": 251, "cookies": false, "type": "", "demo": "migrations\/get.md", @@ -23934,7 +24308,7 @@ "x-appwrite": { "method": "retry", "group": null, - "weight": 266, + "weight": 256, "cookies": false, "type": "", "demo": "migrations\/retry.md", @@ -23985,7 +24359,7 @@ "x-appwrite": { "method": "delete", "group": null, - "weight": 267, + "weight": 257, "cookies": false, "type": "", "demo": "migrations\/delete.md", @@ -24045,7 +24419,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 149, + "weight": 139, "cookies": false, "type": "", "demo": "project\/get-usage.md", @@ -24133,7 +24507,7 @@ "x-appwrite": { "method": "listVariables", "group": null, - "weight": 151, + "weight": 141, "cookies": false, "type": "", "demo": "project\/list-variables.md", @@ -24179,7 +24553,7 @@ "x-appwrite": { "method": "createVariable", "group": null, - "weight": 150, + "weight": 140, "cookies": false, "type": "", "demo": "project\/create-variable.md", @@ -24257,7 +24631,7 @@ "x-appwrite": { "method": "getVariable", "group": null, - "weight": 152, + "weight": 142, "cookies": false, "type": "", "demo": "project\/get-variable.md", @@ -24315,7 +24689,7 @@ "x-appwrite": { "method": "updateVariable", "group": null, - "weight": 153, + "weight": 143, "cookies": false, "type": "", "demo": "project\/update-variable.md", @@ -24397,7 +24771,7 @@ "x-appwrite": { "method": "deleteVariable", "group": null, - "weight": 154, + "weight": 144, "cookies": false, "type": "", "demo": "project\/delete-variable.md", @@ -24540,7 +24914,7 @@ "x-appwrite": { "method": "create", "group": "projects", - "weight": 103, + "weight": 93, "cookies": false, "type": "", "demo": "projects\/create.md", @@ -24674,7 +25048,7 @@ "x-appwrite": { "method": "get", "group": "projects", - "weight": 104, + "weight": 94, "cookies": false, "type": "", "demo": "projects\/get.md", @@ -24732,7 +25106,7 @@ "x-appwrite": { "method": "update", "group": "projects", - "weight": 105, + "weight": 95, "cookies": false, "type": "", "demo": "projects\/update.md", @@ -24847,7 +25221,7 @@ "x-appwrite": { "method": "delete", "group": "projects", - "weight": 122, + "weight": 112, "cookies": false, "type": "", "demo": "projects\/delete.md", @@ -24907,7 +25281,7 @@ "x-appwrite": { "method": "updateApiStatus", "group": "projects", - "weight": 109, + "weight": 99, "cookies": false, "type": "", "demo": "projects\/update-api-status.md", @@ -25061,7 +25435,7 @@ "x-appwrite": { "method": "updateApiStatusAll", "group": "projects", - "weight": 110, + "weight": 100, "cookies": false, "type": "", "demo": "projects\/update-api-status-all.md", @@ -25198,7 +25572,7 @@ "x-appwrite": { "method": "updateAuthDuration", "group": "auth", - "weight": 115, + "weight": 105, "cookies": false, "type": "", "demo": "projects\/update-auth-duration.md", @@ -25277,7 +25651,7 @@ "x-appwrite": { "method": "updateAuthLimit", "group": "auth", - "weight": 114, + "weight": 104, "cookies": false, "type": "", "demo": "projects\/update-auth-limit.md", @@ -25356,7 +25730,7 @@ "x-appwrite": { "method": "updateAuthSessionsLimit", "group": "auth", - "weight": 120, + "weight": 110, "cookies": false, "type": "", "demo": "projects\/update-auth-sessions-limit.md", @@ -25435,7 +25809,7 @@ "x-appwrite": { "method": "updateMembershipsPrivacy", "group": "auth", - "weight": 113, + "weight": 103, "cookies": false, "type": "", "demo": "projects\/update-memberships-privacy.md", @@ -25526,7 +25900,7 @@ "x-appwrite": { "method": "updateMockNumbers", "group": "auth", - "weight": 121, + "weight": 111, "cookies": false, "type": "", "demo": "projects\/update-mock-numbers.md", @@ -25608,7 +25982,7 @@ "x-appwrite": { "method": "updateAuthPasswordDictionary", "group": "auth", - "weight": 118, + "weight": 108, "cookies": false, "type": "", "demo": "projects\/update-auth-password-dictionary.md", @@ -25687,7 +26061,7 @@ "x-appwrite": { "method": "updateAuthPasswordHistory", "group": "auth", - "weight": 117, + "weight": 107, "cookies": false, "type": "", "demo": "projects\/update-auth-password-history.md", @@ -25766,7 +26140,7 @@ "x-appwrite": { "method": "updatePersonalDataCheck", "group": "auth", - "weight": 119, + "weight": 109, "cookies": false, "type": "", "demo": "projects\/update-personal-data-check.md", @@ -25845,7 +26219,7 @@ "x-appwrite": { "method": "updateSessionAlerts", "group": "auth", - "weight": 112, + "weight": 102, "cookies": false, "type": "", "demo": "projects\/update-session-alerts.md", @@ -25924,7 +26298,7 @@ "x-appwrite": { "method": "updateSessionInvalidation", "group": "auth", - "weight": 148, + "weight": 138, "cookies": false, "type": "", "demo": "projects\/update-session-invalidation.md", @@ -26003,7 +26377,7 @@ "x-appwrite": { "method": "updateAuthStatus", "group": "auth", - "weight": 116, + "weight": 106, "cookies": false, "type": "", "demo": "projects\/update-auth-status.md", @@ -26483,7 +26857,7 @@ "x-appwrite": { "method": "createJWT", "group": "auth", - "weight": 134, + "weight": 124, "cookies": false, "type": "", "demo": "projects\/create-jwt.md", @@ -26528,7 +26902,66 @@ "description": "List of scopes allowed for JWT key. Maximum of 100 scopes are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "duration": { @@ -26570,7 +27003,7 @@ "x-appwrite": { "method": "listKeys", "group": "keys", - "weight": 130, + "weight": 120, "cookies": false, "type": "", "demo": "projects\/list-keys.md", @@ -26639,7 +27072,7 @@ "x-appwrite": { "method": "createKey", "group": "keys", - "weight": 129, + "weight": 119, "cookies": false, "type": "", "demo": "projects\/create-key.md", @@ -26689,7 +27122,66 @@ "description": "Key scopes list. Maximum of 100 scopes are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "x-nullable": true }, @@ -26734,7 +27226,7 @@ "x-appwrite": { "method": "getKey", "group": "keys", - "weight": 131, + "weight": 121, "cookies": false, "type": "", "demo": "projects\/get-key.md", @@ -26802,7 +27294,7 @@ "x-appwrite": { "method": "updateKey", "group": "keys", - "weight": 132, + "weight": 122, "cookies": false, "type": "", "demo": "projects\/update-key.md", @@ -26862,7 +27354,66 @@ "description": "Key scopes list. Maximum of 100 events are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "x-nullable": true }, @@ -26898,7 +27449,7 @@ "x-appwrite": { "method": "deleteKey", "group": "keys", - "weight": 133, + "weight": 123, "cookies": false, "type": "", "demo": "projects\/delete-key.md", @@ -26968,7 +27519,7 @@ "x-appwrite": { "method": "updateOAuth2", "group": "auth", - "weight": 111, + "weight": 101, "cookies": false, "type": "", "demo": "projects\/update-o-auth-2.md", @@ -27109,7 +27660,7 @@ "x-appwrite": { "method": "listPlatforms", "group": "platforms", - "weight": 136, + "weight": 126, "cookies": false, "type": "", "demo": "projects\/list-platforms.md", @@ -27178,7 +27729,7 @@ "x-appwrite": { "method": "createPlatform", "group": "platforms", - "weight": 135, + "weight": 125, "cookies": false, "type": "", "demo": "projects\/create-platform.md", @@ -27297,7 +27848,7 @@ "x-appwrite": { "method": "getPlatform", "group": "platforms", - "weight": 137, + "weight": 127, "cookies": false, "type": "", "demo": "projects\/get-platform.md", @@ -27365,7 +27916,7 @@ "x-appwrite": { "method": "updatePlatform", "group": "platforms", - "weight": 138, + "weight": 128, "cookies": false, "type": "", "demo": "projects\/update-platform.md", @@ -27460,7 +28011,7 @@ "x-appwrite": { "method": "deletePlatform", "group": "platforms", - "weight": 139, + "weight": 129, "cookies": false, "type": "", "demo": "projects\/delete-platform.md", @@ -27530,7 +28081,7 @@ "x-appwrite": { "method": "updateServiceStatus", "group": "projects", - "weight": 107, + "weight": 97, "cookies": false, "type": "", "demo": "projects\/update-service-status.md", @@ -27632,7 +28183,7 @@ "x-appwrite": { "method": "updateServiceStatusAll", "group": "projects", - "weight": 108, + "weight": 98, "cookies": false, "type": "", "demo": "projects\/update-service-status-all.md", @@ -27711,7 +28262,7 @@ "x-appwrite": { "method": "updateSmtp", "group": "templates", - "weight": 140, + "weight": 130, "cookies": false, "type": "", "demo": "projects\/update-smtp.md", @@ -27903,7 +28454,7 @@ "x-appwrite": { "method": "createSmtpTest", "group": "templates", - "weight": 141, + "weight": 131, "cookies": false, "type": "", "demo": "projects\/create-smtp-test.md", @@ -28112,7 +28663,7 @@ "x-appwrite": { "method": "updateTeam", "group": "projects", - "weight": 106, + "weight": 96, "cookies": false, "type": "", "demo": "projects\/update-team.md", @@ -28191,7 +28742,7 @@ "x-appwrite": { "method": "getEmailTemplate", "group": "templates", - "weight": 143, + "weight": 133, "cookies": false, "type": "", "demo": "projects\/get-email-template.md", @@ -28415,7 +28966,7 @@ "x-appwrite": { "method": "updateEmailTemplate", "group": "templates", - "weight": 145, + "weight": 135, "cookies": false, "type": "", "demo": "projects\/update-email-template.md", @@ -28679,7 +29230,7 @@ "x-appwrite": { "method": "deleteEmailTemplate", "group": "templates", - "weight": 147, + "weight": 137, "cookies": false, "type": "", "demo": "projects\/delete-email-template.md", @@ -28905,7 +29456,7 @@ "x-appwrite": { "method": "getSmsTemplate", "group": "templates", - "weight": 142, + "weight": 132, "cookies": false, "type": "", "demo": "projects\/get-sms-template.md", @@ -29188,7 +29739,7 @@ "x-appwrite": { "method": "updateSmsTemplate", "group": "templates", - "weight": 144, + "weight": 134, "cookies": false, "type": "", "demo": "projects\/update-sms-template.md", @@ -29494,7 +30045,7 @@ "x-appwrite": { "method": "deleteSmsTemplate", "group": "templates", - "weight": 146, + "weight": 136, "cookies": false, "type": "", "demo": "projects\/delete-sms-template.md", @@ -29779,7 +30330,7 @@ "x-appwrite": { "method": "listWebhooks", "group": "webhooks", - "weight": 124, + "weight": 114, "cookies": false, "type": "", "demo": "projects\/list-webhooks.md", @@ -29848,7 +30399,7 @@ "x-appwrite": { "method": "createWebhook", "group": "webhooks", - "weight": 123, + "weight": 113, "cookies": false, "type": "", "demo": "projects\/create-webhook.md", @@ -29963,7 +30514,7 @@ "x-appwrite": { "method": "getWebhook", "group": "webhooks", - "weight": 125, + "weight": 115, "cookies": false, "type": "", "demo": "projects\/get-webhook.md", @@ -30031,7 +30582,7 @@ "x-appwrite": { "method": "updateWebhook", "group": "webhooks", - "weight": 126, + "weight": 116, "cookies": false, "type": "", "demo": "projects\/update-webhook.md", @@ -30147,7 +30698,7 @@ "x-appwrite": { "method": "deleteWebhook", "group": "webhooks", - "weight": 128, + "weight": 118, "cookies": false, "type": "", "demo": "projects\/delete-webhook.md", @@ -30217,7 +30768,7 @@ "x-appwrite": { "method": "updateWebhookSignature", "group": "webhooks", - "weight": 127, + "weight": 117, "cookies": false, "type": "", "demo": "projects\/update-webhook-signature.md", @@ -31344,7 +31895,26 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "analog", + "angular", + "nextjs", + "react", + "nuxt", + "vue", + "sveltekit", + "astro", + "tanstack-start", + "remix", + "lynx", + "flutter", + "react-native", + "vite", + "other" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [] }, @@ -31357,7 +31927,17 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "dev-tools", + "starter", + "databases", + "ai", + "messaging", + "utilities" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [] }, @@ -32337,7 +32917,7 @@ "commit", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -32438,7 +33018,7 @@ "commit", "tag" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -33456,7 +34036,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 156, + "weight": 146, "cookies": false, "type": "", "demo": "storage\/list-buckets.md", @@ -33540,7 +34120,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 155, + "weight": 145, "cookies": false, "type": "", "demo": "storage\/create-bucket.md", @@ -33673,7 +34253,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 157, + "weight": 147, "cookies": false, "type": "", "demo": "storage\/get-bucket.md", @@ -33732,7 +34312,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 158, + "weight": 148, "cookies": false, "type": "", "demo": "storage\/update-bucket.md", @@ -33862,7 +34442,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 159, + "weight": 149, "cookies": false, "type": "", "demo": "storage\/delete-bucket.md", @@ -33923,7 +34503,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 161, + "weight": 151, "cookies": false, "type": "", "demo": "storage\/list-files.md", @@ -34020,7 +34600,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 160, + "weight": 150, "cookies": false, "type": "upload", "demo": "storage\/create-file.md", @@ -34119,7 +34699,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 162, + "weight": 152, "cookies": false, "type": "", "demo": "storage\/get-file.md", @@ -34191,7 +34771,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 167, + "weight": 157, "cookies": false, "type": "", "demo": "storage\/update-file.md", @@ -34282,7 +34862,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 168, + "weight": 158, "cookies": false, "type": "", "demo": "storage\/delete-file.md", @@ -34349,7 +34929,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 164, + "weight": 154, "cookies": false, "type": "location", "demo": "storage\/get-file-download.md", @@ -34427,7 +35007,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 163, + "weight": 153, "cookies": false, "type": "location", "demo": "storage\/get-file-preview.md", @@ -34655,7 +35235,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 165, + "weight": 155, "cookies": false, "type": "location", "demo": "storage\/get-file-view.md", @@ -34740,7 +35320,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 169, + "weight": 159, "cookies": false, "type": "", "demo": "storage\/get-usage.md", @@ -34812,7 +35392,7 @@ "x-appwrite": { "method": "getBucketUsage", "group": null, - "weight": 170, + "weight": 160, "cookies": false, "type": "", "demo": "storage\/get-bucket-usage.md", @@ -35955,6 +36535,22 @@ "type": "boolean", "description": "Is table enabled? When set to 'disabled', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.", "x-example": false + }, + "columns": { + "type": "array", + "description": "Array of column definitions to create. Each column should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of column keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -39763,7 +40359,13 @@ "description": "Array of index orders. Maximum of 100 orders are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -41692,7 +42294,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 172, + "weight": 162, "cookies": false, "type": "", "demo": "teams\/list.md", @@ -41779,7 +42381,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 171, + "weight": 161, "cookies": false, "type": "", "demo": "teams\/create.md", @@ -41864,7 +42466,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 173, + "weight": 163, "cookies": false, "type": "", "demo": "teams\/get.md", @@ -41926,7 +42528,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 175, + "weight": 165, "cookies": false, "type": "", "demo": "teams\/update-name.md", @@ -42000,7 +42602,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 177, + "weight": 167, "cookies": false, "type": "", "demo": "teams\/delete.md", @@ -42064,7 +42666,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 184, + "weight": 174, "cookies": false, "type": "", "demo": "teams\/list-logs.md", @@ -42148,7 +42750,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 179, + "weight": 169, "cookies": false, "type": "", "demo": "teams\/list-memberships.md", @@ -42245,7 +42847,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 178, + "weight": 168, "cookies": false, "type": "", "demo": "teams\/create-membership.md", @@ -42309,7 +42911,14 @@ "description": "Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions). Maximum of 100 roles are allowed, each 32 characters long.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "url": { @@ -42356,7 +42965,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 180, + "weight": 170, "cookies": false, "type": "", "demo": "teams\/get-membership.md", @@ -42428,7 +43037,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 181, + "weight": 171, "cookies": false, "type": "", "demo": "teams\/update-membership.md", @@ -42487,7 +43096,14 @@ "description": "An array of strings. Use this param to set the user's roles in the team. A role can be any string. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions). Maximum of 100 roles are allowed, each 32 characters long.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } } }, @@ -42515,7 +43131,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 183, + "weight": 173, "cookies": false, "type": "", "demo": "teams\/delete-membership.md", @@ -42589,7 +43205,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 182, + "weight": 172, "cookies": false, "type": "", "demo": "teams\/update-membership-status.md", @@ -42686,7 +43302,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 174, + "weight": 164, "cookies": false, "type": "", "demo": "teams\/get-prefs.md", @@ -42746,7 +43362,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 176, + "weight": 166, "cookies": false, "type": "", "demo": "teams\/update-prefs.md", @@ -43202,7 +43818,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 194, + "weight": 184, "cookies": false, "type": "", "demo": "users\/list.md", @@ -43286,7 +43902,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 185, + "weight": 175, "cookies": false, "type": "", "demo": "users\/create.md", @@ -43376,7 +43992,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 188, + "weight": 178, "cookies": false, "type": "", "demo": "users\/create-argon-2-user.md", @@ -43461,7 +44077,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 186, + "weight": 176, "cookies": false, "type": "", "demo": "users\/create-bcrypt-user.md", @@ -43546,7 +44162,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 202, + "weight": 192, "cookies": false, "type": "", "demo": "users\/list-identities.md", @@ -43625,7 +44241,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 225, + "weight": 215, "cookies": false, "type": "", "demo": "users\/delete-identity.md", @@ -43686,7 +44302,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 187, + "weight": 177, "cookies": false, "type": "", "demo": "users\/create-md-5-user.md", @@ -43771,7 +44387,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 190, + "weight": 180, "cookies": false, "type": "", "demo": "users\/create-ph-pass-user.md", @@ -43856,7 +44472,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 191, + "weight": 181, "cookies": false, "type": "", "demo": "users\/create-scrypt-user.md", @@ -43971,7 +44587,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 192, + "weight": 182, "cookies": false, "type": "", "demo": "users\/create-scrypt-modified-user.md", @@ -44074,7 +44690,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 189, + "weight": 179, "cookies": false, "type": "", "demo": "users\/create-sha-user.md", @@ -44179,7 +44795,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 227, + "weight": 217, "cookies": false, "type": "", "demo": "users\/get-usage.md", @@ -44251,7 +44867,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 195, + "weight": 185, "cookies": false, "type": "", "demo": "users\/get.md", @@ -44303,7 +44919,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 223, + "weight": 213, "cookies": false, "type": "", "demo": "users\/delete.md", @@ -44364,7 +44980,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 208, + "weight": 198, "cookies": false, "type": "", "demo": "users\/update-email.md", @@ -44444,7 +45060,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 226, + "weight": 216, "cookies": false, "type": "", "demo": "users\/create-jwt.md", @@ -44526,7 +45142,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 204, + "weight": 194, "cookies": false, "type": "", "demo": "users\/update-labels.md", @@ -44609,7 +45225,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 200, + "weight": 190, "cookies": false, "type": "", "demo": "users\/list-logs.md", @@ -44694,7 +45310,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 199, + "weight": 189, "cookies": false, "type": "", "demo": "users\/list-memberships.md", @@ -44790,7 +45406,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 213, + "weight": 203, "cookies": false, "type": "", "demo": "users\/update-mfa.md", @@ -44921,7 +45537,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 218, + "weight": 208, "cookies": false, "type": "", "demo": "users\/delete-mfa-authenticator.md", @@ -45053,7 +45669,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 214, + "weight": 204, "cookies": false, "type": "", "demo": "users\/list-mfa-factors.md", @@ -45168,7 +45784,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 215, + "weight": 205, "cookies": false, "type": "", "demo": "users\/get-mfa-recovery-codes.md", @@ -45281,7 +45897,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 217, + "weight": 207, "cookies": false, "type": "", "demo": "users\/update-mfa-recovery-codes.md", @@ -45394,7 +46010,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 216, + "weight": 206, "cookies": false, "type": "", "demo": "users\/create-mfa-recovery-codes.md", @@ -45509,7 +46125,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 206, + "weight": 196, "cookies": false, "type": "", "demo": "users\/update-name.md", @@ -45589,7 +46205,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 207, + "weight": 197, "cookies": false, "type": "", "demo": "users\/update-password.md", @@ -45669,7 +46285,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 209, + "weight": 199, "cookies": false, "type": "", "demo": "users\/update-phone.md", @@ -45749,7 +46365,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 196, + "weight": 186, "cookies": false, "type": "", "demo": "users\/get-prefs.md", @@ -45808,7 +46424,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 211, + "weight": 201, "cookies": false, "type": "", "demo": "users\/update-prefs.md", @@ -45888,7 +46504,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 198, + "weight": 188, "cookies": false, "type": "", "demo": "users\/list-sessions.md", @@ -45958,7 +46574,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 219, + "weight": 209, "cookies": false, "type": "", "demo": "users\/create-session.md", @@ -46010,7 +46626,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 222, + "weight": 212, "cookies": false, "type": "", "demo": "users\/delete-sessions.md", @@ -46064,7 +46680,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 221, + "weight": 211, "cookies": false, "type": "", "demo": "users\/delete-session.md", @@ -46135,7 +46751,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 203, + "weight": 193, "cookies": false, "type": "", "demo": "users\/update-status.md", @@ -46215,7 +46831,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 201, + "weight": 191, "cookies": false, "type": "", "demo": "users\/list-targets.md", @@ -46299,7 +46915,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 193, + "weight": 183, "cookies": false, "type": "", "demo": "users\/create-target.md", @@ -46409,7 +47025,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 197, + "weight": 187, "cookies": false, "type": "", "demo": "users\/get-target.md", @@ -46479,7 +47095,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 212, + "weight": 202, "cookies": false, "type": "", "demo": "users\/update-target.md", @@ -46568,7 +47184,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 224, + "weight": 214, "cookies": false, "type": "", "demo": "users\/delete-target.md", @@ -46640,7 +47256,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 220, + "weight": 210, "cookies": false, "type": "", "demo": "users\/create-token.md", @@ -46722,7 +47338,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 210, + "weight": 200, "cookies": false, "type": "", "demo": "users\/update-email-verification.md", @@ -46802,7 +47418,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 205, + "weight": 195, "cookies": false, "type": "", "demo": "users\/update-phone-verification.md", @@ -46882,7 +47498,7 @@ "x-appwrite": { "method": "createRepositoryDetection", "group": "repositories", - "weight": 231, + "weight": 221, "cookies": false, "type": "", "demo": "vcs\/create-repository-detection.md", @@ -46978,7 +47594,7 @@ "x-appwrite": { "method": "listRepositories", "group": "repositories", - "weight": 232, + "weight": 222, "cookies": false, "type": "", "demo": "vcs\/list-repositories.md", @@ -47037,6 +47653,19 @@ "default": "" }, "in": "query" + }, + { + "name": "queries", + "description": "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", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" } ] }, @@ -47063,7 +47692,7 @@ "x-appwrite": { "method": "createRepository", "group": "repositories", - "weight": 233, + "weight": 223, "cookies": false, "type": "", "demo": "vcs\/create-repository.md", @@ -47148,7 +47777,7 @@ "x-appwrite": { "method": "getRepository", "group": "repositories", - "weight": 234, + "weight": 224, "cookies": false, "type": "", "demo": "vcs\/get-repository.md", @@ -47218,7 +47847,7 @@ "x-appwrite": { "method": "listRepositoryBranches", "group": "repositories", - "weight": 235, + "weight": 225, "cookies": false, "type": "", "demo": "vcs\/list-repository-branches.md", @@ -47288,7 +47917,7 @@ "x-appwrite": { "method": "getRepositoryContents", "group": "repositories", - "weight": 230, + "weight": 220, "cookies": false, "type": "", "demo": "vcs\/get-repository-contents.md", @@ -47373,7 +48002,7 @@ "x-appwrite": { "method": "updateExternalDeployments", "group": "repositories", - "weight": 240, + "weight": 230, "cookies": false, "type": "", "demo": "vcs\/update-external-deployments.md", @@ -47462,7 +48091,7 @@ "x-appwrite": { "method": "listInstallations", "group": "installations", - "weight": 237, + "weight": 227, "cookies": false, "type": "", "demo": "vcs\/list-installations.md", @@ -47547,7 +48176,7 @@ "x-appwrite": { "method": "getInstallation", "group": "installations", - "weight": 238, + "weight": 228, "cookies": false, "type": "", "demo": "vcs\/get-installation.md", @@ -47598,7 +48227,7 @@ "x-appwrite": { "method": "deleteInstallation", "group": "installations", - "weight": 239, + "weight": 229, "cookies": false, "type": "", "demo": "vcs\/delete-installation.md", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index 2cb6a74853..1803b42b53 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -260,7 +260,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 58, + "weight": 48, "cookies": false, "type": "", "demo": "account\/list-identities.md", @@ -331,7 +331,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 59, + "weight": 49, "cookies": false, "type": "", "demo": "account\/delete-identity.md", @@ -522,7 +522,7 @@ "x-appwrite": { "method": "updateMFA", "group": "mfa", - "weight": 45, + "weight": 306, "cookies": false, "type": "", "demo": "account\/update-mfa.md", @@ -593,7 +593,7 @@ "x-appwrite": { "method": "createMfaAuthenticator", "group": "mfa", - "weight": 47, + "weight": 308, "cookies": false, "type": "", "demo": "account\/create-mfa-authenticator.md", @@ -716,7 +716,7 @@ "x-appwrite": { "method": "updateMfaAuthenticator", "group": "mfa", - "weight": 48, + "weight": 309, "cookies": false, "type": "", "demo": "account\/update-mfa-authenticator.md", @@ -855,7 +855,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 52, + "weight": 310, "cookies": false, "type": "", "demo": "account\/delete-mfa-authenticator.md", @@ -954,7 +954,7 @@ ] } }, - "\/account\/mfa\/challenge": { + "\/account\/mfa\/challenges": { "post": { "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", @@ -978,7 +978,7 @@ "x-appwrite": { "method": "createMfaChallenge", "group": "mfa", - "weight": 53, + "weight": 314, "cookies": false, "type": "", "demo": "account\/create-mfa-challenge.md", @@ -1106,7 +1106,7 @@ "x-appwrite": { "method": "updateMfaChallenge", "group": "mfa", - "weight": 54, + "weight": 315, "cookies": false, "type": "", "demo": "account\/update-mfa-challenge.md", @@ -1243,7 +1243,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 46, + "weight": 307, "cookies": false, "type": "", "demo": "account\/list-mfa-factors.md", @@ -1343,7 +1343,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 51, + "weight": 313, "cookies": false, "type": "", "demo": "account\/get-mfa-recovery-codes.md", @@ -1441,7 +1441,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 49, + "weight": 311, "cookies": false, "type": "", "demo": "account\/create-mfa-recovery-codes.md", @@ -1539,7 +1539,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 50, + "weight": 312, "cookies": false, "type": "", "demo": "account\/update-mfa-recovery-codes.md", @@ -3584,7 +3584,7 @@ "x-appwrite": { "method": "getBrowser", "group": null, - "weight": 61, + "weight": 51, "cookies": false, "type": "location", "demo": "avatars\/get-browser.md", @@ -3712,7 +3712,7 @@ "x-appwrite": { "method": "getCreditCard", "group": null, - "weight": 60, + "weight": 50, "cookies": false, "type": "location", "demo": "avatars\/get-credit-card.md", @@ -3846,7 +3846,7 @@ "x-appwrite": { "method": "getFavicon", "group": null, - "weight": 64, + "weight": 54, "cookies": false, "type": "location", "demo": "avatars\/get-favicon.md", @@ -3906,7 +3906,7 @@ "x-appwrite": { "method": "getFlag", "group": null, - "weight": 62, + "weight": 52, "cookies": false, "type": "location", "demo": "avatars\/get-flag.md", @@ -4396,7 +4396,7 @@ "x-appwrite": { "method": "getImage", "group": null, - "weight": 63, + "weight": 53, "cookies": false, "type": "location", "demo": "avatars\/get-image.md", @@ -4480,7 +4480,7 @@ "x-appwrite": { "method": "getInitials", "group": null, - "weight": 66, + "weight": 56, "cookies": false, "type": "location", "demo": "avatars\/get-initials.md", @@ -4574,7 +4574,7 @@ "x-appwrite": { "method": "getQR", "group": null, - "weight": 65, + "weight": 55, "cookies": false, "type": "location", "demo": "avatars\/get-qr.md", @@ -4668,7 +4668,7 @@ "x-appwrite": { "method": "getScreenshot", "group": null, - "weight": 67, + "weight": 57, "cookies": false, "type": "location", "demo": "avatars\/get-screenshot.md", @@ -4714,7 +4714,7 @@ "required": false, "schema": { "type": "object", - "x-example": "{}", + "x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}", "default": {} }, "in": "query" @@ -4726,7 +4726,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1920", "default": 1280 }, "in": "query" @@ -4738,7 +4738,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1080", "default": 720 }, "in": "query" @@ -4750,7 +4750,7 @@ "schema": { "type": "number", "format": "float", - "x-example": 0.1, + "x-example": "2", "default": 1 }, "in": "query" @@ -4761,7 +4761,7 @@ "required": false, "schema": { "type": "string", - "x-example": "light", + "x-example": "dark", "enum": [ "light", "dark" @@ -4778,7 +4778,7 @@ "required": false, "schema": { "type": "string", - "x-example": "", + "x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15", "default": "" }, "in": "query" @@ -4789,7 +4789,7 @@ "required": false, "schema": { "type": "boolean", - "x-example": false, + "x-example": "true", "default": false }, "in": "query" @@ -4800,7 +4800,7 @@ "required": false, "schema": { "type": "string", - "x-example": "", + "x-example": "en-US", "default": "" }, "in": "query" @@ -4811,7 +4811,7 @@ "required": false, "schema": { "type": "string", - "x-example": "africa\/abidjan", + "x-example": "america\/new_york", "enum": [ "africa\/abidjan", "africa\/accra", @@ -5246,7 +5246,7 @@ "schema": { "type": "number", "format": "float", - "x-example": -90, + "x-example": "37.7749", "default": 0 }, "in": "query" @@ -5258,7 +5258,7 @@ "schema": { "type": "number", "format": "float", - "x-example": -180, + "x-example": "-122.4194", "default": 0 }, "in": "query" @@ -5270,7 +5270,7 @@ "schema": { "type": "number", "format": "float", - "x-example": 0, + "x-example": "100", "default": 0 }, "in": "query" @@ -5281,7 +5281,7 @@ "required": false, "schema": { "type": "boolean", - "x-example": false, + "x-example": "true", "default": false }, "in": "query" @@ -5293,8 +5293,33 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "geolocation", + "camera", + "microphone", + "notifications", + "midi", + "push", + "clipboard-read", + "clipboard-write", + "payment-handler", + "usb", + "bluetooth", + "accelerometer", + "gyroscope", + "magnetometer", + "ambient-light-sensor", + "background-sync", + "persistent-storage", + "screen-wake-lock", + "web-share", + "xr-spatial-tracking" + ], + "x-enum-name": "BrowserPermission", + "x-enum-keys": [] }, + "x-example": "[\"geolocation\",\"notifications\"]", "default": [] }, "in": "query" @@ -5306,7 +5331,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "3", "default": 0 }, "in": "query" @@ -5318,7 +5343,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "800", "default": 0 }, "in": "query" @@ -5330,7 +5355,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "600", "default": 0 }, "in": "query" @@ -5342,7 +5367,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": -1, + "x-example": "85", "default": -1 }, "in": "query" @@ -5353,7 +5378,7 @@ "required": false, "schema": { "type": "string", - "x-example": "jpg", + "x-example": "jpeg", "enum": [ "jpg", "jpeg", @@ -6533,6 +6558,22 @@ "type": "boolean", "description": "Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.", "x-example": false + }, + "attributes": { + "type": "array", + "description": "Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of attribute keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -11801,7 +11842,13 @@ "description": "Array of index orders. Maximum of 100 orders are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -12261,7 +12308,66 @@ "description": "List of scopes allowed for API key auto-generated for every execution. Maximum of 100 scopes are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -12665,7 +12771,66 @@ "description": "List of scopes allowed for API Key auto-generated for every execution. Maximum of 100 scopes are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -13213,7 +13378,7 @@ "branch", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -13314,7 +13479,7 @@ "branch", "commit" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -14387,7 +14552,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 251, + "weight": 241, "cookies": false, "type": "graphql", "demo": "graphql\/query.md", @@ -14441,7 +14606,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 250, + "weight": 240, "cookies": false, "type": "graphql", "demo": "graphql\/mutation.md", @@ -14495,7 +14660,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 79, + "weight": 69, "cookies": false, "type": "", "demo": "health\/get.md", @@ -14545,7 +14710,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 100, + "weight": 90, "cookies": false, "type": "", "demo": "health\/get-antivirus.md", @@ -14595,7 +14760,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 82, + "weight": 72, "cookies": false, "type": "", "demo": "health\/get-cache.md", @@ -14645,7 +14810,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 87, + "weight": 77, "cookies": false, "type": "", "demo": "health\/get-certificate.md", @@ -14706,7 +14871,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 81, + "weight": 71, "cookies": false, "type": "", "demo": "health\/get-db.md", @@ -14756,7 +14921,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 83, + "weight": 73, "cookies": false, "type": "", "demo": "health\/get-pub-sub.md", @@ -14806,7 +14971,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 89, + "weight": 79, "cookies": false, "type": "", "demo": "health\/get-queue-builds.md", @@ -14869,7 +15034,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 88, + "weight": 78, "cookies": false, "type": "", "demo": "health\/get-queue-certificates.md", @@ -14932,7 +15097,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 90, + "weight": 80, "cookies": false, "type": "", "demo": "health\/get-queue-databases.md", @@ -15006,7 +15171,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 91, + "weight": 81, "cookies": false, "type": "", "demo": "health\/get-queue-deletes.md", @@ -15069,7 +15234,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 101, + "weight": 91, "cookies": false, "type": "", "demo": "health\/get-failed-jobs.md", @@ -15158,7 +15323,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 95, + "weight": 85, "cookies": false, "type": "", "demo": "health\/get-queue-functions.md", @@ -15221,7 +15386,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 86, + "weight": 76, "cookies": false, "type": "", "demo": "health\/get-queue-logs.md", @@ -15284,7 +15449,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 92, + "weight": 82, "cookies": false, "type": "", "demo": "health\/get-queue-mails.md", @@ -15347,7 +15512,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 93, + "weight": 83, "cookies": false, "type": "", "demo": "health\/get-queue-messaging.md", @@ -15410,7 +15575,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 94, + "weight": 84, "cookies": false, "type": "", "demo": "health\/get-queue-migrations.md", @@ -15473,7 +15638,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 96, + "weight": 86, "cookies": false, "type": "", "demo": "health\/get-queue-stats-resources.md", @@ -15536,7 +15701,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 97, + "weight": 87, "cookies": false, "type": "", "demo": "health\/get-queue-usage.md", @@ -15599,7 +15764,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 85, + "weight": 75, "cookies": false, "type": "", "demo": "health\/get-queue-webhooks.md", @@ -15662,7 +15827,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 99, + "weight": 89, "cookies": false, "type": "", "demo": "health\/get-storage.md", @@ -15712,7 +15877,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 98, + "weight": 88, "cookies": false, "type": "", "demo": "health\/get-storage-local.md", @@ -15762,7 +15927,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 84, + "weight": 74, "cookies": false, "type": "", "demo": "health\/get-time.md", @@ -15812,7 +15977,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 71, + "weight": 61, "cookies": false, "type": "", "demo": "locale\/get.md", @@ -15866,7 +16031,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 72, + "weight": 62, "cookies": false, "type": "", "demo": "locale\/list-codes.md", @@ -15920,7 +16085,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 76, + "weight": 66, "cookies": false, "type": "", "demo": "locale\/list-continents.md", @@ -15974,7 +16139,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 73, + "weight": 63, "cookies": false, "type": "", "demo": "locale\/list-countries.md", @@ -16028,7 +16193,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 74, + "weight": 64, "cookies": false, "type": "", "demo": "locale\/list-countries-eu.md", @@ -16082,7 +16247,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 75, + "weight": 65, "cookies": false, "type": "", "demo": "locale\/list-countries-phones.md", @@ -16136,7 +16301,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 77, + "weight": 67, "cookies": false, "type": "", "demo": "locale\/list-currencies.md", @@ -16190,7 +16355,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 78, + "weight": 68, "cookies": false, "type": "", "demo": "locale\/list-languages.md", @@ -16244,7 +16409,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 308, + "weight": 298, "cookies": false, "type": "", "demo": "messaging\/list-messages.md", @@ -16332,7 +16497,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 305, + "weight": 295, "cookies": false, "type": "", "demo": "messaging\/create-email.md", @@ -16478,7 +16643,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 312, + "weight": 302, "cookies": false, "type": "", "demo": "messaging\/update-email.md", @@ -16636,7 +16801,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 307, + "weight": 297, "cookies": false, "type": "", "demo": "messaging\/create-push.md", @@ -16813,7 +16978,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 314, + "weight": 304, "cookies": false, "type": "", "demo": "messaging\/update-push.md", @@ -17010,7 +17175,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 306, + "weight": 296, "cookies": false, "type": "", "demo": "messaging\/create-sms.md", @@ -17191,7 +17356,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 313, + "weight": 303, "cookies": false, "type": "", "demo": "messaging\/update-sms.md", @@ -17378,7 +17543,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 311, + "weight": 301, "cookies": false, "type": "", "demo": "messaging\/get-message.md", @@ -17432,7 +17597,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 315, + "weight": 305, "cookies": false, "type": "", "demo": "messaging\/delete.md", @@ -17495,7 +17660,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 309, + "weight": 299, "cookies": false, "type": "", "demo": "messaging\/list-message-logs.md", @@ -17582,7 +17747,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 310, + "weight": 300, "cookies": false, "type": "", "demo": "messaging\/list-targets.md", @@ -17669,7 +17834,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 279, + "weight": 269, "cookies": false, "type": "", "demo": "messaging\/list-providers.md", @@ -17757,7 +17922,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 278, + "weight": 268, "cookies": false, "type": "", "demo": "messaging\/create-apns-provider.md", @@ -17936,7 +18101,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 292, + "weight": 282, "cookies": false, "type": "", "demo": "messaging\/update-apns-provider.md", @@ -18117,7 +18282,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 277, + "weight": 267, "cookies": false, "type": "", "demo": "messaging\/create-fcm-provider.md", @@ -18269,7 +18434,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 291, + "weight": 281, "cookies": false, "type": "", "demo": "messaging\/update-fcm-provider.md", @@ -18422,7 +18587,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 268, + "weight": 258, "cookies": false, "type": "", "demo": "messaging\/create-mailgun-provider.md", @@ -18540,7 +18705,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 282, + "weight": 272, "cookies": false, "type": "", "demo": "messaging\/update-mailgun-provider.md", @@ -18661,7 +18826,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 272, + "weight": 262, "cookies": false, "type": "", "demo": "messaging\/create-msg-91-provider.md", @@ -18758,7 +18923,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 286, + "weight": 276, "cookies": false, "type": "", "demo": "messaging\/update-msg-91-provider.md", @@ -18858,7 +19023,7 @@ "x-appwrite": { "method": "createResendProvider", "group": "providers", - "weight": 270, + "weight": 260, "cookies": false, "type": "", "demo": "messaging\/create-resend-provider.md", @@ -18965,7 +19130,7 @@ "x-appwrite": { "method": "updateResendProvider", "group": "providers", - "weight": 284, + "weight": 274, "cookies": false, "type": "", "demo": "messaging\/update-resend-provider.md", @@ -19075,7 +19240,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 269, + "weight": 259, "cookies": false, "type": "", "demo": "messaging\/create-sendgrid-provider.md", @@ -19182,7 +19347,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 283, + "weight": 273, "cookies": false, "type": "", "demo": "messaging\/update-sendgrid-provider.md", @@ -19292,7 +19457,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 271, + "weight": 261, "cookies": false, "type": "", "demo": "messaging\/create-smtp-provider.md", @@ -19523,7 +19688,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 285, + "weight": 275, "cookies": false, "type": "", "demo": "messaging\/update-smtp-provider.md", @@ -19754,7 +19919,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 273, + "weight": 263, "cookies": false, "type": "", "demo": "messaging\/create-telesign-provider.md", @@ -19851,7 +20016,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 287, + "weight": 277, "cookies": false, "type": "", "demo": "messaging\/update-telesign-provider.md", @@ -19951,7 +20116,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 274, + "weight": 264, "cookies": false, "type": "", "demo": "messaging\/create-textmagic-provider.md", @@ -20048,7 +20213,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 288, + "weight": 278, "cookies": false, "type": "", "demo": "messaging\/update-textmagic-provider.md", @@ -20148,7 +20313,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 275, + "weight": 265, "cookies": false, "type": "", "demo": "messaging\/create-twilio-provider.md", @@ -20245,7 +20410,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 289, + "weight": 279, "cookies": false, "type": "", "demo": "messaging\/update-twilio-provider.md", @@ -20345,7 +20510,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 276, + "weight": 266, "cookies": false, "type": "", "demo": "messaging\/create-vonage-provider.md", @@ -20442,7 +20607,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 290, + "weight": 280, "cookies": false, "type": "", "demo": "messaging\/update-vonage-provider.md", @@ -20542,7 +20707,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 281, + "weight": 271, "cookies": false, "type": "", "demo": "messaging\/get-provider.md", @@ -20596,7 +20761,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 293, + "weight": 283, "cookies": false, "type": "", "demo": "messaging\/delete-provider.md", @@ -20659,7 +20824,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 280, + "weight": 270, "cookies": false, "type": "", "demo": "messaging\/list-provider-logs.md", @@ -20746,7 +20911,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 302, + "weight": 292, "cookies": false, "type": "", "demo": "messaging\/list-subscriber-logs.md", @@ -20833,7 +20998,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 295, + "weight": 285, "cookies": false, "type": "", "demo": "messaging\/list-topics.md", @@ -20919,7 +21084,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 294, + "weight": 284, "cookies": false, "type": "", "demo": "messaging\/create-topic.md", @@ -21003,7 +21168,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 297, + "weight": 287, "cookies": false, "type": "", "demo": "messaging\/get-topic.md", @@ -21064,7 +21229,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 298, + "weight": 288, "cookies": false, "type": "", "demo": "messaging\/update-topic.md", @@ -21144,7 +21309,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 299, + "weight": 289, "cookies": false, "type": "", "demo": "messaging\/delete-topic.md", @@ -21207,7 +21372,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 296, + "weight": 286, "cookies": false, "type": "", "demo": "messaging\/list-topic-logs.md", @@ -21294,7 +21459,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 301, + "weight": 291, "cookies": false, "type": "", "demo": "messaging\/list-subscribers.md", @@ -21390,7 +21555,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 300, + "weight": 290, "cookies": false, "type": "", "demo": "messaging\/create-subscriber.md", @@ -21482,7 +21647,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 303, + "weight": 293, "cookies": false, "type": "", "demo": "messaging\/get-subscriber.md", @@ -21546,7 +21711,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 304, + "weight": 294, "cookies": false, "type": "", "demo": "messaging\/delete-subscriber.md", @@ -22861,7 +23026,7 @@ "commit", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -22963,7 +23128,7 @@ "commit", "tag" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -23911,7 +24076,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 156, + "weight": 146, "cookies": false, "type": "", "demo": "storage\/list-buckets.md", @@ -23996,7 +24161,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 155, + "weight": 145, "cookies": false, "type": "", "demo": "storage\/create-bucket.md", @@ -24130,7 +24295,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 157, + "weight": 147, "cookies": false, "type": "", "demo": "storage\/get-bucket.md", @@ -24190,7 +24355,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 158, + "weight": 148, "cookies": false, "type": "", "demo": "storage\/update-bucket.md", @@ -24321,7 +24486,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 159, + "weight": 149, "cookies": false, "type": "", "demo": "storage\/delete-bucket.md", @@ -24383,7 +24548,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 161, + "weight": 151, "cookies": false, "type": "", "demo": "storage\/list-files.md", @@ -24482,7 +24647,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 160, + "weight": 150, "cookies": false, "type": "upload", "demo": "storage\/create-file.md", @@ -24583,7 +24748,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 162, + "weight": 152, "cookies": false, "type": "", "demo": "storage\/get-file.md", @@ -24657,7 +24822,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 167, + "weight": 157, "cookies": false, "type": "", "demo": "storage\/update-file.md", @@ -24750,7 +24915,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 168, + "weight": 158, "cookies": false, "type": "", "demo": "storage\/delete-file.md", @@ -24819,7 +24984,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 164, + "weight": 154, "cookies": false, "type": "location", "demo": "storage\/get-file-download.md", @@ -24899,7 +25064,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 163, + "weight": 153, "cookies": false, "type": "location", "demo": "storage\/get-file-preview.md", @@ -25129,7 +25294,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 165, + "weight": 155, "cookies": false, "type": "location", "demo": "storage\/get-file-view.md", @@ -26199,6 +26364,22 @@ "type": "boolean", "description": "Is table enabled? When set to 'disabled', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.", "x-example": false + }, + "columns": { + "type": "array", + "description": "Array of column definitions to create. Each column should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of column keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -30041,7 +30222,13 @@ "description": "Array of index orders. Maximum of 100 orders are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -31608,7 +31795,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 172, + "weight": 162, "cookies": false, "type": "", "demo": "teams\/list.md", @@ -31697,7 +31884,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 171, + "weight": 161, "cookies": false, "type": "", "demo": "teams\/create.md", @@ -31784,7 +31971,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 173, + "weight": 163, "cookies": false, "type": "", "demo": "teams\/get.md", @@ -31848,7 +32035,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 175, + "weight": 165, "cookies": false, "type": "", "demo": "teams\/update-name.md", @@ -31924,7 +32111,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 177, + "weight": 167, "cookies": false, "type": "", "demo": "teams\/delete.md", @@ -31990,7 +32177,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 179, + "weight": 169, "cookies": false, "type": "", "demo": "teams\/list-memberships.md", @@ -32089,7 +32276,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 178, + "weight": 168, "cookies": false, "type": "", "demo": "teams\/create-membership.md", @@ -32155,7 +32342,14 @@ "description": "Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions). Maximum of 100 roles are allowed, each 32 characters long.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "url": { @@ -32202,7 +32396,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 180, + "weight": 170, "cookies": false, "type": "", "demo": "teams\/get-membership.md", @@ -32276,7 +32470,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 181, + "weight": 171, "cookies": false, "type": "", "demo": "teams\/update-membership.md", @@ -32337,7 +32531,14 @@ "description": "An array of strings. Use this param to set the user's roles in the team. A role can be any string. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions). Maximum of 100 roles are allowed, each 32 characters long.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } } }, @@ -32365,7 +32566,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 183, + "weight": 173, "cookies": false, "type": "", "demo": "teams\/delete-membership.md", @@ -32441,7 +32642,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 182, + "weight": 172, "cookies": false, "type": "", "demo": "teams\/update-membership-status.md", @@ -32540,7 +32741,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 174, + "weight": 164, "cookies": false, "type": "", "demo": "teams\/get-prefs.md", @@ -32602,7 +32803,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 176, + "weight": 166, "cookies": false, "type": "", "demo": "teams\/update-prefs.md", @@ -33065,7 +33266,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 194, + "weight": 184, "cookies": false, "type": "", "demo": "users\/list.md", @@ -33150,7 +33351,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 185, + "weight": 175, "cookies": false, "type": "", "demo": "users\/create.md", @@ -33241,7 +33442,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 188, + "weight": 178, "cookies": false, "type": "", "demo": "users\/create-argon-2-user.md", @@ -33327,7 +33528,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 186, + "weight": 176, "cookies": false, "type": "", "demo": "users\/create-bcrypt-user.md", @@ -33413,7 +33614,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 202, + "weight": 192, "cookies": false, "type": "", "demo": "users\/list-identities.md", @@ -33493,7 +33694,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 225, + "weight": 215, "cookies": false, "type": "", "demo": "users\/delete-identity.md", @@ -33555,7 +33756,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 187, + "weight": 177, "cookies": false, "type": "", "demo": "users\/create-md-5-user.md", @@ -33641,7 +33842,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 190, + "weight": 180, "cookies": false, "type": "", "demo": "users\/create-ph-pass-user.md", @@ -33727,7 +33928,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 191, + "weight": 181, "cookies": false, "type": "", "demo": "users\/create-scrypt-user.md", @@ -33843,7 +34044,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 192, + "weight": 182, "cookies": false, "type": "", "demo": "users\/create-scrypt-modified-user.md", @@ -33947,7 +34148,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 189, + "weight": 179, "cookies": false, "type": "", "demo": "users\/create-sha-user.md", @@ -34053,7 +34254,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 195, + "weight": 185, "cookies": false, "type": "", "demo": "users\/get.md", @@ -34106,7 +34307,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 223, + "weight": 213, "cookies": false, "type": "", "demo": "users\/delete.md", @@ -34168,7 +34369,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 208, + "weight": 198, "cookies": false, "type": "", "demo": "users\/update-email.md", @@ -34249,7 +34450,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 226, + "weight": 216, "cookies": false, "type": "", "demo": "users\/create-jwt.md", @@ -34332,7 +34533,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 204, + "weight": 194, "cookies": false, "type": "", "demo": "users\/update-labels.md", @@ -34416,7 +34617,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 200, + "weight": 190, "cookies": false, "type": "", "demo": "users\/list-logs.md", @@ -34502,7 +34703,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 199, + "weight": 189, "cookies": false, "type": "", "demo": "users\/list-memberships.md", @@ -34599,7 +34800,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 213, + "weight": 203, "cookies": false, "type": "", "demo": "users\/update-mfa.md", @@ -34733,7 +34934,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 218, + "weight": 208, "cookies": false, "type": "", "demo": "users\/delete-mfa-authenticator.md", @@ -34868,7 +35069,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 214, + "weight": 204, "cookies": false, "type": "", "demo": "users\/list-mfa-factors.md", @@ -34986,7 +35187,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 215, + "weight": 205, "cookies": false, "type": "", "demo": "users\/get-mfa-recovery-codes.md", @@ -35102,7 +35303,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 217, + "weight": 207, "cookies": false, "type": "", "demo": "users\/update-mfa-recovery-codes.md", @@ -35218,7 +35419,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 216, + "weight": 206, "cookies": false, "type": "", "demo": "users\/create-mfa-recovery-codes.md", @@ -35336,7 +35537,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 206, + "weight": 196, "cookies": false, "type": "", "demo": "users\/update-name.md", @@ -35417,7 +35618,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 207, + "weight": 197, "cookies": false, "type": "", "demo": "users\/update-password.md", @@ -35498,7 +35699,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 209, + "weight": 199, "cookies": false, "type": "", "demo": "users\/update-phone.md", @@ -35579,7 +35780,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 196, + "weight": 186, "cookies": false, "type": "", "demo": "users\/get-prefs.md", @@ -35639,7 +35840,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 211, + "weight": 201, "cookies": false, "type": "", "demo": "users\/update-prefs.md", @@ -35720,7 +35921,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 198, + "weight": 188, "cookies": false, "type": "", "demo": "users\/list-sessions.md", @@ -35791,7 +35992,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 219, + "weight": 209, "cookies": false, "type": "", "demo": "users\/create-session.md", @@ -35844,7 +36045,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 222, + "weight": 212, "cookies": false, "type": "", "demo": "users\/delete-sessions.md", @@ -35899,7 +36100,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 221, + "weight": 211, "cookies": false, "type": "", "demo": "users\/delete-session.md", @@ -35971,7 +36172,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 203, + "weight": 193, "cookies": false, "type": "", "demo": "users\/update-status.md", @@ -36052,7 +36253,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 201, + "weight": 191, "cookies": false, "type": "", "demo": "users\/list-targets.md", @@ -36137,7 +36338,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 193, + "weight": 183, "cookies": false, "type": "", "demo": "users\/create-target.md", @@ -36248,7 +36449,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 197, + "weight": 187, "cookies": false, "type": "", "demo": "users\/get-target.md", @@ -36319,7 +36520,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 212, + "weight": 202, "cookies": false, "type": "", "demo": "users\/update-target.md", @@ -36409,7 +36610,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 224, + "weight": 214, "cookies": false, "type": "", "demo": "users\/delete-target.md", @@ -36482,7 +36683,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 220, + "weight": 210, "cookies": false, "type": "", "demo": "users\/create-token.md", @@ -36565,7 +36766,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 210, + "weight": 200, "cookies": false, "type": "", "demo": "users\/update-email-verification.md", @@ -36646,7 +36847,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 205, + "weight": 195, "cookies": false, "type": "", "demo": "users\/update-phone-verification.md", diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 9adb21d960..5d645ac86e 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -258,7 +258,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 58, + "weight": 48, "cookies": false, "type": "", "demo": "account\/list-identities.md", @@ -328,7 +328,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 59, + "weight": 49, "cookies": false, "type": "", "demo": "account\/delete-identity.md", @@ -517,7 +517,7 @@ "x-appwrite": { "method": "updateMFA", "group": "mfa", - "weight": 45, + "weight": 306, "cookies": false, "type": "", "demo": "account\/update-mfa.md", @@ -587,7 +587,7 @@ "x-appwrite": { "method": "createMfaAuthenticator", "group": "mfa", - "weight": 47, + "weight": 308, "cookies": false, "type": "", "demo": "account\/create-mfa-authenticator.md", @@ -707,7 +707,7 @@ "x-appwrite": { "method": "updateMfaAuthenticator", "group": "mfa", - "weight": 48, + "weight": 309, "cookies": false, "type": "", "demo": "account\/update-mfa-authenticator.md", @@ -843,7 +843,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 52, + "weight": 310, "cookies": false, "type": "", "demo": "account\/delete-mfa-authenticator.md", @@ -939,7 +939,7 @@ ] } }, - "\/account\/mfa\/challenge": { + "\/account\/mfa\/challenges": { "post": { "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", @@ -963,7 +963,7 @@ "x-appwrite": { "method": "createMfaChallenge", "group": "mfa", - "weight": 53, + "weight": 314, "cookies": false, "type": "", "demo": "account\/create-mfa-challenge.md", @@ -1091,7 +1091,7 @@ "x-appwrite": { "method": "updateMfaChallenge", "group": "mfa", - "weight": 54, + "weight": 315, "cookies": false, "type": "", "demo": "account\/update-mfa-challenge.md", @@ -1225,7 +1225,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 46, + "weight": 307, "cookies": false, "type": "", "demo": "account\/list-mfa-factors.md", @@ -1322,7 +1322,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 51, + "weight": 313, "cookies": false, "type": "", "demo": "account\/get-mfa-recovery-codes.md", @@ -1417,7 +1417,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 49, + "weight": 311, "cookies": false, "type": "", "demo": "account\/create-mfa-recovery-codes.md", @@ -1512,7 +1512,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 50, + "weight": 312, "cookies": false, "type": "", "demo": "account\/update-mfa-recovery-codes.md", @@ -2918,7 +2918,7 @@ "x-appwrite": { "method": "createPushTarget", "group": "pushTargets", - "weight": 55, + "weight": 45, "cookies": false, "type": "", "demo": "account\/create-push-target.md", @@ -2997,7 +2997,7 @@ "x-appwrite": { "method": "updatePushTarget", "group": "pushTargets", - "weight": 56, + "weight": 46, "cookies": false, "type": "", "demo": "account\/update-push-target.md", @@ -3068,7 +3068,7 @@ "x-appwrite": { "method": "deletePushTarget", "group": "pushTargets", - "weight": 57, + "weight": 47, "cookies": false, "type": "", "demo": "account\/delete-push-target.md", @@ -3879,7 +3879,7 @@ "x-appwrite": { "method": "getBrowser", "group": null, - "weight": 61, + "weight": 51, "cookies": false, "type": "location", "demo": "avatars\/get-browser.md", @@ -4005,7 +4005,7 @@ "x-appwrite": { "method": "getCreditCard", "group": null, - "weight": 60, + "weight": 50, "cookies": false, "type": "location", "demo": "avatars\/get-credit-card.md", @@ -4137,7 +4137,7 @@ "x-appwrite": { "method": "getFavicon", "group": null, - "weight": 64, + "weight": 54, "cookies": false, "type": "location", "demo": "avatars\/get-favicon.md", @@ -4195,7 +4195,7 @@ "x-appwrite": { "method": "getFlag", "group": null, - "weight": 62, + "weight": 52, "cookies": false, "type": "location", "demo": "avatars\/get-flag.md", @@ -4683,7 +4683,7 @@ "x-appwrite": { "method": "getImage", "group": null, - "weight": 63, + "weight": 53, "cookies": false, "type": "location", "demo": "avatars\/get-image.md", @@ -4765,7 +4765,7 @@ "x-appwrite": { "method": "getInitials", "group": null, - "weight": 66, + "weight": 56, "cookies": false, "type": "location", "demo": "avatars\/get-initials.md", @@ -4857,7 +4857,7 @@ "x-appwrite": { "method": "getQR", "group": null, - "weight": 65, + "weight": 55, "cookies": false, "type": "location", "demo": "avatars\/get-qr.md", @@ -4949,7 +4949,7 @@ "x-appwrite": { "method": "getScreenshot", "group": null, - "weight": 67, + "weight": 57, "cookies": false, "type": "location", "demo": "avatars\/get-screenshot.md", @@ -4993,7 +4993,7 @@ "required": false, "schema": { "type": "object", - "x-example": "{}", + "x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}", "default": {} }, "in": "query" @@ -5005,7 +5005,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1920", "default": 1280 }, "in": "query" @@ -5017,7 +5017,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1080", "default": 720 }, "in": "query" @@ -5029,7 +5029,7 @@ "schema": { "type": "number", "format": "float", - "x-example": 0.1, + "x-example": "2", "default": 1 }, "in": "query" @@ -5040,7 +5040,7 @@ "required": false, "schema": { "type": "string", - "x-example": "light", + "x-example": "dark", "enum": [ "light", "dark" @@ -5057,7 +5057,7 @@ "required": false, "schema": { "type": "string", - "x-example": "", + "x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15", "default": "" }, "in": "query" @@ -5068,7 +5068,7 @@ "required": false, "schema": { "type": "boolean", - "x-example": false, + "x-example": "true", "default": false }, "in": "query" @@ -5079,7 +5079,7 @@ "required": false, "schema": { "type": "string", - "x-example": "", + "x-example": "en-US", "default": "" }, "in": "query" @@ -5090,7 +5090,7 @@ "required": false, "schema": { "type": "string", - "x-example": "africa\/abidjan", + "x-example": "america\/new_york", "enum": [ "africa\/abidjan", "africa\/accra", @@ -5525,7 +5525,7 @@ "schema": { "type": "number", "format": "float", - "x-example": -90, + "x-example": "37.7749", "default": 0 }, "in": "query" @@ -5537,7 +5537,7 @@ "schema": { "type": "number", "format": "float", - "x-example": -180, + "x-example": "-122.4194", "default": 0 }, "in": "query" @@ -5549,7 +5549,7 @@ "schema": { "type": "number", "format": "float", - "x-example": 0, + "x-example": "100", "default": 0 }, "in": "query" @@ -5560,7 +5560,7 @@ "required": false, "schema": { "type": "boolean", - "x-example": false, + "x-example": "true", "default": false }, "in": "query" @@ -5572,8 +5572,33 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "geolocation", + "camera", + "microphone", + "notifications", + "midi", + "push", + "clipboard-read", + "clipboard-write", + "payment-handler", + "usb", + "bluetooth", + "accelerometer", + "gyroscope", + "magnetometer", + "ambient-light-sensor", + "background-sync", + "persistent-storage", + "screen-wake-lock", + "web-share", + "xr-spatial-tracking" + ], + "x-enum-name": "BrowserPermission", + "x-enum-keys": [] }, + "x-example": "[\"geolocation\",\"notifications\"]", "default": [] }, "in": "query" @@ -5585,7 +5610,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "3", "default": 0 }, "in": "query" @@ -5597,7 +5622,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "800", "default": 0 }, "in": "query" @@ -5609,7 +5634,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "600", "default": 0 }, "in": "query" @@ -5621,7 +5646,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": -1, + "x-example": "85", "default": -1 }, "in": "query" @@ -5632,7 +5657,7 @@ "required": false, "schema": { "type": "string", - "x-example": "jpg", + "x-example": "jpeg", "enum": [ "jpg", "jpeg", @@ -7370,7 +7395,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 251, + "weight": 241, "cookies": false, "type": "graphql", "demo": "graphql\/query.md", @@ -7422,7 +7447,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 250, + "weight": 240, "cookies": false, "type": "graphql", "demo": "graphql\/mutation.md", @@ -7474,7 +7499,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 71, + "weight": 61, "cookies": false, "type": "", "demo": "locale\/get.md", @@ -7526,7 +7551,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 72, + "weight": 62, "cookies": false, "type": "", "demo": "locale\/list-codes.md", @@ -7578,7 +7603,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 76, + "weight": 66, "cookies": false, "type": "", "demo": "locale\/list-continents.md", @@ -7630,7 +7655,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 73, + "weight": 63, "cookies": false, "type": "", "demo": "locale\/list-countries.md", @@ -7682,7 +7707,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 74, + "weight": 64, "cookies": false, "type": "", "demo": "locale\/list-countries-eu.md", @@ -7734,7 +7759,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 75, + "weight": 65, "cookies": false, "type": "", "demo": "locale\/list-countries-phones.md", @@ -7786,7 +7811,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 77, + "weight": 67, "cookies": false, "type": "", "demo": "locale\/list-currencies.md", @@ -7838,7 +7863,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 78, + "weight": 68, "cookies": false, "type": "", "demo": "locale\/list-languages.md", @@ -7890,7 +7915,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 300, + "weight": 290, "cookies": false, "type": "", "demo": "messaging\/create-subscriber.md", @@ -7973,7 +7998,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 304, + "weight": 294, "cookies": false, "type": "", "demo": "messaging\/delete-subscriber.md", @@ -8048,7 +8073,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 161, + "weight": 151, "cookies": false, "type": "", "demo": "storage\/list-files.md", @@ -8145,7 +8170,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 160, + "weight": 150, "cookies": false, "type": "upload", "demo": "storage\/create-file.md", @@ -8244,7 +8269,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 162, + "weight": 152, "cookies": false, "type": "", "demo": "storage\/get-file.md", @@ -8316,7 +8341,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 167, + "weight": 157, "cookies": false, "type": "", "demo": "storage\/update-file.md", @@ -8407,7 +8432,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 168, + "weight": 158, "cookies": false, "type": "", "demo": "storage\/delete-file.md", @@ -8474,7 +8499,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 164, + "weight": 154, "cookies": false, "type": "location", "demo": "storage\/get-file-download.md", @@ -8552,7 +8577,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 163, + "weight": 153, "cookies": false, "type": "location", "demo": "storage\/get-file-preview.md", @@ -8780,7 +8805,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 165, + "weight": 155, "cookies": false, "type": "location", "demo": "storage\/get-file-view.md", @@ -10281,7 +10306,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 172, + "weight": 162, "cookies": false, "type": "", "demo": "teams\/list.md", @@ -10368,7 +10393,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 171, + "weight": 161, "cookies": false, "type": "", "demo": "teams\/create.md", @@ -10453,7 +10478,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 173, + "weight": 163, "cookies": false, "type": "", "demo": "teams\/get.md", @@ -10515,7 +10540,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 175, + "weight": 165, "cookies": false, "type": "", "demo": "teams\/update-name.md", @@ -10589,7 +10614,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 177, + "weight": 167, "cookies": false, "type": "", "demo": "teams\/delete.md", @@ -10653,7 +10678,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 179, + "weight": 169, "cookies": false, "type": "", "demo": "teams\/list-memberships.md", @@ -10750,7 +10775,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 178, + "weight": 168, "cookies": false, "type": "", "demo": "teams\/create-membership.md", @@ -10814,7 +10839,14 @@ "description": "Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions). Maximum of 100 roles are allowed, each 32 characters long.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "url": { @@ -10861,7 +10893,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 180, + "weight": 170, "cookies": false, "type": "", "demo": "teams\/get-membership.md", @@ -10933,7 +10965,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 181, + "weight": 171, "cookies": false, "type": "", "demo": "teams\/update-membership.md", @@ -10992,7 +11024,14 @@ "description": "An array of strings. Use this param to set the user's roles in the team. A role can be any string. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions). Maximum of 100 roles are allowed, each 32 characters long.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } } }, @@ -11020,7 +11059,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 183, + "weight": 173, "cookies": false, "type": "", "demo": "teams\/delete-membership.md", @@ -11094,7 +11133,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 182, + "weight": 172, "cookies": false, "type": "", "demo": "teams\/update-membership-status.md", @@ -11192,7 +11231,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 174, + "weight": 164, "cookies": false, "type": "", "demo": "teams\/get-prefs.md", @@ -11253,7 +11292,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 176, + "weight": 166, "cookies": false, "type": "", "demo": "teams\/update-prefs.md", diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index fded6c46b0..e6a6bdfee5 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -295,7 +295,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 58, + "weight": 48, "cookies": false, "type": "", "demo": "account\/list-identities.md", @@ -364,7 +364,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 59, + "weight": 49, "cookies": false, "type": "", "demo": "account\/delete-identity.md", @@ -551,7 +551,7 @@ "x-appwrite": { "method": "updateMFA", "group": "mfa", - "weight": 45, + "weight": 306, "cookies": false, "type": "", "demo": "account\/update-mfa.md", @@ -620,7 +620,7 @@ "x-appwrite": { "method": "createMfaAuthenticator", "group": "mfa", - "weight": 47, + "weight": 308, "cookies": false, "type": "", "demo": "account\/create-mfa-authenticator.md", @@ -739,7 +739,7 @@ "x-appwrite": { "method": "updateMfaAuthenticator", "group": "mfa", - "weight": 48, + "weight": 309, "cookies": false, "type": "", "demo": "account\/update-mfa-authenticator.md", @@ -874,7 +874,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 52, + "weight": 310, "cookies": false, "type": "", "demo": "account\/delete-mfa-authenticator.md", @@ -969,7 +969,7 @@ ] } }, - "\/account\/mfa\/challenge": { + "\/account\/mfa\/challenges": { "post": { "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", @@ -993,7 +993,7 @@ "x-appwrite": { "method": "createMfaChallenge", "group": "mfa", - "weight": 53, + "weight": 314, "cookies": false, "type": "", "demo": "account\/create-mfa-challenge.md", @@ -1121,7 +1121,7 @@ "x-appwrite": { "method": "updateMfaChallenge", "group": "mfa", - "weight": 54, + "weight": 315, "cookies": false, "type": "", "demo": "account\/update-mfa-challenge.md", @@ -1254,7 +1254,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 46, + "weight": 307, "cookies": false, "type": "", "demo": "account\/list-mfa-factors.md", @@ -1350,7 +1350,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 51, + "weight": 313, "cookies": false, "type": "", "demo": "account\/get-mfa-recovery-codes.md", @@ -1444,7 +1444,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 49, + "weight": 311, "cookies": false, "type": "", "demo": "account\/create-mfa-recovery-codes.md", @@ -1538,7 +1538,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 50, + "weight": 312, "cookies": false, "type": "", "demo": "account\/update-mfa-recovery-codes.md", @@ -2930,7 +2930,7 @@ "x-appwrite": { "method": "createPushTarget", "group": "pushTargets", - "weight": 55, + "weight": 45, "cookies": false, "type": "", "demo": "account\/create-push-target.md", @@ -3008,7 +3008,7 @@ "x-appwrite": { "method": "updatePushTarget", "group": "pushTargets", - "weight": 56, + "weight": 46, "cookies": false, "type": "", "demo": "account\/update-push-target.md", @@ -3078,7 +3078,7 @@ "x-appwrite": { "method": "deletePushTarget", "group": "pushTargets", - "weight": 57, + "weight": 47, "cookies": false, "type": "", "demo": "account\/delete-push-target.md", @@ -3884,7 +3884,7 @@ "x-appwrite": { "method": "getBrowser", "group": null, - "weight": 61, + "weight": 51, "cookies": false, "type": "location", "demo": "avatars\/get-browser.md", @@ -4010,7 +4010,7 @@ "x-appwrite": { "method": "getCreditCard", "group": null, - "weight": 60, + "weight": 50, "cookies": false, "type": "location", "demo": "avatars\/get-credit-card.md", @@ -4142,7 +4142,7 @@ "x-appwrite": { "method": "getFavicon", "group": null, - "weight": 64, + "weight": 54, "cookies": false, "type": "location", "demo": "avatars\/get-favicon.md", @@ -4200,7 +4200,7 @@ "x-appwrite": { "method": "getFlag", "group": null, - "weight": 62, + "weight": 52, "cookies": false, "type": "location", "demo": "avatars\/get-flag.md", @@ -4688,7 +4688,7 @@ "x-appwrite": { "method": "getImage", "group": null, - "weight": 63, + "weight": 53, "cookies": false, "type": "location", "demo": "avatars\/get-image.md", @@ -4770,7 +4770,7 @@ "x-appwrite": { "method": "getInitials", "group": null, - "weight": 66, + "weight": 56, "cookies": false, "type": "location", "demo": "avatars\/get-initials.md", @@ -4862,7 +4862,7 @@ "x-appwrite": { "method": "getQR", "group": null, - "weight": 65, + "weight": 55, "cookies": false, "type": "location", "demo": "avatars\/get-qr.md", @@ -4954,7 +4954,7 @@ "x-appwrite": { "method": "getScreenshot", "group": null, - "weight": 67, + "weight": 57, "cookies": false, "type": "location", "demo": "avatars\/get-screenshot.md", @@ -4998,7 +4998,7 @@ "required": false, "schema": { "type": "object", - "x-example": "{}", + "x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}", "default": {} }, "in": "query" @@ -5010,7 +5010,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1920", "default": 1280 }, "in": "query" @@ -5022,7 +5022,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1080", "default": 720 }, "in": "query" @@ -5034,7 +5034,7 @@ "schema": { "type": "number", "format": "float", - "x-example": 0.1, + "x-example": "2", "default": 1 }, "in": "query" @@ -5045,7 +5045,7 @@ "required": false, "schema": { "type": "string", - "x-example": "light", + "x-example": "dark", "enum": [ "light", "dark" @@ -5062,7 +5062,7 @@ "required": false, "schema": { "type": "string", - "x-example": "", + "x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15", "default": "" }, "in": "query" @@ -5073,7 +5073,7 @@ "required": false, "schema": { "type": "boolean", - "x-example": false, + "x-example": "true", "default": false }, "in": "query" @@ -5084,7 +5084,7 @@ "required": false, "schema": { "type": "string", - "x-example": "", + "x-example": "en-US", "default": "" }, "in": "query" @@ -5095,7 +5095,7 @@ "required": false, "schema": { "type": "string", - "x-example": "africa\/abidjan", + "x-example": "america\/new_york", "enum": [ "africa\/abidjan", "africa\/accra", @@ -5530,7 +5530,7 @@ "schema": { "type": "number", "format": "float", - "x-example": -90, + "x-example": "37.7749", "default": 0 }, "in": "query" @@ -5542,7 +5542,7 @@ "schema": { "type": "number", "format": "float", - "x-example": -180, + "x-example": "-122.4194", "default": 0 }, "in": "query" @@ -5554,7 +5554,7 @@ "schema": { "type": "number", "format": "float", - "x-example": 0, + "x-example": "100", "default": 0 }, "in": "query" @@ -5565,7 +5565,7 @@ "required": false, "schema": { "type": "boolean", - "x-example": false, + "x-example": "true", "default": false }, "in": "query" @@ -5577,8 +5577,33 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "geolocation", + "camera", + "microphone", + "notifications", + "midi", + "push", + "clipboard-read", + "clipboard-write", + "payment-handler", + "usb", + "bluetooth", + "accelerometer", + "gyroscope", + "magnetometer", + "ambient-light-sensor", + "background-sync", + "persistent-storage", + "screen-wake-lock", + "web-share", + "xr-spatial-tracking" + ], + "x-enum-name": "BrowserPermission", + "x-enum-keys": [] }, + "x-example": "[\"geolocation\",\"notifications\"]", "default": [] }, "in": "query" @@ -5590,7 +5615,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "3", "default": 0 }, "in": "query" @@ -5602,7 +5627,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "800", "default": 0 }, "in": "query" @@ -5614,7 +5639,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "600", "default": 0 }, "in": "query" @@ -5626,7 +5651,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": -1, + "x-example": "85", "default": -1 }, "in": "query" @@ -5637,7 +5662,7 @@ "required": false, "schema": { "type": "string", - "x-example": "jpg", + "x-example": "jpeg", "enum": [ "jpg", "jpeg", @@ -5673,7 +5698,7 @@ "x-appwrite": { "method": "chat", "group": "console", - "weight": 253, + "weight": 243, "cookies": false, "type": "", "demo": "assistant\/chat.md", @@ -5808,7 +5833,7 @@ "x-appwrite": { "method": "variables", "group": "console", - "weight": 252, + "weight": 242, "cookies": false, "type": "", "demo": "console\/variables.md", @@ -7071,6 +7096,22 @@ "type": "boolean", "description": "Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.", "x-example": false + }, + "attributes": { + "type": "array", + "description": "Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of attribute keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -12379,7 +12420,13 @@ "description": "Array of index orders. Maximum of 100 orders are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -13239,7 +13286,66 @@ "description": "List of scopes allowed for API key auto-generated for every execution. Maximum of 100 scopes are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -13437,7 +13543,78 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "node-14.5", + "node-16.0", + "node-18.0", + "node-19.0", + "node-20.0", + "node-21.0", + "node-22", + "php-8.0", + "php-8.1", + "php-8.2", + "php-8.3", + "ruby-3.0", + "ruby-3.1", + "ruby-3.2", + "ruby-3.3", + "python-3.8", + "python-3.9", + "python-3.10", + "python-3.11", + "python-3.12", + "python-ml-3.11", + "python-ml-3.12", + "deno-1.21", + "deno-1.24", + "deno-1.35", + "deno-1.40", + "deno-1.46", + "deno-2.0", + "dart-2.15", + "dart-2.16", + "dart-2.17", + "dart-2.18", + "dart-2.19", + "dart-3.0", + "dart-3.1", + "dart-3.3", + "dart-3.5", + "dart-3.8", + "dart-3.9", + "dotnet-6.0", + "dotnet-7.0", + "dotnet-8.0", + "java-8.0", + "java-11.0", + "java-17.0", + "java-18.0", + "java-21.0", + "java-22", + "swift-5.5", + "swift-5.8", + "swift-5.9", + "swift-5.10", + "kotlin-1.6", + "kotlin-1.8", + "kotlin-1.9", + "kotlin-2.0", + "cpp-17", + "cpp-20", + "bun-1.0", + "bun-1.1", + "go-1.23", + "static-1", + "flutter-3.24", + "flutter-3.27", + "flutter-3.29", + "flutter-3.32", + "flutter-3.35" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [] }, @@ -13450,7 +13627,17 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "dev-tools", + "starter", + "databases", + "ai", + "messaging", + "utilities" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [] }, @@ -13882,7 +14069,66 @@ "description": "List of scopes allowed for API Key auto-generated for every execution. Maximum of 100 scopes are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -14424,7 +14670,7 @@ "branch", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -14524,7 +14770,7 @@ "branch", "commit" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -15663,7 +15909,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 251, + "weight": 241, "cookies": false, "type": "graphql", "demo": "graphql\/query.md", @@ -15715,7 +15961,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 250, + "weight": 240, "cookies": false, "type": "graphql", "demo": "graphql\/mutation.md", @@ -15767,7 +16013,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 79, + "weight": 69, "cookies": false, "type": "", "demo": "health\/get.md", @@ -15816,7 +16062,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 100, + "weight": 90, "cookies": false, "type": "", "demo": "health\/get-antivirus.md", @@ -15865,7 +16111,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 82, + "weight": 72, "cookies": false, "type": "", "demo": "health\/get-cache.md", @@ -15914,7 +16160,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 87, + "weight": 77, "cookies": false, "type": "", "demo": "health\/get-certificate.md", @@ -15974,7 +16220,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 81, + "weight": 71, "cookies": false, "type": "", "demo": "health\/get-db.md", @@ -16023,7 +16269,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 83, + "weight": 73, "cookies": false, "type": "", "demo": "health\/get-pub-sub.md", @@ -16072,7 +16318,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 89, + "weight": 79, "cookies": false, "type": "", "demo": "health\/get-queue-builds.md", @@ -16134,7 +16380,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 88, + "weight": 78, "cookies": false, "type": "", "demo": "health\/get-queue-certificates.md", @@ -16196,7 +16442,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 90, + "weight": 80, "cookies": false, "type": "", "demo": "health\/get-queue-databases.md", @@ -16269,7 +16515,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 91, + "weight": 81, "cookies": false, "type": "", "demo": "health\/get-queue-deletes.md", @@ -16331,7 +16577,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 101, + "weight": 91, "cookies": false, "type": "", "demo": "health\/get-failed-jobs.md", @@ -16419,7 +16665,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 95, + "weight": 85, "cookies": false, "type": "", "demo": "health\/get-queue-functions.md", @@ -16481,7 +16727,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 86, + "weight": 76, "cookies": false, "type": "", "demo": "health\/get-queue-logs.md", @@ -16543,7 +16789,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 92, + "weight": 82, "cookies": false, "type": "", "demo": "health\/get-queue-mails.md", @@ -16605,7 +16851,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 93, + "weight": 83, "cookies": false, "type": "", "demo": "health\/get-queue-messaging.md", @@ -16667,7 +16913,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 94, + "weight": 84, "cookies": false, "type": "", "demo": "health\/get-queue-migrations.md", @@ -16729,7 +16975,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 96, + "weight": 86, "cookies": false, "type": "", "demo": "health\/get-queue-stats-resources.md", @@ -16791,7 +17037,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 97, + "weight": 87, "cookies": false, "type": "", "demo": "health\/get-queue-usage.md", @@ -16853,7 +17099,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 85, + "weight": 75, "cookies": false, "type": "", "demo": "health\/get-queue-webhooks.md", @@ -16915,7 +17161,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 99, + "weight": 89, "cookies": false, "type": "", "demo": "health\/get-storage.md", @@ -16964,7 +17210,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 98, + "weight": 88, "cookies": false, "type": "", "demo": "health\/get-storage-local.md", @@ -17013,7 +17259,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 84, + "weight": 74, "cookies": false, "type": "", "demo": "health\/get-time.md", @@ -17062,7 +17308,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 71, + "weight": 61, "cookies": false, "type": "", "demo": "locale\/get.md", @@ -17114,7 +17360,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 72, + "weight": 62, "cookies": false, "type": "", "demo": "locale\/list-codes.md", @@ -17166,7 +17412,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 76, + "weight": 66, "cookies": false, "type": "", "demo": "locale\/list-continents.md", @@ -17218,7 +17464,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 73, + "weight": 63, "cookies": false, "type": "", "demo": "locale\/list-countries.md", @@ -17270,7 +17516,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 74, + "weight": 64, "cookies": false, "type": "", "demo": "locale\/list-countries-eu.md", @@ -17322,7 +17568,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 75, + "weight": 65, "cookies": false, "type": "", "demo": "locale\/list-countries-phones.md", @@ -17374,7 +17620,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 77, + "weight": 67, "cookies": false, "type": "", "demo": "locale\/list-currencies.md", @@ -17426,7 +17672,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 78, + "weight": 68, "cookies": false, "type": "", "demo": "locale\/list-languages.md", @@ -17478,7 +17724,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 308, + "weight": 298, "cookies": false, "type": "", "demo": "messaging\/list-messages.md", @@ -17565,7 +17811,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 305, + "weight": 295, "cookies": false, "type": "", "demo": "messaging\/create-email.md", @@ -17710,7 +17956,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 312, + "weight": 302, "cookies": false, "type": "", "demo": "messaging\/update-email.md", @@ -17867,7 +18113,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 307, + "weight": 297, "cookies": false, "type": "", "demo": "messaging\/create-push.md", @@ -18043,7 +18289,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 314, + "weight": 304, "cookies": false, "type": "", "demo": "messaging\/update-push.md", @@ -18239,7 +18485,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 306, + "weight": 296, "cookies": false, "type": "", "demo": "messaging\/create-sms.md", @@ -18417,7 +18663,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 313, + "weight": 303, "cookies": false, "type": "", "demo": "messaging\/update-sms.md", @@ -18601,7 +18847,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 311, + "weight": 301, "cookies": false, "type": "", "demo": "messaging\/get-message.md", @@ -18654,7 +18900,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 315, + "weight": 305, "cookies": false, "type": "", "demo": "messaging\/delete.md", @@ -18716,7 +18962,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 309, + "weight": 299, "cookies": false, "type": "", "demo": "messaging\/list-message-logs.md", @@ -18802,7 +19048,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 310, + "weight": 300, "cookies": false, "type": "", "demo": "messaging\/list-targets.md", @@ -18888,7 +19134,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 279, + "weight": 269, "cookies": false, "type": "", "demo": "messaging\/list-providers.md", @@ -18975,7 +19221,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 278, + "weight": 268, "cookies": false, "type": "", "demo": "messaging\/create-apns-provider.md", @@ -19151,7 +19397,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 292, + "weight": 282, "cookies": false, "type": "", "demo": "messaging\/update-apns-provider.md", @@ -19329,7 +19575,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 277, + "weight": 267, "cookies": false, "type": "", "demo": "messaging\/create-fcm-provider.md", @@ -19478,7 +19724,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 291, + "weight": 281, "cookies": false, "type": "", "demo": "messaging\/update-fcm-provider.md", @@ -19628,7 +19874,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 268, + "weight": 258, "cookies": false, "type": "", "demo": "messaging\/create-mailgun-provider.md", @@ -19745,7 +19991,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 282, + "weight": 272, "cookies": false, "type": "", "demo": "messaging\/update-mailgun-provider.md", @@ -19865,7 +20111,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 272, + "weight": 262, "cookies": false, "type": "", "demo": "messaging\/create-msg-91-provider.md", @@ -19961,7 +20207,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 286, + "weight": 276, "cookies": false, "type": "", "demo": "messaging\/update-msg-91-provider.md", @@ -20060,7 +20306,7 @@ "x-appwrite": { "method": "createResendProvider", "group": "providers", - "weight": 270, + "weight": 260, "cookies": false, "type": "", "demo": "messaging\/create-resend-provider.md", @@ -20166,7 +20412,7 @@ "x-appwrite": { "method": "updateResendProvider", "group": "providers", - "weight": 284, + "weight": 274, "cookies": false, "type": "", "demo": "messaging\/update-resend-provider.md", @@ -20275,7 +20521,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 269, + "weight": 259, "cookies": false, "type": "", "demo": "messaging\/create-sendgrid-provider.md", @@ -20381,7 +20627,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 283, + "weight": 273, "cookies": false, "type": "", "demo": "messaging\/update-sendgrid-provider.md", @@ -20490,7 +20736,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 271, + "weight": 261, "cookies": false, "type": "", "demo": "messaging\/create-smtp-provider.md", @@ -20718,7 +20964,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 285, + "weight": 275, "cookies": false, "type": "", "demo": "messaging\/update-smtp-provider.md", @@ -20946,7 +21192,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 273, + "weight": 263, "cookies": false, "type": "", "demo": "messaging\/create-telesign-provider.md", @@ -21042,7 +21288,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 287, + "weight": 277, "cookies": false, "type": "", "demo": "messaging\/update-telesign-provider.md", @@ -21141,7 +21387,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 274, + "weight": 264, "cookies": false, "type": "", "demo": "messaging\/create-textmagic-provider.md", @@ -21237,7 +21483,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 288, + "weight": 278, "cookies": false, "type": "", "demo": "messaging\/update-textmagic-provider.md", @@ -21336,7 +21582,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 275, + "weight": 265, "cookies": false, "type": "", "demo": "messaging\/create-twilio-provider.md", @@ -21432,7 +21678,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 289, + "weight": 279, "cookies": false, "type": "", "demo": "messaging\/update-twilio-provider.md", @@ -21531,7 +21777,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 276, + "weight": 266, "cookies": false, "type": "", "demo": "messaging\/create-vonage-provider.md", @@ -21627,7 +21873,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 290, + "weight": 280, "cookies": false, "type": "", "demo": "messaging\/update-vonage-provider.md", @@ -21726,7 +21972,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 281, + "weight": 271, "cookies": false, "type": "", "demo": "messaging\/get-provider.md", @@ -21779,7 +22025,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 293, + "weight": 283, "cookies": false, "type": "", "demo": "messaging\/delete-provider.md", @@ -21841,7 +22087,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 280, + "weight": 270, "cookies": false, "type": "", "demo": "messaging\/list-provider-logs.md", @@ -21927,7 +22173,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 302, + "weight": 292, "cookies": false, "type": "", "demo": "messaging\/list-subscriber-logs.md", @@ -22013,7 +22259,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 295, + "weight": 285, "cookies": false, "type": "", "demo": "messaging\/list-topics.md", @@ -22098,7 +22344,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 294, + "weight": 284, "cookies": false, "type": "", "demo": "messaging\/create-topic.md", @@ -22181,7 +22427,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 297, + "weight": 287, "cookies": false, "type": "", "demo": "messaging\/get-topic.md", @@ -22241,7 +22487,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 298, + "weight": 288, "cookies": false, "type": "", "demo": "messaging\/update-topic.md", @@ -22320,7 +22566,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 299, + "weight": 289, "cookies": false, "type": "", "demo": "messaging\/delete-topic.md", @@ -22382,7 +22628,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 296, + "weight": 286, "cookies": false, "type": "", "demo": "messaging\/list-topic-logs.md", @@ -22468,7 +22714,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 301, + "weight": 291, "cookies": false, "type": "", "demo": "messaging\/list-subscribers.md", @@ -22563,7 +22809,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 300, + "weight": 290, "cookies": false, "type": "", "demo": "messaging\/create-subscriber.md", @@ -22653,7 +22899,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 303, + "weight": 293, "cookies": false, "type": "", "demo": "messaging\/get-subscriber.md", @@ -22716,7 +22962,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 304, + "weight": 294, "cookies": false, "type": "", "demo": "messaging\/delete-subscriber.md", @@ -22791,7 +23037,7 @@ "x-appwrite": { "method": "list", "group": null, - "weight": 260, + "weight": 250, "cookies": false, "type": "", "demo": "migrations\/list.md", @@ -22876,7 +23122,7 @@ "x-appwrite": { "method": "createAppwriteMigration", "group": null, - "weight": 254, + "weight": 244, "cookies": false, "type": "", "demo": "migrations\/create-appwrite-migration.md", @@ -22909,7 +23155,27 @@ "description": "List of resources to migrate", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "team", + "membership", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file", + "function", + "deployment", + "environment-variable" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "endpoint": { @@ -22964,7 +23230,7 @@ "x-appwrite": { "method": "getAppwriteReport", "group": null, - "weight": 262, + "weight": 252, "cookies": false, "type": "", "demo": "migrations\/get-appwrite-report.md", @@ -22994,7 +23260,27 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "team", + "membership", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file", + "function", + "deployment", + "environment-variable" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "in": "query" @@ -23057,7 +23343,7 @@ "x-appwrite": { "method": "createCSVExport", "group": null, - "weight": 259, + "weight": 249, "cookies": false, "type": "", "demo": "migrations\/create-csv-export.md", @@ -23171,7 +23457,7 @@ "x-appwrite": { "method": "createCSVImport", "group": null, - "weight": 258, + "weight": 248, "cookies": false, "type": "", "demo": "migrations\/create-csv-import.md", @@ -23255,7 +23541,7 @@ "x-appwrite": { "method": "createFirebaseMigration", "group": null, - "weight": 255, + "weight": 245, "cookies": false, "type": "", "demo": "migrations\/create-firebase-migration.md", @@ -23288,7 +23574,21 @@ "description": "List of resources to migrate", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "serviceAccount": { @@ -23331,7 +23631,7 @@ "x-appwrite": { "method": "getFirebaseReport", "group": null, - "weight": 263, + "weight": 253, "cookies": false, "type": "", "demo": "migrations\/get-firebase-report.md", @@ -23361,7 +23661,21 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "in": "query" @@ -23403,7 +23717,7 @@ "x-appwrite": { "method": "createNHostMigration", "group": null, - "weight": 257, + "weight": 247, "cookies": false, "type": "", "demo": "migrations\/create-n-host-migration.md", @@ -23436,7 +23750,22 @@ "description": "List of resources to migrate", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "subdomain": { @@ -23514,7 +23843,7 @@ "x-appwrite": { "method": "getNHostReport", "group": null, - "weight": 265, + "weight": 255, "cookies": false, "type": "", "demo": "migrations\/get-n-host-report.md", @@ -23544,7 +23873,22 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "in": "query" @@ -23647,7 +23991,7 @@ "x-appwrite": { "method": "createSupabaseMigration", "group": null, - "weight": 256, + "weight": 246, "cookies": false, "type": "", "demo": "migrations\/create-supabase-migration.md", @@ -23680,7 +24024,22 @@ "description": "List of resources to migrate", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "endpoint": { @@ -23752,7 +24111,7 @@ "x-appwrite": { "method": "getSupabaseReport", "group": null, - "weight": 264, + "weight": 254, "cookies": false, "type": "", "demo": "migrations\/get-supabase-report.md", @@ -23782,7 +24141,22 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "in": "query" @@ -23876,7 +24250,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 261, + "weight": 251, "cookies": false, "type": "", "demo": "migrations\/get.md", @@ -23934,7 +24308,7 @@ "x-appwrite": { "method": "retry", "group": null, - "weight": 266, + "weight": 256, "cookies": false, "type": "", "demo": "migrations\/retry.md", @@ -23985,7 +24359,7 @@ "x-appwrite": { "method": "delete", "group": null, - "weight": 267, + "weight": 257, "cookies": false, "type": "", "demo": "migrations\/delete.md", @@ -24045,7 +24419,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 149, + "weight": 139, "cookies": false, "type": "", "demo": "project\/get-usage.md", @@ -24133,7 +24507,7 @@ "x-appwrite": { "method": "listVariables", "group": null, - "weight": 151, + "weight": 141, "cookies": false, "type": "", "demo": "project\/list-variables.md", @@ -24179,7 +24553,7 @@ "x-appwrite": { "method": "createVariable", "group": null, - "weight": 150, + "weight": 140, "cookies": false, "type": "", "demo": "project\/create-variable.md", @@ -24257,7 +24631,7 @@ "x-appwrite": { "method": "getVariable", "group": null, - "weight": 152, + "weight": 142, "cookies": false, "type": "", "demo": "project\/get-variable.md", @@ -24315,7 +24689,7 @@ "x-appwrite": { "method": "updateVariable", "group": null, - "weight": 153, + "weight": 143, "cookies": false, "type": "", "demo": "project\/update-variable.md", @@ -24397,7 +24771,7 @@ "x-appwrite": { "method": "deleteVariable", "group": null, - "weight": 154, + "weight": 144, "cookies": false, "type": "", "demo": "project\/delete-variable.md", @@ -24540,7 +24914,7 @@ "x-appwrite": { "method": "create", "group": "projects", - "weight": 103, + "weight": 93, "cookies": false, "type": "", "demo": "projects\/create.md", @@ -24674,7 +25048,7 @@ "x-appwrite": { "method": "get", "group": "projects", - "weight": 104, + "weight": 94, "cookies": false, "type": "", "demo": "projects\/get.md", @@ -24732,7 +25106,7 @@ "x-appwrite": { "method": "update", "group": "projects", - "weight": 105, + "weight": 95, "cookies": false, "type": "", "demo": "projects\/update.md", @@ -24847,7 +25221,7 @@ "x-appwrite": { "method": "delete", "group": "projects", - "weight": 122, + "weight": 112, "cookies": false, "type": "", "demo": "projects\/delete.md", @@ -24907,7 +25281,7 @@ "x-appwrite": { "method": "updateApiStatus", "group": "projects", - "weight": 109, + "weight": 99, "cookies": false, "type": "", "demo": "projects\/update-api-status.md", @@ -25061,7 +25435,7 @@ "x-appwrite": { "method": "updateApiStatusAll", "group": "projects", - "weight": 110, + "weight": 100, "cookies": false, "type": "", "demo": "projects\/update-api-status-all.md", @@ -25198,7 +25572,7 @@ "x-appwrite": { "method": "updateAuthDuration", "group": "auth", - "weight": 115, + "weight": 105, "cookies": false, "type": "", "demo": "projects\/update-auth-duration.md", @@ -25277,7 +25651,7 @@ "x-appwrite": { "method": "updateAuthLimit", "group": "auth", - "weight": 114, + "weight": 104, "cookies": false, "type": "", "demo": "projects\/update-auth-limit.md", @@ -25356,7 +25730,7 @@ "x-appwrite": { "method": "updateAuthSessionsLimit", "group": "auth", - "weight": 120, + "weight": 110, "cookies": false, "type": "", "demo": "projects\/update-auth-sessions-limit.md", @@ -25435,7 +25809,7 @@ "x-appwrite": { "method": "updateMembershipsPrivacy", "group": "auth", - "weight": 113, + "weight": 103, "cookies": false, "type": "", "demo": "projects\/update-memberships-privacy.md", @@ -25526,7 +25900,7 @@ "x-appwrite": { "method": "updateMockNumbers", "group": "auth", - "weight": 121, + "weight": 111, "cookies": false, "type": "", "demo": "projects\/update-mock-numbers.md", @@ -25608,7 +25982,7 @@ "x-appwrite": { "method": "updateAuthPasswordDictionary", "group": "auth", - "weight": 118, + "weight": 108, "cookies": false, "type": "", "demo": "projects\/update-auth-password-dictionary.md", @@ -25687,7 +26061,7 @@ "x-appwrite": { "method": "updateAuthPasswordHistory", "group": "auth", - "weight": 117, + "weight": 107, "cookies": false, "type": "", "demo": "projects\/update-auth-password-history.md", @@ -25766,7 +26140,7 @@ "x-appwrite": { "method": "updatePersonalDataCheck", "group": "auth", - "weight": 119, + "weight": 109, "cookies": false, "type": "", "demo": "projects\/update-personal-data-check.md", @@ -25845,7 +26219,7 @@ "x-appwrite": { "method": "updateSessionAlerts", "group": "auth", - "weight": 112, + "weight": 102, "cookies": false, "type": "", "demo": "projects\/update-session-alerts.md", @@ -25924,7 +26298,7 @@ "x-appwrite": { "method": "updateSessionInvalidation", "group": "auth", - "weight": 148, + "weight": 138, "cookies": false, "type": "", "demo": "projects\/update-session-invalidation.md", @@ -26003,7 +26377,7 @@ "x-appwrite": { "method": "updateAuthStatus", "group": "auth", - "weight": 116, + "weight": 106, "cookies": false, "type": "", "demo": "projects\/update-auth-status.md", @@ -26483,7 +26857,7 @@ "x-appwrite": { "method": "createJWT", "group": "auth", - "weight": 134, + "weight": 124, "cookies": false, "type": "", "demo": "projects\/create-jwt.md", @@ -26528,7 +26902,66 @@ "description": "List of scopes allowed for JWT key. Maximum of 100 scopes are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "duration": { @@ -26570,7 +27003,7 @@ "x-appwrite": { "method": "listKeys", "group": "keys", - "weight": 130, + "weight": 120, "cookies": false, "type": "", "demo": "projects\/list-keys.md", @@ -26639,7 +27072,7 @@ "x-appwrite": { "method": "createKey", "group": "keys", - "weight": 129, + "weight": 119, "cookies": false, "type": "", "demo": "projects\/create-key.md", @@ -26689,7 +27122,66 @@ "description": "Key scopes list. Maximum of 100 scopes are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "x-nullable": true }, @@ -26734,7 +27226,7 @@ "x-appwrite": { "method": "getKey", "group": "keys", - "weight": 131, + "weight": 121, "cookies": false, "type": "", "demo": "projects\/get-key.md", @@ -26802,7 +27294,7 @@ "x-appwrite": { "method": "updateKey", "group": "keys", - "weight": 132, + "weight": 122, "cookies": false, "type": "", "demo": "projects\/update-key.md", @@ -26862,7 +27354,66 @@ "description": "Key scopes list. Maximum of 100 events are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "x-nullable": true }, @@ -26898,7 +27449,7 @@ "x-appwrite": { "method": "deleteKey", "group": "keys", - "weight": 133, + "weight": 123, "cookies": false, "type": "", "demo": "projects\/delete-key.md", @@ -26968,7 +27519,7 @@ "x-appwrite": { "method": "updateOAuth2", "group": "auth", - "weight": 111, + "weight": 101, "cookies": false, "type": "", "demo": "projects\/update-o-auth-2.md", @@ -27109,7 +27660,7 @@ "x-appwrite": { "method": "listPlatforms", "group": "platforms", - "weight": 136, + "weight": 126, "cookies": false, "type": "", "demo": "projects\/list-platforms.md", @@ -27178,7 +27729,7 @@ "x-appwrite": { "method": "createPlatform", "group": "platforms", - "weight": 135, + "weight": 125, "cookies": false, "type": "", "demo": "projects\/create-platform.md", @@ -27297,7 +27848,7 @@ "x-appwrite": { "method": "getPlatform", "group": "platforms", - "weight": 137, + "weight": 127, "cookies": false, "type": "", "demo": "projects\/get-platform.md", @@ -27365,7 +27916,7 @@ "x-appwrite": { "method": "updatePlatform", "group": "platforms", - "weight": 138, + "weight": 128, "cookies": false, "type": "", "demo": "projects\/update-platform.md", @@ -27460,7 +28011,7 @@ "x-appwrite": { "method": "deletePlatform", "group": "platforms", - "weight": 139, + "weight": 129, "cookies": false, "type": "", "demo": "projects\/delete-platform.md", @@ -27530,7 +28081,7 @@ "x-appwrite": { "method": "updateServiceStatus", "group": "projects", - "weight": 107, + "weight": 97, "cookies": false, "type": "", "demo": "projects\/update-service-status.md", @@ -27632,7 +28183,7 @@ "x-appwrite": { "method": "updateServiceStatusAll", "group": "projects", - "weight": 108, + "weight": 98, "cookies": false, "type": "", "demo": "projects\/update-service-status-all.md", @@ -27711,7 +28262,7 @@ "x-appwrite": { "method": "updateSmtp", "group": "templates", - "weight": 140, + "weight": 130, "cookies": false, "type": "", "demo": "projects\/update-smtp.md", @@ -27903,7 +28454,7 @@ "x-appwrite": { "method": "createSmtpTest", "group": "templates", - "weight": 141, + "weight": 131, "cookies": false, "type": "", "demo": "projects\/create-smtp-test.md", @@ -28112,7 +28663,7 @@ "x-appwrite": { "method": "updateTeam", "group": "projects", - "weight": 106, + "weight": 96, "cookies": false, "type": "", "demo": "projects\/update-team.md", @@ -28191,7 +28742,7 @@ "x-appwrite": { "method": "getEmailTemplate", "group": "templates", - "weight": 143, + "weight": 133, "cookies": false, "type": "", "demo": "projects\/get-email-template.md", @@ -28415,7 +28966,7 @@ "x-appwrite": { "method": "updateEmailTemplate", "group": "templates", - "weight": 145, + "weight": 135, "cookies": false, "type": "", "demo": "projects\/update-email-template.md", @@ -28679,7 +29230,7 @@ "x-appwrite": { "method": "deleteEmailTemplate", "group": "templates", - "weight": 147, + "weight": 137, "cookies": false, "type": "", "demo": "projects\/delete-email-template.md", @@ -28905,7 +29456,7 @@ "x-appwrite": { "method": "getSmsTemplate", "group": "templates", - "weight": 142, + "weight": 132, "cookies": false, "type": "", "demo": "projects\/get-sms-template.md", @@ -29188,7 +29739,7 @@ "x-appwrite": { "method": "updateSmsTemplate", "group": "templates", - "weight": 144, + "weight": 134, "cookies": false, "type": "", "demo": "projects\/update-sms-template.md", @@ -29494,7 +30045,7 @@ "x-appwrite": { "method": "deleteSmsTemplate", "group": "templates", - "weight": 146, + "weight": 136, "cookies": false, "type": "", "demo": "projects\/delete-sms-template.md", @@ -29779,7 +30330,7 @@ "x-appwrite": { "method": "listWebhooks", "group": "webhooks", - "weight": 124, + "weight": 114, "cookies": false, "type": "", "demo": "projects\/list-webhooks.md", @@ -29848,7 +30399,7 @@ "x-appwrite": { "method": "createWebhook", "group": "webhooks", - "weight": 123, + "weight": 113, "cookies": false, "type": "", "demo": "projects\/create-webhook.md", @@ -29963,7 +30514,7 @@ "x-appwrite": { "method": "getWebhook", "group": "webhooks", - "weight": 125, + "weight": 115, "cookies": false, "type": "", "demo": "projects\/get-webhook.md", @@ -30031,7 +30582,7 @@ "x-appwrite": { "method": "updateWebhook", "group": "webhooks", - "weight": 126, + "weight": 116, "cookies": false, "type": "", "demo": "projects\/update-webhook.md", @@ -30147,7 +30698,7 @@ "x-appwrite": { "method": "deleteWebhook", "group": "webhooks", - "weight": 128, + "weight": 118, "cookies": false, "type": "", "demo": "projects\/delete-webhook.md", @@ -30217,7 +30768,7 @@ "x-appwrite": { "method": "updateWebhookSignature", "group": "webhooks", - "weight": 127, + "weight": 117, "cookies": false, "type": "", "demo": "projects\/update-webhook-signature.md", @@ -31344,7 +31895,26 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "analog", + "angular", + "nextjs", + "react", + "nuxt", + "vue", + "sveltekit", + "astro", + "tanstack-start", + "remix", + "lynx", + "flutter", + "react-native", + "vite", + "other" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [] }, @@ -31357,7 +31927,17 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "dev-tools", + "starter", + "databases", + "ai", + "messaging", + "utilities" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [] }, @@ -32337,7 +32917,7 @@ "commit", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -32438,7 +33018,7 @@ "commit", "tag" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -33456,7 +34036,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 156, + "weight": 146, "cookies": false, "type": "", "demo": "storage\/list-buckets.md", @@ -33540,7 +34120,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 155, + "weight": 145, "cookies": false, "type": "", "demo": "storage\/create-bucket.md", @@ -33673,7 +34253,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 157, + "weight": 147, "cookies": false, "type": "", "demo": "storage\/get-bucket.md", @@ -33732,7 +34312,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 158, + "weight": 148, "cookies": false, "type": "", "demo": "storage\/update-bucket.md", @@ -33862,7 +34442,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 159, + "weight": 149, "cookies": false, "type": "", "demo": "storage\/delete-bucket.md", @@ -33923,7 +34503,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 161, + "weight": 151, "cookies": false, "type": "", "demo": "storage\/list-files.md", @@ -34020,7 +34600,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 160, + "weight": 150, "cookies": false, "type": "upload", "demo": "storage\/create-file.md", @@ -34119,7 +34699,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 162, + "weight": 152, "cookies": false, "type": "", "demo": "storage\/get-file.md", @@ -34191,7 +34771,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 167, + "weight": 157, "cookies": false, "type": "", "demo": "storage\/update-file.md", @@ -34282,7 +34862,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 168, + "weight": 158, "cookies": false, "type": "", "demo": "storage\/delete-file.md", @@ -34349,7 +34929,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 164, + "weight": 154, "cookies": false, "type": "location", "demo": "storage\/get-file-download.md", @@ -34427,7 +35007,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 163, + "weight": 153, "cookies": false, "type": "location", "demo": "storage\/get-file-preview.md", @@ -34655,7 +35235,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 165, + "weight": 155, "cookies": false, "type": "location", "demo": "storage\/get-file-view.md", @@ -34740,7 +35320,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 169, + "weight": 159, "cookies": false, "type": "", "demo": "storage\/get-usage.md", @@ -34812,7 +35392,7 @@ "x-appwrite": { "method": "getBucketUsage", "group": null, - "weight": 170, + "weight": 160, "cookies": false, "type": "", "demo": "storage\/get-bucket-usage.md", @@ -35955,6 +36535,22 @@ "type": "boolean", "description": "Is table enabled? When set to 'disabled', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.", "x-example": false + }, + "columns": { + "type": "array", + "description": "Array of column definitions to create. Each column should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of column keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -39763,7 +40359,13 @@ "description": "Array of index orders. Maximum of 100 orders are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -41692,7 +42294,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 172, + "weight": 162, "cookies": false, "type": "", "demo": "teams\/list.md", @@ -41779,7 +42381,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 171, + "weight": 161, "cookies": false, "type": "", "demo": "teams\/create.md", @@ -41864,7 +42466,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 173, + "weight": 163, "cookies": false, "type": "", "demo": "teams\/get.md", @@ -41926,7 +42528,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 175, + "weight": 165, "cookies": false, "type": "", "demo": "teams\/update-name.md", @@ -42000,7 +42602,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 177, + "weight": 167, "cookies": false, "type": "", "demo": "teams\/delete.md", @@ -42064,7 +42666,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 184, + "weight": 174, "cookies": false, "type": "", "demo": "teams\/list-logs.md", @@ -42148,7 +42750,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 179, + "weight": 169, "cookies": false, "type": "", "demo": "teams\/list-memberships.md", @@ -42245,7 +42847,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 178, + "weight": 168, "cookies": false, "type": "", "demo": "teams\/create-membership.md", @@ -42309,7 +42911,14 @@ "description": "Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions). Maximum of 100 roles are allowed, each 32 characters long.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "url": { @@ -42356,7 +42965,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 180, + "weight": 170, "cookies": false, "type": "", "demo": "teams\/get-membership.md", @@ -42428,7 +43037,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 181, + "weight": 171, "cookies": false, "type": "", "demo": "teams\/update-membership.md", @@ -42487,7 +43096,14 @@ "description": "An array of strings. Use this param to set the user's roles in the team. A role can be any string. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions). Maximum of 100 roles are allowed, each 32 characters long.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } } }, @@ -42515,7 +43131,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 183, + "weight": 173, "cookies": false, "type": "", "demo": "teams\/delete-membership.md", @@ -42589,7 +43205,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 182, + "weight": 172, "cookies": false, "type": "", "demo": "teams\/update-membership-status.md", @@ -42686,7 +43302,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 174, + "weight": 164, "cookies": false, "type": "", "demo": "teams\/get-prefs.md", @@ -42746,7 +43362,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 176, + "weight": 166, "cookies": false, "type": "", "demo": "teams\/update-prefs.md", @@ -43202,7 +43818,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 194, + "weight": 184, "cookies": false, "type": "", "demo": "users\/list.md", @@ -43286,7 +43902,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 185, + "weight": 175, "cookies": false, "type": "", "demo": "users\/create.md", @@ -43376,7 +43992,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 188, + "weight": 178, "cookies": false, "type": "", "demo": "users\/create-argon-2-user.md", @@ -43461,7 +44077,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 186, + "weight": 176, "cookies": false, "type": "", "demo": "users\/create-bcrypt-user.md", @@ -43546,7 +44162,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 202, + "weight": 192, "cookies": false, "type": "", "demo": "users\/list-identities.md", @@ -43625,7 +44241,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 225, + "weight": 215, "cookies": false, "type": "", "demo": "users\/delete-identity.md", @@ -43686,7 +44302,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 187, + "weight": 177, "cookies": false, "type": "", "demo": "users\/create-md-5-user.md", @@ -43771,7 +44387,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 190, + "weight": 180, "cookies": false, "type": "", "demo": "users\/create-ph-pass-user.md", @@ -43856,7 +44472,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 191, + "weight": 181, "cookies": false, "type": "", "demo": "users\/create-scrypt-user.md", @@ -43971,7 +44587,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 192, + "weight": 182, "cookies": false, "type": "", "demo": "users\/create-scrypt-modified-user.md", @@ -44074,7 +44690,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 189, + "weight": 179, "cookies": false, "type": "", "demo": "users\/create-sha-user.md", @@ -44179,7 +44795,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 227, + "weight": 217, "cookies": false, "type": "", "demo": "users\/get-usage.md", @@ -44251,7 +44867,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 195, + "weight": 185, "cookies": false, "type": "", "demo": "users\/get.md", @@ -44303,7 +44919,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 223, + "weight": 213, "cookies": false, "type": "", "demo": "users\/delete.md", @@ -44364,7 +44980,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 208, + "weight": 198, "cookies": false, "type": "", "demo": "users\/update-email.md", @@ -44444,7 +45060,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 226, + "weight": 216, "cookies": false, "type": "", "demo": "users\/create-jwt.md", @@ -44526,7 +45142,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 204, + "weight": 194, "cookies": false, "type": "", "demo": "users\/update-labels.md", @@ -44609,7 +45225,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 200, + "weight": 190, "cookies": false, "type": "", "demo": "users\/list-logs.md", @@ -44694,7 +45310,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 199, + "weight": 189, "cookies": false, "type": "", "demo": "users\/list-memberships.md", @@ -44790,7 +45406,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 213, + "weight": 203, "cookies": false, "type": "", "demo": "users\/update-mfa.md", @@ -44921,7 +45537,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 218, + "weight": 208, "cookies": false, "type": "", "demo": "users\/delete-mfa-authenticator.md", @@ -45053,7 +45669,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 214, + "weight": 204, "cookies": false, "type": "", "demo": "users\/list-mfa-factors.md", @@ -45168,7 +45784,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 215, + "weight": 205, "cookies": false, "type": "", "demo": "users\/get-mfa-recovery-codes.md", @@ -45281,7 +45897,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 217, + "weight": 207, "cookies": false, "type": "", "demo": "users\/update-mfa-recovery-codes.md", @@ -45394,7 +46010,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 216, + "weight": 206, "cookies": false, "type": "", "demo": "users\/create-mfa-recovery-codes.md", @@ -45509,7 +46125,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 206, + "weight": 196, "cookies": false, "type": "", "demo": "users\/update-name.md", @@ -45589,7 +46205,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 207, + "weight": 197, "cookies": false, "type": "", "demo": "users\/update-password.md", @@ -45669,7 +46285,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 209, + "weight": 199, "cookies": false, "type": "", "demo": "users\/update-phone.md", @@ -45749,7 +46365,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 196, + "weight": 186, "cookies": false, "type": "", "demo": "users\/get-prefs.md", @@ -45808,7 +46424,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 211, + "weight": 201, "cookies": false, "type": "", "demo": "users\/update-prefs.md", @@ -45888,7 +46504,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 198, + "weight": 188, "cookies": false, "type": "", "demo": "users\/list-sessions.md", @@ -45958,7 +46574,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 219, + "weight": 209, "cookies": false, "type": "", "demo": "users\/create-session.md", @@ -46010,7 +46626,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 222, + "weight": 212, "cookies": false, "type": "", "demo": "users\/delete-sessions.md", @@ -46064,7 +46680,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 221, + "weight": 211, "cookies": false, "type": "", "demo": "users\/delete-session.md", @@ -46135,7 +46751,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 203, + "weight": 193, "cookies": false, "type": "", "demo": "users\/update-status.md", @@ -46215,7 +46831,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 201, + "weight": 191, "cookies": false, "type": "", "demo": "users\/list-targets.md", @@ -46299,7 +46915,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 193, + "weight": 183, "cookies": false, "type": "", "demo": "users\/create-target.md", @@ -46409,7 +47025,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 197, + "weight": 187, "cookies": false, "type": "", "demo": "users\/get-target.md", @@ -46479,7 +47095,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 212, + "weight": 202, "cookies": false, "type": "", "demo": "users\/update-target.md", @@ -46568,7 +47184,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 224, + "weight": 214, "cookies": false, "type": "", "demo": "users\/delete-target.md", @@ -46640,7 +47256,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 220, + "weight": 210, "cookies": false, "type": "", "demo": "users\/create-token.md", @@ -46722,7 +47338,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 210, + "weight": 200, "cookies": false, "type": "", "demo": "users\/update-email-verification.md", @@ -46802,7 +47418,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 205, + "weight": 195, "cookies": false, "type": "", "demo": "users\/update-phone-verification.md", @@ -46882,7 +47498,7 @@ "x-appwrite": { "method": "createRepositoryDetection", "group": "repositories", - "weight": 231, + "weight": 221, "cookies": false, "type": "", "demo": "vcs\/create-repository-detection.md", @@ -46978,7 +47594,7 @@ "x-appwrite": { "method": "listRepositories", "group": "repositories", - "weight": 232, + "weight": 222, "cookies": false, "type": "", "demo": "vcs\/list-repositories.md", @@ -47037,6 +47653,19 @@ "default": "" }, "in": "query" + }, + { + "name": "queries", + "description": "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", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "in": "query" } ] }, @@ -47063,7 +47692,7 @@ "x-appwrite": { "method": "createRepository", "group": "repositories", - "weight": 233, + "weight": 223, "cookies": false, "type": "", "demo": "vcs\/create-repository.md", @@ -47148,7 +47777,7 @@ "x-appwrite": { "method": "getRepository", "group": "repositories", - "weight": 234, + "weight": 224, "cookies": false, "type": "", "demo": "vcs\/get-repository.md", @@ -47218,7 +47847,7 @@ "x-appwrite": { "method": "listRepositoryBranches", "group": "repositories", - "weight": 235, + "weight": 225, "cookies": false, "type": "", "demo": "vcs\/list-repository-branches.md", @@ -47288,7 +47917,7 @@ "x-appwrite": { "method": "getRepositoryContents", "group": "repositories", - "weight": 230, + "weight": 220, "cookies": false, "type": "", "demo": "vcs\/get-repository-contents.md", @@ -47373,7 +48002,7 @@ "x-appwrite": { "method": "updateExternalDeployments", "group": "repositories", - "weight": 240, + "weight": 230, "cookies": false, "type": "", "demo": "vcs\/update-external-deployments.md", @@ -47462,7 +48091,7 @@ "x-appwrite": { "method": "listInstallations", "group": "installations", - "weight": 237, + "weight": 227, "cookies": false, "type": "", "demo": "vcs\/list-installations.md", @@ -47547,7 +48176,7 @@ "x-appwrite": { "method": "getInstallation", "group": "installations", - "weight": 238, + "weight": 228, "cookies": false, "type": "", "demo": "vcs\/get-installation.md", @@ -47598,7 +48227,7 @@ "x-appwrite": { "method": "deleteInstallation", "group": "installations", - "weight": 239, + "weight": 229, "cookies": false, "type": "", "demo": "vcs\/delete-installation.md", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 2cb6a74853..1803b42b53 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -260,7 +260,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 58, + "weight": 48, "cookies": false, "type": "", "demo": "account\/list-identities.md", @@ -331,7 +331,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 59, + "weight": 49, "cookies": false, "type": "", "demo": "account\/delete-identity.md", @@ -522,7 +522,7 @@ "x-appwrite": { "method": "updateMFA", "group": "mfa", - "weight": 45, + "weight": 306, "cookies": false, "type": "", "demo": "account\/update-mfa.md", @@ -593,7 +593,7 @@ "x-appwrite": { "method": "createMfaAuthenticator", "group": "mfa", - "weight": 47, + "weight": 308, "cookies": false, "type": "", "demo": "account\/create-mfa-authenticator.md", @@ -716,7 +716,7 @@ "x-appwrite": { "method": "updateMfaAuthenticator", "group": "mfa", - "weight": 48, + "weight": 309, "cookies": false, "type": "", "demo": "account\/update-mfa-authenticator.md", @@ -855,7 +855,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 52, + "weight": 310, "cookies": false, "type": "", "demo": "account\/delete-mfa-authenticator.md", @@ -954,7 +954,7 @@ ] } }, - "\/account\/mfa\/challenge": { + "\/account\/mfa\/challenges": { "post": { "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", @@ -978,7 +978,7 @@ "x-appwrite": { "method": "createMfaChallenge", "group": "mfa", - "weight": 53, + "weight": 314, "cookies": false, "type": "", "demo": "account\/create-mfa-challenge.md", @@ -1106,7 +1106,7 @@ "x-appwrite": { "method": "updateMfaChallenge", "group": "mfa", - "weight": 54, + "weight": 315, "cookies": false, "type": "", "demo": "account\/update-mfa-challenge.md", @@ -1243,7 +1243,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 46, + "weight": 307, "cookies": false, "type": "", "demo": "account\/list-mfa-factors.md", @@ -1343,7 +1343,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 51, + "weight": 313, "cookies": false, "type": "", "demo": "account\/get-mfa-recovery-codes.md", @@ -1441,7 +1441,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 49, + "weight": 311, "cookies": false, "type": "", "demo": "account\/create-mfa-recovery-codes.md", @@ -1539,7 +1539,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 50, + "weight": 312, "cookies": false, "type": "", "demo": "account\/update-mfa-recovery-codes.md", @@ -3584,7 +3584,7 @@ "x-appwrite": { "method": "getBrowser", "group": null, - "weight": 61, + "weight": 51, "cookies": false, "type": "location", "demo": "avatars\/get-browser.md", @@ -3712,7 +3712,7 @@ "x-appwrite": { "method": "getCreditCard", "group": null, - "weight": 60, + "weight": 50, "cookies": false, "type": "location", "demo": "avatars\/get-credit-card.md", @@ -3846,7 +3846,7 @@ "x-appwrite": { "method": "getFavicon", "group": null, - "weight": 64, + "weight": 54, "cookies": false, "type": "location", "demo": "avatars\/get-favicon.md", @@ -3906,7 +3906,7 @@ "x-appwrite": { "method": "getFlag", "group": null, - "weight": 62, + "weight": 52, "cookies": false, "type": "location", "demo": "avatars\/get-flag.md", @@ -4396,7 +4396,7 @@ "x-appwrite": { "method": "getImage", "group": null, - "weight": 63, + "weight": 53, "cookies": false, "type": "location", "demo": "avatars\/get-image.md", @@ -4480,7 +4480,7 @@ "x-appwrite": { "method": "getInitials", "group": null, - "weight": 66, + "weight": 56, "cookies": false, "type": "location", "demo": "avatars\/get-initials.md", @@ -4574,7 +4574,7 @@ "x-appwrite": { "method": "getQR", "group": null, - "weight": 65, + "weight": 55, "cookies": false, "type": "location", "demo": "avatars\/get-qr.md", @@ -4668,7 +4668,7 @@ "x-appwrite": { "method": "getScreenshot", "group": null, - "weight": 67, + "weight": 57, "cookies": false, "type": "location", "demo": "avatars\/get-screenshot.md", @@ -4714,7 +4714,7 @@ "required": false, "schema": { "type": "object", - "x-example": "{}", + "x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}", "default": {} }, "in": "query" @@ -4726,7 +4726,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1920", "default": 1280 }, "in": "query" @@ -4738,7 +4738,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1080", "default": 720 }, "in": "query" @@ -4750,7 +4750,7 @@ "schema": { "type": "number", "format": "float", - "x-example": 0.1, + "x-example": "2", "default": 1 }, "in": "query" @@ -4761,7 +4761,7 @@ "required": false, "schema": { "type": "string", - "x-example": "light", + "x-example": "dark", "enum": [ "light", "dark" @@ -4778,7 +4778,7 @@ "required": false, "schema": { "type": "string", - "x-example": "", + "x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15", "default": "" }, "in": "query" @@ -4789,7 +4789,7 @@ "required": false, "schema": { "type": "boolean", - "x-example": false, + "x-example": "true", "default": false }, "in": "query" @@ -4800,7 +4800,7 @@ "required": false, "schema": { "type": "string", - "x-example": "", + "x-example": "en-US", "default": "" }, "in": "query" @@ -4811,7 +4811,7 @@ "required": false, "schema": { "type": "string", - "x-example": "africa\/abidjan", + "x-example": "america\/new_york", "enum": [ "africa\/abidjan", "africa\/accra", @@ -5246,7 +5246,7 @@ "schema": { "type": "number", "format": "float", - "x-example": -90, + "x-example": "37.7749", "default": 0 }, "in": "query" @@ -5258,7 +5258,7 @@ "schema": { "type": "number", "format": "float", - "x-example": -180, + "x-example": "-122.4194", "default": 0 }, "in": "query" @@ -5270,7 +5270,7 @@ "schema": { "type": "number", "format": "float", - "x-example": 0, + "x-example": "100", "default": 0 }, "in": "query" @@ -5281,7 +5281,7 @@ "required": false, "schema": { "type": "boolean", - "x-example": false, + "x-example": "true", "default": false }, "in": "query" @@ -5293,8 +5293,33 @@ "schema": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "geolocation", + "camera", + "microphone", + "notifications", + "midi", + "push", + "clipboard-read", + "clipboard-write", + "payment-handler", + "usb", + "bluetooth", + "accelerometer", + "gyroscope", + "magnetometer", + "ambient-light-sensor", + "background-sync", + "persistent-storage", + "screen-wake-lock", + "web-share", + "xr-spatial-tracking" + ], + "x-enum-name": "BrowserPermission", + "x-enum-keys": [] }, + "x-example": "[\"geolocation\",\"notifications\"]", "default": [] }, "in": "query" @@ -5306,7 +5331,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "3", "default": 0 }, "in": "query" @@ -5318,7 +5343,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "800", "default": 0 }, "in": "query" @@ -5330,7 +5355,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "600", "default": 0 }, "in": "query" @@ -5342,7 +5367,7 @@ "schema": { "type": "integer", "format": "int32", - "x-example": -1, + "x-example": "85", "default": -1 }, "in": "query" @@ -5353,7 +5378,7 @@ "required": false, "schema": { "type": "string", - "x-example": "jpg", + "x-example": "jpeg", "enum": [ "jpg", "jpeg", @@ -6533,6 +6558,22 @@ "type": "boolean", "description": "Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.", "x-example": false + }, + "attributes": { + "type": "array", + "description": "Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of attribute keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -11801,7 +11842,13 @@ "description": "Array of index orders. Maximum of 100 orders are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -12261,7 +12308,66 @@ "description": "List of scopes allowed for API key auto-generated for every execution. Maximum of 100 scopes are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -12665,7 +12771,66 @@ "description": "List of scopes allowed for API Key auto-generated for every execution. Maximum of 100 scopes are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -13213,7 +13378,7 @@ "branch", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -13314,7 +13479,7 @@ "branch", "commit" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -14387,7 +14552,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 251, + "weight": 241, "cookies": false, "type": "graphql", "demo": "graphql\/query.md", @@ -14441,7 +14606,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 250, + "weight": 240, "cookies": false, "type": "graphql", "demo": "graphql\/mutation.md", @@ -14495,7 +14660,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 79, + "weight": 69, "cookies": false, "type": "", "demo": "health\/get.md", @@ -14545,7 +14710,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 100, + "weight": 90, "cookies": false, "type": "", "demo": "health\/get-antivirus.md", @@ -14595,7 +14760,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 82, + "weight": 72, "cookies": false, "type": "", "demo": "health\/get-cache.md", @@ -14645,7 +14810,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 87, + "weight": 77, "cookies": false, "type": "", "demo": "health\/get-certificate.md", @@ -14706,7 +14871,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 81, + "weight": 71, "cookies": false, "type": "", "demo": "health\/get-db.md", @@ -14756,7 +14921,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 83, + "weight": 73, "cookies": false, "type": "", "demo": "health\/get-pub-sub.md", @@ -14806,7 +14971,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 89, + "weight": 79, "cookies": false, "type": "", "demo": "health\/get-queue-builds.md", @@ -14869,7 +15034,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 88, + "weight": 78, "cookies": false, "type": "", "demo": "health\/get-queue-certificates.md", @@ -14932,7 +15097,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 90, + "weight": 80, "cookies": false, "type": "", "demo": "health\/get-queue-databases.md", @@ -15006,7 +15171,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 91, + "weight": 81, "cookies": false, "type": "", "demo": "health\/get-queue-deletes.md", @@ -15069,7 +15234,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 101, + "weight": 91, "cookies": false, "type": "", "demo": "health\/get-failed-jobs.md", @@ -15158,7 +15323,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 95, + "weight": 85, "cookies": false, "type": "", "demo": "health\/get-queue-functions.md", @@ -15221,7 +15386,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 86, + "weight": 76, "cookies": false, "type": "", "demo": "health\/get-queue-logs.md", @@ -15284,7 +15449,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 92, + "weight": 82, "cookies": false, "type": "", "demo": "health\/get-queue-mails.md", @@ -15347,7 +15512,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 93, + "weight": 83, "cookies": false, "type": "", "demo": "health\/get-queue-messaging.md", @@ -15410,7 +15575,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 94, + "weight": 84, "cookies": false, "type": "", "demo": "health\/get-queue-migrations.md", @@ -15473,7 +15638,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 96, + "weight": 86, "cookies": false, "type": "", "demo": "health\/get-queue-stats-resources.md", @@ -15536,7 +15701,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 97, + "weight": 87, "cookies": false, "type": "", "demo": "health\/get-queue-usage.md", @@ -15599,7 +15764,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 85, + "weight": 75, "cookies": false, "type": "", "demo": "health\/get-queue-webhooks.md", @@ -15662,7 +15827,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 99, + "weight": 89, "cookies": false, "type": "", "demo": "health\/get-storage.md", @@ -15712,7 +15877,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 98, + "weight": 88, "cookies": false, "type": "", "demo": "health\/get-storage-local.md", @@ -15762,7 +15927,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 84, + "weight": 74, "cookies": false, "type": "", "demo": "health\/get-time.md", @@ -15812,7 +15977,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 71, + "weight": 61, "cookies": false, "type": "", "demo": "locale\/get.md", @@ -15866,7 +16031,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 72, + "weight": 62, "cookies": false, "type": "", "demo": "locale\/list-codes.md", @@ -15920,7 +16085,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 76, + "weight": 66, "cookies": false, "type": "", "demo": "locale\/list-continents.md", @@ -15974,7 +16139,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 73, + "weight": 63, "cookies": false, "type": "", "demo": "locale\/list-countries.md", @@ -16028,7 +16193,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 74, + "weight": 64, "cookies": false, "type": "", "demo": "locale\/list-countries-eu.md", @@ -16082,7 +16247,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 75, + "weight": 65, "cookies": false, "type": "", "demo": "locale\/list-countries-phones.md", @@ -16136,7 +16301,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 77, + "weight": 67, "cookies": false, "type": "", "demo": "locale\/list-currencies.md", @@ -16190,7 +16355,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 78, + "weight": 68, "cookies": false, "type": "", "demo": "locale\/list-languages.md", @@ -16244,7 +16409,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 308, + "weight": 298, "cookies": false, "type": "", "demo": "messaging\/list-messages.md", @@ -16332,7 +16497,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 305, + "weight": 295, "cookies": false, "type": "", "demo": "messaging\/create-email.md", @@ -16478,7 +16643,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 312, + "weight": 302, "cookies": false, "type": "", "demo": "messaging\/update-email.md", @@ -16636,7 +16801,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 307, + "weight": 297, "cookies": false, "type": "", "demo": "messaging\/create-push.md", @@ -16813,7 +16978,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 314, + "weight": 304, "cookies": false, "type": "", "demo": "messaging\/update-push.md", @@ -17010,7 +17175,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 306, + "weight": 296, "cookies": false, "type": "", "demo": "messaging\/create-sms.md", @@ -17191,7 +17356,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 313, + "weight": 303, "cookies": false, "type": "", "demo": "messaging\/update-sms.md", @@ -17378,7 +17543,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 311, + "weight": 301, "cookies": false, "type": "", "demo": "messaging\/get-message.md", @@ -17432,7 +17597,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 315, + "weight": 305, "cookies": false, "type": "", "demo": "messaging\/delete.md", @@ -17495,7 +17660,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 309, + "weight": 299, "cookies": false, "type": "", "demo": "messaging\/list-message-logs.md", @@ -17582,7 +17747,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 310, + "weight": 300, "cookies": false, "type": "", "demo": "messaging\/list-targets.md", @@ -17669,7 +17834,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 279, + "weight": 269, "cookies": false, "type": "", "demo": "messaging\/list-providers.md", @@ -17757,7 +17922,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 278, + "weight": 268, "cookies": false, "type": "", "demo": "messaging\/create-apns-provider.md", @@ -17936,7 +18101,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 292, + "weight": 282, "cookies": false, "type": "", "demo": "messaging\/update-apns-provider.md", @@ -18117,7 +18282,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 277, + "weight": 267, "cookies": false, "type": "", "demo": "messaging\/create-fcm-provider.md", @@ -18269,7 +18434,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 291, + "weight": 281, "cookies": false, "type": "", "demo": "messaging\/update-fcm-provider.md", @@ -18422,7 +18587,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 268, + "weight": 258, "cookies": false, "type": "", "demo": "messaging\/create-mailgun-provider.md", @@ -18540,7 +18705,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 282, + "weight": 272, "cookies": false, "type": "", "demo": "messaging\/update-mailgun-provider.md", @@ -18661,7 +18826,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 272, + "weight": 262, "cookies": false, "type": "", "demo": "messaging\/create-msg-91-provider.md", @@ -18758,7 +18923,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 286, + "weight": 276, "cookies": false, "type": "", "demo": "messaging\/update-msg-91-provider.md", @@ -18858,7 +19023,7 @@ "x-appwrite": { "method": "createResendProvider", "group": "providers", - "weight": 270, + "weight": 260, "cookies": false, "type": "", "demo": "messaging\/create-resend-provider.md", @@ -18965,7 +19130,7 @@ "x-appwrite": { "method": "updateResendProvider", "group": "providers", - "weight": 284, + "weight": 274, "cookies": false, "type": "", "demo": "messaging\/update-resend-provider.md", @@ -19075,7 +19240,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 269, + "weight": 259, "cookies": false, "type": "", "demo": "messaging\/create-sendgrid-provider.md", @@ -19182,7 +19347,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 283, + "weight": 273, "cookies": false, "type": "", "demo": "messaging\/update-sendgrid-provider.md", @@ -19292,7 +19457,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 271, + "weight": 261, "cookies": false, "type": "", "demo": "messaging\/create-smtp-provider.md", @@ -19523,7 +19688,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 285, + "weight": 275, "cookies": false, "type": "", "demo": "messaging\/update-smtp-provider.md", @@ -19754,7 +19919,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 273, + "weight": 263, "cookies": false, "type": "", "demo": "messaging\/create-telesign-provider.md", @@ -19851,7 +20016,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 287, + "weight": 277, "cookies": false, "type": "", "demo": "messaging\/update-telesign-provider.md", @@ -19951,7 +20116,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 274, + "weight": 264, "cookies": false, "type": "", "demo": "messaging\/create-textmagic-provider.md", @@ -20048,7 +20213,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 288, + "weight": 278, "cookies": false, "type": "", "demo": "messaging\/update-textmagic-provider.md", @@ -20148,7 +20313,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 275, + "weight": 265, "cookies": false, "type": "", "demo": "messaging\/create-twilio-provider.md", @@ -20245,7 +20410,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 289, + "weight": 279, "cookies": false, "type": "", "demo": "messaging\/update-twilio-provider.md", @@ -20345,7 +20510,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 276, + "weight": 266, "cookies": false, "type": "", "demo": "messaging\/create-vonage-provider.md", @@ -20442,7 +20607,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 290, + "weight": 280, "cookies": false, "type": "", "demo": "messaging\/update-vonage-provider.md", @@ -20542,7 +20707,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 281, + "weight": 271, "cookies": false, "type": "", "demo": "messaging\/get-provider.md", @@ -20596,7 +20761,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 293, + "weight": 283, "cookies": false, "type": "", "demo": "messaging\/delete-provider.md", @@ -20659,7 +20824,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 280, + "weight": 270, "cookies": false, "type": "", "demo": "messaging\/list-provider-logs.md", @@ -20746,7 +20911,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 302, + "weight": 292, "cookies": false, "type": "", "demo": "messaging\/list-subscriber-logs.md", @@ -20833,7 +20998,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 295, + "weight": 285, "cookies": false, "type": "", "demo": "messaging\/list-topics.md", @@ -20919,7 +21084,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 294, + "weight": 284, "cookies": false, "type": "", "demo": "messaging\/create-topic.md", @@ -21003,7 +21168,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 297, + "weight": 287, "cookies": false, "type": "", "demo": "messaging\/get-topic.md", @@ -21064,7 +21229,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 298, + "weight": 288, "cookies": false, "type": "", "demo": "messaging\/update-topic.md", @@ -21144,7 +21309,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 299, + "weight": 289, "cookies": false, "type": "", "demo": "messaging\/delete-topic.md", @@ -21207,7 +21372,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 296, + "weight": 286, "cookies": false, "type": "", "demo": "messaging\/list-topic-logs.md", @@ -21294,7 +21459,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 301, + "weight": 291, "cookies": false, "type": "", "demo": "messaging\/list-subscribers.md", @@ -21390,7 +21555,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 300, + "weight": 290, "cookies": false, "type": "", "demo": "messaging\/create-subscriber.md", @@ -21482,7 +21647,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 303, + "weight": 293, "cookies": false, "type": "", "demo": "messaging\/get-subscriber.md", @@ -21546,7 +21711,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 304, + "weight": 294, "cookies": false, "type": "", "demo": "messaging\/delete-subscriber.md", @@ -22861,7 +23026,7 @@ "commit", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -22963,7 +23128,7 @@ "commit", "tag" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -23911,7 +24076,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 156, + "weight": 146, "cookies": false, "type": "", "demo": "storage\/list-buckets.md", @@ -23996,7 +24161,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 155, + "weight": 145, "cookies": false, "type": "", "demo": "storage\/create-bucket.md", @@ -24130,7 +24295,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 157, + "weight": 147, "cookies": false, "type": "", "demo": "storage\/get-bucket.md", @@ -24190,7 +24355,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 158, + "weight": 148, "cookies": false, "type": "", "demo": "storage\/update-bucket.md", @@ -24321,7 +24486,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 159, + "weight": 149, "cookies": false, "type": "", "demo": "storage\/delete-bucket.md", @@ -24383,7 +24548,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 161, + "weight": 151, "cookies": false, "type": "", "demo": "storage\/list-files.md", @@ -24482,7 +24647,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 160, + "weight": 150, "cookies": false, "type": "upload", "demo": "storage\/create-file.md", @@ -24583,7 +24748,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 162, + "weight": 152, "cookies": false, "type": "", "demo": "storage\/get-file.md", @@ -24657,7 +24822,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 167, + "weight": 157, "cookies": false, "type": "", "demo": "storage\/update-file.md", @@ -24750,7 +24915,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 168, + "weight": 158, "cookies": false, "type": "", "demo": "storage\/delete-file.md", @@ -24819,7 +24984,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 164, + "weight": 154, "cookies": false, "type": "location", "demo": "storage\/get-file-download.md", @@ -24899,7 +25064,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 163, + "weight": 153, "cookies": false, "type": "location", "demo": "storage\/get-file-preview.md", @@ -25129,7 +25294,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 165, + "weight": 155, "cookies": false, "type": "location", "demo": "storage\/get-file-view.md", @@ -26199,6 +26364,22 @@ "type": "boolean", "description": "Is table enabled? When set to 'disabled', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.", "x-example": false + }, + "columns": { + "type": "array", + "description": "Array of column definitions to create. Each column should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of column keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -30041,7 +30222,13 @@ "description": "Array of index orders. Maximum of 100 orders are allowed.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -31608,7 +31795,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 172, + "weight": 162, "cookies": false, "type": "", "demo": "teams\/list.md", @@ -31697,7 +31884,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 171, + "weight": 161, "cookies": false, "type": "", "demo": "teams\/create.md", @@ -31784,7 +31971,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 173, + "weight": 163, "cookies": false, "type": "", "demo": "teams\/get.md", @@ -31848,7 +32035,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 175, + "weight": 165, "cookies": false, "type": "", "demo": "teams\/update-name.md", @@ -31924,7 +32111,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 177, + "weight": 167, "cookies": false, "type": "", "demo": "teams\/delete.md", @@ -31990,7 +32177,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 179, + "weight": 169, "cookies": false, "type": "", "demo": "teams\/list-memberships.md", @@ -32089,7 +32276,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 178, + "weight": 168, "cookies": false, "type": "", "demo": "teams\/create-membership.md", @@ -32155,7 +32342,14 @@ "description": "Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions). Maximum of 100 roles are allowed, each 32 characters long.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "url": { @@ -32202,7 +32396,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 180, + "weight": 170, "cookies": false, "type": "", "demo": "teams\/get-membership.md", @@ -32276,7 +32470,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 181, + "weight": 171, "cookies": false, "type": "", "demo": "teams\/update-membership.md", @@ -32337,7 +32531,14 @@ "description": "An array of strings. Use this param to set the user's roles in the team. A role can be any string. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions). Maximum of 100 roles are allowed, each 32 characters long.", "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } } }, @@ -32365,7 +32566,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 183, + "weight": 173, "cookies": false, "type": "", "demo": "teams\/delete-membership.md", @@ -32441,7 +32642,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 182, + "weight": 172, "cookies": false, "type": "", "demo": "teams\/update-membership-status.md", @@ -32540,7 +32741,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 174, + "weight": 164, "cookies": false, "type": "", "demo": "teams\/get-prefs.md", @@ -32602,7 +32803,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 176, + "weight": 166, "cookies": false, "type": "", "demo": "teams\/update-prefs.md", @@ -33065,7 +33266,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 194, + "weight": 184, "cookies": false, "type": "", "demo": "users\/list.md", @@ -33150,7 +33351,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 185, + "weight": 175, "cookies": false, "type": "", "demo": "users\/create.md", @@ -33241,7 +33442,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 188, + "weight": 178, "cookies": false, "type": "", "demo": "users\/create-argon-2-user.md", @@ -33327,7 +33528,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 186, + "weight": 176, "cookies": false, "type": "", "demo": "users\/create-bcrypt-user.md", @@ -33413,7 +33614,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 202, + "weight": 192, "cookies": false, "type": "", "demo": "users\/list-identities.md", @@ -33493,7 +33694,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 225, + "weight": 215, "cookies": false, "type": "", "demo": "users\/delete-identity.md", @@ -33555,7 +33756,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 187, + "weight": 177, "cookies": false, "type": "", "demo": "users\/create-md-5-user.md", @@ -33641,7 +33842,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 190, + "weight": 180, "cookies": false, "type": "", "demo": "users\/create-ph-pass-user.md", @@ -33727,7 +33928,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 191, + "weight": 181, "cookies": false, "type": "", "demo": "users\/create-scrypt-user.md", @@ -33843,7 +34044,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 192, + "weight": 182, "cookies": false, "type": "", "demo": "users\/create-scrypt-modified-user.md", @@ -33947,7 +34148,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 189, + "weight": 179, "cookies": false, "type": "", "demo": "users\/create-sha-user.md", @@ -34053,7 +34254,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 195, + "weight": 185, "cookies": false, "type": "", "demo": "users\/get.md", @@ -34106,7 +34307,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 223, + "weight": 213, "cookies": false, "type": "", "demo": "users\/delete.md", @@ -34168,7 +34369,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 208, + "weight": 198, "cookies": false, "type": "", "demo": "users\/update-email.md", @@ -34249,7 +34450,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 226, + "weight": 216, "cookies": false, "type": "", "demo": "users\/create-jwt.md", @@ -34332,7 +34533,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 204, + "weight": 194, "cookies": false, "type": "", "demo": "users\/update-labels.md", @@ -34416,7 +34617,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 200, + "weight": 190, "cookies": false, "type": "", "demo": "users\/list-logs.md", @@ -34502,7 +34703,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 199, + "weight": 189, "cookies": false, "type": "", "demo": "users\/list-memberships.md", @@ -34599,7 +34800,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 213, + "weight": 203, "cookies": false, "type": "", "demo": "users\/update-mfa.md", @@ -34733,7 +34934,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 218, + "weight": 208, "cookies": false, "type": "", "demo": "users\/delete-mfa-authenticator.md", @@ -34868,7 +35069,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 214, + "weight": 204, "cookies": false, "type": "", "demo": "users\/list-mfa-factors.md", @@ -34986,7 +35187,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 215, + "weight": 205, "cookies": false, "type": "", "demo": "users\/get-mfa-recovery-codes.md", @@ -35102,7 +35303,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 217, + "weight": 207, "cookies": false, "type": "", "demo": "users\/update-mfa-recovery-codes.md", @@ -35218,7 +35419,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 216, + "weight": 206, "cookies": false, "type": "", "demo": "users\/create-mfa-recovery-codes.md", @@ -35336,7 +35537,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 206, + "weight": 196, "cookies": false, "type": "", "demo": "users\/update-name.md", @@ -35417,7 +35618,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 207, + "weight": 197, "cookies": false, "type": "", "demo": "users\/update-password.md", @@ -35498,7 +35699,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 209, + "weight": 199, "cookies": false, "type": "", "demo": "users\/update-phone.md", @@ -35579,7 +35780,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 196, + "weight": 186, "cookies": false, "type": "", "demo": "users\/get-prefs.md", @@ -35639,7 +35840,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 211, + "weight": 201, "cookies": false, "type": "", "demo": "users\/update-prefs.md", @@ -35720,7 +35921,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 198, + "weight": 188, "cookies": false, "type": "", "demo": "users\/list-sessions.md", @@ -35791,7 +35992,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 219, + "weight": 209, "cookies": false, "type": "", "demo": "users\/create-session.md", @@ -35844,7 +36045,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 222, + "weight": 212, "cookies": false, "type": "", "demo": "users\/delete-sessions.md", @@ -35899,7 +36100,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 221, + "weight": 211, "cookies": false, "type": "", "demo": "users\/delete-session.md", @@ -35971,7 +36172,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 203, + "weight": 193, "cookies": false, "type": "", "demo": "users\/update-status.md", @@ -36052,7 +36253,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 201, + "weight": 191, "cookies": false, "type": "", "demo": "users\/list-targets.md", @@ -36137,7 +36338,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 193, + "weight": 183, "cookies": false, "type": "", "demo": "users\/create-target.md", @@ -36248,7 +36449,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 197, + "weight": 187, "cookies": false, "type": "", "demo": "users\/get-target.md", @@ -36319,7 +36520,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 212, + "weight": 202, "cookies": false, "type": "", "demo": "users\/update-target.md", @@ -36409,7 +36610,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 224, + "weight": 214, "cookies": false, "type": "", "demo": "users\/delete-target.md", @@ -36482,7 +36683,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 220, + "weight": 210, "cookies": false, "type": "", "demo": "users\/create-token.md", @@ -36565,7 +36766,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 210, + "weight": 200, "cookies": false, "type": "", "demo": "users\/update-email-verification.md", @@ -36646,7 +36847,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 205, + "weight": 195, "cookies": false, "type": "", "demo": "users\/update-phone-verification.md", diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index 1594f816ef..aaa0ca3eba 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -314,7 +314,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 58, + "weight": 48, "cookies": false, "type": "", "demo": "account\/list-identities.md", @@ -385,7 +385,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 59, + "weight": 49, "cookies": false, "type": "", "demo": "account\/delete-identity.md", @@ -573,7 +573,7 @@ "x-appwrite": { "method": "updateMFA", "group": "mfa", - "weight": 45, + "weight": 306, "cookies": false, "type": "", "demo": "account\/update-mfa.md", @@ -646,7 +646,7 @@ "x-appwrite": { "method": "createMfaAuthenticator", "group": "mfa", - "weight": 47, + "weight": 308, "cookies": false, "type": "", "demo": "account\/create-mfa-authenticator.md", @@ -766,7 +766,7 @@ "x-appwrite": { "method": "updateMfaAuthenticator", "group": "mfa", - "weight": 48, + "weight": 309, "cookies": false, "type": "", "demo": "account\/update-mfa-authenticator.md", @@ -903,7 +903,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 52, + "weight": 310, "cookies": false, "type": "", "demo": "account\/delete-mfa-authenticator.md", @@ -997,7 +997,7 @@ ] } }, - "\/account\/mfa\/challenge": { + "\/account\/mfa\/challenges": { "post": { "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", @@ -1023,7 +1023,7 @@ "x-appwrite": { "method": "createMfaChallenge", "group": "mfa", - "weight": 53, + "weight": 314, "cookies": false, "type": "", "demo": "account\/create-mfa-challenge.md", @@ -1154,7 +1154,7 @@ "x-appwrite": { "method": "updateMfaChallenge", "group": "mfa", - "weight": 54, + "weight": 315, "cookies": false, "type": "", "demo": "account\/update-mfa-challenge.md", @@ -1290,7 +1290,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 46, + "weight": 307, "cookies": false, "type": "", "demo": "account\/list-mfa-factors.md", @@ -1387,7 +1387,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 51, + "weight": 313, "cookies": false, "type": "", "demo": "account\/get-mfa-recovery-codes.md", @@ -1484,7 +1484,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 49, + "weight": 311, "cookies": false, "type": "", "demo": "account\/create-mfa-recovery-codes.md", @@ -1581,7 +1581,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 50, + "weight": 312, "cookies": false, "type": "", "demo": "account\/update-mfa-recovery-codes.md", @@ -3033,7 +3033,7 @@ "x-appwrite": { "method": "createPushTarget", "group": "pushTargets", - "weight": 55, + "weight": 45, "cookies": false, "type": "", "demo": "account\/create-push-target.md", @@ -3117,7 +3117,7 @@ "x-appwrite": { "method": "updatePushTarget", "group": "pushTargets", - "weight": 56, + "weight": 46, "cookies": false, "type": "", "demo": "account\/update-push-target.md", @@ -3189,7 +3189,7 @@ "x-appwrite": { "method": "deletePushTarget", "group": "pushTargets", - "weight": 57, + "weight": 47, "cookies": false, "type": "", "demo": "account\/delete-push-target.md", @@ -4030,7 +4030,7 @@ "x-appwrite": { "method": "getBrowser", "group": null, - "weight": 61, + "weight": 51, "cookies": false, "type": "location", "demo": "avatars\/get-browser.md", @@ -4154,7 +4154,7 @@ "x-appwrite": { "method": "getCreditCard", "group": null, - "weight": 60, + "weight": 50, "cookies": false, "type": "location", "demo": "avatars\/get-credit-card.md", @@ -4284,7 +4284,7 @@ "x-appwrite": { "method": "getFavicon", "group": null, - "weight": 64, + "weight": 54, "cookies": false, "type": "location", "demo": "avatars\/get-favicon.md", @@ -4346,7 +4346,7 @@ "x-appwrite": { "method": "getFlag", "group": null, - "weight": 62, + "weight": 52, "cookies": false, "type": "location", "demo": "avatars\/get-flag.md", @@ -4832,7 +4832,7 @@ "x-appwrite": { "method": "getImage", "group": null, - "weight": 63, + "weight": 53, "cookies": false, "type": "location", "demo": "avatars\/get-image.md", @@ -4914,7 +4914,7 @@ "x-appwrite": { "method": "getInitials", "group": null, - "weight": 66, + "weight": 56, "cookies": false, "type": "location", "demo": "avatars\/get-initials.md", @@ -5004,7 +5004,7 @@ "x-appwrite": { "method": "getQR", "group": null, - "weight": 65, + "weight": 55, "cookies": false, "type": "location", "demo": "avatars\/get-qr.md", @@ -5094,7 +5094,7 @@ "x-appwrite": { "method": "getScreenshot", "group": null, - "weight": 67, + "weight": 57, "cookies": false, "type": "location", "demo": "avatars\/get-screenshot.md", @@ -5135,7 +5135,7 @@ "required": false, "type": "object", "default": [], - "x-example": "{}", + "x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}", "in": "query" }, { @@ -5144,7 +5144,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1920", "default": 1280, "in": "query" }, @@ -5154,7 +5154,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1080", "default": 720, "in": "query" }, @@ -5164,7 +5164,7 @@ "required": false, "type": "number", "format": "float", - "x-example": 0.1, + "x-example": "2", "default": 1, "in": "query" }, @@ -5173,7 +5173,7 @@ "description": "Browser theme. Pass \"light\" or \"dark\". Defaults to \"light\".", "required": false, "type": "string", - "x-example": "light", + "x-example": "dark", "enum": [ "light", "dark" @@ -5188,7 +5188,7 @@ "description": "Custom user agent string. Defaults to browser default.", "required": false, "type": "string", - "x-example": "", + "x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15", "default": "", "in": "query" }, @@ -5197,7 +5197,7 @@ "description": "Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.", "required": false, "type": "boolean", - "x-example": false, + "x-example": "true", "default": false, "in": "query" }, @@ -5206,7 +5206,7 @@ "description": "Browser locale (e.g., \"en-US\", \"fr-FR\"). Defaults to browser default.", "required": false, "type": "string", - "x-example": "", + "x-example": "en-US", "default": "", "in": "query" }, @@ -5215,7 +5215,7 @@ "description": "IANA timezone identifier (e.g., \"America\/New_York\", \"Europe\/London\"). Defaults to browser default.", "required": false, "type": "string", - "x-example": "africa\/abidjan", + "x-example": "america\/new_york", "enum": [ "africa\/abidjan", "africa\/accra", @@ -5648,7 +5648,7 @@ "required": false, "type": "number", "format": "float", - "x-example": -90, + "x-example": "37.7749", "default": 0, "in": "query" }, @@ -5658,7 +5658,7 @@ "required": false, "type": "number", "format": "float", - "x-example": -180, + "x-example": "-122.4194", "default": 0, "in": "query" }, @@ -5668,7 +5668,7 @@ "required": false, "type": "number", "format": "float", - "x-example": 0, + "x-example": "100", "default": 0, "in": "query" }, @@ -5677,7 +5677,7 @@ "description": "Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.", "required": false, "type": "boolean", - "x-example": false, + "x-example": "true", "default": false, "in": "query" }, @@ -5688,8 +5688,33 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "geolocation", + "camera", + "microphone", + "notifications", + "midi", + "push", + "clipboard-read", + "clipboard-write", + "payment-handler", + "usb", + "bluetooth", + "accelerometer", + "gyroscope", + "magnetometer", + "ambient-light-sensor", + "background-sync", + "persistent-storage", + "screen-wake-lock", + "web-share", + "xr-spatial-tracking" + ], + "x-enum-name": "BrowserPermission", + "x-enum-keys": [] }, + "x-example": "[\"geolocation\",\"notifications\"]", "default": [], "in": "query" }, @@ -5699,7 +5724,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "3", "default": 0, "in": "query" }, @@ -5709,7 +5734,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "800", "default": 0, "in": "query" }, @@ -5719,7 +5744,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "600", "default": 0, "in": "query" }, @@ -5729,7 +5754,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": -1, + "x-example": "85", "default": -1, "in": "query" }, @@ -5738,7 +5763,7 @@ "description": "Output format type (jpeg, jpg, png, gif and webp).", "required": false, "type": "string", - "x-example": "jpg", + "x-example": "jpeg", "enum": [ "jpg", "jpeg", @@ -7420,7 +7445,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 251, + "weight": 241, "cookies": false, "type": "graphql", "demo": "graphql\/query.md", @@ -7493,7 +7518,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 250, + "weight": 240, "cookies": false, "type": "graphql", "demo": "graphql\/mutation.md", @@ -7564,7 +7589,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 71, + "weight": 61, "cookies": false, "type": "", "demo": "locale\/get.md", @@ -7615,7 +7640,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 72, + "weight": 62, "cookies": false, "type": "", "demo": "locale\/list-codes.md", @@ -7666,7 +7691,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 76, + "weight": 66, "cookies": false, "type": "", "demo": "locale\/list-continents.md", @@ -7717,7 +7742,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 73, + "weight": 63, "cookies": false, "type": "", "demo": "locale\/list-countries.md", @@ -7768,7 +7793,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 74, + "weight": 64, "cookies": false, "type": "", "demo": "locale\/list-countries-eu.md", @@ -7819,7 +7844,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 75, + "weight": 65, "cookies": false, "type": "", "demo": "locale\/list-countries-phones.md", @@ -7870,7 +7895,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 77, + "weight": 67, "cookies": false, "type": "", "demo": "locale\/list-currencies.md", @@ -7921,7 +7946,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 78, + "weight": 68, "cookies": false, "type": "", "demo": "locale\/list-languages.md", @@ -7974,7 +7999,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 300, + "weight": 290, "cookies": false, "type": "", "demo": "messaging\/create-subscriber.md", @@ -8058,7 +8083,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 304, + "weight": 294, "cookies": false, "type": "", "demo": "messaging\/delete-subscriber.md", @@ -8128,7 +8153,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 161, + "weight": 151, "cookies": false, "type": "", "demo": "storage\/list-files.md", @@ -8219,7 +8244,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 160, + "weight": 150, "cookies": false, "type": "upload", "demo": "storage\/create-file.md", @@ -8308,7 +8333,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 162, + "weight": 152, "cookies": false, "type": "", "demo": "storage\/get-file.md", @@ -8377,7 +8402,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 167, + "weight": 157, "cookies": false, "type": "", "demo": "storage\/update-file.md", @@ -8467,7 +8492,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 168, + "weight": 158, "cookies": false, "type": "", "demo": "storage\/delete-file.md", @@ -8536,7 +8561,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 164, + "weight": 154, "cookies": false, "type": "location", "demo": "storage\/get-file-download.md", @@ -8614,7 +8639,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 163, + "weight": 153, "cookies": false, "type": "location", "demo": "storage\/get-file-preview.md", @@ -8820,7 +8845,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 165, + "weight": 155, "cookies": false, "type": "location", "demo": "storage\/get-file-view.md", @@ -10265,7 +10290,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 172, + "weight": 162, "cookies": false, "type": "", "demo": "teams\/list.md", @@ -10348,7 +10373,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 171, + "weight": 161, "cookies": false, "type": "", "demo": "teams\/create.md", @@ -10437,7 +10462,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 173, + "weight": 163, "cookies": false, "type": "", "demo": "teams\/get.md", @@ -10498,7 +10523,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 175, + "weight": 165, "cookies": false, "type": "", "demo": "teams\/update-name.md", @@ -10572,7 +10597,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 177, + "weight": 167, "cookies": false, "type": "", "demo": "teams\/delete.md", @@ -10633,7 +10658,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 179, + "weight": 169, "cookies": false, "type": "", "demo": "teams\/list-memberships.md", @@ -10724,7 +10749,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 178, + "weight": 168, "cookies": false, "type": "", "demo": "teams\/create-membership.md", @@ -10788,7 +10813,14 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "url": { @@ -10836,7 +10868,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 180, + "weight": 170, "cookies": false, "type": "", "demo": "teams\/get-membership.md", @@ -10905,7 +10937,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 181, + "weight": 171, "cookies": false, "type": "", "demo": "teams\/update-membership.md", @@ -10959,7 +10991,14 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } } }, @@ -10990,7 +11029,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 183, + "weight": 173, "cookies": false, "type": "", "demo": "teams\/delete-membership.md", @@ -11061,7 +11100,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 182, + "weight": 172, "cookies": false, "type": "", "demo": "teams\/update-membership-status.md", @@ -11155,7 +11194,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 174, + "weight": 164, "cookies": false, "type": "", "demo": "teams\/get-prefs.md", @@ -11216,7 +11255,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 176, + "weight": 166, "cookies": false, "type": "", "demo": "teams\/update-prefs.md", diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 28a4f76c61..e1e66611a6 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -361,7 +361,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 58, + "weight": 48, "cookies": false, "type": "", "demo": "account\/list-identities.md", @@ -431,7 +431,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 59, + "weight": 49, "cookies": false, "type": "", "demo": "account\/delete-identity.md", @@ -617,7 +617,7 @@ "x-appwrite": { "method": "updateMFA", "group": "mfa", - "weight": 45, + "weight": 306, "cookies": false, "type": "", "demo": "account\/update-mfa.md", @@ -689,7 +689,7 @@ "x-appwrite": { "method": "createMfaAuthenticator", "group": "mfa", - "weight": 47, + "weight": 308, "cookies": false, "type": "", "demo": "account\/create-mfa-authenticator.md", @@ -808,7 +808,7 @@ "x-appwrite": { "method": "updateMfaAuthenticator", "group": "mfa", - "weight": 48, + "weight": 309, "cookies": false, "type": "", "demo": "account\/update-mfa-authenticator.md", @@ -944,7 +944,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 52, + "weight": 310, "cookies": false, "type": "", "demo": "account\/delete-mfa-authenticator.md", @@ -1037,7 +1037,7 @@ ] } }, - "\/account\/mfa\/challenge": { + "\/account\/mfa\/challenges": { "post": { "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", @@ -1063,7 +1063,7 @@ "x-appwrite": { "method": "createMfaChallenge", "group": "mfa", - "weight": 53, + "weight": 314, "cookies": false, "type": "", "demo": "account\/create-mfa-challenge.md", @@ -1194,7 +1194,7 @@ "x-appwrite": { "method": "updateMfaChallenge", "group": "mfa", - "weight": 54, + "weight": 315, "cookies": false, "type": "", "demo": "account\/update-mfa-challenge.md", @@ -1329,7 +1329,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 46, + "weight": 307, "cookies": false, "type": "", "demo": "account\/list-mfa-factors.md", @@ -1425,7 +1425,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 51, + "weight": 313, "cookies": false, "type": "", "demo": "account\/get-mfa-recovery-codes.md", @@ -1521,7 +1521,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 49, + "weight": 311, "cookies": false, "type": "", "demo": "account\/create-mfa-recovery-codes.md", @@ -1617,7 +1617,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 50, + "weight": 312, "cookies": false, "type": "", "demo": "account\/update-mfa-recovery-codes.md", @@ -3055,7 +3055,7 @@ "x-appwrite": { "method": "createPushTarget", "group": "pushTargets", - "weight": 55, + "weight": 45, "cookies": false, "type": "", "demo": "account\/create-push-target.md", @@ -3138,7 +3138,7 @@ "x-appwrite": { "method": "updatePushTarget", "group": "pushTargets", - "weight": 56, + "weight": 46, "cookies": false, "type": "", "demo": "account\/update-push-target.md", @@ -3209,7 +3209,7 @@ "x-appwrite": { "method": "deletePushTarget", "group": "pushTargets", - "weight": 57, + "weight": 47, "cookies": false, "type": "", "demo": "account\/delete-push-target.md", @@ -4045,7 +4045,7 @@ "x-appwrite": { "method": "getBrowser", "group": null, - "weight": 61, + "weight": 51, "cookies": false, "type": "location", "demo": "avatars\/get-browser.md", @@ -4169,7 +4169,7 @@ "x-appwrite": { "method": "getCreditCard", "group": null, - "weight": 60, + "weight": 50, "cookies": false, "type": "location", "demo": "avatars\/get-credit-card.md", @@ -4299,7 +4299,7 @@ "x-appwrite": { "method": "getFavicon", "group": null, - "weight": 64, + "weight": 54, "cookies": false, "type": "location", "demo": "avatars\/get-favicon.md", @@ -4361,7 +4361,7 @@ "x-appwrite": { "method": "getFlag", "group": null, - "weight": 62, + "weight": 52, "cookies": false, "type": "location", "demo": "avatars\/get-flag.md", @@ -4847,7 +4847,7 @@ "x-appwrite": { "method": "getImage", "group": null, - "weight": 63, + "weight": 53, "cookies": false, "type": "location", "demo": "avatars\/get-image.md", @@ -4929,7 +4929,7 @@ "x-appwrite": { "method": "getInitials", "group": null, - "weight": 66, + "weight": 56, "cookies": false, "type": "location", "demo": "avatars\/get-initials.md", @@ -5019,7 +5019,7 @@ "x-appwrite": { "method": "getQR", "group": null, - "weight": 65, + "weight": 55, "cookies": false, "type": "location", "demo": "avatars\/get-qr.md", @@ -5109,7 +5109,7 @@ "x-appwrite": { "method": "getScreenshot", "group": null, - "weight": 67, + "weight": 57, "cookies": false, "type": "location", "demo": "avatars\/get-screenshot.md", @@ -5150,7 +5150,7 @@ "required": false, "type": "object", "default": [], - "x-example": "{}", + "x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}", "in": "query" }, { @@ -5159,7 +5159,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1920", "default": 1280, "in": "query" }, @@ -5169,7 +5169,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1080", "default": 720, "in": "query" }, @@ -5179,7 +5179,7 @@ "required": false, "type": "number", "format": "float", - "x-example": 0.1, + "x-example": "2", "default": 1, "in": "query" }, @@ -5188,7 +5188,7 @@ "description": "Browser theme. Pass \"light\" or \"dark\". Defaults to \"light\".", "required": false, "type": "string", - "x-example": "light", + "x-example": "dark", "enum": [ "light", "dark" @@ -5203,7 +5203,7 @@ "description": "Custom user agent string. Defaults to browser default.", "required": false, "type": "string", - "x-example": "", + "x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15", "default": "", "in": "query" }, @@ -5212,7 +5212,7 @@ "description": "Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.", "required": false, "type": "boolean", - "x-example": false, + "x-example": "true", "default": false, "in": "query" }, @@ -5221,7 +5221,7 @@ "description": "Browser locale (e.g., \"en-US\", \"fr-FR\"). Defaults to browser default.", "required": false, "type": "string", - "x-example": "", + "x-example": "en-US", "default": "", "in": "query" }, @@ -5230,7 +5230,7 @@ "description": "IANA timezone identifier (e.g., \"America\/New_York\", \"Europe\/London\"). Defaults to browser default.", "required": false, "type": "string", - "x-example": "africa\/abidjan", + "x-example": "america\/new_york", "enum": [ "africa\/abidjan", "africa\/accra", @@ -5663,7 +5663,7 @@ "required": false, "type": "number", "format": "float", - "x-example": -90, + "x-example": "37.7749", "default": 0, "in": "query" }, @@ -5673,7 +5673,7 @@ "required": false, "type": "number", "format": "float", - "x-example": -180, + "x-example": "-122.4194", "default": 0, "in": "query" }, @@ -5683,7 +5683,7 @@ "required": false, "type": "number", "format": "float", - "x-example": 0, + "x-example": "100", "default": 0, "in": "query" }, @@ -5692,7 +5692,7 @@ "description": "Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.", "required": false, "type": "boolean", - "x-example": false, + "x-example": "true", "default": false, "in": "query" }, @@ -5703,8 +5703,33 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "geolocation", + "camera", + "microphone", + "notifications", + "midi", + "push", + "clipboard-read", + "clipboard-write", + "payment-handler", + "usb", + "bluetooth", + "accelerometer", + "gyroscope", + "magnetometer", + "ambient-light-sensor", + "background-sync", + "persistent-storage", + "screen-wake-lock", + "web-share", + "xr-spatial-tracking" + ], + "x-enum-name": "BrowserPermission", + "x-enum-keys": [] }, + "x-example": "[\"geolocation\",\"notifications\"]", "default": [], "in": "query" }, @@ -5714,7 +5739,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "3", "default": 0, "in": "query" }, @@ -5724,7 +5749,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "800", "default": 0, "in": "query" }, @@ -5734,7 +5759,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "600", "default": 0, "in": "query" }, @@ -5744,7 +5769,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": -1, + "x-example": "85", "default": -1, "in": "query" }, @@ -5753,7 +5778,7 @@ "description": "Output format type (jpeg, jpg, png, gif and webp).", "required": false, "type": "string", - "x-example": "jpg", + "x-example": "jpeg", "enum": [ "jpg", "jpeg", @@ -5797,7 +5822,7 @@ "x-appwrite": { "method": "chat", "group": "console", - "weight": 253, + "weight": 243, "cookies": false, "type": "", "demo": "assistant\/chat.md", @@ -5931,7 +5956,7 @@ "x-appwrite": { "method": "variables", "group": "console", - "weight": 252, + "weight": 242, "cookies": false, "type": "", "demo": "console\/variables.md", @@ -7184,6 +7209,24 @@ "description": "Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.", "default": true, "x-example": false + }, + "attributes": { + "type": "array", + "description": "Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "default": [], + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of attribute keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "default": [], + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -12340,7 +12383,13 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -13184,7 +13233,66 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -13387,7 +13495,78 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "node-14.5", + "node-16.0", + "node-18.0", + "node-19.0", + "node-20.0", + "node-21.0", + "node-22", + "php-8.0", + "php-8.1", + "php-8.2", + "php-8.3", + "ruby-3.0", + "ruby-3.1", + "ruby-3.2", + "ruby-3.3", + "python-3.8", + "python-3.9", + "python-3.10", + "python-3.11", + "python-3.12", + "python-ml-3.11", + "python-ml-3.12", + "deno-1.21", + "deno-1.24", + "deno-1.35", + "deno-1.40", + "deno-1.46", + "deno-2.0", + "dart-2.15", + "dart-2.16", + "dart-2.17", + "dart-2.18", + "dart-2.19", + "dart-3.0", + "dart-3.1", + "dart-3.3", + "dart-3.5", + "dart-3.8", + "dart-3.9", + "dotnet-6.0", + "dotnet-7.0", + "dotnet-8.0", + "java-8.0", + "java-11.0", + "java-17.0", + "java-18.0", + "java-21.0", + "java-22", + "swift-5.5", + "swift-5.8", + "swift-5.9", + "swift-5.10", + "kotlin-1.6", + "kotlin-1.8", + "kotlin-1.9", + "kotlin-2.0", + "cpp-17", + "cpp-20", + "bun-1.0", + "bun-1.1", + "go-1.23", + "static-1", + "flutter-3.24", + "flutter-3.27", + "flutter-3.29", + "flutter-3.32", + "flutter-3.35" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [], "in": "query" @@ -13399,7 +13578,17 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "dev-tools", + "starter", + "databases", + "ai", + "messaging", + "utilities" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [], "in": "query" @@ -13828,7 +14017,66 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -14366,7 +14614,7 @@ "branch", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -14467,7 +14715,7 @@ "branch", "commit" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -15585,7 +15833,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 251, + "weight": 241, "cookies": false, "type": "graphql", "demo": "graphql\/query.md", @@ -15658,7 +15906,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 250, + "weight": 240, "cookies": false, "type": "graphql", "demo": "graphql\/mutation.md", @@ -15729,7 +15977,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 79, + "weight": 69, "cookies": false, "type": "", "demo": "health\/get.md", @@ -15778,7 +16026,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 100, + "weight": 90, "cookies": false, "type": "", "demo": "health\/get-antivirus.md", @@ -15827,7 +16075,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 82, + "weight": 72, "cookies": false, "type": "", "demo": "health\/get-cache.md", @@ -15876,7 +16124,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 87, + "weight": 77, "cookies": false, "type": "", "demo": "health\/get-certificate.md", @@ -15934,7 +16182,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 81, + "weight": 71, "cookies": false, "type": "", "demo": "health\/get-db.md", @@ -15983,7 +16231,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 83, + "weight": 73, "cookies": false, "type": "", "demo": "health\/get-pub-sub.md", @@ -16032,7 +16280,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 89, + "weight": 79, "cookies": false, "type": "", "demo": "health\/get-queue-builds.md", @@ -16092,7 +16340,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 88, + "weight": 78, "cookies": false, "type": "", "demo": "health\/get-queue-certificates.md", @@ -16152,7 +16400,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 90, + "weight": 80, "cookies": false, "type": "", "demo": "health\/get-queue-databases.md", @@ -16221,7 +16469,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 91, + "weight": 81, "cookies": false, "type": "", "demo": "health\/get-queue-deletes.md", @@ -16281,7 +16529,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 101, + "weight": 91, "cookies": false, "type": "", "demo": "health\/get-failed-jobs.md", @@ -16365,7 +16613,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 95, + "weight": 85, "cookies": false, "type": "", "demo": "health\/get-queue-functions.md", @@ -16425,7 +16673,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 86, + "weight": 76, "cookies": false, "type": "", "demo": "health\/get-queue-logs.md", @@ -16485,7 +16733,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 92, + "weight": 82, "cookies": false, "type": "", "demo": "health\/get-queue-mails.md", @@ -16545,7 +16793,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 93, + "weight": 83, "cookies": false, "type": "", "demo": "health\/get-queue-messaging.md", @@ -16605,7 +16853,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 94, + "weight": 84, "cookies": false, "type": "", "demo": "health\/get-queue-migrations.md", @@ -16665,7 +16913,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 96, + "weight": 86, "cookies": false, "type": "", "demo": "health\/get-queue-stats-resources.md", @@ -16725,7 +16973,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 97, + "weight": 87, "cookies": false, "type": "", "demo": "health\/get-queue-usage.md", @@ -16785,7 +17033,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 85, + "weight": 75, "cookies": false, "type": "", "demo": "health\/get-queue-webhooks.md", @@ -16845,7 +17093,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 99, + "weight": 89, "cookies": false, "type": "", "demo": "health\/get-storage.md", @@ -16894,7 +17142,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 98, + "weight": 88, "cookies": false, "type": "", "demo": "health\/get-storage-local.md", @@ -16943,7 +17191,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 84, + "weight": 74, "cookies": false, "type": "", "demo": "health\/get-time.md", @@ -16992,7 +17240,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 71, + "weight": 61, "cookies": false, "type": "", "demo": "locale\/get.md", @@ -17043,7 +17291,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 72, + "weight": 62, "cookies": false, "type": "", "demo": "locale\/list-codes.md", @@ -17094,7 +17342,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 76, + "weight": 66, "cookies": false, "type": "", "demo": "locale\/list-continents.md", @@ -17145,7 +17393,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 73, + "weight": 63, "cookies": false, "type": "", "demo": "locale\/list-countries.md", @@ -17196,7 +17444,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 74, + "weight": 64, "cookies": false, "type": "", "demo": "locale\/list-countries-eu.md", @@ -17247,7 +17495,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 75, + "weight": 65, "cookies": false, "type": "", "demo": "locale\/list-countries-phones.md", @@ -17298,7 +17546,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 77, + "weight": 67, "cookies": false, "type": "", "demo": "locale\/list-currencies.md", @@ -17349,7 +17597,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 78, + "weight": 68, "cookies": false, "type": "", "demo": "locale\/list-languages.md", @@ -17400,7 +17648,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 308, + "weight": 298, "cookies": false, "type": "", "demo": "messaging\/list-messages.md", @@ -17484,7 +17732,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 305, + "weight": 295, "cookies": false, "type": "", "demo": "messaging\/create-email.md", @@ -17643,7 +17891,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 312, + "weight": 302, "cookies": false, "type": "", "demo": "messaging\/update-email.md", @@ -17809,7 +18057,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 307, + "weight": 297, "cookies": false, "type": "", "demo": "messaging\/create-push.md", @@ -18006,7 +18254,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 314, + "weight": 304, "cookies": false, "type": "", "demo": "messaging\/update-push.md", @@ -18218,7 +18466,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 306, + "weight": 296, "cookies": false, "type": "", "demo": "messaging\/create-sms.md", @@ -18405,7 +18653,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 313, + "weight": 303, "cookies": false, "type": "", "demo": "messaging\/update-sms.md", @@ -18591,7 +18839,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 311, + "weight": 301, "cookies": false, "type": "", "demo": "messaging\/get-message.md", @@ -18646,7 +18894,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 315, + "weight": 305, "cookies": false, "type": "", "demo": "messaging\/delete.md", @@ -18706,7 +18954,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 309, + "weight": 299, "cookies": false, "type": "", "demo": "messaging\/list-message-logs.md", @@ -18787,7 +19035,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 310, + "weight": 300, "cookies": false, "type": "", "demo": "messaging\/list-targets.md", @@ -18868,7 +19116,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 279, + "weight": 269, "cookies": false, "type": "", "demo": "messaging\/list-providers.md", @@ -18952,7 +19200,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 278, + "weight": 268, "cookies": false, "type": "", "demo": "messaging\/create-apns-provider.md", @@ -19138,7 +19386,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 292, + "weight": 282, "cookies": false, "type": "", "demo": "messaging\/update-apns-provider.md", @@ -19321,7 +19569,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 277, + "weight": 267, "cookies": false, "type": "", "demo": "messaging\/create-fcm-provider.md", @@ -19476,7 +19724,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 291, + "weight": 281, "cookies": false, "type": "", "demo": "messaging\/update-fcm-provider.md", @@ -19627,7 +19875,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 268, + "weight": 258, "cookies": false, "type": "", "demo": "messaging\/create-mailgun-provider.md", @@ -19756,7 +20004,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 282, + "weight": 272, "cookies": false, "type": "", "demo": "messaging\/update-mailgun-provider.md", @@ -19883,7 +20131,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 272, + "weight": 262, "cookies": false, "type": "", "demo": "messaging\/create-msg-91-provider.md", @@ -19987,7 +20235,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 286, + "weight": 276, "cookies": false, "type": "", "demo": "messaging\/update-msg-91-provider.md", @@ -20089,7 +20337,7 @@ "x-appwrite": { "method": "createResendProvider", "group": "providers", - "weight": 270, + "weight": 260, "cookies": false, "type": "", "demo": "messaging\/create-resend-provider.md", @@ -20205,7 +20453,7 @@ "x-appwrite": { "method": "updateResendProvider", "group": "providers", - "weight": 284, + "weight": 274, "cookies": false, "type": "", "demo": "messaging\/update-resend-provider.md", @@ -20319,7 +20567,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 269, + "weight": 259, "cookies": false, "type": "", "demo": "messaging\/create-sendgrid-provider.md", @@ -20435,7 +20683,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 283, + "weight": 273, "cookies": false, "type": "", "demo": "messaging\/update-sendgrid-provider.md", @@ -20549,7 +20797,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 271, + "weight": 261, "cookies": false, "type": "", "demo": "messaging\/create-smtp-provider.md", @@ -20793,7 +21041,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 285, + "weight": 275, "cookies": false, "type": "", "demo": "messaging\/update-smtp-provider.md", @@ -21032,7 +21280,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 273, + "weight": 263, "cookies": false, "type": "", "demo": "messaging\/create-telesign-provider.md", @@ -21136,7 +21384,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 287, + "weight": 277, "cookies": false, "type": "", "demo": "messaging\/update-telesign-provider.md", @@ -21238,7 +21486,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 274, + "weight": 264, "cookies": false, "type": "", "demo": "messaging\/create-textmagic-provider.md", @@ -21342,7 +21590,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 288, + "weight": 278, "cookies": false, "type": "", "demo": "messaging\/update-textmagic-provider.md", @@ -21444,7 +21692,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 275, + "weight": 265, "cookies": false, "type": "", "demo": "messaging\/create-twilio-provider.md", @@ -21548,7 +21796,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 289, + "weight": 279, "cookies": false, "type": "", "demo": "messaging\/update-twilio-provider.md", @@ -21650,7 +21898,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 276, + "weight": 266, "cookies": false, "type": "", "demo": "messaging\/create-vonage-provider.md", @@ -21754,7 +22002,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 290, + "weight": 280, "cookies": false, "type": "", "demo": "messaging\/update-vonage-provider.md", @@ -21854,7 +22102,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 281, + "weight": 271, "cookies": false, "type": "", "demo": "messaging\/get-provider.md", @@ -21909,7 +22157,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 293, + "weight": 283, "cookies": false, "type": "", "demo": "messaging\/delete-provider.md", @@ -21969,7 +22217,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 280, + "weight": 270, "cookies": false, "type": "", "demo": "messaging\/list-provider-logs.md", @@ -22050,7 +22298,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 302, + "weight": 292, "cookies": false, "type": "", "demo": "messaging\/list-subscriber-logs.md", @@ -22131,7 +22379,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 295, + "weight": 285, "cookies": false, "type": "", "demo": "messaging\/list-topics.md", @@ -22213,7 +22461,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 294, + "weight": 284, "cookies": false, "type": "", "demo": "messaging\/create-topic.md", @@ -22301,7 +22549,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 297, + "weight": 287, "cookies": false, "type": "", "demo": "messaging\/get-topic.md", @@ -22361,7 +22609,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 298, + "weight": 288, "cookies": false, "type": "", "demo": "messaging\/update-topic.md", @@ -22442,7 +22690,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 299, + "weight": 289, "cookies": false, "type": "", "demo": "messaging\/delete-topic.md", @@ -22502,7 +22750,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 296, + "weight": 286, "cookies": false, "type": "", "demo": "messaging\/list-topic-logs.md", @@ -22583,7 +22831,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 301, + "weight": 291, "cookies": false, "type": "", "demo": "messaging\/list-subscribers.md", @@ -22673,7 +22921,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 300, + "weight": 290, "cookies": false, "type": "", "demo": "messaging\/create-subscriber.md", @@ -22760,7 +23008,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 303, + "weight": 293, "cookies": false, "type": "", "demo": "messaging\/get-subscriber.md", @@ -22823,7 +23071,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 304, + "weight": 294, "cookies": false, "type": "", "demo": "messaging\/delete-subscriber.md", @@ -22893,7 +23141,7 @@ "x-appwrite": { "method": "list", "group": null, - "weight": 260, + "weight": 250, "cookies": false, "type": "", "demo": "migrations\/list.md", @@ -22975,7 +23223,7 @@ "x-appwrite": { "method": "createAppwriteMigration", "group": null, - "weight": 254, + "weight": 244, "cookies": false, "type": "", "demo": "migrations\/create-appwrite-migration.md", @@ -23010,7 +23258,27 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "team", + "membership", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file", + "function", + "deployment", + "environment-variable" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "endpoint": { @@ -23067,7 +23335,7 @@ "x-appwrite": { "method": "getAppwriteReport", "group": null, - "weight": 262, + "weight": 252, "cookies": false, "type": "", "demo": "migrations\/get-appwrite-report.md", @@ -23097,7 +23365,27 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "team", + "membership", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file", + "function", + "deployment", + "environment-variable" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "in": "query" }, @@ -23155,7 +23443,7 @@ "x-appwrite": { "method": "createCSVExport", "group": null, - "weight": 259, + "weight": 249, "cookies": false, "type": "", "demo": "migrations\/create-csv-export.md", @@ -23280,7 +23568,7 @@ "x-appwrite": { "method": "createCSVImport", "group": null, - "weight": 258, + "weight": 248, "cookies": false, "type": "", "demo": "migrations\/create-csv-import.md", @@ -23370,7 +23658,7 @@ "x-appwrite": { "method": "createFirebaseMigration", "group": null, - "weight": 255, + "weight": 245, "cookies": false, "type": "", "demo": "migrations\/create-firebase-migration.md", @@ -23405,7 +23693,21 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "serviceAccount": { @@ -23448,7 +23750,7 @@ "x-appwrite": { "method": "getFirebaseReport", "group": null, - "weight": 263, + "weight": 253, "cookies": false, "type": "", "demo": "migrations\/get-firebase-report.md", @@ -23478,7 +23780,21 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "in": "query" }, @@ -23519,7 +23835,7 @@ "x-appwrite": { "method": "createNHostMigration", "group": null, - "weight": 257, + "weight": 247, "cookies": false, "type": "", "demo": "migrations\/create-n-host-migration.md", @@ -23554,7 +23870,22 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "subdomain": { @@ -23638,7 +23969,7 @@ "x-appwrite": { "method": "getNHostReport", "group": null, - "weight": 265, + "weight": 255, "cookies": false, "type": "", "demo": "migrations\/get-n-host-report.md", @@ -23668,7 +23999,22 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "in": "query" }, @@ -23758,7 +24104,7 @@ "x-appwrite": { "method": "createSupabaseMigration", "group": null, - "weight": 256, + "weight": 246, "cookies": false, "type": "", "demo": "migrations\/create-supabase-migration.md", @@ -23793,7 +24139,22 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "endpoint": { @@ -23870,7 +24231,7 @@ "x-appwrite": { "method": "getSupabaseReport", "group": null, - "weight": 264, + "weight": 254, "cookies": false, "type": "", "demo": "migrations\/get-supabase-report.md", @@ -23900,7 +24261,22 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "in": "query" }, @@ -23981,7 +24357,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 261, + "weight": 251, "cookies": false, "type": "", "demo": "migrations\/get.md", @@ -24039,7 +24415,7 @@ "x-appwrite": { "method": "retry", "group": null, - "weight": 266, + "weight": 256, "cookies": false, "type": "", "demo": "migrations\/retry.md", @@ -24092,7 +24468,7 @@ "x-appwrite": { "method": "delete", "group": null, - "weight": 267, + "weight": 257, "cookies": false, "type": "", "demo": "migrations\/delete.md", @@ -24150,7 +24526,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 149, + "weight": 139, "cookies": false, "type": "", "demo": "project\/get-usage.md", @@ -24232,7 +24608,7 @@ "x-appwrite": { "method": "listVariables", "group": null, - "weight": 151, + "weight": 141, "cookies": false, "type": "", "demo": "project\/list-variables.md", @@ -24280,7 +24656,7 @@ "x-appwrite": { "method": "createVariable", "group": null, - "weight": 150, + "weight": 140, "cookies": false, "type": "", "demo": "project\/create-variable.md", @@ -24361,7 +24737,7 @@ "x-appwrite": { "method": "getVariable", "group": null, - "weight": 152, + "weight": 142, "cookies": false, "type": "", "demo": "project\/get-variable.md", @@ -24419,7 +24795,7 @@ "x-appwrite": { "method": "updateVariable", "group": null, - "weight": 153, + "weight": 143, "cookies": false, "type": "", "demo": "project\/update-variable.md", @@ -24504,7 +24880,7 @@ "x-appwrite": { "method": "deleteVariable", "group": null, - "weight": 154, + "weight": 144, "cookies": false, "type": "", "demo": "project\/delete-variable.md", @@ -24642,7 +25018,7 @@ "x-appwrite": { "method": "create", "group": "projects", - "weight": 103, + "weight": 93, "cookies": false, "type": "", "demo": "projects\/create.md", @@ -24789,7 +25165,7 @@ "x-appwrite": { "method": "get", "group": "projects", - "weight": 104, + "weight": 94, "cookies": false, "type": "", "demo": "projects\/get.md", @@ -24847,7 +25223,7 @@ "x-appwrite": { "method": "update", "group": "projects", - "weight": 105, + "weight": 95, "cookies": false, "type": "", "demo": "projects\/update.md", @@ -24972,7 +25348,7 @@ "x-appwrite": { "method": "delete", "group": "projects", - "weight": 122, + "weight": 112, "cookies": false, "type": "", "demo": "projects\/delete.md", @@ -25032,7 +25408,7 @@ "x-appwrite": { "method": "updateApiStatus", "group": "projects", - "weight": 109, + "weight": 99, "cookies": false, "type": "", "demo": "projects\/update-api-status.md", @@ -25186,7 +25562,7 @@ "x-appwrite": { "method": "updateApiStatusAll", "group": "projects", - "weight": 110, + "weight": 100, "cookies": false, "type": "", "demo": "projects\/update-api-status-all.md", @@ -25322,7 +25698,7 @@ "x-appwrite": { "method": "updateAuthDuration", "group": "auth", - "weight": 115, + "weight": 105, "cookies": false, "type": "", "demo": "projects\/update-auth-duration.md", @@ -25400,7 +25776,7 @@ "x-appwrite": { "method": "updateAuthLimit", "group": "auth", - "weight": 114, + "weight": 104, "cookies": false, "type": "", "demo": "projects\/update-auth-limit.md", @@ -25478,7 +25854,7 @@ "x-appwrite": { "method": "updateAuthSessionsLimit", "group": "auth", - "weight": 120, + "weight": 110, "cookies": false, "type": "", "demo": "projects\/update-auth-sessions-limit.md", @@ -25556,7 +25932,7 @@ "x-appwrite": { "method": "updateMembershipsPrivacy", "group": "auth", - "weight": 113, + "weight": 103, "cookies": false, "type": "", "demo": "projects\/update-memberships-privacy.md", @@ -25648,7 +26024,7 @@ "x-appwrite": { "method": "updateMockNumbers", "group": "auth", - "weight": 121, + "weight": 111, "cookies": false, "type": "", "demo": "projects\/update-mock-numbers.md", @@ -25729,7 +26105,7 @@ "x-appwrite": { "method": "updateAuthPasswordDictionary", "group": "auth", - "weight": 118, + "weight": 108, "cookies": false, "type": "", "demo": "projects\/update-auth-password-dictionary.md", @@ -25807,7 +26183,7 @@ "x-appwrite": { "method": "updateAuthPasswordHistory", "group": "auth", - "weight": 117, + "weight": 107, "cookies": false, "type": "", "demo": "projects\/update-auth-password-history.md", @@ -25885,7 +26261,7 @@ "x-appwrite": { "method": "updatePersonalDataCheck", "group": "auth", - "weight": 119, + "weight": 109, "cookies": false, "type": "", "demo": "projects\/update-personal-data-check.md", @@ -25963,7 +26339,7 @@ "x-appwrite": { "method": "updateSessionAlerts", "group": "auth", - "weight": 112, + "weight": 102, "cookies": false, "type": "", "demo": "projects\/update-session-alerts.md", @@ -26041,7 +26417,7 @@ "x-appwrite": { "method": "updateSessionInvalidation", "group": "auth", - "weight": 148, + "weight": 138, "cookies": false, "type": "", "demo": "projects\/update-session-invalidation.md", @@ -26119,7 +26495,7 @@ "x-appwrite": { "method": "updateAuthStatus", "group": "auth", - "weight": 116, + "weight": 106, "cookies": false, "type": "", "demo": "projects\/update-auth-status.md", @@ -26587,7 +26963,7 @@ "x-appwrite": { "method": "createJWT", "group": "auth", - "weight": 134, + "weight": 124, "cookies": false, "type": "", "demo": "projects\/create-jwt.md", @@ -26630,7 +27006,66 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "duration": { @@ -26672,7 +27107,7 @@ "x-appwrite": { "method": "listKeys", "group": "keys", - "weight": 130, + "weight": 120, "cookies": false, "type": "", "demo": "projects\/list-keys.md", @@ -26739,7 +27174,7 @@ "x-appwrite": { "method": "createKey", "group": "keys", - "weight": 129, + "weight": 119, "cookies": false, "type": "", "demo": "projects\/create-key.md", @@ -26789,7 +27224,66 @@ "x-example": null, "x-nullable": true, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "expire": { @@ -26833,7 +27327,7 @@ "x-appwrite": { "method": "getKey", "group": "keys", - "weight": 131, + "weight": 121, "cookies": false, "type": "", "demo": "projects\/get-key.md", @@ -26899,7 +27393,7 @@ "x-appwrite": { "method": "updateKey", "group": "keys", - "weight": 132, + "weight": 122, "cookies": false, "type": "", "demo": "projects\/update-key.md", @@ -26957,7 +27451,66 @@ "x-example": null, "x-nullable": true, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "expire": { @@ -26996,7 +27549,7 @@ "x-appwrite": { "method": "deleteKey", "group": "keys", - "weight": 133, + "weight": 123, "cookies": false, "type": "", "demo": "projects\/delete-key.md", @@ -27064,7 +27617,7 @@ "x-appwrite": { "method": "updateOAuth2", "group": "auth", - "weight": 111, + "weight": 101, "cookies": false, "type": "", "demo": "projects\/update-o-auth-2.md", @@ -27205,7 +27758,7 @@ "x-appwrite": { "method": "listPlatforms", "group": "platforms", - "weight": 136, + "weight": 126, "cookies": false, "type": "", "demo": "projects\/list-platforms.md", @@ -27272,7 +27825,7 @@ "x-appwrite": { "method": "createPlatform", "group": "platforms", - "weight": 135, + "weight": 125, "cookies": false, "type": "", "demo": "projects\/create-platform.md", @@ -27392,7 +27945,7 @@ "x-appwrite": { "method": "getPlatform", "group": "platforms", - "weight": 137, + "weight": 127, "cookies": false, "type": "", "demo": "projects\/get-platform.md", @@ -27458,7 +28011,7 @@ "x-appwrite": { "method": "updatePlatform", "group": "platforms", - "weight": 138, + "weight": 128, "cookies": false, "type": "", "demo": "projects\/update-platform.md", @@ -27555,7 +28108,7 @@ "x-appwrite": { "method": "deletePlatform", "group": "platforms", - "weight": 139, + "weight": 129, "cookies": false, "type": "", "demo": "projects\/delete-platform.md", @@ -27623,7 +28176,7 @@ "x-appwrite": { "method": "updateServiceStatus", "group": "projects", - "weight": 107, + "weight": 97, "cookies": false, "type": "", "demo": "projects\/update-service-status.md", @@ -27725,7 +28278,7 @@ "x-appwrite": { "method": "updateServiceStatusAll", "group": "projects", - "weight": 108, + "weight": 98, "cookies": false, "type": "", "demo": "projects\/update-service-status-all.md", @@ -27803,7 +28356,7 @@ "x-appwrite": { "method": "updateSmtp", "group": "templates", - "weight": 140, + "weight": 130, "cookies": false, "type": "", "demo": "projects\/update-smtp.md", @@ -28006,7 +28559,7 @@ "x-appwrite": { "method": "createSmtpTest", "group": "templates", - "weight": 141, + "weight": 131, "cookies": false, "type": "", "demo": "projects\/create-smtp-test.md", @@ -28222,7 +28775,7 @@ "x-appwrite": { "method": "updateTeam", "group": "projects", - "weight": 106, + "weight": 96, "cookies": false, "type": "", "demo": "projects\/update-team.md", @@ -28298,7 +28851,7 @@ "x-appwrite": { "method": "getEmailTemplate", "group": "templates", - "weight": 143, + "weight": 133, "cookies": false, "type": "", "demo": "projects\/get-email-template.md", @@ -28518,7 +29071,7 @@ "x-appwrite": { "method": "updateEmailTemplate", "group": "templates", - "weight": 145, + "weight": 135, "cookies": false, "type": "", "demo": "projects\/update-email-template.md", @@ -28781,7 +29334,7 @@ "x-appwrite": { "method": "deleteEmailTemplate", "group": "templates", - "weight": 147, + "weight": 137, "cookies": false, "type": "", "demo": "projects\/delete-email-template.md", @@ -29001,7 +29554,7 @@ "x-appwrite": { "method": "getSmsTemplate", "group": "templates", - "weight": 142, + "weight": 132, "cookies": false, "type": "", "demo": "projects\/get-sms-template.md", @@ -29280,7 +29833,7 @@ "x-appwrite": { "method": "updateSmsTemplate", "group": "templates", - "weight": 144, + "weight": 134, "cookies": false, "type": "", "demo": "projects\/update-sms-template.md", @@ -29581,7 +30134,7 @@ "x-appwrite": { "method": "deleteSmsTemplate", "group": "templates", - "weight": 146, + "weight": 136, "cookies": false, "type": "", "demo": "projects\/delete-sms-template.md", @@ -29860,7 +30413,7 @@ "x-appwrite": { "method": "listWebhooks", "group": "webhooks", - "weight": 124, + "weight": 114, "cookies": false, "type": "", "demo": "projects\/list-webhooks.md", @@ -29927,7 +30480,7 @@ "x-appwrite": { "method": "createWebhook", "group": "webhooks", - "weight": 123, + "weight": 113, "cookies": false, "type": "", "demo": "projects\/create-webhook.md", @@ -30045,7 +30598,7 @@ "x-appwrite": { "method": "getWebhook", "group": "webhooks", - "weight": 125, + "weight": 115, "cookies": false, "type": "", "demo": "projects\/get-webhook.md", @@ -30111,7 +30664,7 @@ "x-appwrite": { "method": "updateWebhook", "group": "webhooks", - "weight": 126, + "weight": 116, "cookies": false, "type": "", "demo": "projects\/update-webhook.md", @@ -30232,7 +30785,7 @@ "x-appwrite": { "method": "deleteWebhook", "group": "webhooks", - "weight": 128, + "weight": 118, "cookies": false, "type": "", "demo": "projects\/delete-webhook.md", @@ -30300,7 +30853,7 @@ "x-appwrite": { "method": "updateWebhookSignature", "group": "webhooks", - "weight": 127, + "weight": 117, "cookies": false, "type": "", "demo": "projects\/update-webhook-signature.md", @@ -31453,7 +32006,26 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "analog", + "angular", + "nextjs", + "react", + "nuxt", + "vue", + "sveltekit", + "astro", + "tanstack-start", + "remix", + "lynx", + "flutter", + "react-native", + "vite", + "other" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [], "in": "query" @@ -31465,7 +32037,17 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "dev-tools", + "starter", + "databases", + "ai", + "messaging", + "utilities" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [], "in": "query" @@ -32441,7 +33023,7 @@ "commit", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -32543,7 +33125,7 @@ "commit", "tag" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -33539,7 +34121,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 156, + "weight": 146, "cookies": false, "type": "", "demo": "storage\/list-buckets.md", @@ -33620,7 +34202,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 155, + "weight": 145, "cookies": false, "type": "", "demo": "storage\/create-bucket.md", @@ -33764,7 +34346,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 157, + "weight": 147, "cookies": false, "type": "", "demo": "storage\/get-bucket.md", @@ -33823,7 +34405,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 158, + "weight": 148, "cookies": false, "type": "", "demo": "storage\/update-bucket.md", @@ -33963,7 +34545,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 159, + "weight": 149, "cookies": false, "type": "", "demo": "storage\/delete-bucket.md", @@ -34022,7 +34604,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 161, + "weight": 151, "cookies": false, "type": "", "demo": "storage\/list-files.md", @@ -34113,7 +34695,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 160, + "weight": 150, "cookies": false, "type": "upload", "demo": "storage\/create-file.md", @@ -34202,7 +34784,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 162, + "weight": 152, "cookies": false, "type": "", "demo": "storage\/get-file.md", @@ -34271,7 +34853,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 167, + "weight": 157, "cookies": false, "type": "", "demo": "storage\/update-file.md", @@ -34361,7 +34943,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 168, + "weight": 158, "cookies": false, "type": "", "demo": "storage\/delete-file.md", @@ -34430,7 +35012,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 164, + "weight": 154, "cookies": false, "type": "location", "demo": "storage\/get-file-download.md", @@ -34508,7 +35090,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 163, + "weight": 153, "cookies": false, "type": "location", "demo": "storage\/get-file-preview.md", @@ -34714,7 +35296,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 165, + "weight": 155, "cookies": false, "type": "location", "demo": "storage\/get-file-view.md", @@ -34792,7 +35374,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 169, + "weight": 159, "cookies": false, "type": "", "demo": "storage\/get-usage.md", @@ -34862,7 +35444,7 @@ "x-appwrite": { "method": "getBucketUsage", "group": null, - "weight": 170, + "weight": 160, "cookies": false, "type": "", "demo": "storage\/get-bucket-usage.md", @@ -35991,6 +36573,24 @@ "description": "Is table enabled? When set to 'disabled', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.", "default": true, "x-example": false + }, + "columns": { + "type": "array", + "description": "Array of column definitions to create. Each column should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "default": [], + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of column keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "default": [], + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -39703,7 +40303,13 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -41553,7 +42159,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 172, + "weight": 162, "cookies": false, "type": "", "demo": "teams\/list.md", @@ -41636,7 +42242,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 171, + "weight": 161, "cookies": false, "type": "", "demo": "teams\/create.md", @@ -41725,7 +42331,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 173, + "weight": 163, "cookies": false, "type": "", "demo": "teams\/get.md", @@ -41786,7 +42392,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 175, + "weight": 165, "cookies": false, "type": "", "demo": "teams\/update-name.md", @@ -41860,7 +42466,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 177, + "weight": 167, "cookies": false, "type": "", "demo": "teams\/delete.md", @@ -41921,7 +42527,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 184, + "weight": 174, "cookies": false, "type": "", "demo": "teams\/list-logs.md", @@ -42000,7 +42606,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 179, + "weight": 169, "cookies": false, "type": "", "demo": "teams\/list-memberships.md", @@ -42091,7 +42697,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 178, + "weight": 168, "cookies": false, "type": "", "demo": "teams\/create-membership.md", @@ -42155,7 +42761,14 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "url": { @@ -42203,7 +42816,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 180, + "weight": 170, "cookies": false, "type": "", "demo": "teams\/get-membership.md", @@ -42272,7 +42885,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 181, + "weight": 171, "cookies": false, "type": "", "demo": "teams\/update-membership.md", @@ -42326,7 +42939,14 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } } }, @@ -42357,7 +42977,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 183, + "weight": 173, "cookies": false, "type": "", "demo": "teams\/delete-membership.md", @@ -42428,7 +43048,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 182, + "weight": 172, "cookies": false, "type": "", "demo": "teams\/update-membership-status.md", @@ -42521,7 +43141,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 174, + "weight": 164, "cookies": false, "type": "", "demo": "teams\/get-prefs.md", @@ -42581,7 +43201,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 176, + "weight": 166, "cookies": false, "type": "", "demo": "teams\/update-prefs.md", @@ -43023,7 +43643,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 194, + "weight": 184, "cookies": false, "type": "", "demo": "users\/list.md", @@ -43104,7 +43724,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 185, + "weight": 175, "cookies": false, "type": "", "demo": "users\/create.md", @@ -43201,7 +43821,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 188, + "weight": 178, "cookies": false, "type": "", "demo": "users\/create-argon-2-user.md", @@ -43292,7 +43912,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 186, + "weight": 176, "cookies": false, "type": "", "demo": "users\/create-bcrypt-user.md", @@ -43381,7 +44001,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 202, + "weight": 192, "cookies": false, "type": "", "demo": "users\/list-identities.md", @@ -43459,7 +44079,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 225, + "weight": 215, "cookies": false, "type": "", "demo": "users\/delete-identity.md", @@ -43520,7 +44140,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 187, + "weight": 177, "cookies": false, "type": "", "demo": "users\/create-md-5-user.md", @@ -43611,7 +44231,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 190, + "weight": 180, "cookies": false, "type": "", "demo": "users\/create-ph-pass-user.md", @@ -43702,7 +44322,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 191, + "weight": 181, "cookies": false, "type": "", "demo": "users\/create-scrypt-user.md", @@ -43828,7 +44448,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 192, + "weight": 182, "cookies": false, "type": "", "demo": "users\/create-scrypt-modified-user.md", @@ -43940,7 +44560,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 189, + "weight": 179, "cookies": false, "type": "", "demo": "users\/create-sha-user.md", @@ -44050,7 +44670,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 227, + "weight": 217, "cookies": false, "type": "", "demo": "users\/get-usage.md", @@ -44120,7 +44740,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 195, + "weight": 185, "cookies": false, "type": "", "demo": "users\/get.md", @@ -44174,7 +44794,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 223, + "weight": 213, "cookies": false, "type": "", "demo": "users\/delete.md", @@ -44235,7 +44855,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 208, + "weight": 198, "cookies": false, "type": "", "demo": "users\/update-email.md", @@ -44314,7 +44934,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 226, + "weight": 216, "cookies": false, "type": "", "demo": "users\/create-jwt.md", @@ -44396,7 +45016,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 204, + "weight": 194, "cookies": false, "type": "", "demo": "users\/update-labels.md", @@ -44476,7 +45096,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 200, + "weight": 190, "cookies": false, "type": "", "demo": "users\/list-logs.md", @@ -44556,7 +45176,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 199, + "weight": 189, "cookies": false, "type": "", "demo": "users\/list-memberships.md", @@ -44647,7 +45267,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 213, + "weight": 203, "cookies": false, "type": "", "demo": "users\/update-mfa.md", @@ -44779,7 +45399,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 218, + "weight": 208, "cookies": false, "type": "", "demo": "users\/delete-mfa-authenticator.md", @@ -44907,7 +45527,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 214, + "weight": 204, "cookies": false, "type": "", "demo": "users\/list-mfa-factors.md", @@ -45020,7 +45640,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 215, + "weight": 205, "cookies": false, "type": "", "demo": "users\/get-mfa-recovery-codes.md", @@ -45133,7 +45753,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 217, + "weight": 207, "cookies": false, "type": "", "demo": "users\/update-mfa-recovery-codes.md", @@ -45246,7 +45866,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 216, + "weight": 206, "cookies": false, "type": "", "demo": "users\/create-mfa-recovery-codes.md", @@ -45361,7 +45981,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 206, + "weight": 196, "cookies": false, "type": "", "demo": "users\/update-name.md", @@ -45440,7 +46060,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 207, + "weight": 197, "cookies": false, "type": "", "demo": "users\/update-password.md", @@ -45519,7 +46139,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 209, + "weight": 199, "cookies": false, "type": "", "demo": "users\/update-phone.md", @@ -45596,7 +46216,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 196, + "weight": 186, "cookies": false, "type": "", "demo": "users\/get-prefs.md", @@ -45655,7 +46275,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 211, + "weight": 201, "cookies": false, "type": "", "demo": "users\/update-prefs.md", @@ -45732,7 +46352,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 198, + "weight": 188, "cookies": false, "type": "", "demo": "users\/list-sessions.md", @@ -45800,7 +46420,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 219, + "weight": 209, "cookies": false, "type": "", "demo": "users\/create-session.md", @@ -45854,7 +46474,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 222, + "weight": 212, "cookies": false, "type": "", "demo": "users\/delete-sessions.md", @@ -45910,7 +46530,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 221, + "weight": 211, "cookies": false, "type": "", "demo": "users\/delete-session.md", @@ -45979,7 +46599,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 203, + "weight": 193, "cookies": false, "type": "", "demo": "users\/update-status.md", @@ -46056,7 +46676,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 201, + "weight": 191, "cookies": false, "type": "", "demo": "users\/list-targets.md", @@ -46137,7 +46757,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 193, + "weight": 183, "cookies": false, "type": "", "demo": "users\/create-target.md", @@ -46248,7 +46868,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 197, + "weight": 187, "cookies": false, "type": "", "demo": "users\/get-target.md", @@ -46316,7 +46936,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 212, + "weight": 202, "cookies": false, "type": "", "demo": "users\/update-target.md", @@ -46406,7 +47026,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 224, + "weight": 214, "cookies": false, "type": "", "demo": "users\/delete-target.md", @@ -46476,7 +47096,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 220, + "weight": 210, "cookies": false, "type": "", "demo": "users\/create-token.md", @@ -46558,7 +47178,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 210, + "weight": 200, "cookies": false, "type": "", "demo": "users\/update-email-verification.md", @@ -46637,7 +47257,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 205, + "weight": 195, "cookies": false, "type": "", "demo": "users\/update-phone-verification.md", @@ -46716,7 +47336,7 @@ "x-appwrite": { "method": "createRepositoryDetection", "group": "repositories", - "weight": 231, + "weight": 221, "cookies": false, "type": "", "demo": "vcs\/create-repository-detection.md", @@ -46811,7 +47431,7 @@ "x-appwrite": { "method": "listRepositories", "group": "repositories", - "weight": 232, + "weight": 222, "cookies": false, "type": "", "demo": "vcs\/list-repositories.md", @@ -46864,6 +47484,18 @@ "x-example": "", "default": "", "in": "query" + }, + { + "name": "queries", + "description": "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", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" } ] }, @@ -46892,7 +47524,7 @@ "x-appwrite": { "method": "createRepository", "group": "repositories", - "weight": 233, + "weight": 223, "cookies": false, "type": "", "demo": "vcs\/create-repository.md", @@ -46975,7 +47607,7 @@ "x-appwrite": { "method": "getRepository", "group": "repositories", - "weight": 234, + "weight": 224, "cookies": false, "type": "", "demo": "vcs\/get-repository.md", @@ -47041,7 +47673,7 @@ "x-appwrite": { "method": "listRepositoryBranches", "group": "repositories", - "weight": 235, + "weight": 225, "cookies": false, "type": "", "demo": "vcs\/list-repository-branches.md", @@ -47107,7 +47739,7 @@ "x-appwrite": { "method": "getRepositoryContents", "group": "repositories", - "weight": 230, + "weight": 220, "cookies": false, "type": "", "demo": "vcs\/get-repository-contents.md", @@ -47190,7 +47822,7 @@ "x-appwrite": { "method": "updateExternalDeployments", "group": "repositories", - "weight": 240, + "weight": 230, "cookies": false, "type": "", "demo": "vcs\/update-external-deployments.md", @@ -47274,7 +47906,7 @@ "x-appwrite": { "method": "listInstallations", "group": "installations", - "weight": 237, + "weight": 227, "cookies": false, "type": "", "demo": "vcs\/list-installations.md", @@ -47354,7 +47986,7 @@ "x-appwrite": { "method": "getInstallation", "group": "installations", - "weight": 238, + "weight": 228, "cookies": false, "type": "", "demo": "vcs\/get-installation.md", @@ -47407,7 +48039,7 @@ "x-appwrite": { "method": "deleteInstallation", "group": "installations", - "weight": 239, + "weight": 229, "cookies": false, "type": "", "demo": "vcs\/delete-installation.md", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index e760c1bb7d..cda8c7682f 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -325,7 +325,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 58, + "weight": 48, "cookies": false, "type": "", "demo": "account\/list-identities.md", @@ -397,7 +397,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 59, + "weight": 49, "cookies": false, "type": "", "demo": "account\/delete-identity.md", @@ -587,7 +587,7 @@ "x-appwrite": { "method": "updateMFA", "group": "mfa", - "weight": 45, + "weight": 306, "cookies": false, "type": "", "demo": "account\/update-mfa.md", @@ -661,7 +661,7 @@ "x-appwrite": { "method": "createMfaAuthenticator", "group": "mfa", - "weight": 47, + "weight": 308, "cookies": false, "type": "", "demo": "account\/create-mfa-authenticator.md", @@ -784,7 +784,7 @@ "x-appwrite": { "method": "updateMfaAuthenticator", "group": "mfa", - "weight": 48, + "weight": 309, "cookies": false, "type": "", "demo": "account\/update-mfa-authenticator.md", @@ -924,7 +924,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 52, + "weight": 310, "cookies": false, "type": "", "demo": "account\/delete-mfa-authenticator.md", @@ -1021,7 +1021,7 @@ ] } }, - "\/account\/mfa\/challenge": { + "\/account\/mfa\/challenges": { "post": { "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", @@ -1047,7 +1047,7 @@ "x-appwrite": { "method": "createMfaChallenge", "group": "mfa", - "weight": 53, + "weight": 314, "cookies": false, "type": "", "demo": "account\/create-mfa-challenge.md", @@ -1178,7 +1178,7 @@ "x-appwrite": { "method": "updateMfaChallenge", "group": "mfa", - "weight": 54, + "weight": 315, "cookies": false, "type": "", "demo": "account\/update-mfa-challenge.md", @@ -1317,7 +1317,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 46, + "weight": 307, "cookies": false, "type": "", "demo": "account\/list-mfa-factors.md", @@ -1417,7 +1417,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 51, + "weight": 313, "cookies": false, "type": "", "demo": "account\/get-mfa-recovery-codes.md", @@ -1517,7 +1517,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 49, + "weight": 311, "cookies": false, "type": "", "demo": "account\/create-mfa-recovery-codes.md", @@ -1617,7 +1617,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 50, + "weight": 312, "cookies": false, "type": "", "demo": "account\/update-mfa-recovery-codes.md", @@ -3741,7 +3741,7 @@ "x-appwrite": { "method": "getBrowser", "group": null, - "weight": 61, + "weight": 51, "cookies": false, "type": "location", "demo": "avatars\/get-browser.md", @@ -3867,7 +3867,7 @@ "x-appwrite": { "method": "getCreditCard", "group": null, - "weight": 60, + "weight": 50, "cookies": false, "type": "location", "demo": "avatars\/get-credit-card.md", @@ -3999,7 +3999,7 @@ "x-appwrite": { "method": "getFavicon", "group": null, - "weight": 64, + "weight": 54, "cookies": false, "type": "location", "demo": "avatars\/get-favicon.md", @@ -4063,7 +4063,7 @@ "x-appwrite": { "method": "getFlag", "group": null, - "weight": 62, + "weight": 52, "cookies": false, "type": "location", "demo": "avatars\/get-flag.md", @@ -4551,7 +4551,7 @@ "x-appwrite": { "method": "getImage", "group": null, - "weight": 63, + "weight": 53, "cookies": false, "type": "location", "demo": "avatars\/get-image.md", @@ -4635,7 +4635,7 @@ "x-appwrite": { "method": "getInitials", "group": null, - "weight": 66, + "weight": 56, "cookies": false, "type": "location", "demo": "avatars\/get-initials.md", @@ -4727,7 +4727,7 @@ "x-appwrite": { "method": "getQR", "group": null, - "weight": 65, + "weight": 55, "cookies": false, "type": "location", "demo": "avatars\/get-qr.md", @@ -4819,7 +4819,7 @@ "x-appwrite": { "method": "getScreenshot", "group": null, - "weight": 67, + "weight": 57, "cookies": false, "type": "location", "demo": "avatars\/get-screenshot.md", @@ -4862,7 +4862,7 @@ "required": false, "type": "object", "default": [], - "x-example": "{}", + "x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}", "in": "query" }, { @@ -4871,7 +4871,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1920", "default": 1280, "in": "query" }, @@ -4881,7 +4881,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1080", "default": 720, "in": "query" }, @@ -4891,7 +4891,7 @@ "required": false, "type": "number", "format": "float", - "x-example": 0.1, + "x-example": "2", "default": 1, "in": "query" }, @@ -4900,7 +4900,7 @@ "description": "Browser theme. Pass \"light\" or \"dark\". Defaults to \"light\".", "required": false, "type": "string", - "x-example": "light", + "x-example": "dark", "enum": [ "light", "dark" @@ -4915,7 +4915,7 @@ "description": "Custom user agent string. Defaults to browser default.", "required": false, "type": "string", - "x-example": "", + "x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15", "default": "", "in": "query" }, @@ -4924,7 +4924,7 @@ "description": "Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.", "required": false, "type": "boolean", - "x-example": false, + "x-example": "true", "default": false, "in": "query" }, @@ -4933,7 +4933,7 @@ "description": "Browser locale (e.g., \"en-US\", \"fr-FR\"). Defaults to browser default.", "required": false, "type": "string", - "x-example": "", + "x-example": "en-US", "default": "", "in": "query" }, @@ -4942,7 +4942,7 @@ "description": "IANA timezone identifier (e.g., \"America\/New_York\", \"Europe\/London\"). Defaults to browser default.", "required": false, "type": "string", - "x-example": "africa\/abidjan", + "x-example": "america\/new_york", "enum": [ "africa\/abidjan", "africa\/accra", @@ -5375,7 +5375,7 @@ "required": false, "type": "number", "format": "float", - "x-example": -90, + "x-example": "37.7749", "default": 0, "in": "query" }, @@ -5385,7 +5385,7 @@ "required": false, "type": "number", "format": "float", - "x-example": -180, + "x-example": "-122.4194", "default": 0, "in": "query" }, @@ -5395,7 +5395,7 @@ "required": false, "type": "number", "format": "float", - "x-example": 0, + "x-example": "100", "default": 0, "in": "query" }, @@ -5404,7 +5404,7 @@ "description": "Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.", "required": false, "type": "boolean", - "x-example": false, + "x-example": "true", "default": false, "in": "query" }, @@ -5415,8 +5415,33 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "geolocation", + "camera", + "microphone", + "notifications", + "midi", + "push", + "clipboard-read", + "clipboard-write", + "payment-handler", + "usb", + "bluetooth", + "accelerometer", + "gyroscope", + "magnetometer", + "ambient-light-sensor", + "background-sync", + "persistent-storage", + "screen-wake-lock", + "web-share", + "xr-spatial-tracking" + ], + "x-enum-name": "BrowserPermission", + "x-enum-keys": [] }, + "x-example": "[\"geolocation\",\"notifications\"]", "default": [], "in": "query" }, @@ -5426,7 +5451,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "3", "default": 0, "in": "query" }, @@ -5436,7 +5461,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "800", "default": 0, "in": "query" }, @@ -5446,7 +5471,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "600", "default": 0, "in": "query" }, @@ -5456,7 +5481,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": -1, + "x-example": "85", "default": -1, "in": "query" }, @@ -5465,7 +5490,7 @@ "description": "Output format type (jpeg, jpg, png, gif and webp).", "required": false, "type": "string", - "x-example": "jpg", + "x-example": "jpeg", "enum": [ "jpg", "jpeg", @@ -6636,6 +6661,24 @@ "description": "Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.", "default": true, "x-example": false + }, + "attributes": { + "type": "array", + "description": "Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "default": [], + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of attribute keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "default": [], + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -11759,7 +11802,13 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -12221,7 +12270,66 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -12638,7 +12746,66 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -13182,7 +13349,7 @@ "branch", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -13284,7 +13451,7 @@ "branch", "commit" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -14340,7 +14507,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 251, + "weight": 241, "cookies": false, "type": "graphql", "demo": "graphql\/query.md", @@ -14415,7 +14582,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 250, + "weight": 240, "cookies": false, "type": "graphql", "demo": "graphql\/mutation.md", @@ -14488,7 +14655,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 79, + "weight": 69, "cookies": false, "type": "", "demo": "health\/get.md", @@ -14538,7 +14705,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 100, + "weight": 90, "cookies": false, "type": "", "demo": "health\/get-antivirus.md", @@ -14588,7 +14755,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 82, + "weight": 72, "cookies": false, "type": "", "demo": "health\/get-cache.md", @@ -14638,7 +14805,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 87, + "weight": 77, "cookies": false, "type": "", "demo": "health\/get-certificate.md", @@ -14697,7 +14864,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 81, + "weight": 71, "cookies": false, "type": "", "demo": "health\/get-db.md", @@ -14747,7 +14914,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 83, + "weight": 73, "cookies": false, "type": "", "demo": "health\/get-pub-sub.md", @@ -14797,7 +14964,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 89, + "weight": 79, "cookies": false, "type": "", "demo": "health\/get-queue-builds.md", @@ -14858,7 +15025,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 88, + "weight": 78, "cookies": false, "type": "", "demo": "health\/get-queue-certificates.md", @@ -14919,7 +15086,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 90, + "weight": 80, "cookies": false, "type": "", "demo": "health\/get-queue-databases.md", @@ -14989,7 +15156,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 91, + "weight": 81, "cookies": false, "type": "", "demo": "health\/get-queue-deletes.md", @@ -15050,7 +15217,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 101, + "weight": 91, "cookies": false, "type": "", "demo": "health\/get-failed-jobs.md", @@ -15135,7 +15302,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 95, + "weight": 85, "cookies": false, "type": "", "demo": "health\/get-queue-functions.md", @@ -15196,7 +15363,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 86, + "weight": 76, "cookies": false, "type": "", "demo": "health\/get-queue-logs.md", @@ -15257,7 +15424,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 92, + "weight": 82, "cookies": false, "type": "", "demo": "health\/get-queue-mails.md", @@ -15318,7 +15485,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 93, + "weight": 83, "cookies": false, "type": "", "demo": "health\/get-queue-messaging.md", @@ -15379,7 +15546,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 94, + "weight": 84, "cookies": false, "type": "", "demo": "health\/get-queue-migrations.md", @@ -15440,7 +15607,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 96, + "weight": 86, "cookies": false, "type": "", "demo": "health\/get-queue-stats-resources.md", @@ -15501,7 +15668,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 97, + "weight": 87, "cookies": false, "type": "", "demo": "health\/get-queue-usage.md", @@ -15562,7 +15729,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 85, + "weight": 75, "cookies": false, "type": "", "demo": "health\/get-queue-webhooks.md", @@ -15623,7 +15790,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 99, + "weight": 89, "cookies": false, "type": "", "demo": "health\/get-storage.md", @@ -15673,7 +15840,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 98, + "weight": 88, "cookies": false, "type": "", "demo": "health\/get-storage-local.md", @@ -15723,7 +15890,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 84, + "weight": 74, "cookies": false, "type": "", "demo": "health\/get-time.md", @@ -15773,7 +15940,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 71, + "weight": 61, "cookies": false, "type": "", "demo": "locale\/get.md", @@ -15826,7 +15993,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 72, + "weight": 62, "cookies": false, "type": "", "demo": "locale\/list-codes.md", @@ -15879,7 +16046,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 76, + "weight": 66, "cookies": false, "type": "", "demo": "locale\/list-continents.md", @@ -15932,7 +16099,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 73, + "weight": 63, "cookies": false, "type": "", "demo": "locale\/list-countries.md", @@ -15985,7 +16152,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 74, + "weight": 64, "cookies": false, "type": "", "demo": "locale\/list-countries-eu.md", @@ -16038,7 +16205,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 75, + "weight": 65, "cookies": false, "type": "", "demo": "locale\/list-countries-phones.md", @@ -16091,7 +16258,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 77, + "weight": 67, "cookies": false, "type": "", "demo": "locale\/list-currencies.md", @@ -16144,7 +16311,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 78, + "weight": 68, "cookies": false, "type": "", "demo": "locale\/list-languages.md", @@ -16197,7 +16364,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 308, + "weight": 298, "cookies": false, "type": "", "demo": "messaging\/list-messages.md", @@ -16282,7 +16449,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 305, + "weight": 295, "cookies": false, "type": "", "demo": "messaging\/create-email.md", @@ -16442,7 +16609,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 312, + "weight": 302, "cookies": false, "type": "", "demo": "messaging\/update-email.md", @@ -16609,7 +16776,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 307, + "weight": 297, "cookies": false, "type": "", "demo": "messaging\/create-push.md", @@ -16807,7 +16974,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 314, + "weight": 304, "cookies": false, "type": "", "demo": "messaging\/update-push.md", @@ -17020,7 +17187,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 306, + "weight": 296, "cookies": false, "type": "", "demo": "messaging\/create-sms.md", @@ -17210,7 +17377,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 313, + "weight": 303, "cookies": false, "type": "", "demo": "messaging\/update-sms.md", @@ -17399,7 +17566,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 311, + "weight": 301, "cookies": false, "type": "", "demo": "messaging\/get-message.md", @@ -17455,7 +17622,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 315, + "weight": 305, "cookies": false, "type": "", "demo": "messaging\/delete.md", @@ -17516,7 +17683,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 309, + "weight": 299, "cookies": false, "type": "", "demo": "messaging\/list-message-logs.md", @@ -17598,7 +17765,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 310, + "weight": 300, "cookies": false, "type": "", "demo": "messaging\/list-targets.md", @@ -17680,7 +17847,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 279, + "weight": 269, "cookies": false, "type": "", "demo": "messaging\/list-providers.md", @@ -17765,7 +17932,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 278, + "weight": 268, "cookies": false, "type": "", "demo": "messaging\/create-apns-provider.md", @@ -17954,7 +18121,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 292, + "weight": 282, "cookies": false, "type": "", "demo": "messaging\/update-apns-provider.md", @@ -18140,7 +18307,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 277, + "weight": 267, "cookies": false, "type": "", "demo": "messaging\/create-fcm-provider.md", @@ -18298,7 +18465,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 291, + "weight": 281, "cookies": false, "type": "", "demo": "messaging\/update-fcm-provider.md", @@ -18452,7 +18619,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 268, + "weight": 258, "cookies": false, "type": "", "demo": "messaging\/create-mailgun-provider.md", @@ -18582,7 +18749,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 282, + "weight": 272, "cookies": false, "type": "", "demo": "messaging\/update-mailgun-provider.md", @@ -18710,7 +18877,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 272, + "weight": 262, "cookies": false, "type": "", "demo": "messaging\/create-msg-91-provider.md", @@ -18815,7 +18982,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 286, + "weight": 276, "cookies": false, "type": "", "demo": "messaging\/update-msg-91-provider.md", @@ -18918,7 +19085,7 @@ "x-appwrite": { "method": "createResendProvider", "group": "providers", - "weight": 270, + "weight": 260, "cookies": false, "type": "", "demo": "messaging\/create-resend-provider.md", @@ -19035,7 +19202,7 @@ "x-appwrite": { "method": "updateResendProvider", "group": "providers", - "weight": 284, + "weight": 274, "cookies": false, "type": "", "demo": "messaging\/update-resend-provider.md", @@ -19150,7 +19317,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 269, + "weight": 259, "cookies": false, "type": "", "demo": "messaging\/create-sendgrid-provider.md", @@ -19267,7 +19434,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 283, + "weight": 273, "cookies": false, "type": "", "demo": "messaging\/update-sendgrid-provider.md", @@ -19382,7 +19549,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 271, + "weight": 261, "cookies": false, "type": "", "demo": "messaging\/create-smtp-provider.md", @@ -19629,7 +19796,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 285, + "weight": 275, "cookies": false, "type": "", "demo": "messaging\/update-smtp-provider.md", @@ -19871,7 +20038,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 273, + "weight": 263, "cookies": false, "type": "", "demo": "messaging\/create-telesign-provider.md", @@ -19976,7 +20143,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 287, + "weight": 277, "cookies": false, "type": "", "demo": "messaging\/update-telesign-provider.md", @@ -20079,7 +20246,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 274, + "weight": 264, "cookies": false, "type": "", "demo": "messaging\/create-textmagic-provider.md", @@ -20184,7 +20351,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 288, + "weight": 278, "cookies": false, "type": "", "demo": "messaging\/update-textmagic-provider.md", @@ -20287,7 +20454,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 275, + "weight": 265, "cookies": false, "type": "", "demo": "messaging\/create-twilio-provider.md", @@ -20392,7 +20559,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 289, + "weight": 279, "cookies": false, "type": "", "demo": "messaging\/update-twilio-provider.md", @@ -20495,7 +20662,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 276, + "weight": 266, "cookies": false, "type": "", "demo": "messaging\/create-vonage-provider.md", @@ -20600,7 +20767,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 290, + "weight": 280, "cookies": false, "type": "", "demo": "messaging\/update-vonage-provider.md", @@ -20701,7 +20868,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 281, + "weight": 271, "cookies": false, "type": "", "demo": "messaging\/get-provider.md", @@ -20757,7 +20924,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 293, + "weight": 283, "cookies": false, "type": "", "demo": "messaging\/delete-provider.md", @@ -20818,7 +20985,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 280, + "weight": 270, "cookies": false, "type": "", "demo": "messaging\/list-provider-logs.md", @@ -20900,7 +21067,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 302, + "weight": 292, "cookies": false, "type": "", "demo": "messaging\/list-subscriber-logs.md", @@ -20982,7 +21149,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 295, + "weight": 285, "cookies": false, "type": "", "demo": "messaging\/list-topics.md", @@ -21065,7 +21232,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 294, + "weight": 284, "cookies": false, "type": "", "demo": "messaging\/create-topic.md", @@ -21154,7 +21321,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 297, + "weight": 287, "cookies": false, "type": "", "demo": "messaging\/get-topic.md", @@ -21215,7 +21382,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 298, + "weight": 288, "cookies": false, "type": "", "demo": "messaging\/update-topic.md", @@ -21297,7 +21464,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 299, + "weight": 289, "cookies": false, "type": "", "demo": "messaging\/delete-topic.md", @@ -21358,7 +21525,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 296, + "weight": 286, "cookies": false, "type": "", "demo": "messaging\/list-topic-logs.md", @@ -21440,7 +21607,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 301, + "weight": 291, "cookies": false, "type": "", "demo": "messaging\/list-subscribers.md", @@ -21531,7 +21698,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 300, + "weight": 290, "cookies": false, "type": "", "demo": "messaging\/create-subscriber.md", @@ -21620,7 +21787,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 303, + "weight": 293, "cookies": false, "type": "", "demo": "messaging\/get-subscriber.md", @@ -21684,7 +21851,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 304, + "weight": 294, "cookies": false, "type": "", "demo": "messaging\/delete-subscriber.md", @@ -23014,7 +23181,7 @@ "commit", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -23117,7 +23284,7 @@ "commit", "tag" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -24047,7 +24214,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 156, + "weight": 146, "cookies": false, "type": "", "demo": "storage\/list-buckets.md", @@ -24129,7 +24296,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 155, + "weight": 145, "cookies": false, "type": "", "demo": "storage\/create-bucket.md", @@ -24274,7 +24441,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 157, + "weight": 147, "cookies": false, "type": "", "demo": "storage\/get-bucket.md", @@ -24334,7 +24501,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 158, + "weight": 148, "cookies": false, "type": "", "demo": "storage\/update-bucket.md", @@ -24475,7 +24642,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 159, + "weight": 149, "cookies": false, "type": "", "demo": "storage\/delete-bucket.md", @@ -24535,7 +24702,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 161, + "weight": 151, "cookies": false, "type": "", "demo": "storage\/list-files.md", @@ -24628,7 +24795,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 160, + "weight": 150, "cookies": false, "type": "upload", "demo": "storage\/create-file.md", @@ -24719,7 +24886,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 162, + "weight": 152, "cookies": false, "type": "", "demo": "storage\/get-file.md", @@ -24790,7 +24957,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 167, + "weight": 157, "cookies": false, "type": "", "demo": "storage\/update-file.md", @@ -24882,7 +25049,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 168, + "weight": 158, "cookies": false, "type": "", "demo": "storage\/delete-file.md", @@ -24953,7 +25120,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 164, + "weight": 154, "cookies": false, "type": "location", "demo": "storage\/get-file-download.md", @@ -25033,7 +25200,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 163, + "weight": 153, "cookies": false, "type": "location", "demo": "storage\/get-file-preview.md", @@ -25241,7 +25408,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 165, + "weight": 155, "cookies": false, "type": "location", "demo": "storage\/get-file-view.md", @@ -26296,6 +26463,24 @@ "description": "Is table enabled? When set to 'disabled', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.", "default": true, "x-example": false + }, + "columns": { + "type": "array", + "description": "Array of column definitions to create. Each column should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "default": [], + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of column keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "default": [], + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -30042,7 +30227,13 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -31552,7 +31743,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 172, + "weight": 162, "cookies": false, "type": "", "demo": "teams\/list.md", @@ -31637,7 +31828,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 171, + "weight": 161, "cookies": false, "type": "", "demo": "teams\/create.md", @@ -31728,7 +31919,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 173, + "weight": 163, "cookies": false, "type": "", "demo": "teams\/get.md", @@ -31791,7 +31982,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 175, + "weight": 165, "cookies": false, "type": "", "demo": "teams\/update-name.md", @@ -31867,7 +32058,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 177, + "weight": 167, "cookies": false, "type": "", "demo": "teams\/delete.md", @@ -31930,7 +32121,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 179, + "weight": 169, "cookies": false, "type": "", "demo": "teams\/list-memberships.md", @@ -32023,7 +32214,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 178, + "weight": 168, "cookies": false, "type": "", "demo": "teams\/create-membership.md", @@ -32089,7 +32280,14 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "url": { @@ -32137,7 +32335,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 180, + "weight": 170, "cookies": false, "type": "", "demo": "teams\/get-membership.md", @@ -32208,7 +32406,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 181, + "weight": 171, "cookies": false, "type": "", "demo": "teams\/update-membership.md", @@ -32264,7 +32462,14 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } } }, @@ -32295,7 +32500,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 183, + "weight": 173, "cookies": false, "type": "", "demo": "teams\/delete-membership.md", @@ -32368,7 +32573,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 182, + "weight": 172, "cookies": false, "type": "", "demo": "teams\/update-membership-status.md", @@ -32463,7 +32668,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 174, + "weight": 164, "cookies": false, "type": "", "demo": "teams\/get-prefs.md", @@ -32525,7 +32730,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 176, + "weight": 166, "cookies": false, "type": "", "demo": "teams\/update-prefs.md", @@ -32974,7 +33179,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 194, + "weight": 184, "cookies": false, "type": "", "demo": "users\/list.md", @@ -33056,7 +33261,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 185, + "weight": 175, "cookies": false, "type": "", "demo": "users\/create.md", @@ -33154,7 +33359,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 188, + "weight": 178, "cookies": false, "type": "", "demo": "users\/create-argon-2-user.md", @@ -33246,7 +33451,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 186, + "weight": 176, "cookies": false, "type": "", "demo": "users\/create-bcrypt-user.md", @@ -33336,7 +33541,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 202, + "weight": 192, "cookies": false, "type": "", "demo": "users\/list-identities.md", @@ -33415,7 +33620,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 225, + "weight": 215, "cookies": false, "type": "", "demo": "users\/delete-identity.md", @@ -33477,7 +33682,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 187, + "weight": 177, "cookies": false, "type": "", "demo": "users\/create-md-5-user.md", @@ -33569,7 +33774,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 190, + "weight": 180, "cookies": false, "type": "", "demo": "users\/create-ph-pass-user.md", @@ -33661,7 +33866,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 191, + "weight": 181, "cookies": false, "type": "", "demo": "users\/create-scrypt-user.md", @@ -33788,7 +33993,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 192, + "weight": 182, "cookies": false, "type": "", "demo": "users\/create-scrypt-modified-user.md", @@ -33901,7 +34106,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 189, + "weight": 179, "cookies": false, "type": "", "demo": "users\/create-sha-user.md", @@ -34012,7 +34217,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 195, + "weight": 185, "cookies": false, "type": "", "demo": "users\/get.md", @@ -34067,7 +34272,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 223, + "weight": 213, "cookies": false, "type": "", "demo": "users\/delete.md", @@ -34129,7 +34334,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 208, + "weight": 198, "cookies": false, "type": "", "demo": "users\/update-email.md", @@ -34209,7 +34414,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 226, + "weight": 216, "cookies": false, "type": "", "demo": "users\/create-jwt.md", @@ -34292,7 +34497,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 204, + "weight": 194, "cookies": false, "type": "", "demo": "users\/update-labels.md", @@ -34373,7 +34578,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 200, + "weight": 190, "cookies": false, "type": "", "demo": "users\/list-logs.md", @@ -34454,7 +34659,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 199, + "weight": 189, "cookies": false, "type": "", "demo": "users\/list-memberships.md", @@ -34546,7 +34751,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 213, + "weight": 203, "cookies": false, "type": "", "demo": "users\/update-mfa.md", @@ -34681,7 +34886,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 218, + "weight": 208, "cookies": false, "type": "", "demo": "users\/delete-mfa-authenticator.md", @@ -34812,7 +35017,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 214, + "weight": 204, "cookies": false, "type": "", "demo": "users\/list-mfa-factors.md", @@ -34928,7 +35133,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 215, + "weight": 205, "cookies": false, "type": "", "demo": "users\/get-mfa-recovery-codes.md", @@ -35044,7 +35249,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 217, + "weight": 207, "cookies": false, "type": "", "demo": "users\/update-mfa-recovery-codes.md", @@ -35160,7 +35365,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 216, + "weight": 206, "cookies": false, "type": "", "demo": "users\/create-mfa-recovery-codes.md", @@ -35278,7 +35483,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 206, + "weight": 196, "cookies": false, "type": "", "demo": "users\/update-name.md", @@ -35358,7 +35563,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 207, + "weight": 197, "cookies": false, "type": "", "demo": "users\/update-password.md", @@ -35438,7 +35643,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 209, + "weight": 199, "cookies": false, "type": "", "demo": "users\/update-phone.md", @@ -35516,7 +35721,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 196, + "weight": 186, "cookies": false, "type": "", "demo": "users\/get-prefs.md", @@ -35576,7 +35781,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 211, + "weight": 201, "cookies": false, "type": "", "demo": "users\/update-prefs.md", @@ -35654,7 +35859,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 198, + "weight": 188, "cookies": false, "type": "", "demo": "users\/list-sessions.md", @@ -35723,7 +35928,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 219, + "weight": 209, "cookies": false, "type": "", "demo": "users\/create-session.md", @@ -35778,7 +35983,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 222, + "weight": 212, "cookies": false, "type": "", "demo": "users\/delete-sessions.md", @@ -35835,7 +36040,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 221, + "weight": 211, "cookies": false, "type": "", "demo": "users\/delete-session.md", @@ -35905,7 +36110,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 203, + "weight": 193, "cookies": false, "type": "", "demo": "users\/update-status.md", @@ -35983,7 +36188,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 201, + "weight": 191, "cookies": false, "type": "", "demo": "users\/list-targets.md", @@ -36065,7 +36270,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 193, + "weight": 183, "cookies": false, "type": "", "demo": "users\/create-target.md", @@ -36177,7 +36382,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 197, + "weight": 187, "cookies": false, "type": "", "demo": "users\/get-target.md", @@ -36246,7 +36451,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 212, + "weight": 202, "cookies": false, "type": "", "demo": "users\/update-target.md", @@ -36337,7 +36542,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 224, + "weight": 214, "cookies": false, "type": "", "demo": "users\/delete-target.md", @@ -36408,7 +36613,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 220, + "weight": 210, "cookies": false, "type": "", "demo": "users\/create-token.md", @@ -36491,7 +36696,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 210, + "weight": 200, "cookies": false, "type": "", "demo": "users\/update-email-verification.md", @@ -36571,7 +36776,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 205, + "weight": 195, "cookies": false, "type": "", "demo": "users\/update-phone-verification.md", diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 1594f816ef..aaa0ca3eba 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -314,7 +314,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 58, + "weight": 48, "cookies": false, "type": "", "demo": "account\/list-identities.md", @@ -385,7 +385,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 59, + "weight": 49, "cookies": false, "type": "", "demo": "account\/delete-identity.md", @@ -573,7 +573,7 @@ "x-appwrite": { "method": "updateMFA", "group": "mfa", - "weight": 45, + "weight": 306, "cookies": false, "type": "", "demo": "account\/update-mfa.md", @@ -646,7 +646,7 @@ "x-appwrite": { "method": "createMfaAuthenticator", "group": "mfa", - "weight": 47, + "weight": 308, "cookies": false, "type": "", "demo": "account\/create-mfa-authenticator.md", @@ -766,7 +766,7 @@ "x-appwrite": { "method": "updateMfaAuthenticator", "group": "mfa", - "weight": 48, + "weight": 309, "cookies": false, "type": "", "demo": "account\/update-mfa-authenticator.md", @@ -903,7 +903,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 52, + "weight": 310, "cookies": false, "type": "", "demo": "account\/delete-mfa-authenticator.md", @@ -997,7 +997,7 @@ ] } }, - "\/account\/mfa\/challenge": { + "\/account\/mfa\/challenges": { "post": { "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", @@ -1023,7 +1023,7 @@ "x-appwrite": { "method": "createMfaChallenge", "group": "mfa", - "weight": 53, + "weight": 314, "cookies": false, "type": "", "demo": "account\/create-mfa-challenge.md", @@ -1154,7 +1154,7 @@ "x-appwrite": { "method": "updateMfaChallenge", "group": "mfa", - "weight": 54, + "weight": 315, "cookies": false, "type": "", "demo": "account\/update-mfa-challenge.md", @@ -1290,7 +1290,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 46, + "weight": 307, "cookies": false, "type": "", "demo": "account\/list-mfa-factors.md", @@ -1387,7 +1387,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 51, + "weight": 313, "cookies": false, "type": "", "demo": "account\/get-mfa-recovery-codes.md", @@ -1484,7 +1484,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 49, + "weight": 311, "cookies": false, "type": "", "demo": "account\/create-mfa-recovery-codes.md", @@ -1581,7 +1581,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 50, + "weight": 312, "cookies": false, "type": "", "demo": "account\/update-mfa-recovery-codes.md", @@ -3033,7 +3033,7 @@ "x-appwrite": { "method": "createPushTarget", "group": "pushTargets", - "weight": 55, + "weight": 45, "cookies": false, "type": "", "demo": "account\/create-push-target.md", @@ -3117,7 +3117,7 @@ "x-appwrite": { "method": "updatePushTarget", "group": "pushTargets", - "weight": 56, + "weight": 46, "cookies": false, "type": "", "demo": "account\/update-push-target.md", @@ -3189,7 +3189,7 @@ "x-appwrite": { "method": "deletePushTarget", "group": "pushTargets", - "weight": 57, + "weight": 47, "cookies": false, "type": "", "demo": "account\/delete-push-target.md", @@ -4030,7 +4030,7 @@ "x-appwrite": { "method": "getBrowser", "group": null, - "weight": 61, + "weight": 51, "cookies": false, "type": "location", "demo": "avatars\/get-browser.md", @@ -4154,7 +4154,7 @@ "x-appwrite": { "method": "getCreditCard", "group": null, - "weight": 60, + "weight": 50, "cookies": false, "type": "location", "demo": "avatars\/get-credit-card.md", @@ -4284,7 +4284,7 @@ "x-appwrite": { "method": "getFavicon", "group": null, - "weight": 64, + "weight": 54, "cookies": false, "type": "location", "demo": "avatars\/get-favicon.md", @@ -4346,7 +4346,7 @@ "x-appwrite": { "method": "getFlag", "group": null, - "weight": 62, + "weight": 52, "cookies": false, "type": "location", "demo": "avatars\/get-flag.md", @@ -4832,7 +4832,7 @@ "x-appwrite": { "method": "getImage", "group": null, - "weight": 63, + "weight": 53, "cookies": false, "type": "location", "demo": "avatars\/get-image.md", @@ -4914,7 +4914,7 @@ "x-appwrite": { "method": "getInitials", "group": null, - "weight": 66, + "weight": 56, "cookies": false, "type": "location", "demo": "avatars\/get-initials.md", @@ -5004,7 +5004,7 @@ "x-appwrite": { "method": "getQR", "group": null, - "weight": 65, + "weight": 55, "cookies": false, "type": "location", "demo": "avatars\/get-qr.md", @@ -5094,7 +5094,7 @@ "x-appwrite": { "method": "getScreenshot", "group": null, - "weight": 67, + "weight": 57, "cookies": false, "type": "location", "demo": "avatars\/get-screenshot.md", @@ -5135,7 +5135,7 @@ "required": false, "type": "object", "default": [], - "x-example": "{}", + "x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}", "in": "query" }, { @@ -5144,7 +5144,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1920", "default": 1280, "in": "query" }, @@ -5154,7 +5154,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1080", "default": 720, "in": "query" }, @@ -5164,7 +5164,7 @@ "required": false, "type": "number", "format": "float", - "x-example": 0.1, + "x-example": "2", "default": 1, "in": "query" }, @@ -5173,7 +5173,7 @@ "description": "Browser theme. Pass \"light\" or \"dark\". Defaults to \"light\".", "required": false, "type": "string", - "x-example": "light", + "x-example": "dark", "enum": [ "light", "dark" @@ -5188,7 +5188,7 @@ "description": "Custom user agent string. Defaults to browser default.", "required": false, "type": "string", - "x-example": "", + "x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15", "default": "", "in": "query" }, @@ -5197,7 +5197,7 @@ "description": "Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.", "required": false, "type": "boolean", - "x-example": false, + "x-example": "true", "default": false, "in": "query" }, @@ -5206,7 +5206,7 @@ "description": "Browser locale (e.g., \"en-US\", \"fr-FR\"). Defaults to browser default.", "required": false, "type": "string", - "x-example": "", + "x-example": "en-US", "default": "", "in": "query" }, @@ -5215,7 +5215,7 @@ "description": "IANA timezone identifier (e.g., \"America\/New_York\", \"Europe\/London\"). Defaults to browser default.", "required": false, "type": "string", - "x-example": "africa\/abidjan", + "x-example": "america\/new_york", "enum": [ "africa\/abidjan", "africa\/accra", @@ -5648,7 +5648,7 @@ "required": false, "type": "number", "format": "float", - "x-example": -90, + "x-example": "37.7749", "default": 0, "in": "query" }, @@ -5658,7 +5658,7 @@ "required": false, "type": "number", "format": "float", - "x-example": -180, + "x-example": "-122.4194", "default": 0, "in": "query" }, @@ -5668,7 +5668,7 @@ "required": false, "type": "number", "format": "float", - "x-example": 0, + "x-example": "100", "default": 0, "in": "query" }, @@ -5677,7 +5677,7 @@ "description": "Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.", "required": false, "type": "boolean", - "x-example": false, + "x-example": "true", "default": false, "in": "query" }, @@ -5688,8 +5688,33 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "geolocation", + "camera", + "microphone", + "notifications", + "midi", + "push", + "clipboard-read", + "clipboard-write", + "payment-handler", + "usb", + "bluetooth", + "accelerometer", + "gyroscope", + "magnetometer", + "ambient-light-sensor", + "background-sync", + "persistent-storage", + "screen-wake-lock", + "web-share", + "xr-spatial-tracking" + ], + "x-enum-name": "BrowserPermission", + "x-enum-keys": [] }, + "x-example": "[\"geolocation\",\"notifications\"]", "default": [], "in": "query" }, @@ -5699,7 +5724,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "3", "default": 0, "in": "query" }, @@ -5709,7 +5734,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "800", "default": 0, "in": "query" }, @@ -5719,7 +5744,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "600", "default": 0, "in": "query" }, @@ -5729,7 +5754,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": -1, + "x-example": "85", "default": -1, "in": "query" }, @@ -5738,7 +5763,7 @@ "description": "Output format type (jpeg, jpg, png, gif and webp).", "required": false, "type": "string", - "x-example": "jpg", + "x-example": "jpeg", "enum": [ "jpg", "jpeg", @@ -7420,7 +7445,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 251, + "weight": 241, "cookies": false, "type": "graphql", "demo": "graphql\/query.md", @@ -7493,7 +7518,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 250, + "weight": 240, "cookies": false, "type": "graphql", "demo": "graphql\/mutation.md", @@ -7564,7 +7589,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 71, + "weight": 61, "cookies": false, "type": "", "demo": "locale\/get.md", @@ -7615,7 +7640,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 72, + "weight": 62, "cookies": false, "type": "", "demo": "locale\/list-codes.md", @@ -7666,7 +7691,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 76, + "weight": 66, "cookies": false, "type": "", "demo": "locale\/list-continents.md", @@ -7717,7 +7742,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 73, + "weight": 63, "cookies": false, "type": "", "demo": "locale\/list-countries.md", @@ -7768,7 +7793,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 74, + "weight": 64, "cookies": false, "type": "", "demo": "locale\/list-countries-eu.md", @@ -7819,7 +7844,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 75, + "weight": 65, "cookies": false, "type": "", "demo": "locale\/list-countries-phones.md", @@ -7870,7 +7895,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 77, + "weight": 67, "cookies": false, "type": "", "demo": "locale\/list-currencies.md", @@ -7921,7 +7946,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 78, + "weight": 68, "cookies": false, "type": "", "demo": "locale\/list-languages.md", @@ -7974,7 +7999,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 300, + "weight": 290, "cookies": false, "type": "", "demo": "messaging\/create-subscriber.md", @@ -8058,7 +8083,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 304, + "weight": 294, "cookies": false, "type": "", "demo": "messaging\/delete-subscriber.md", @@ -8128,7 +8153,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 161, + "weight": 151, "cookies": false, "type": "", "demo": "storage\/list-files.md", @@ -8219,7 +8244,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 160, + "weight": 150, "cookies": false, "type": "upload", "demo": "storage\/create-file.md", @@ -8308,7 +8333,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 162, + "weight": 152, "cookies": false, "type": "", "demo": "storage\/get-file.md", @@ -8377,7 +8402,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 167, + "weight": 157, "cookies": false, "type": "", "demo": "storage\/update-file.md", @@ -8467,7 +8492,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 168, + "weight": 158, "cookies": false, "type": "", "demo": "storage\/delete-file.md", @@ -8536,7 +8561,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 164, + "weight": 154, "cookies": false, "type": "location", "demo": "storage\/get-file-download.md", @@ -8614,7 +8639,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 163, + "weight": 153, "cookies": false, "type": "location", "demo": "storage\/get-file-preview.md", @@ -8820,7 +8845,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 165, + "weight": 155, "cookies": false, "type": "location", "demo": "storage\/get-file-view.md", @@ -10265,7 +10290,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 172, + "weight": 162, "cookies": false, "type": "", "demo": "teams\/list.md", @@ -10348,7 +10373,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 171, + "weight": 161, "cookies": false, "type": "", "demo": "teams\/create.md", @@ -10437,7 +10462,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 173, + "weight": 163, "cookies": false, "type": "", "demo": "teams\/get.md", @@ -10498,7 +10523,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 175, + "weight": 165, "cookies": false, "type": "", "demo": "teams\/update-name.md", @@ -10572,7 +10597,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 177, + "weight": 167, "cookies": false, "type": "", "demo": "teams\/delete.md", @@ -10633,7 +10658,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 179, + "weight": 169, "cookies": false, "type": "", "demo": "teams\/list-memberships.md", @@ -10724,7 +10749,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 178, + "weight": 168, "cookies": false, "type": "", "demo": "teams\/create-membership.md", @@ -10788,7 +10813,14 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "url": { @@ -10836,7 +10868,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 180, + "weight": 170, "cookies": false, "type": "", "demo": "teams\/get-membership.md", @@ -10905,7 +10937,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 181, + "weight": 171, "cookies": false, "type": "", "demo": "teams\/update-membership.md", @@ -10959,7 +10991,14 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } } }, @@ -10990,7 +11029,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 183, + "weight": 173, "cookies": false, "type": "", "demo": "teams\/delete-membership.md", @@ -11061,7 +11100,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 182, + "weight": 172, "cookies": false, "type": "", "demo": "teams\/update-membership-status.md", @@ -11155,7 +11194,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 174, + "weight": 164, "cookies": false, "type": "", "demo": "teams\/get-prefs.md", @@ -11216,7 +11255,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 176, + "weight": 166, "cookies": false, "type": "", "demo": "teams\/update-prefs.md", diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 28a4f76c61..e1e66611a6 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -361,7 +361,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 58, + "weight": 48, "cookies": false, "type": "", "demo": "account\/list-identities.md", @@ -431,7 +431,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 59, + "weight": 49, "cookies": false, "type": "", "demo": "account\/delete-identity.md", @@ -617,7 +617,7 @@ "x-appwrite": { "method": "updateMFA", "group": "mfa", - "weight": 45, + "weight": 306, "cookies": false, "type": "", "demo": "account\/update-mfa.md", @@ -689,7 +689,7 @@ "x-appwrite": { "method": "createMfaAuthenticator", "group": "mfa", - "weight": 47, + "weight": 308, "cookies": false, "type": "", "demo": "account\/create-mfa-authenticator.md", @@ -808,7 +808,7 @@ "x-appwrite": { "method": "updateMfaAuthenticator", "group": "mfa", - "weight": 48, + "weight": 309, "cookies": false, "type": "", "demo": "account\/update-mfa-authenticator.md", @@ -944,7 +944,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 52, + "weight": 310, "cookies": false, "type": "", "demo": "account\/delete-mfa-authenticator.md", @@ -1037,7 +1037,7 @@ ] } }, - "\/account\/mfa\/challenge": { + "\/account\/mfa\/challenges": { "post": { "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", @@ -1063,7 +1063,7 @@ "x-appwrite": { "method": "createMfaChallenge", "group": "mfa", - "weight": 53, + "weight": 314, "cookies": false, "type": "", "demo": "account\/create-mfa-challenge.md", @@ -1194,7 +1194,7 @@ "x-appwrite": { "method": "updateMfaChallenge", "group": "mfa", - "weight": 54, + "weight": 315, "cookies": false, "type": "", "demo": "account\/update-mfa-challenge.md", @@ -1329,7 +1329,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 46, + "weight": 307, "cookies": false, "type": "", "demo": "account\/list-mfa-factors.md", @@ -1425,7 +1425,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 51, + "weight": 313, "cookies": false, "type": "", "demo": "account\/get-mfa-recovery-codes.md", @@ -1521,7 +1521,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 49, + "weight": 311, "cookies": false, "type": "", "demo": "account\/create-mfa-recovery-codes.md", @@ -1617,7 +1617,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 50, + "weight": 312, "cookies": false, "type": "", "demo": "account\/update-mfa-recovery-codes.md", @@ -3055,7 +3055,7 @@ "x-appwrite": { "method": "createPushTarget", "group": "pushTargets", - "weight": 55, + "weight": 45, "cookies": false, "type": "", "demo": "account\/create-push-target.md", @@ -3138,7 +3138,7 @@ "x-appwrite": { "method": "updatePushTarget", "group": "pushTargets", - "weight": 56, + "weight": 46, "cookies": false, "type": "", "demo": "account\/update-push-target.md", @@ -3209,7 +3209,7 @@ "x-appwrite": { "method": "deletePushTarget", "group": "pushTargets", - "weight": 57, + "weight": 47, "cookies": false, "type": "", "demo": "account\/delete-push-target.md", @@ -4045,7 +4045,7 @@ "x-appwrite": { "method": "getBrowser", "group": null, - "weight": 61, + "weight": 51, "cookies": false, "type": "location", "demo": "avatars\/get-browser.md", @@ -4169,7 +4169,7 @@ "x-appwrite": { "method": "getCreditCard", "group": null, - "weight": 60, + "weight": 50, "cookies": false, "type": "location", "demo": "avatars\/get-credit-card.md", @@ -4299,7 +4299,7 @@ "x-appwrite": { "method": "getFavicon", "group": null, - "weight": 64, + "weight": 54, "cookies": false, "type": "location", "demo": "avatars\/get-favicon.md", @@ -4361,7 +4361,7 @@ "x-appwrite": { "method": "getFlag", "group": null, - "weight": 62, + "weight": 52, "cookies": false, "type": "location", "demo": "avatars\/get-flag.md", @@ -4847,7 +4847,7 @@ "x-appwrite": { "method": "getImage", "group": null, - "weight": 63, + "weight": 53, "cookies": false, "type": "location", "demo": "avatars\/get-image.md", @@ -4929,7 +4929,7 @@ "x-appwrite": { "method": "getInitials", "group": null, - "weight": 66, + "weight": 56, "cookies": false, "type": "location", "demo": "avatars\/get-initials.md", @@ -5019,7 +5019,7 @@ "x-appwrite": { "method": "getQR", "group": null, - "weight": 65, + "weight": 55, "cookies": false, "type": "location", "demo": "avatars\/get-qr.md", @@ -5109,7 +5109,7 @@ "x-appwrite": { "method": "getScreenshot", "group": null, - "weight": 67, + "weight": 57, "cookies": false, "type": "location", "demo": "avatars\/get-screenshot.md", @@ -5150,7 +5150,7 @@ "required": false, "type": "object", "default": [], - "x-example": "{}", + "x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}", "in": "query" }, { @@ -5159,7 +5159,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1920", "default": 1280, "in": "query" }, @@ -5169,7 +5169,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1080", "default": 720, "in": "query" }, @@ -5179,7 +5179,7 @@ "required": false, "type": "number", "format": "float", - "x-example": 0.1, + "x-example": "2", "default": 1, "in": "query" }, @@ -5188,7 +5188,7 @@ "description": "Browser theme. Pass \"light\" or \"dark\". Defaults to \"light\".", "required": false, "type": "string", - "x-example": "light", + "x-example": "dark", "enum": [ "light", "dark" @@ -5203,7 +5203,7 @@ "description": "Custom user agent string. Defaults to browser default.", "required": false, "type": "string", - "x-example": "", + "x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15", "default": "", "in": "query" }, @@ -5212,7 +5212,7 @@ "description": "Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.", "required": false, "type": "boolean", - "x-example": false, + "x-example": "true", "default": false, "in": "query" }, @@ -5221,7 +5221,7 @@ "description": "Browser locale (e.g., \"en-US\", \"fr-FR\"). Defaults to browser default.", "required": false, "type": "string", - "x-example": "", + "x-example": "en-US", "default": "", "in": "query" }, @@ -5230,7 +5230,7 @@ "description": "IANA timezone identifier (e.g., \"America\/New_York\", \"Europe\/London\"). Defaults to browser default.", "required": false, "type": "string", - "x-example": "africa\/abidjan", + "x-example": "america\/new_york", "enum": [ "africa\/abidjan", "africa\/accra", @@ -5663,7 +5663,7 @@ "required": false, "type": "number", "format": "float", - "x-example": -90, + "x-example": "37.7749", "default": 0, "in": "query" }, @@ -5673,7 +5673,7 @@ "required": false, "type": "number", "format": "float", - "x-example": -180, + "x-example": "-122.4194", "default": 0, "in": "query" }, @@ -5683,7 +5683,7 @@ "required": false, "type": "number", "format": "float", - "x-example": 0, + "x-example": "100", "default": 0, "in": "query" }, @@ -5692,7 +5692,7 @@ "description": "Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.", "required": false, "type": "boolean", - "x-example": false, + "x-example": "true", "default": false, "in": "query" }, @@ -5703,8 +5703,33 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "geolocation", + "camera", + "microphone", + "notifications", + "midi", + "push", + "clipboard-read", + "clipboard-write", + "payment-handler", + "usb", + "bluetooth", + "accelerometer", + "gyroscope", + "magnetometer", + "ambient-light-sensor", + "background-sync", + "persistent-storage", + "screen-wake-lock", + "web-share", + "xr-spatial-tracking" + ], + "x-enum-name": "BrowserPermission", + "x-enum-keys": [] }, + "x-example": "[\"geolocation\",\"notifications\"]", "default": [], "in": "query" }, @@ -5714,7 +5739,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "3", "default": 0, "in": "query" }, @@ -5724,7 +5749,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "800", "default": 0, "in": "query" }, @@ -5734,7 +5759,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "600", "default": 0, "in": "query" }, @@ -5744,7 +5769,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": -1, + "x-example": "85", "default": -1, "in": "query" }, @@ -5753,7 +5778,7 @@ "description": "Output format type (jpeg, jpg, png, gif and webp).", "required": false, "type": "string", - "x-example": "jpg", + "x-example": "jpeg", "enum": [ "jpg", "jpeg", @@ -5797,7 +5822,7 @@ "x-appwrite": { "method": "chat", "group": "console", - "weight": 253, + "weight": 243, "cookies": false, "type": "", "demo": "assistant\/chat.md", @@ -5931,7 +5956,7 @@ "x-appwrite": { "method": "variables", "group": "console", - "weight": 252, + "weight": 242, "cookies": false, "type": "", "demo": "console\/variables.md", @@ -7184,6 +7209,24 @@ "description": "Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.", "default": true, "x-example": false + }, + "attributes": { + "type": "array", + "description": "Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "default": [], + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of attribute keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "default": [], + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -12340,7 +12383,13 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -13184,7 +13233,66 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -13387,7 +13495,78 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "node-14.5", + "node-16.0", + "node-18.0", + "node-19.0", + "node-20.0", + "node-21.0", + "node-22", + "php-8.0", + "php-8.1", + "php-8.2", + "php-8.3", + "ruby-3.0", + "ruby-3.1", + "ruby-3.2", + "ruby-3.3", + "python-3.8", + "python-3.9", + "python-3.10", + "python-3.11", + "python-3.12", + "python-ml-3.11", + "python-ml-3.12", + "deno-1.21", + "deno-1.24", + "deno-1.35", + "deno-1.40", + "deno-1.46", + "deno-2.0", + "dart-2.15", + "dart-2.16", + "dart-2.17", + "dart-2.18", + "dart-2.19", + "dart-3.0", + "dart-3.1", + "dart-3.3", + "dart-3.5", + "dart-3.8", + "dart-3.9", + "dotnet-6.0", + "dotnet-7.0", + "dotnet-8.0", + "java-8.0", + "java-11.0", + "java-17.0", + "java-18.0", + "java-21.0", + "java-22", + "swift-5.5", + "swift-5.8", + "swift-5.9", + "swift-5.10", + "kotlin-1.6", + "kotlin-1.8", + "kotlin-1.9", + "kotlin-2.0", + "cpp-17", + "cpp-20", + "bun-1.0", + "bun-1.1", + "go-1.23", + "static-1", + "flutter-3.24", + "flutter-3.27", + "flutter-3.29", + "flutter-3.32", + "flutter-3.35" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [], "in": "query" @@ -13399,7 +13578,17 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "dev-tools", + "starter", + "databases", + "ai", + "messaging", + "utilities" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [], "in": "query" @@ -13828,7 +14017,66 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -14366,7 +14614,7 @@ "branch", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -14467,7 +14715,7 @@ "branch", "commit" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -15585,7 +15833,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 251, + "weight": 241, "cookies": false, "type": "graphql", "demo": "graphql\/query.md", @@ -15658,7 +15906,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 250, + "weight": 240, "cookies": false, "type": "graphql", "demo": "graphql\/mutation.md", @@ -15729,7 +15977,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 79, + "weight": 69, "cookies": false, "type": "", "demo": "health\/get.md", @@ -15778,7 +16026,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 100, + "weight": 90, "cookies": false, "type": "", "demo": "health\/get-antivirus.md", @@ -15827,7 +16075,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 82, + "weight": 72, "cookies": false, "type": "", "demo": "health\/get-cache.md", @@ -15876,7 +16124,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 87, + "weight": 77, "cookies": false, "type": "", "demo": "health\/get-certificate.md", @@ -15934,7 +16182,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 81, + "weight": 71, "cookies": false, "type": "", "demo": "health\/get-db.md", @@ -15983,7 +16231,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 83, + "weight": 73, "cookies": false, "type": "", "demo": "health\/get-pub-sub.md", @@ -16032,7 +16280,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 89, + "weight": 79, "cookies": false, "type": "", "demo": "health\/get-queue-builds.md", @@ -16092,7 +16340,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 88, + "weight": 78, "cookies": false, "type": "", "demo": "health\/get-queue-certificates.md", @@ -16152,7 +16400,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 90, + "weight": 80, "cookies": false, "type": "", "demo": "health\/get-queue-databases.md", @@ -16221,7 +16469,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 91, + "weight": 81, "cookies": false, "type": "", "demo": "health\/get-queue-deletes.md", @@ -16281,7 +16529,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 101, + "weight": 91, "cookies": false, "type": "", "demo": "health\/get-failed-jobs.md", @@ -16365,7 +16613,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 95, + "weight": 85, "cookies": false, "type": "", "demo": "health\/get-queue-functions.md", @@ -16425,7 +16673,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 86, + "weight": 76, "cookies": false, "type": "", "demo": "health\/get-queue-logs.md", @@ -16485,7 +16733,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 92, + "weight": 82, "cookies": false, "type": "", "demo": "health\/get-queue-mails.md", @@ -16545,7 +16793,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 93, + "weight": 83, "cookies": false, "type": "", "demo": "health\/get-queue-messaging.md", @@ -16605,7 +16853,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 94, + "weight": 84, "cookies": false, "type": "", "demo": "health\/get-queue-migrations.md", @@ -16665,7 +16913,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 96, + "weight": 86, "cookies": false, "type": "", "demo": "health\/get-queue-stats-resources.md", @@ -16725,7 +16973,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 97, + "weight": 87, "cookies": false, "type": "", "demo": "health\/get-queue-usage.md", @@ -16785,7 +17033,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 85, + "weight": 75, "cookies": false, "type": "", "demo": "health\/get-queue-webhooks.md", @@ -16845,7 +17093,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 99, + "weight": 89, "cookies": false, "type": "", "demo": "health\/get-storage.md", @@ -16894,7 +17142,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 98, + "weight": 88, "cookies": false, "type": "", "demo": "health\/get-storage-local.md", @@ -16943,7 +17191,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 84, + "weight": 74, "cookies": false, "type": "", "demo": "health\/get-time.md", @@ -16992,7 +17240,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 71, + "weight": 61, "cookies": false, "type": "", "demo": "locale\/get.md", @@ -17043,7 +17291,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 72, + "weight": 62, "cookies": false, "type": "", "demo": "locale\/list-codes.md", @@ -17094,7 +17342,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 76, + "weight": 66, "cookies": false, "type": "", "demo": "locale\/list-continents.md", @@ -17145,7 +17393,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 73, + "weight": 63, "cookies": false, "type": "", "demo": "locale\/list-countries.md", @@ -17196,7 +17444,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 74, + "weight": 64, "cookies": false, "type": "", "demo": "locale\/list-countries-eu.md", @@ -17247,7 +17495,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 75, + "weight": 65, "cookies": false, "type": "", "demo": "locale\/list-countries-phones.md", @@ -17298,7 +17546,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 77, + "weight": 67, "cookies": false, "type": "", "demo": "locale\/list-currencies.md", @@ -17349,7 +17597,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 78, + "weight": 68, "cookies": false, "type": "", "demo": "locale\/list-languages.md", @@ -17400,7 +17648,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 308, + "weight": 298, "cookies": false, "type": "", "demo": "messaging\/list-messages.md", @@ -17484,7 +17732,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 305, + "weight": 295, "cookies": false, "type": "", "demo": "messaging\/create-email.md", @@ -17643,7 +17891,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 312, + "weight": 302, "cookies": false, "type": "", "demo": "messaging\/update-email.md", @@ -17809,7 +18057,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 307, + "weight": 297, "cookies": false, "type": "", "demo": "messaging\/create-push.md", @@ -18006,7 +18254,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 314, + "weight": 304, "cookies": false, "type": "", "demo": "messaging\/update-push.md", @@ -18218,7 +18466,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 306, + "weight": 296, "cookies": false, "type": "", "demo": "messaging\/create-sms.md", @@ -18405,7 +18653,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 313, + "weight": 303, "cookies": false, "type": "", "demo": "messaging\/update-sms.md", @@ -18591,7 +18839,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 311, + "weight": 301, "cookies": false, "type": "", "demo": "messaging\/get-message.md", @@ -18646,7 +18894,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 315, + "weight": 305, "cookies": false, "type": "", "demo": "messaging\/delete.md", @@ -18706,7 +18954,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 309, + "weight": 299, "cookies": false, "type": "", "demo": "messaging\/list-message-logs.md", @@ -18787,7 +19035,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 310, + "weight": 300, "cookies": false, "type": "", "demo": "messaging\/list-targets.md", @@ -18868,7 +19116,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 279, + "weight": 269, "cookies": false, "type": "", "demo": "messaging\/list-providers.md", @@ -18952,7 +19200,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 278, + "weight": 268, "cookies": false, "type": "", "demo": "messaging\/create-apns-provider.md", @@ -19138,7 +19386,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 292, + "weight": 282, "cookies": false, "type": "", "demo": "messaging\/update-apns-provider.md", @@ -19321,7 +19569,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 277, + "weight": 267, "cookies": false, "type": "", "demo": "messaging\/create-fcm-provider.md", @@ -19476,7 +19724,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 291, + "weight": 281, "cookies": false, "type": "", "demo": "messaging\/update-fcm-provider.md", @@ -19627,7 +19875,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 268, + "weight": 258, "cookies": false, "type": "", "demo": "messaging\/create-mailgun-provider.md", @@ -19756,7 +20004,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 282, + "weight": 272, "cookies": false, "type": "", "demo": "messaging\/update-mailgun-provider.md", @@ -19883,7 +20131,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 272, + "weight": 262, "cookies": false, "type": "", "demo": "messaging\/create-msg-91-provider.md", @@ -19987,7 +20235,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 286, + "weight": 276, "cookies": false, "type": "", "demo": "messaging\/update-msg-91-provider.md", @@ -20089,7 +20337,7 @@ "x-appwrite": { "method": "createResendProvider", "group": "providers", - "weight": 270, + "weight": 260, "cookies": false, "type": "", "demo": "messaging\/create-resend-provider.md", @@ -20205,7 +20453,7 @@ "x-appwrite": { "method": "updateResendProvider", "group": "providers", - "weight": 284, + "weight": 274, "cookies": false, "type": "", "demo": "messaging\/update-resend-provider.md", @@ -20319,7 +20567,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 269, + "weight": 259, "cookies": false, "type": "", "demo": "messaging\/create-sendgrid-provider.md", @@ -20435,7 +20683,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 283, + "weight": 273, "cookies": false, "type": "", "demo": "messaging\/update-sendgrid-provider.md", @@ -20549,7 +20797,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 271, + "weight": 261, "cookies": false, "type": "", "demo": "messaging\/create-smtp-provider.md", @@ -20793,7 +21041,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 285, + "weight": 275, "cookies": false, "type": "", "demo": "messaging\/update-smtp-provider.md", @@ -21032,7 +21280,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 273, + "weight": 263, "cookies": false, "type": "", "demo": "messaging\/create-telesign-provider.md", @@ -21136,7 +21384,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 287, + "weight": 277, "cookies": false, "type": "", "demo": "messaging\/update-telesign-provider.md", @@ -21238,7 +21486,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 274, + "weight": 264, "cookies": false, "type": "", "demo": "messaging\/create-textmagic-provider.md", @@ -21342,7 +21590,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 288, + "weight": 278, "cookies": false, "type": "", "demo": "messaging\/update-textmagic-provider.md", @@ -21444,7 +21692,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 275, + "weight": 265, "cookies": false, "type": "", "demo": "messaging\/create-twilio-provider.md", @@ -21548,7 +21796,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 289, + "weight": 279, "cookies": false, "type": "", "demo": "messaging\/update-twilio-provider.md", @@ -21650,7 +21898,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 276, + "weight": 266, "cookies": false, "type": "", "demo": "messaging\/create-vonage-provider.md", @@ -21754,7 +22002,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 290, + "weight": 280, "cookies": false, "type": "", "demo": "messaging\/update-vonage-provider.md", @@ -21854,7 +22102,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 281, + "weight": 271, "cookies": false, "type": "", "demo": "messaging\/get-provider.md", @@ -21909,7 +22157,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 293, + "weight": 283, "cookies": false, "type": "", "demo": "messaging\/delete-provider.md", @@ -21969,7 +22217,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 280, + "weight": 270, "cookies": false, "type": "", "demo": "messaging\/list-provider-logs.md", @@ -22050,7 +22298,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 302, + "weight": 292, "cookies": false, "type": "", "demo": "messaging\/list-subscriber-logs.md", @@ -22131,7 +22379,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 295, + "weight": 285, "cookies": false, "type": "", "demo": "messaging\/list-topics.md", @@ -22213,7 +22461,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 294, + "weight": 284, "cookies": false, "type": "", "demo": "messaging\/create-topic.md", @@ -22301,7 +22549,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 297, + "weight": 287, "cookies": false, "type": "", "demo": "messaging\/get-topic.md", @@ -22361,7 +22609,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 298, + "weight": 288, "cookies": false, "type": "", "demo": "messaging\/update-topic.md", @@ -22442,7 +22690,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 299, + "weight": 289, "cookies": false, "type": "", "demo": "messaging\/delete-topic.md", @@ -22502,7 +22750,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 296, + "weight": 286, "cookies": false, "type": "", "demo": "messaging\/list-topic-logs.md", @@ -22583,7 +22831,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 301, + "weight": 291, "cookies": false, "type": "", "demo": "messaging\/list-subscribers.md", @@ -22673,7 +22921,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 300, + "weight": 290, "cookies": false, "type": "", "demo": "messaging\/create-subscriber.md", @@ -22760,7 +23008,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 303, + "weight": 293, "cookies": false, "type": "", "demo": "messaging\/get-subscriber.md", @@ -22823,7 +23071,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 304, + "weight": 294, "cookies": false, "type": "", "demo": "messaging\/delete-subscriber.md", @@ -22893,7 +23141,7 @@ "x-appwrite": { "method": "list", "group": null, - "weight": 260, + "weight": 250, "cookies": false, "type": "", "demo": "migrations\/list.md", @@ -22975,7 +23223,7 @@ "x-appwrite": { "method": "createAppwriteMigration", "group": null, - "weight": 254, + "weight": 244, "cookies": false, "type": "", "demo": "migrations\/create-appwrite-migration.md", @@ -23010,7 +23258,27 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "team", + "membership", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file", + "function", + "deployment", + "environment-variable" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "endpoint": { @@ -23067,7 +23335,7 @@ "x-appwrite": { "method": "getAppwriteReport", "group": null, - "weight": 262, + "weight": 252, "cookies": false, "type": "", "demo": "migrations\/get-appwrite-report.md", @@ -23097,7 +23365,27 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "team", + "membership", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file", + "function", + "deployment", + "environment-variable" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "in": "query" }, @@ -23155,7 +23443,7 @@ "x-appwrite": { "method": "createCSVExport", "group": null, - "weight": 259, + "weight": 249, "cookies": false, "type": "", "demo": "migrations\/create-csv-export.md", @@ -23280,7 +23568,7 @@ "x-appwrite": { "method": "createCSVImport", "group": null, - "weight": 258, + "weight": 248, "cookies": false, "type": "", "demo": "migrations\/create-csv-import.md", @@ -23370,7 +23658,7 @@ "x-appwrite": { "method": "createFirebaseMigration", "group": null, - "weight": 255, + "weight": 245, "cookies": false, "type": "", "demo": "migrations\/create-firebase-migration.md", @@ -23405,7 +23693,21 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "serviceAccount": { @@ -23448,7 +23750,7 @@ "x-appwrite": { "method": "getFirebaseReport", "group": null, - "weight": 263, + "weight": 253, "cookies": false, "type": "", "demo": "migrations\/get-firebase-report.md", @@ -23478,7 +23780,21 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "in": "query" }, @@ -23519,7 +23835,7 @@ "x-appwrite": { "method": "createNHostMigration", "group": null, - "weight": 257, + "weight": 247, "cookies": false, "type": "", "demo": "migrations\/create-n-host-migration.md", @@ -23554,7 +23870,22 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "subdomain": { @@ -23638,7 +23969,7 @@ "x-appwrite": { "method": "getNHostReport", "group": null, - "weight": 265, + "weight": 255, "cookies": false, "type": "", "demo": "migrations\/get-n-host-report.md", @@ -23668,7 +23999,22 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "in": "query" }, @@ -23758,7 +24104,7 @@ "x-appwrite": { "method": "createSupabaseMigration", "group": null, - "weight": 256, + "weight": 246, "cookies": false, "type": "", "demo": "migrations\/create-supabase-migration.md", @@ -23793,7 +24139,22 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "endpoint": { @@ -23870,7 +24231,7 @@ "x-appwrite": { "method": "getSupabaseReport", "group": null, - "weight": 264, + "weight": 254, "cookies": false, "type": "", "demo": "migrations\/get-supabase-report.md", @@ -23900,7 +24261,22 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "user", + "database", + "table", + "column", + "index", + "row", + "document", + "attribute", + "collection", + "bucket", + "file" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "in": "query" }, @@ -23981,7 +24357,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 261, + "weight": 251, "cookies": false, "type": "", "demo": "migrations\/get.md", @@ -24039,7 +24415,7 @@ "x-appwrite": { "method": "retry", "group": null, - "weight": 266, + "weight": 256, "cookies": false, "type": "", "demo": "migrations\/retry.md", @@ -24092,7 +24468,7 @@ "x-appwrite": { "method": "delete", "group": null, - "weight": 267, + "weight": 257, "cookies": false, "type": "", "demo": "migrations\/delete.md", @@ -24150,7 +24526,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 149, + "weight": 139, "cookies": false, "type": "", "demo": "project\/get-usage.md", @@ -24232,7 +24608,7 @@ "x-appwrite": { "method": "listVariables", "group": null, - "weight": 151, + "weight": 141, "cookies": false, "type": "", "demo": "project\/list-variables.md", @@ -24280,7 +24656,7 @@ "x-appwrite": { "method": "createVariable", "group": null, - "weight": 150, + "weight": 140, "cookies": false, "type": "", "demo": "project\/create-variable.md", @@ -24361,7 +24737,7 @@ "x-appwrite": { "method": "getVariable", "group": null, - "weight": 152, + "weight": 142, "cookies": false, "type": "", "demo": "project\/get-variable.md", @@ -24419,7 +24795,7 @@ "x-appwrite": { "method": "updateVariable", "group": null, - "weight": 153, + "weight": 143, "cookies": false, "type": "", "demo": "project\/update-variable.md", @@ -24504,7 +24880,7 @@ "x-appwrite": { "method": "deleteVariable", "group": null, - "weight": 154, + "weight": 144, "cookies": false, "type": "", "demo": "project\/delete-variable.md", @@ -24642,7 +25018,7 @@ "x-appwrite": { "method": "create", "group": "projects", - "weight": 103, + "weight": 93, "cookies": false, "type": "", "demo": "projects\/create.md", @@ -24789,7 +25165,7 @@ "x-appwrite": { "method": "get", "group": "projects", - "weight": 104, + "weight": 94, "cookies": false, "type": "", "demo": "projects\/get.md", @@ -24847,7 +25223,7 @@ "x-appwrite": { "method": "update", "group": "projects", - "weight": 105, + "weight": 95, "cookies": false, "type": "", "demo": "projects\/update.md", @@ -24972,7 +25348,7 @@ "x-appwrite": { "method": "delete", "group": "projects", - "weight": 122, + "weight": 112, "cookies": false, "type": "", "demo": "projects\/delete.md", @@ -25032,7 +25408,7 @@ "x-appwrite": { "method": "updateApiStatus", "group": "projects", - "weight": 109, + "weight": 99, "cookies": false, "type": "", "demo": "projects\/update-api-status.md", @@ -25186,7 +25562,7 @@ "x-appwrite": { "method": "updateApiStatusAll", "group": "projects", - "weight": 110, + "weight": 100, "cookies": false, "type": "", "demo": "projects\/update-api-status-all.md", @@ -25322,7 +25698,7 @@ "x-appwrite": { "method": "updateAuthDuration", "group": "auth", - "weight": 115, + "weight": 105, "cookies": false, "type": "", "demo": "projects\/update-auth-duration.md", @@ -25400,7 +25776,7 @@ "x-appwrite": { "method": "updateAuthLimit", "group": "auth", - "weight": 114, + "weight": 104, "cookies": false, "type": "", "demo": "projects\/update-auth-limit.md", @@ -25478,7 +25854,7 @@ "x-appwrite": { "method": "updateAuthSessionsLimit", "group": "auth", - "weight": 120, + "weight": 110, "cookies": false, "type": "", "demo": "projects\/update-auth-sessions-limit.md", @@ -25556,7 +25932,7 @@ "x-appwrite": { "method": "updateMembershipsPrivacy", "group": "auth", - "weight": 113, + "weight": 103, "cookies": false, "type": "", "demo": "projects\/update-memberships-privacy.md", @@ -25648,7 +26024,7 @@ "x-appwrite": { "method": "updateMockNumbers", "group": "auth", - "weight": 121, + "weight": 111, "cookies": false, "type": "", "demo": "projects\/update-mock-numbers.md", @@ -25729,7 +26105,7 @@ "x-appwrite": { "method": "updateAuthPasswordDictionary", "group": "auth", - "weight": 118, + "weight": 108, "cookies": false, "type": "", "demo": "projects\/update-auth-password-dictionary.md", @@ -25807,7 +26183,7 @@ "x-appwrite": { "method": "updateAuthPasswordHistory", "group": "auth", - "weight": 117, + "weight": 107, "cookies": false, "type": "", "demo": "projects\/update-auth-password-history.md", @@ -25885,7 +26261,7 @@ "x-appwrite": { "method": "updatePersonalDataCheck", "group": "auth", - "weight": 119, + "weight": 109, "cookies": false, "type": "", "demo": "projects\/update-personal-data-check.md", @@ -25963,7 +26339,7 @@ "x-appwrite": { "method": "updateSessionAlerts", "group": "auth", - "weight": 112, + "weight": 102, "cookies": false, "type": "", "demo": "projects\/update-session-alerts.md", @@ -26041,7 +26417,7 @@ "x-appwrite": { "method": "updateSessionInvalidation", "group": "auth", - "weight": 148, + "weight": 138, "cookies": false, "type": "", "demo": "projects\/update-session-invalidation.md", @@ -26119,7 +26495,7 @@ "x-appwrite": { "method": "updateAuthStatus", "group": "auth", - "weight": 116, + "weight": 106, "cookies": false, "type": "", "demo": "projects\/update-auth-status.md", @@ -26587,7 +26963,7 @@ "x-appwrite": { "method": "createJWT", "group": "auth", - "weight": 134, + "weight": 124, "cookies": false, "type": "", "demo": "projects\/create-jwt.md", @@ -26630,7 +27006,66 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "duration": { @@ -26672,7 +27107,7 @@ "x-appwrite": { "method": "listKeys", "group": "keys", - "weight": 130, + "weight": 120, "cookies": false, "type": "", "demo": "projects\/list-keys.md", @@ -26739,7 +27174,7 @@ "x-appwrite": { "method": "createKey", "group": "keys", - "weight": 129, + "weight": 119, "cookies": false, "type": "", "demo": "projects\/create-key.md", @@ -26789,7 +27224,66 @@ "x-example": null, "x-nullable": true, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "expire": { @@ -26833,7 +27327,7 @@ "x-appwrite": { "method": "getKey", "group": "keys", - "weight": 131, + "weight": 121, "cookies": false, "type": "", "demo": "projects\/get-key.md", @@ -26899,7 +27393,7 @@ "x-appwrite": { "method": "updateKey", "group": "keys", - "weight": 132, + "weight": 122, "cookies": false, "type": "", "demo": "projects\/update-key.md", @@ -26957,7 +27451,66 @@ "x-example": null, "x-nullable": true, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "expire": { @@ -26996,7 +27549,7 @@ "x-appwrite": { "method": "deleteKey", "group": "keys", - "weight": 133, + "weight": 123, "cookies": false, "type": "", "demo": "projects\/delete-key.md", @@ -27064,7 +27617,7 @@ "x-appwrite": { "method": "updateOAuth2", "group": "auth", - "weight": 111, + "weight": 101, "cookies": false, "type": "", "demo": "projects\/update-o-auth-2.md", @@ -27205,7 +27758,7 @@ "x-appwrite": { "method": "listPlatforms", "group": "platforms", - "weight": 136, + "weight": 126, "cookies": false, "type": "", "demo": "projects\/list-platforms.md", @@ -27272,7 +27825,7 @@ "x-appwrite": { "method": "createPlatform", "group": "platforms", - "weight": 135, + "weight": 125, "cookies": false, "type": "", "demo": "projects\/create-platform.md", @@ -27392,7 +27945,7 @@ "x-appwrite": { "method": "getPlatform", "group": "platforms", - "weight": 137, + "weight": 127, "cookies": false, "type": "", "demo": "projects\/get-platform.md", @@ -27458,7 +28011,7 @@ "x-appwrite": { "method": "updatePlatform", "group": "platforms", - "weight": 138, + "weight": 128, "cookies": false, "type": "", "demo": "projects\/update-platform.md", @@ -27555,7 +28108,7 @@ "x-appwrite": { "method": "deletePlatform", "group": "platforms", - "weight": 139, + "weight": 129, "cookies": false, "type": "", "demo": "projects\/delete-platform.md", @@ -27623,7 +28176,7 @@ "x-appwrite": { "method": "updateServiceStatus", "group": "projects", - "weight": 107, + "weight": 97, "cookies": false, "type": "", "demo": "projects\/update-service-status.md", @@ -27725,7 +28278,7 @@ "x-appwrite": { "method": "updateServiceStatusAll", "group": "projects", - "weight": 108, + "weight": 98, "cookies": false, "type": "", "demo": "projects\/update-service-status-all.md", @@ -27803,7 +28356,7 @@ "x-appwrite": { "method": "updateSmtp", "group": "templates", - "weight": 140, + "weight": 130, "cookies": false, "type": "", "demo": "projects\/update-smtp.md", @@ -28006,7 +28559,7 @@ "x-appwrite": { "method": "createSmtpTest", "group": "templates", - "weight": 141, + "weight": 131, "cookies": false, "type": "", "demo": "projects\/create-smtp-test.md", @@ -28222,7 +28775,7 @@ "x-appwrite": { "method": "updateTeam", "group": "projects", - "weight": 106, + "weight": 96, "cookies": false, "type": "", "demo": "projects\/update-team.md", @@ -28298,7 +28851,7 @@ "x-appwrite": { "method": "getEmailTemplate", "group": "templates", - "weight": 143, + "weight": 133, "cookies": false, "type": "", "demo": "projects\/get-email-template.md", @@ -28518,7 +29071,7 @@ "x-appwrite": { "method": "updateEmailTemplate", "group": "templates", - "weight": 145, + "weight": 135, "cookies": false, "type": "", "demo": "projects\/update-email-template.md", @@ -28781,7 +29334,7 @@ "x-appwrite": { "method": "deleteEmailTemplate", "group": "templates", - "weight": 147, + "weight": 137, "cookies": false, "type": "", "demo": "projects\/delete-email-template.md", @@ -29001,7 +29554,7 @@ "x-appwrite": { "method": "getSmsTemplate", "group": "templates", - "weight": 142, + "weight": 132, "cookies": false, "type": "", "demo": "projects\/get-sms-template.md", @@ -29280,7 +29833,7 @@ "x-appwrite": { "method": "updateSmsTemplate", "group": "templates", - "weight": 144, + "weight": 134, "cookies": false, "type": "", "demo": "projects\/update-sms-template.md", @@ -29581,7 +30134,7 @@ "x-appwrite": { "method": "deleteSmsTemplate", "group": "templates", - "weight": 146, + "weight": 136, "cookies": false, "type": "", "demo": "projects\/delete-sms-template.md", @@ -29860,7 +30413,7 @@ "x-appwrite": { "method": "listWebhooks", "group": "webhooks", - "weight": 124, + "weight": 114, "cookies": false, "type": "", "demo": "projects\/list-webhooks.md", @@ -29927,7 +30480,7 @@ "x-appwrite": { "method": "createWebhook", "group": "webhooks", - "weight": 123, + "weight": 113, "cookies": false, "type": "", "demo": "projects\/create-webhook.md", @@ -30045,7 +30598,7 @@ "x-appwrite": { "method": "getWebhook", "group": "webhooks", - "weight": 125, + "weight": 115, "cookies": false, "type": "", "demo": "projects\/get-webhook.md", @@ -30111,7 +30664,7 @@ "x-appwrite": { "method": "updateWebhook", "group": "webhooks", - "weight": 126, + "weight": 116, "cookies": false, "type": "", "demo": "projects\/update-webhook.md", @@ -30232,7 +30785,7 @@ "x-appwrite": { "method": "deleteWebhook", "group": "webhooks", - "weight": 128, + "weight": 118, "cookies": false, "type": "", "demo": "projects\/delete-webhook.md", @@ -30300,7 +30853,7 @@ "x-appwrite": { "method": "updateWebhookSignature", "group": "webhooks", - "weight": 127, + "weight": 117, "cookies": false, "type": "", "demo": "projects\/update-webhook-signature.md", @@ -31453,7 +32006,26 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "analog", + "angular", + "nextjs", + "react", + "nuxt", + "vue", + "sveltekit", + "astro", + "tanstack-start", + "remix", + "lynx", + "flutter", + "react-native", + "vite", + "other" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [], "in": "query" @@ -31465,7 +32037,17 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "dev-tools", + "starter", + "databases", + "ai", + "messaging", + "utilities" + ], + "x-enum-name": null, + "x-enum-keys": [] }, "default": [], "in": "query" @@ -32441,7 +33023,7 @@ "commit", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -32543,7 +33125,7 @@ "commit", "tag" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -33539,7 +34121,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 156, + "weight": 146, "cookies": false, "type": "", "demo": "storage\/list-buckets.md", @@ -33620,7 +34202,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 155, + "weight": 145, "cookies": false, "type": "", "demo": "storage\/create-bucket.md", @@ -33764,7 +34346,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 157, + "weight": 147, "cookies": false, "type": "", "demo": "storage\/get-bucket.md", @@ -33823,7 +34405,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 158, + "weight": 148, "cookies": false, "type": "", "demo": "storage\/update-bucket.md", @@ -33963,7 +34545,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 159, + "weight": 149, "cookies": false, "type": "", "demo": "storage\/delete-bucket.md", @@ -34022,7 +34604,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 161, + "weight": 151, "cookies": false, "type": "", "demo": "storage\/list-files.md", @@ -34113,7 +34695,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 160, + "weight": 150, "cookies": false, "type": "upload", "demo": "storage\/create-file.md", @@ -34202,7 +34784,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 162, + "weight": 152, "cookies": false, "type": "", "demo": "storage\/get-file.md", @@ -34271,7 +34853,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 167, + "weight": 157, "cookies": false, "type": "", "demo": "storage\/update-file.md", @@ -34361,7 +34943,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 168, + "weight": 158, "cookies": false, "type": "", "demo": "storage\/delete-file.md", @@ -34430,7 +35012,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 164, + "weight": 154, "cookies": false, "type": "location", "demo": "storage\/get-file-download.md", @@ -34508,7 +35090,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 163, + "weight": 153, "cookies": false, "type": "location", "demo": "storage\/get-file-preview.md", @@ -34714,7 +35296,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 165, + "weight": 155, "cookies": false, "type": "location", "demo": "storage\/get-file-view.md", @@ -34792,7 +35374,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 169, + "weight": 159, "cookies": false, "type": "", "demo": "storage\/get-usage.md", @@ -34862,7 +35444,7 @@ "x-appwrite": { "method": "getBucketUsage", "group": null, - "weight": 170, + "weight": 160, "cookies": false, "type": "", "demo": "storage\/get-bucket-usage.md", @@ -35991,6 +36573,24 @@ "description": "Is table enabled? When set to 'disabled', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.", "default": true, "x-example": false + }, + "columns": { + "type": "array", + "description": "Array of column definitions to create. Each column should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "default": [], + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of column keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "default": [], + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -39703,7 +40303,13 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -41553,7 +42159,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 172, + "weight": 162, "cookies": false, "type": "", "demo": "teams\/list.md", @@ -41636,7 +42242,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 171, + "weight": 161, "cookies": false, "type": "", "demo": "teams\/create.md", @@ -41725,7 +42331,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 173, + "weight": 163, "cookies": false, "type": "", "demo": "teams\/get.md", @@ -41786,7 +42392,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 175, + "weight": 165, "cookies": false, "type": "", "demo": "teams\/update-name.md", @@ -41860,7 +42466,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 177, + "weight": 167, "cookies": false, "type": "", "demo": "teams\/delete.md", @@ -41921,7 +42527,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 184, + "weight": 174, "cookies": false, "type": "", "demo": "teams\/list-logs.md", @@ -42000,7 +42606,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 179, + "weight": 169, "cookies": false, "type": "", "demo": "teams\/list-memberships.md", @@ -42091,7 +42697,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 178, + "weight": 168, "cookies": false, "type": "", "demo": "teams\/create-membership.md", @@ -42155,7 +42761,14 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "url": { @@ -42203,7 +42816,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 180, + "weight": 170, "cookies": false, "type": "", "demo": "teams\/get-membership.md", @@ -42272,7 +42885,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 181, + "weight": 171, "cookies": false, "type": "", "demo": "teams\/update-membership.md", @@ -42326,7 +42939,14 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } } }, @@ -42357,7 +42977,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 183, + "weight": 173, "cookies": false, "type": "", "demo": "teams\/delete-membership.md", @@ -42428,7 +43048,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 182, + "weight": 172, "cookies": false, "type": "", "demo": "teams\/update-membership-status.md", @@ -42521,7 +43141,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 174, + "weight": 164, "cookies": false, "type": "", "demo": "teams\/get-prefs.md", @@ -42581,7 +43201,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 176, + "weight": 166, "cookies": false, "type": "", "demo": "teams\/update-prefs.md", @@ -43023,7 +43643,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 194, + "weight": 184, "cookies": false, "type": "", "demo": "users\/list.md", @@ -43104,7 +43724,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 185, + "weight": 175, "cookies": false, "type": "", "demo": "users\/create.md", @@ -43201,7 +43821,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 188, + "weight": 178, "cookies": false, "type": "", "demo": "users\/create-argon-2-user.md", @@ -43292,7 +43912,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 186, + "weight": 176, "cookies": false, "type": "", "demo": "users\/create-bcrypt-user.md", @@ -43381,7 +44001,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 202, + "weight": 192, "cookies": false, "type": "", "demo": "users\/list-identities.md", @@ -43459,7 +44079,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 225, + "weight": 215, "cookies": false, "type": "", "demo": "users\/delete-identity.md", @@ -43520,7 +44140,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 187, + "weight": 177, "cookies": false, "type": "", "demo": "users\/create-md-5-user.md", @@ -43611,7 +44231,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 190, + "weight": 180, "cookies": false, "type": "", "demo": "users\/create-ph-pass-user.md", @@ -43702,7 +44322,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 191, + "weight": 181, "cookies": false, "type": "", "demo": "users\/create-scrypt-user.md", @@ -43828,7 +44448,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 192, + "weight": 182, "cookies": false, "type": "", "demo": "users\/create-scrypt-modified-user.md", @@ -43940,7 +44560,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 189, + "weight": 179, "cookies": false, "type": "", "demo": "users\/create-sha-user.md", @@ -44050,7 +44670,7 @@ "x-appwrite": { "method": "getUsage", "group": null, - "weight": 227, + "weight": 217, "cookies": false, "type": "", "demo": "users\/get-usage.md", @@ -44120,7 +44740,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 195, + "weight": 185, "cookies": false, "type": "", "demo": "users\/get.md", @@ -44174,7 +44794,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 223, + "weight": 213, "cookies": false, "type": "", "demo": "users\/delete.md", @@ -44235,7 +44855,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 208, + "weight": 198, "cookies": false, "type": "", "demo": "users\/update-email.md", @@ -44314,7 +44934,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 226, + "weight": 216, "cookies": false, "type": "", "demo": "users\/create-jwt.md", @@ -44396,7 +45016,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 204, + "weight": 194, "cookies": false, "type": "", "demo": "users\/update-labels.md", @@ -44476,7 +45096,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 200, + "weight": 190, "cookies": false, "type": "", "demo": "users\/list-logs.md", @@ -44556,7 +45176,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 199, + "weight": 189, "cookies": false, "type": "", "demo": "users\/list-memberships.md", @@ -44647,7 +45267,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 213, + "weight": 203, "cookies": false, "type": "", "demo": "users\/update-mfa.md", @@ -44779,7 +45399,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 218, + "weight": 208, "cookies": false, "type": "", "demo": "users\/delete-mfa-authenticator.md", @@ -44907,7 +45527,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 214, + "weight": 204, "cookies": false, "type": "", "demo": "users\/list-mfa-factors.md", @@ -45020,7 +45640,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 215, + "weight": 205, "cookies": false, "type": "", "demo": "users\/get-mfa-recovery-codes.md", @@ -45133,7 +45753,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 217, + "weight": 207, "cookies": false, "type": "", "demo": "users\/update-mfa-recovery-codes.md", @@ -45246,7 +45866,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 216, + "weight": 206, "cookies": false, "type": "", "demo": "users\/create-mfa-recovery-codes.md", @@ -45361,7 +45981,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 206, + "weight": 196, "cookies": false, "type": "", "demo": "users\/update-name.md", @@ -45440,7 +46060,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 207, + "weight": 197, "cookies": false, "type": "", "demo": "users\/update-password.md", @@ -45519,7 +46139,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 209, + "weight": 199, "cookies": false, "type": "", "demo": "users\/update-phone.md", @@ -45596,7 +46216,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 196, + "weight": 186, "cookies": false, "type": "", "demo": "users\/get-prefs.md", @@ -45655,7 +46275,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 211, + "weight": 201, "cookies": false, "type": "", "demo": "users\/update-prefs.md", @@ -45732,7 +46352,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 198, + "weight": 188, "cookies": false, "type": "", "demo": "users\/list-sessions.md", @@ -45800,7 +46420,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 219, + "weight": 209, "cookies": false, "type": "", "demo": "users\/create-session.md", @@ -45854,7 +46474,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 222, + "weight": 212, "cookies": false, "type": "", "demo": "users\/delete-sessions.md", @@ -45910,7 +46530,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 221, + "weight": 211, "cookies": false, "type": "", "demo": "users\/delete-session.md", @@ -45979,7 +46599,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 203, + "weight": 193, "cookies": false, "type": "", "demo": "users\/update-status.md", @@ -46056,7 +46676,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 201, + "weight": 191, "cookies": false, "type": "", "demo": "users\/list-targets.md", @@ -46137,7 +46757,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 193, + "weight": 183, "cookies": false, "type": "", "demo": "users\/create-target.md", @@ -46248,7 +46868,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 197, + "weight": 187, "cookies": false, "type": "", "demo": "users\/get-target.md", @@ -46316,7 +46936,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 212, + "weight": 202, "cookies": false, "type": "", "demo": "users\/update-target.md", @@ -46406,7 +47026,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 224, + "weight": 214, "cookies": false, "type": "", "demo": "users\/delete-target.md", @@ -46476,7 +47096,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 220, + "weight": 210, "cookies": false, "type": "", "demo": "users\/create-token.md", @@ -46558,7 +47178,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 210, + "weight": 200, "cookies": false, "type": "", "demo": "users\/update-email-verification.md", @@ -46637,7 +47257,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 205, + "weight": 195, "cookies": false, "type": "", "demo": "users\/update-phone-verification.md", @@ -46716,7 +47336,7 @@ "x-appwrite": { "method": "createRepositoryDetection", "group": "repositories", - "weight": 231, + "weight": 221, "cookies": false, "type": "", "demo": "vcs\/create-repository-detection.md", @@ -46811,7 +47431,7 @@ "x-appwrite": { "method": "listRepositories", "group": "repositories", - "weight": 232, + "weight": 222, "cookies": false, "type": "", "demo": "vcs\/list-repositories.md", @@ -46864,6 +47484,18 @@ "x-example": "", "default": "", "in": "query" + }, + { + "name": "queries", + "description": "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", + "required": false, + "type": "array", + "collectionFormat": "multi", + "items": { + "type": "string" + }, + "default": [], + "in": "query" } ] }, @@ -46892,7 +47524,7 @@ "x-appwrite": { "method": "createRepository", "group": "repositories", - "weight": 233, + "weight": 223, "cookies": false, "type": "", "demo": "vcs\/create-repository.md", @@ -46975,7 +47607,7 @@ "x-appwrite": { "method": "getRepository", "group": "repositories", - "weight": 234, + "weight": 224, "cookies": false, "type": "", "demo": "vcs\/get-repository.md", @@ -47041,7 +47673,7 @@ "x-appwrite": { "method": "listRepositoryBranches", "group": "repositories", - "weight": 235, + "weight": 225, "cookies": false, "type": "", "demo": "vcs\/list-repository-branches.md", @@ -47107,7 +47739,7 @@ "x-appwrite": { "method": "getRepositoryContents", "group": "repositories", - "weight": 230, + "weight": 220, "cookies": false, "type": "", "demo": "vcs\/get-repository-contents.md", @@ -47190,7 +47822,7 @@ "x-appwrite": { "method": "updateExternalDeployments", "group": "repositories", - "weight": 240, + "weight": 230, "cookies": false, "type": "", "demo": "vcs\/update-external-deployments.md", @@ -47274,7 +47906,7 @@ "x-appwrite": { "method": "listInstallations", "group": "installations", - "weight": 237, + "weight": 227, "cookies": false, "type": "", "demo": "vcs\/list-installations.md", @@ -47354,7 +47986,7 @@ "x-appwrite": { "method": "getInstallation", "group": "installations", - "weight": 238, + "weight": 228, "cookies": false, "type": "", "demo": "vcs\/get-installation.md", @@ -47407,7 +48039,7 @@ "x-appwrite": { "method": "deleteInstallation", "group": "installations", - "weight": 239, + "weight": 229, "cookies": false, "type": "", "demo": "vcs\/delete-installation.md", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index e760c1bb7d..cda8c7682f 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -325,7 +325,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 58, + "weight": 48, "cookies": false, "type": "", "demo": "account\/list-identities.md", @@ -397,7 +397,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 59, + "weight": 49, "cookies": false, "type": "", "demo": "account\/delete-identity.md", @@ -587,7 +587,7 @@ "x-appwrite": { "method": "updateMFA", "group": "mfa", - "weight": 45, + "weight": 306, "cookies": false, "type": "", "demo": "account\/update-mfa.md", @@ -661,7 +661,7 @@ "x-appwrite": { "method": "createMfaAuthenticator", "group": "mfa", - "weight": 47, + "weight": 308, "cookies": false, "type": "", "demo": "account\/create-mfa-authenticator.md", @@ -784,7 +784,7 @@ "x-appwrite": { "method": "updateMfaAuthenticator", "group": "mfa", - "weight": 48, + "weight": 309, "cookies": false, "type": "", "demo": "account\/update-mfa-authenticator.md", @@ -924,7 +924,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 52, + "weight": 310, "cookies": false, "type": "", "demo": "account\/delete-mfa-authenticator.md", @@ -1021,7 +1021,7 @@ ] } }, - "\/account\/mfa\/challenge": { + "\/account\/mfa\/challenges": { "post": { "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", @@ -1047,7 +1047,7 @@ "x-appwrite": { "method": "createMfaChallenge", "group": "mfa", - "weight": 53, + "weight": 314, "cookies": false, "type": "", "demo": "account\/create-mfa-challenge.md", @@ -1178,7 +1178,7 @@ "x-appwrite": { "method": "updateMfaChallenge", "group": "mfa", - "weight": 54, + "weight": 315, "cookies": false, "type": "", "demo": "account\/update-mfa-challenge.md", @@ -1317,7 +1317,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 46, + "weight": 307, "cookies": false, "type": "", "demo": "account\/list-mfa-factors.md", @@ -1417,7 +1417,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 51, + "weight": 313, "cookies": false, "type": "", "demo": "account\/get-mfa-recovery-codes.md", @@ -1517,7 +1517,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 49, + "weight": 311, "cookies": false, "type": "", "demo": "account\/create-mfa-recovery-codes.md", @@ -1617,7 +1617,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 50, + "weight": 312, "cookies": false, "type": "", "demo": "account\/update-mfa-recovery-codes.md", @@ -3741,7 +3741,7 @@ "x-appwrite": { "method": "getBrowser", "group": null, - "weight": 61, + "weight": 51, "cookies": false, "type": "location", "demo": "avatars\/get-browser.md", @@ -3867,7 +3867,7 @@ "x-appwrite": { "method": "getCreditCard", "group": null, - "weight": 60, + "weight": 50, "cookies": false, "type": "location", "demo": "avatars\/get-credit-card.md", @@ -3999,7 +3999,7 @@ "x-appwrite": { "method": "getFavicon", "group": null, - "weight": 64, + "weight": 54, "cookies": false, "type": "location", "demo": "avatars\/get-favicon.md", @@ -4063,7 +4063,7 @@ "x-appwrite": { "method": "getFlag", "group": null, - "weight": 62, + "weight": 52, "cookies": false, "type": "location", "demo": "avatars\/get-flag.md", @@ -4551,7 +4551,7 @@ "x-appwrite": { "method": "getImage", "group": null, - "weight": 63, + "weight": 53, "cookies": false, "type": "location", "demo": "avatars\/get-image.md", @@ -4635,7 +4635,7 @@ "x-appwrite": { "method": "getInitials", "group": null, - "weight": 66, + "weight": 56, "cookies": false, "type": "location", "demo": "avatars\/get-initials.md", @@ -4727,7 +4727,7 @@ "x-appwrite": { "method": "getQR", "group": null, - "weight": 65, + "weight": 55, "cookies": false, "type": "location", "demo": "avatars\/get-qr.md", @@ -4819,7 +4819,7 @@ "x-appwrite": { "method": "getScreenshot", "group": null, - "weight": 67, + "weight": 57, "cookies": false, "type": "location", "demo": "avatars\/get-screenshot.md", @@ -4862,7 +4862,7 @@ "required": false, "type": "object", "default": [], - "x-example": "{}", + "x-example": "{\"Authorization\":\"Bearer token123\",\"X-Custom-Header\":\"value\"}", "in": "query" }, { @@ -4871,7 +4871,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1920", "default": 1280, "in": "query" }, @@ -4881,7 +4881,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 1, + "x-example": "1080", "default": 720, "in": "query" }, @@ -4891,7 +4891,7 @@ "required": false, "type": "number", "format": "float", - "x-example": 0.1, + "x-example": "2", "default": 1, "in": "query" }, @@ -4900,7 +4900,7 @@ "description": "Browser theme. Pass \"light\" or \"dark\". Defaults to \"light\".", "required": false, "type": "string", - "x-example": "light", + "x-example": "dark", "enum": [ "light", "dark" @@ -4915,7 +4915,7 @@ "description": "Custom user agent string. Defaults to browser default.", "required": false, "type": "string", - "x-example": "", + "x-example": "Mozilla\/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit\/605.1.15", "default": "", "in": "query" }, @@ -4924,7 +4924,7 @@ "description": "Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.", "required": false, "type": "boolean", - "x-example": false, + "x-example": "true", "default": false, "in": "query" }, @@ -4933,7 +4933,7 @@ "description": "Browser locale (e.g., \"en-US\", \"fr-FR\"). Defaults to browser default.", "required": false, "type": "string", - "x-example": "", + "x-example": "en-US", "default": "", "in": "query" }, @@ -4942,7 +4942,7 @@ "description": "IANA timezone identifier (e.g., \"America\/New_York\", \"Europe\/London\"). Defaults to browser default.", "required": false, "type": "string", - "x-example": "africa\/abidjan", + "x-example": "america\/new_york", "enum": [ "africa\/abidjan", "africa\/accra", @@ -5375,7 +5375,7 @@ "required": false, "type": "number", "format": "float", - "x-example": -90, + "x-example": "37.7749", "default": 0, "in": "query" }, @@ -5385,7 +5385,7 @@ "required": false, "type": "number", "format": "float", - "x-example": -180, + "x-example": "-122.4194", "default": 0, "in": "query" }, @@ -5395,7 +5395,7 @@ "required": false, "type": "number", "format": "float", - "x-example": 0, + "x-example": "100", "default": 0, "in": "query" }, @@ -5404,7 +5404,7 @@ "description": "Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.", "required": false, "type": "boolean", - "x-example": false, + "x-example": "true", "default": false, "in": "query" }, @@ -5415,8 +5415,33 @@ "type": "array", "collectionFormat": "multi", "items": { - "type": "string" + "type": "string", + "enum": [ + "geolocation", + "camera", + "microphone", + "notifications", + "midi", + "push", + "clipboard-read", + "clipboard-write", + "payment-handler", + "usb", + "bluetooth", + "accelerometer", + "gyroscope", + "magnetometer", + "ambient-light-sensor", + "background-sync", + "persistent-storage", + "screen-wake-lock", + "web-share", + "xr-spatial-tracking" + ], + "x-enum-name": "BrowserPermission", + "x-enum-keys": [] }, + "x-example": "[\"geolocation\",\"notifications\"]", "default": [], "in": "query" }, @@ -5426,7 +5451,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "3", "default": 0, "in": "query" }, @@ -5436,7 +5461,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "800", "default": 0, "in": "query" }, @@ -5446,7 +5471,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": 0, + "x-example": "600", "default": 0, "in": "query" }, @@ -5456,7 +5481,7 @@ "required": false, "type": "integer", "format": "int32", - "x-example": -1, + "x-example": "85", "default": -1, "in": "query" }, @@ -5465,7 +5490,7 @@ "description": "Output format type (jpeg, jpg, png, gif and webp).", "required": false, "type": "string", - "x-example": "jpg", + "x-example": "jpeg", "enum": [ "jpg", "jpeg", @@ -6636,6 +6661,24 @@ "description": "Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.", "default": true, "x-example": false + }, + "attributes": { + "type": "array", + "description": "Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "default": [], + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of attribute keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "default": [], + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -11759,7 +11802,13 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -12221,7 +12270,66 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -12638,7 +12746,66 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "sessions.write", + "users.read", + "users.write", + "teams.read", + "teams.write", + "databases.read", + "databases.write", + "collections.read", + "collections.write", + "tables.read", + "tables.write", + "attributes.read", + "attributes.write", + "columns.read", + "columns.write", + "indexes.read", + "indexes.write", + "documents.read", + "documents.write", + "rows.read", + "rows.write", + "files.read", + "files.write", + "buckets.read", + "buckets.write", + "functions.read", + "functions.write", + "sites.read", + "sites.write", + "log.read", + "log.write", + "execution.read", + "execution.write", + "locale.read", + "avatars.read", + "health.read", + "providers.read", + "providers.write", + "messages.read", + "messages.write", + "topics.read", + "topics.write", + "subscribers.read", + "subscribers.write", + "targets.read", + "targets.write", + "rules.read", + "rules.write", + "migrations.read", + "migrations.write", + "vcs.read", + "vcs.write", + "assistant.read", + "tokens.read", + "tokens.write" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "installationId": { @@ -13182,7 +13349,7 @@ "branch", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -13284,7 +13451,7 @@ "branch", "commit" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -14340,7 +14507,7 @@ "x-appwrite": { "method": "query", "group": "graphql", - "weight": 251, + "weight": 241, "cookies": false, "type": "graphql", "demo": "graphql\/query.md", @@ -14415,7 +14582,7 @@ "x-appwrite": { "method": "mutation", "group": "graphql", - "weight": 250, + "weight": 240, "cookies": false, "type": "graphql", "demo": "graphql\/mutation.md", @@ -14488,7 +14655,7 @@ "x-appwrite": { "method": "get", "group": "health", - "weight": 79, + "weight": 69, "cookies": false, "type": "", "demo": "health\/get.md", @@ -14538,7 +14705,7 @@ "x-appwrite": { "method": "getAntivirus", "group": "health", - "weight": 100, + "weight": 90, "cookies": false, "type": "", "demo": "health\/get-antivirus.md", @@ -14588,7 +14755,7 @@ "x-appwrite": { "method": "getCache", "group": "health", - "weight": 82, + "weight": 72, "cookies": false, "type": "", "demo": "health\/get-cache.md", @@ -14638,7 +14805,7 @@ "x-appwrite": { "method": "getCertificate", "group": "health", - "weight": 87, + "weight": 77, "cookies": false, "type": "", "demo": "health\/get-certificate.md", @@ -14697,7 +14864,7 @@ "x-appwrite": { "method": "getDB", "group": "health", - "weight": 81, + "weight": 71, "cookies": false, "type": "", "demo": "health\/get-db.md", @@ -14747,7 +14914,7 @@ "x-appwrite": { "method": "getPubSub", "group": "health", - "weight": 83, + "weight": 73, "cookies": false, "type": "", "demo": "health\/get-pub-sub.md", @@ -14797,7 +14964,7 @@ "x-appwrite": { "method": "getQueueBuilds", "group": "queue", - "weight": 89, + "weight": 79, "cookies": false, "type": "", "demo": "health\/get-queue-builds.md", @@ -14858,7 +15025,7 @@ "x-appwrite": { "method": "getQueueCertificates", "group": "queue", - "weight": 88, + "weight": 78, "cookies": false, "type": "", "demo": "health\/get-queue-certificates.md", @@ -14919,7 +15086,7 @@ "x-appwrite": { "method": "getQueueDatabases", "group": "queue", - "weight": 90, + "weight": 80, "cookies": false, "type": "", "demo": "health\/get-queue-databases.md", @@ -14989,7 +15156,7 @@ "x-appwrite": { "method": "getQueueDeletes", "group": "queue", - "weight": 91, + "weight": 81, "cookies": false, "type": "", "demo": "health\/get-queue-deletes.md", @@ -15050,7 +15217,7 @@ "x-appwrite": { "method": "getFailedJobs", "group": "queue", - "weight": 101, + "weight": 91, "cookies": false, "type": "", "demo": "health\/get-failed-jobs.md", @@ -15135,7 +15302,7 @@ "x-appwrite": { "method": "getQueueFunctions", "group": "queue", - "weight": 95, + "weight": 85, "cookies": false, "type": "", "demo": "health\/get-queue-functions.md", @@ -15196,7 +15363,7 @@ "x-appwrite": { "method": "getQueueLogs", "group": "queue", - "weight": 86, + "weight": 76, "cookies": false, "type": "", "demo": "health\/get-queue-logs.md", @@ -15257,7 +15424,7 @@ "x-appwrite": { "method": "getQueueMails", "group": "queue", - "weight": 92, + "weight": 82, "cookies": false, "type": "", "demo": "health\/get-queue-mails.md", @@ -15318,7 +15485,7 @@ "x-appwrite": { "method": "getQueueMessaging", "group": "queue", - "weight": 93, + "weight": 83, "cookies": false, "type": "", "demo": "health\/get-queue-messaging.md", @@ -15379,7 +15546,7 @@ "x-appwrite": { "method": "getQueueMigrations", "group": "queue", - "weight": 94, + "weight": 84, "cookies": false, "type": "", "demo": "health\/get-queue-migrations.md", @@ -15440,7 +15607,7 @@ "x-appwrite": { "method": "getQueueStatsResources", "group": "queue", - "weight": 96, + "weight": 86, "cookies": false, "type": "", "demo": "health\/get-queue-stats-resources.md", @@ -15501,7 +15668,7 @@ "x-appwrite": { "method": "getQueueUsage", "group": "queue", - "weight": 97, + "weight": 87, "cookies": false, "type": "", "demo": "health\/get-queue-usage.md", @@ -15562,7 +15729,7 @@ "x-appwrite": { "method": "getQueueWebhooks", "group": "queue", - "weight": 85, + "weight": 75, "cookies": false, "type": "", "demo": "health\/get-queue-webhooks.md", @@ -15623,7 +15790,7 @@ "x-appwrite": { "method": "getStorage", "group": "storage", - "weight": 99, + "weight": 89, "cookies": false, "type": "", "demo": "health\/get-storage.md", @@ -15673,7 +15840,7 @@ "x-appwrite": { "method": "getStorageLocal", "group": "storage", - "weight": 98, + "weight": 88, "cookies": false, "type": "", "demo": "health\/get-storage-local.md", @@ -15723,7 +15890,7 @@ "x-appwrite": { "method": "getTime", "group": "health", - "weight": 84, + "weight": 74, "cookies": false, "type": "", "demo": "health\/get-time.md", @@ -15773,7 +15940,7 @@ "x-appwrite": { "method": "get", "group": null, - "weight": 71, + "weight": 61, "cookies": false, "type": "", "demo": "locale\/get.md", @@ -15826,7 +15993,7 @@ "x-appwrite": { "method": "listCodes", "group": null, - "weight": 72, + "weight": 62, "cookies": false, "type": "", "demo": "locale\/list-codes.md", @@ -15879,7 +16046,7 @@ "x-appwrite": { "method": "listContinents", "group": null, - "weight": 76, + "weight": 66, "cookies": false, "type": "", "demo": "locale\/list-continents.md", @@ -15932,7 +16099,7 @@ "x-appwrite": { "method": "listCountries", "group": null, - "weight": 73, + "weight": 63, "cookies": false, "type": "", "demo": "locale\/list-countries.md", @@ -15985,7 +16152,7 @@ "x-appwrite": { "method": "listCountriesEU", "group": null, - "weight": 74, + "weight": 64, "cookies": false, "type": "", "demo": "locale\/list-countries-eu.md", @@ -16038,7 +16205,7 @@ "x-appwrite": { "method": "listCountriesPhones", "group": null, - "weight": 75, + "weight": 65, "cookies": false, "type": "", "demo": "locale\/list-countries-phones.md", @@ -16091,7 +16258,7 @@ "x-appwrite": { "method": "listCurrencies", "group": null, - "weight": 77, + "weight": 67, "cookies": false, "type": "", "demo": "locale\/list-currencies.md", @@ -16144,7 +16311,7 @@ "x-appwrite": { "method": "listLanguages", "group": null, - "weight": 78, + "weight": 68, "cookies": false, "type": "", "demo": "locale\/list-languages.md", @@ -16197,7 +16364,7 @@ "x-appwrite": { "method": "listMessages", "group": "messages", - "weight": 308, + "weight": 298, "cookies": false, "type": "", "demo": "messaging\/list-messages.md", @@ -16282,7 +16449,7 @@ "x-appwrite": { "method": "createEmail", "group": "messages", - "weight": 305, + "weight": 295, "cookies": false, "type": "", "demo": "messaging\/create-email.md", @@ -16442,7 +16609,7 @@ "x-appwrite": { "method": "updateEmail", "group": "messages", - "weight": 312, + "weight": 302, "cookies": false, "type": "", "demo": "messaging\/update-email.md", @@ -16609,7 +16776,7 @@ "x-appwrite": { "method": "createPush", "group": "messages", - "weight": 307, + "weight": 297, "cookies": false, "type": "", "demo": "messaging\/create-push.md", @@ -16807,7 +16974,7 @@ "x-appwrite": { "method": "updatePush", "group": "messages", - "weight": 314, + "weight": 304, "cookies": false, "type": "", "demo": "messaging\/update-push.md", @@ -17020,7 +17187,7 @@ "x-appwrite": { "method": "createSms", "group": "messages", - "weight": 306, + "weight": 296, "cookies": false, "type": "", "demo": "messaging\/create-sms.md", @@ -17210,7 +17377,7 @@ "x-appwrite": { "method": "updateSms", "group": "messages", - "weight": 313, + "weight": 303, "cookies": false, "type": "", "demo": "messaging\/update-sms.md", @@ -17399,7 +17566,7 @@ "x-appwrite": { "method": "getMessage", "group": "messages", - "weight": 311, + "weight": 301, "cookies": false, "type": "", "demo": "messaging\/get-message.md", @@ -17455,7 +17622,7 @@ "x-appwrite": { "method": "delete", "group": "messages", - "weight": 315, + "weight": 305, "cookies": false, "type": "", "demo": "messaging\/delete.md", @@ -17516,7 +17683,7 @@ "x-appwrite": { "method": "listMessageLogs", "group": "logs", - "weight": 309, + "weight": 299, "cookies": false, "type": "", "demo": "messaging\/list-message-logs.md", @@ -17598,7 +17765,7 @@ "x-appwrite": { "method": "listTargets", "group": "messages", - "weight": 310, + "weight": 300, "cookies": false, "type": "", "demo": "messaging\/list-targets.md", @@ -17680,7 +17847,7 @@ "x-appwrite": { "method": "listProviders", "group": "providers", - "weight": 279, + "weight": 269, "cookies": false, "type": "", "demo": "messaging\/list-providers.md", @@ -17765,7 +17932,7 @@ "x-appwrite": { "method": "createApnsProvider", "group": "providers", - "weight": 278, + "weight": 268, "cookies": false, "type": "", "demo": "messaging\/create-apns-provider.md", @@ -17954,7 +18121,7 @@ "x-appwrite": { "method": "updateApnsProvider", "group": "providers", - "weight": 292, + "weight": 282, "cookies": false, "type": "", "demo": "messaging\/update-apns-provider.md", @@ -18140,7 +18307,7 @@ "x-appwrite": { "method": "createFcmProvider", "group": "providers", - "weight": 277, + "weight": 267, "cookies": false, "type": "", "demo": "messaging\/create-fcm-provider.md", @@ -18298,7 +18465,7 @@ "x-appwrite": { "method": "updateFcmProvider", "group": "providers", - "weight": 291, + "weight": 281, "cookies": false, "type": "", "demo": "messaging\/update-fcm-provider.md", @@ -18452,7 +18619,7 @@ "x-appwrite": { "method": "createMailgunProvider", "group": "providers", - "weight": 268, + "weight": 258, "cookies": false, "type": "", "demo": "messaging\/create-mailgun-provider.md", @@ -18582,7 +18749,7 @@ "x-appwrite": { "method": "updateMailgunProvider", "group": "providers", - "weight": 282, + "weight": 272, "cookies": false, "type": "", "demo": "messaging\/update-mailgun-provider.md", @@ -18710,7 +18877,7 @@ "x-appwrite": { "method": "createMsg91Provider", "group": "providers", - "weight": 272, + "weight": 262, "cookies": false, "type": "", "demo": "messaging\/create-msg-91-provider.md", @@ -18815,7 +18982,7 @@ "x-appwrite": { "method": "updateMsg91Provider", "group": "providers", - "weight": 286, + "weight": 276, "cookies": false, "type": "", "demo": "messaging\/update-msg-91-provider.md", @@ -18918,7 +19085,7 @@ "x-appwrite": { "method": "createResendProvider", "group": "providers", - "weight": 270, + "weight": 260, "cookies": false, "type": "", "demo": "messaging\/create-resend-provider.md", @@ -19035,7 +19202,7 @@ "x-appwrite": { "method": "updateResendProvider", "group": "providers", - "weight": 284, + "weight": 274, "cookies": false, "type": "", "demo": "messaging\/update-resend-provider.md", @@ -19150,7 +19317,7 @@ "x-appwrite": { "method": "createSendgridProvider", "group": "providers", - "weight": 269, + "weight": 259, "cookies": false, "type": "", "demo": "messaging\/create-sendgrid-provider.md", @@ -19267,7 +19434,7 @@ "x-appwrite": { "method": "updateSendgridProvider", "group": "providers", - "weight": 283, + "weight": 273, "cookies": false, "type": "", "demo": "messaging\/update-sendgrid-provider.md", @@ -19382,7 +19549,7 @@ "x-appwrite": { "method": "createSmtpProvider", "group": "providers", - "weight": 271, + "weight": 261, "cookies": false, "type": "", "demo": "messaging\/create-smtp-provider.md", @@ -19629,7 +19796,7 @@ "x-appwrite": { "method": "updateSmtpProvider", "group": "providers", - "weight": 285, + "weight": 275, "cookies": false, "type": "", "demo": "messaging\/update-smtp-provider.md", @@ -19871,7 +20038,7 @@ "x-appwrite": { "method": "createTelesignProvider", "group": "providers", - "weight": 273, + "weight": 263, "cookies": false, "type": "", "demo": "messaging\/create-telesign-provider.md", @@ -19976,7 +20143,7 @@ "x-appwrite": { "method": "updateTelesignProvider", "group": "providers", - "weight": 287, + "weight": 277, "cookies": false, "type": "", "demo": "messaging\/update-telesign-provider.md", @@ -20079,7 +20246,7 @@ "x-appwrite": { "method": "createTextmagicProvider", "group": "providers", - "weight": 274, + "weight": 264, "cookies": false, "type": "", "demo": "messaging\/create-textmagic-provider.md", @@ -20184,7 +20351,7 @@ "x-appwrite": { "method": "updateTextmagicProvider", "group": "providers", - "weight": 288, + "weight": 278, "cookies": false, "type": "", "demo": "messaging\/update-textmagic-provider.md", @@ -20287,7 +20454,7 @@ "x-appwrite": { "method": "createTwilioProvider", "group": "providers", - "weight": 275, + "weight": 265, "cookies": false, "type": "", "demo": "messaging\/create-twilio-provider.md", @@ -20392,7 +20559,7 @@ "x-appwrite": { "method": "updateTwilioProvider", "group": "providers", - "weight": 289, + "weight": 279, "cookies": false, "type": "", "demo": "messaging\/update-twilio-provider.md", @@ -20495,7 +20662,7 @@ "x-appwrite": { "method": "createVonageProvider", "group": "providers", - "weight": 276, + "weight": 266, "cookies": false, "type": "", "demo": "messaging\/create-vonage-provider.md", @@ -20600,7 +20767,7 @@ "x-appwrite": { "method": "updateVonageProvider", "group": "providers", - "weight": 290, + "weight": 280, "cookies": false, "type": "", "demo": "messaging\/update-vonage-provider.md", @@ -20701,7 +20868,7 @@ "x-appwrite": { "method": "getProvider", "group": "providers", - "weight": 281, + "weight": 271, "cookies": false, "type": "", "demo": "messaging\/get-provider.md", @@ -20757,7 +20924,7 @@ "x-appwrite": { "method": "deleteProvider", "group": "providers", - "weight": 293, + "weight": 283, "cookies": false, "type": "", "demo": "messaging\/delete-provider.md", @@ -20818,7 +20985,7 @@ "x-appwrite": { "method": "listProviderLogs", "group": "providers", - "weight": 280, + "weight": 270, "cookies": false, "type": "", "demo": "messaging\/list-provider-logs.md", @@ -20900,7 +21067,7 @@ "x-appwrite": { "method": "listSubscriberLogs", "group": "subscribers", - "weight": 302, + "weight": 292, "cookies": false, "type": "", "demo": "messaging\/list-subscriber-logs.md", @@ -20982,7 +21149,7 @@ "x-appwrite": { "method": "listTopics", "group": "topics", - "weight": 295, + "weight": 285, "cookies": false, "type": "", "demo": "messaging\/list-topics.md", @@ -21065,7 +21232,7 @@ "x-appwrite": { "method": "createTopic", "group": "topics", - "weight": 294, + "weight": 284, "cookies": false, "type": "", "demo": "messaging\/create-topic.md", @@ -21154,7 +21321,7 @@ "x-appwrite": { "method": "getTopic", "group": "topics", - "weight": 297, + "weight": 287, "cookies": false, "type": "", "demo": "messaging\/get-topic.md", @@ -21215,7 +21382,7 @@ "x-appwrite": { "method": "updateTopic", "group": "topics", - "weight": 298, + "weight": 288, "cookies": false, "type": "", "demo": "messaging\/update-topic.md", @@ -21297,7 +21464,7 @@ "x-appwrite": { "method": "deleteTopic", "group": "topics", - "weight": 299, + "weight": 289, "cookies": false, "type": "", "demo": "messaging\/delete-topic.md", @@ -21358,7 +21525,7 @@ "x-appwrite": { "method": "listTopicLogs", "group": "topics", - "weight": 296, + "weight": 286, "cookies": false, "type": "", "demo": "messaging\/list-topic-logs.md", @@ -21440,7 +21607,7 @@ "x-appwrite": { "method": "listSubscribers", "group": "subscribers", - "weight": 301, + "weight": 291, "cookies": false, "type": "", "demo": "messaging\/list-subscribers.md", @@ -21531,7 +21698,7 @@ "x-appwrite": { "method": "createSubscriber", "group": "subscribers", - "weight": 300, + "weight": 290, "cookies": false, "type": "", "demo": "messaging\/create-subscriber.md", @@ -21620,7 +21787,7 @@ "x-appwrite": { "method": "getSubscriber", "group": "subscribers", - "weight": 303, + "weight": 293, "cookies": false, "type": "", "demo": "messaging\/get-subscriber.md", @@ -21684,7 +21851,7 @@ "x-appwrite": { "method": "deleteSubscriber", "group": "subscribers", - "weight": 304, + "weight": 294, "cookies": false, "type": "", "demo": "messaging\/delete-subscriber.md", @@ -23014,7 +23181,7 @@ "commit", "tag" ], - "x-enum-name": null, + "x-enum-name": "TemplateReferenceType", "x-enum-keys": [] }, "reference": { @@ -23117,7 +23284,7 @@ "commit", "tag" ], - "x-enum-name": "VCSDeploymentType", + "x-enum-name": "VCSReferenceType", "x-enum-keys": [] }, "reference": { @@ -24047,7 +24214,7 @@ "x-appwrite": { "method": "listBuckets", "group": "buckets", - "weight": 156, + "weight": 146, "cookies": false, "type": "", "demo": "storage\/list-buckets.md", @@ -24129,7 +24296,7 @@ "x-appwrite": { "method": "createBucket", "group": "buckets", - "weight": 155, + "weight": 145, "cookies": false, "type": "", "demo": "storage\/create-bucket.md", @@ -24274,7 +24441,7 @@ "x-appwrite": { "method": "getBucket", "group": "buckets", - "weight": 157, + "weight": 147, "cookies": false, "type": "", "demo": "storage\/get-bucket.md", @@ -24334,7 +24501,7 @@ "x-appwrite": { "method": "updateBucket", "group": "buckets", - "weight": 158, + "weight": 148, "cookies": false, "type": "", "demo": "storage\/update-bucket.md", @@ -24475,7 +24642,7 @@ "x-appwrite": { "method": "deleteBucket", "group": "buckets", - "weight": 159, + "weight": 149, "cookies": false, "type": "", "demo": "storage\/delete-bucket.md", @@ -24535,7 +24702,7 @@ "x-appwrite": { "method": "listFiles", "group": "files", - "weight": 161, + "weight": 151, "cookies": false, "type": "", "demo": "storage\/list-files.md", @@ -24628,7 +24795,7 @@ "x-appwrite": { "method": "createFile", "group": "files", - "weight": 160, + "weight": 150, "cookies": false, "type": "upload", "demo": "storage\/create-file.md", @@ -24719,7 +24886,7 @@ "x-appwrite": { "method": "getFile", "group": "files", - "weight": 162, + "weight": 152, "cookies": false, "type": "", "demo": "storage\/get-file.md", @@ -24790,7 +24957,7 @@ "x-appwrite": { "method": "updateFile", "group": "files", - "weight": 167, + "weight": 157, "cookies": false, "type": "", "demo": "storage\/update-file.md", @@ -24882,7 +25049,7 @@ "x-appwrite": { "method": "deleteFile", "group": "files", - "weight": 168, + "weight": 158, "cookies": false, "type": "", "demo": "storage\/delete-file.md", @@ -24953,7 +25120,7 @@ "x-appwrite": { "method": "getFileDownload", "group": "files", - "weight": 164, + "weight": 154, "cookies": false, "type": "location", "demo": "storage\/get-file-download.md", @@ -25033,7 +25200,7 @@ "x-appwrite": { "method": "getFilePreview", "group": "files", - "weight": 163, + "weight": 153, "cookies": false, "type": "location", "demo": "storage\/get-file-preview.md", @@ -25241,7 +25408,7 @@ "x-appwrite": { "method": "getFileView", "group": "files", - "weight": 165, + "weight": 155, "cookies": false, "type": "location", "demo": "storage\/get-file-view.md", @@ -26296,6 +26463,24 @@ "description": "Is table enabled? When set to 'disabled', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.", "default": true, "x-example": false + }, + "columns": { + "type": "array", + "description": "Array of column definitions to create. Each column should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.", + "default": [], + "x-example": null, + "items": { + "type": "object" + } + }, + "indexes": { + "type": "array", + "description": "Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of column keys), orders (array of ASC\/DESC, optional), and lengths (array of integers, optional).", + "default": [], + "x-example": null, + "items": { + "type": "object" + } } }, "required": [ @@ -30042,7 +30227,13 @@ "default": [], "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "asc", + "desc" + ], + "x-enum-name": "OrderBy", + "x-enum-keys": [] } }, "lengths": { @@ -31552,7 +31743,7 @@ "x-appwrite": { "method": "list", "group": "teams", - "weight": 172, + "weight": 162, "cookies": false, "type": "", "demo": "teams\/list.md", @@ -31637,7 +31828,7 @@ "x-appwrite": { "method": "create", "group": "teams", - "weight": 171, + "weight": 161, "cookies": false, "type": "", "demo": "teams\/create.md", @@ -31728,7 +31919,7 @@ "x-appwrite": { "method": "get", "group": "teams", - "weight": 173, + "weight": 163, "cookies": false, "type": "", "demo": "teams\/get.md", @@ -31791,7 +31982,7 @@ "x-appwrite": { "method": "updateName", "group": "teams", - "weight": 175, + "weight": 165, "cookies": false, "type": "", "demo": "teams\/update-name.md", @@ -31867,7 +32058,7 @@ "x-appwrite": { "method": "delete", "group": "teams", - "weight": 177, + "weight": 167, "cookies": false, "type": "", "demo": "teams\/delete.md", @@ -31930,7 +32121,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 179, + "weight": 169, "cookies": false, "type": "", "demo": "teams\/list-memberships.md", @@ -32023,7 +32214,7 @@ "x-appwrite": { "method": "createMembership", "group": "memberships", - "weight": 178, + "weight": 168, "cookies": false, "type": "", "demo": "teams\/create-membership.md", @@ -32089,7 +32280,14 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } }, "url": { @@ -32137,7 +32335,7 @@ "x-appwrite": { "method": "getMembership", "group": "memberships", - "weight": 180, + "weight": 170, "cookies": false, "type": "", "demo": "teams\/get-membership.md", @@ -32208,7 +32406,7 @@ "x-appwrite": { "method": "updateMembership", "group": "memberships", - "weight": 181, + "weight": 171, "cookies": false, "type": "", "demo": "teams\/update-membership.md", @@ -32264,7 +32462,14 @@ "default": null, "x-example": null, "items": { - "type": "string" + "type": "string", + "enum": [ + "admin", + "developer", + "owner" + ], + "x-enum-name": null, + "x-enum-keys": [] } } }, @@ -32295,7 +32500,7 @@ "x-appwrite": { "method": "deleteMembership", "group": "memberships", - "weight": 183, + "weight": 173, "cookies": false, "type": "", "demo": "teams\/delete-membership.md", @@ -32368,7 +32573,7 @@ "x-appwrite": { "method": "updateMembershipStatus", "group": "memberships", - "weight": 182, + "weight": 172, "cookies": false, "type": "", "demo": "teams\/update-membership-status.md", @@ -32463,7 +32668,7 @@ "x-appwrite": { "method": "getPrefs", "group": "teams", - "weight": 174, + "weight": 164, "cookies": false, "type": "", "demo": "teams\/get-prefs.md", @@ -32525,7 +32730,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "teams", - "weight": 176, + "weight": 166, "cookies": false, "type": "", "demo": "teams\/update-prefs.md", @@ -32974,7 +33179,7 @@ "x-appwrite": { "method": "list", "group": "users", - "weight": 194, + "weight": 184, "cookies": false, "type": "", "demo": "users\/list.md", @@ -33056,7 +33261,7 @@ "x-appwrite": { "method": "create", "group": "users", - "weight": 185, + "weight": 175, "cookies": false, "type": "", "demo": "users\/create.md", @@ -33154,7 +33359,7 @@ "x-appwrite": { "method": "createArgon2User", "group": "users", - "weight": 188, + "weight": 178, "cookies": false, "type": "", "demo": "users\/create-argon-2-user.md", @@ -33246,7 +33451,7 @@ "x-appwrite": { "method": "createBcryptUser", "group": "users", - "weight": 186, + "weight": 176, "cookies": false, "type": "", "demo": "users\/create-bcrypt-user.md", @@ -33336,7 +33541,7 @@ "x-appwrite": { "method": "listIdentities", "group": "identities", - "weight": 202, + "weight": 192, "cookies": false, "type": "", "demo": "users\/list-identities.md", @@ -33415,7 +33620,7 @@ "x-appwrite": { "method": "deleteIdentity", "group": "identities", - "weight": 225, + "weight": 215, "cookies": false, "type": "", "demo": "users\/delete-identity.md", @@ -33477,7 +33682,7 @@ "x-appwrite": { "method": "createMD5User", "group": "users", - "weight": 187, + "weight": 177, "cookies": false, "type": "", "demo": "users\/create-md-5-user.md", @@ -33569,7 +33774,7 @@ "x-appwrite": { "method": "createPHPassUser", "group": "users", - "weight": 190, + "weight": 180, "cookies": false, "type": "", "demo": "users\/create-ph-pass-user.md", @@ -33661,7 +33866,7 @@ "x-appwrite": { "method": "createScryptUser", "group": "users", - "weight": 191, + "weight": 181, "cookies": false, "type": "", "demo": "users\/create-scrypt-user.md", @@ -33788,7 +33993,7 @@ "x-appwrite": { "method": "createScryptModifiedUser", "group": "users", - "weight": 192, + "weight": 182, "cookies": false, "type": "", "demo": "users\/create-scrypt-modified-user.md", @@ -33901,7 +34106,7 @@ "x-appwrite": { "method": "createSHAUser", "group": "users", - "weight": 189, + "weight": 179, "cookies": false, "type": "", "demo": "users\/create-sha-user.md", @@ -34012,7 +34217,7 @@ "x-appwrite": { "method": "get", "group": "users", - "weight": 195, + "weight": 185, "cookies": false, "type": "", "demo": "users\/get.md", @@ -34067,7 +34272,7 @@ "x-appwrite": { "method": "delete", "group": "users", - "weight": 223, + "weight": 213, "cookies": false, "type": "", "demo": "users\/delete.md", @@ -34129,7 +34334,7 @@ "x-appwrite": { "method": "updateEmail", "group": "users", - "weight": 208, + "weight": 198, "cookies": false, "type": "", "demo": "users\/update-email.md", @@ -34209,7 +34414,7 @@ "x-appwrite": { "method": "createJWT", "group": "sessions", - "weight": 226, + "weight": 216, "cookies": false, "type": "", "demo": "users\/create-jwt.md", @@ -34292,7 +34497,7 @@ "x-appwrite": { "method": "updateLabels", "group": "users", - "weight": 204, + "weight": 194, "cookies": false, "type": "", "demo": "users\/update-labels.md", @@ -34373,7 +34578,7 @@ "x-appwrite": { "method": "listLogs", "group": "logs", - "weight": 200, + "weight": 190, "cookies": false, "type": "", "demo": "users\/list-logs.md", @@ -34454,7 +34659,7 @@ "x-appwrite": { "method": "listMemberships", "group": "memberships", - "weight": 199, + "weight": 189, "cookies": false, "type": "", "demo": "users\/list-memberships.md", @@ -34546,7 +34751,7 @@ "x-appwrite": { "method": "updateMfa", "group": "users", - "weight": 213, + "weight": 203, "cookies": false, "type": "", "demo": "users\/update-mfa.md", @@ -34681,7 +34886,7 @@ "x-appwrite": { "method": "deleteMfaAuthenticator", "group": "mfa", - "weight": 218, + "weight": 208, "cookies": false, "type": "", "demo": "users\/delete-mfa-authenticator.md", @@ -34812,7 +35017,7 @@ "x-appwrite": { "method": "listMfaFactors", "group": "mfa", - "weight": 214, + "weight": 204, "cookies": false, "type": "", "demo": "users\/list-mfa-factors.md", @@ -34928,7 +35133,7 @@ "x-appwrite": { "method": "getMfaRecoveryCodes", "group": "mfa", - "weight": 215, + "weight": 205, "cookies": false, "type": "", "demo": "users\/get-mfa-recovery-codes.md", @@ -35044,7 +35249,7 @@ "x-appwrite": { "method": "updateMfaRecoveryCodes", "group": "mfa", - "weight": 217, + "weight": 207, "cookies": false, "type": "", "demo": "users\/update-mfa-recovery-codes.md", @@ -35160,7 +35365,7 @@ "x-appwrite": { "method": "createMfaRecoveryCodes", "group": "mfa", - "weight": 216, + "weight": 206, "cookies": false, "type": "", "demo": "users\/create-mfa-recovery-codes.md", @@ -35278,7 +35483,7 @@ "x-appwrite": { "method": "updateName", "group": "users", - "weight": 206, + "weight": 196, "cookies": false, "type": "", "demo": "users\/update-name.md", @@ -35358,7 +35563,7 @@ "x-appwrite": { "method": "updatePassword", "group": "users", - "weight": 207, + "weight": 197, "cookies": false, "type": "", "demo": "users\/update-password.md", @@ -35438,7 +35643,7 @@ "x-appwrite": { "method": "updatePhone", "group": "users", - "weight": 209, + "weight": 199, "cookies": false, "type": "", "demo": "users\/update-phone.md", @@ -35516,7 +35721,7 @@ "x-appwrite": { "method": "getPrefs", "group": "users", - "weight": 196, + "weight": 186, "cookies": false, "type": "", "demo": "users\/get-prefs.md", @@ -35576,7 +35781,7 @@ "x-appwrite": { "method": "updatePrefs", "group": "users", - "weight": 211, + "weight": 201, "cookies": false, "type": "", "demo": "users\/update-prefs.md", @@ -35654,7 +35859,7 @@ "x-appwrite": { "method": "listSessions", "group": "sessions", - "weight": 198, + "weight": 188, "cookies": false, "type": "", "demo": "users\/list-sessions.md", @@ -35723,7 +35928,7 @@ "x-appwrite": { "method": "createSession", "group": "sessions", - "weight": 219, + "weight": 209, "cookies": false, "type": "", "demo": "users\/create-session.md", @@ -35778,7 +35983,7 @@ "x-appwrite": { "method": "deleteSessions", "group": "sessions", - "weight": 222, + "weight": 212, "cookies": false, "type": "", "demo": "users\/delete-sessions.md", @@ -35835,7 +36040,7 @@ "x-appwrite": { "method": "deleteSession", "group": "sessions", - "weight": 221, + "weight": 211, "cookies": false, "type": "", "demo": "users\/delete-session.md", @@ -35905,7 +36110,7 @@ "x-appwrite": { "method": "updateStatus", "group": "users", - "weight": 203, + "weight": 193, "cookies": false, "type": "", "demo": "users\/update-status.md", @@ -35983,7 +36188,7 @@ "x-appwrite": { "method": "listTargets", "group": "targets", - "weight": 201, + "weight": 191, "cookies": false, "type": "", "demo": "users\/list-targets.md", @@ -36065,7 +36270,7 @@ "x-appwrite": { "method": "createTarget", "group": "targets", - "weight": 193, + "weight": 183, "cookies": false, "type": "", "demo": "users\/create-target.md", @@ -36177,7 +36382,7 @@ "x-appwrite": { "method": "getTarget", "group": "targets", - "weight": 197, + "weight": 187, "cookies": false, "type": "", "demo": "users\/get-target.md", @@ -36246,7 +36451,7 @@ "x-appwrite": { "method": "updateTarget", "group": "targets", - "weight": 212, + "weight": 202, "cookies": false, "type": "", "demo": "users\/update-target.md", @@ -36337,7 +36542,7 @@ "x-appwrite": { "method": "deleteTarget", "group": "targets", - "weight": 224, + "weight": 214, "cookies": false, "type": "", "demo": "users\/delete-target.md", @@ -36408,7 +36613,7 @@ "x-appwrite": { "method": "createToken", "group": "sessions", - "weight": 220, + "weight": 210, "cookies": false, "type": "", "demo": "users\/create-token.md", @@ -36491,7 +36696,7 @@ "x-appwrite": { "method": "updateEmailVerification", "group": "users", - "weight": 210, + "weight": 200, "cookies": false, "type": "", "demo": "users\/update-email-verification.md", @@ -36571,7 +36776,7 @@ "x-appwrite": { "method": "updatePhoneVerification", "group": "users", - "weight": 205, + "weight": 195, "cookies": false, "type": "", "demo": "users\/update-phone-verification.md", diff --git a/app/config/template-runtimes.php b/app/config/template-runtimes.php index 04eaba2c44..4a58bf00b5 100644 --- a/app/config/template-runtimes.php +++ b/app/config/template-runtimes.php @@ -1,43 +1,12 @@ [ - 'name' => 'node', - 'versions' => ['22', '21.0', '20.0', '19.0', '18.0', '16.0', '14.5'] - ], - 'PYTHON' => [ - 'name' => 'python', - 'versions' => ['3.12', '3.11', '3.10', '3.9', '3.8'] - ], - 'DART' => [ - 'name' => 'dart', - 'versions' => ['3.9', '3.8', '3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16'] - ], - 'GO' => [ - 'name' => 'go', - 'versions' => ['1.23'] - ], - 'PHP' => [ - 'name' => 'php', - 'versions' => ['8.3', '8.2', '8.1', '8.0'] - ], - 'DENO' => [ - 'name' => 'deno', - 'versions' => ['2.0', '1.46', '1.40', '1.35', '1.24', '1.21'] - ], - 'BUN' => [ - 'name' => 'bun', - 'versions' => ['1.1', '1.0'] - ], - 'RUBY' => [ - 'name' => 'ruby', - 'versions' => ['3.3', '3.2', '3.1', '3.0'] - ], - 'FLUTTER' => [ - 'name' => 'flutter', - 'versions' => ['3.35', '3.32', '3.24'] - ], -]; +$runtimes = Config::getParam('runtimes'); + +$mappedRuntimes = \array_reduce($runtimes, function ($acc, $runtime) { + $acc[strtoupper($runtime['key'])][] = $runtime['key'] . '-' . $runtime['version']; + return $acc; +}, []); + +return $mappedRuntimes; diff --git a/app/config/templates/function.php b/app/config/templates/function.php index 16838341a9..e10cd4648c 100644 --- a/app/config/templates/function.php +++ b/app/config/templates/function.php @@ -1,20 +1,22 @@ $runtime['name'] . '-' . $version, + 'name' => $runtime, 'commands' => $commands, 'entrypoint' => $entrypoint, 'providerRootDirectory' => $providerRootDirectory ]; - }, array_filter($runtime['versions'], function ($version) use ($versionsDenyList) { - return !in_array($version, $versionsDenyList); + }, array_filter($runtimes, function ($runtime) use ($allowList) { + return in_array($runtime, $allowList); })); } @@ -32,24 +34,26 @@ return [ 'timeout' => 15, 'useCases' => ['starter'], 'runtimes' => [ - ...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/starter'), + ...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/starter', $allowList), ...getRuntimes( $templateRuntimes['PYTHON'], 'pip install -r requirements.txt', 'src/main.py', - 'python/starter' + 'python/starter', + $allowList ), - ...getRuntimes($templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', 'dart/starter'), - ...getRuntimes($templateRuntimes['GO'], '', 'main.go', 'go/starter'), + ...getRuntimes($templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', 'dart/starter', $allowList), + ...getRuntimes($templateRuntimes['GO'], '', 'main.go', 'go/starter', $allowList), ...getRuntimes( $templateRuntimes['PHP'], 'composer install', 'src/index.php', - 'php/starter' + 'php/starter', + $allowList ), - ...getRuntimes($templateRuntimes['DENO'], 'deno cache src/main.ts', 'src/main.ts', 'deno/starter'), - ...getRuntimes($templateRuntimes['BUN'], 'bun install', 'src/main.ts', 'bun/starter'), - ...getRuntimes($templateRuntimes['RUBY'], 'bundle install', 'lib/main.rb', 'ruby/starter'), + ...getRuntimes($templateRuntimes['DENO'], 'deno cache src/main.ts', 'src/main.ts', 'deno/starter', $allowList), + ...getRuntimes($templateRuntimes['BUN'], 'bun install', 'src/main.ts', 'bun/starter', $allowList), + ...getRuntimes($templateRuntimes['RUBY'], 'bundle install', 'lib/main.rb', 'ruby/starter', $allowList), ], 'instructions' => 'For documentation and instructions check out file.', 'vcsProvider' => 'github', @@ -75,7 +79,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/query-upstash-vector' + 'node/query-upstash-vector', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -120,7 +125,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/query-redis-labs' + 'node/query-redis-labs', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -164,7 +170,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/query-neo4j-auradb' + 'node/query-neo4j-auradb', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -217,7 +224,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/query-mongo-atlas' + 'node/query-mongo-atlas', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -255,7 +263,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/query-neon-postgres' + 'node/query-neon-postgres', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -323,25 +332,29 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/prompt-chatgpt' + 'node/prompt-chatgpt', + $allowList ), ...getRuntimes( $templateRuntimes['PYTHON'], 'pip install -r requirements.txt', 'src/main.py', - 'python/prompt_chatgpt' + 'python/prompt_chatgpt', + $allowList ), ...getRuntimes( $templateRuntimes['PHP'], 'composer install', 'src/index.php', - 'php/prompt-chatgpt' + 'php/prompt-chatgpt', + $allowList ), ...getRuntimes( $templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', - 'dart/prompt_chatgpt' + 'dart/prompt_chatgpt', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -385,19 +398,22 @@ return [ $templateRuntimes['NODE'], 'npm install && npm run setup', 'src/main.js', - 'node/discord-command-bot' + 'node/discord-command-bot', + $allowList ), ...getRuntimes( $templateRuntimes['PYTHON'], 'pip install -r requirements.txt && python src/setup.py', 'src/main.py', - 'python/discord_command_bot' + 'python/discord_command_bot', + $allowList ), ...getRuntimes( $templateRuntimes['GO'], '', 'main.go', - 'go/discord-command-bot' + 'go/discord-command-bot', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -449,7 +465,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/analyze-with-perspectiveapi' + 'node/analyze-with-perspectiveapi', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -486,19 +503,22 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/censor-with-redact' + 'node/censor-with-redact', + $allowList ), ...getRuntimes( $templateRuntimes['PYTHON'], 'pip install -r requirements.txt', 'src/main.py', - 'python/censor_with_redact' + 'python/censor_with_redact', + $allowList ), ...getRuntimes( $templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', - 'dart/censor_with_redact' + 'dart/censor_with_redact', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -530,7 +550,7 @@ return [ 'timeout' => 15, 'useCases' => ['utilities'], 'runtimes' => [ - ...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/generate-pdf') + ...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/generate-pdf', $allowList) ], 'instructions' => 'For documentation and instructions check out file.', 'vcsProvider' => 'github', @@ -557,7 +577,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/github-issue-bot' + 'node/github-issue-bot', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -601,7 +622,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/url-shortener' + 'node/url-shortener', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -653,19 +675,22 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/sync-with-algolia' + 'node/sync-with-algolia', + $allowList ), ...getRuntimes( $templateRuntimes['PYTHON'], 'pip install -r requirements.txt', 'src/main.py', - 'python/sync_with_algolia' + 'python/sync_with_algolia', + $allowList ), ...getRuntimes( $templateRuntimes['PHP'], 'composer install', 'src/index.php', - 'php/sync-with-algolia' + 'php/sync-with-algolia', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -735,31 +760,36 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/sync-with-meilisearch' + 'node/sync-with-meilisearch', + $allowList ), ...getRuntimes( $templateRuntimes['PYTHON'], 'pip install -r requirements.txt', 'src/main.py', - 'python/sync-with-meilisearch' + 'python/sync-with-meilisearch', + $allowList ), ...getRuntimes( $templateRuntimes['PHP'], 'composer install', 'src/index.php', - 'php/sync-with-meilisearch' + 'php/sync-with-meilisearch', + $allowList ), ...getRuntimes( $templateRuntimes['BUN'], 'bun install', 'src/main.ts', - 'bun/sync-with-meilisearch' + 'bun/sync-with-meilisearch', + $allowList ), ...getRuntimes( $templateRuntimes['RUBY'], 'bundle install', 'lib/main.rb', - 'ruby/sync-with-meilisearch' + 'ruby/sync-with-meilisearch', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -829,37 +859,43 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/whatsapp-with-vonage' + 'node/whatsapp-with-vonage', + $allowList ), ...getRuntimes( $templateRuntimes['PYTHON'], 'pip install -r requirements.txt', 'src/main.py', - 'python/whatsapp_with_vonage' + 'python/whatsapp_with_vonage', + $allowList ), ...getRuntimes( $templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', - 'dart/whatsapp-with-vonage' + 'dart/whatsapp-with-vonage', + $allowList ), ...getRuntimes( $templateRuntimes['PHP'], 'composer install', 'src/index.php', - 'php/whatsapp-with-vonage' + 'php/whatsapp-with-vonage', + $allowList ), ...getRuntimes( $templateRuntimes['BUN'], 'bun install', 'src/main.ts', - 'bun/whatsapp-with-vonage' + 'bun/whatsapp-with-vonage', + $allowList ), ...getRuntimes( $templateRuntimes['RUBY'], 'bundle install', 'lib/main.rb', - 'ruby/whatsapp-with-vonage' + 'ruby/whatsapp-with-vonage', + $allowList ), ], 'instructions' => 'For documentation and instructions check out file.', @@ -916,7 +952,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/push-notification-with-fcm' + 'node/push-notification-with-fcm', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -973,19 +1010,22 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/email-contact-form' + 'node/email-contact-form', + $allowList ), ...getRuntimes( $templateRuntimes['PYTHON'], 'pip install -r requirements.txt', 'src/main.py', - 'python/email_contact_form' + 'python/email_contact_form', + $allowList ), ...getRuntimes( $templateRuntimes['PHP'], 'composer install', 'src/index.php', - 'php/email-contact-form' + 'php/email-contact-form', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1057,7 +1097,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/subscriptions-with-stripe' + 'node/subscriptions-with-stripe', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1099,7 +1140,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/payments-with-stripe' + 'node/payments-with-stripe', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1157,7 +1199,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/text-generation-with-huggingface' + 'node/text-generation-with-huggingface', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1192,7 +1235,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/language-translation-with-huggingface' + 'node/language-translation-with-huggingface', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1227,7 +1271,8 @@ return [ $templateRuntimes['NODE'], 'npm install && npm run setup', 'src/main.js', - 'node/image-classification-with-huggingface' + 'node/image-classification-with-huggingface', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1286,7 +1331,8 @@ return [ $templateRuntimes['NODE'], 'npm install && npm run setup', 'src/main.js', - 'node/object-detection-with-huggingface' + 'node/object-detection-with-huggingface', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1345,7 +1391,8 @@ return [ $templateRuntimes['NODE'], 'npm install && npm run setup', 'src/main.js', - 'node/speech-recognition-with-huggingface' + 'node/speech-recognition-with-huggingface', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1407,7 +1454,8 @@ return [ $templateRuntimes['NODE'], 'npm install && npm run setup', 'src/main.js', - 'node/text-to-speech-with-huggingface' + 'node/text-to-speech-with-huggingface', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1466,7 +1514,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/generate-with-replicate' + 'node/generate-with-replicate', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1502,7 +1551,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/generate-with-together-ai' + 'node/generate-with-together-ai', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1545,7 +1595,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/chat-with-perplexity-ai' + 'node/chat-with-perplexity-ai', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1587,7 +1638,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/generate-with-replicate' + 'node/generate-with-replicate', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1623,7 +1675,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/sync-with-pinecone' + 'node/sync-with-pinecone', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1687,7 +1740,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/rag-with-langchain' + 'node/rag-with-langchain', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1751,7 +1805,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/speak-with-elevenlabs' + 'node/speak-with-elevenlabs', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1807,7 +1862,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/speak-with-lmnt' + 'node/speak-with-lmnt', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1849,7 +1905,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/chat-with-anyscale' + 'node/chat-with-anyscale', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1891,7 +1948,8 @@ return [ $templateRuntimes['NODE'], 'npm install && npm run setup', 'src/main.js', - 'node/music-generation-with-huggingface' + 'node/music-generation-with-huggingface', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1934,7 +1992,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/generate-with-fal-ai' + 'node/generate-with-fal-ai', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -1970,7 +2029,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/subscriptions-with-lemon-squeezy' + 'node/subscriptions-with-lemon-squeezy', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -2026,7 +2086,8 @@ return [ $templateRuntimes['NODE'], 'npm install', 'src/main.js', - 'node/payments-with-lemon-squeezy' + 'node/payments-with-lemon-squeezy', + $allowList ) ], 'instructions' => 'For documentation and instructions check out file.', @@ -2094,7 +2155,7 @@ return [ 'timeout' => 15, 'useCases' => ['auth'], 'runtimes' => [ - ...getRuntimes($templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', 'dart/sign_in_with_apple') + ...getRuntimes($templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', 'dart/sign_in_with_apple', $allowList) ], 'instructions' => 'For documentation and instructions, check out file.', 'vcsProvider' => 'github', diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index b6e1ebcc61..4beebdb4a6 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1,10 +1,7 @@ skip(fn () => $dbForProject->getDocument('users', $userId)); if ($userFromRequest->isEmpty()) { throw new Exception(Exception::USER_INVALID_TOKEN); } - $verifiedToken = Auth::tokenVerify($userFromRequest->getAttribute('tokens', []), null, $secret); + $verifiedToken = $userFromRequest->tokenVerify(null, $secret, $proofForToken) + ?: $userFromRequest->tokenVerify(null, $secret, $proofForCode); if (!$verifiedToken) { throw new Exception(Exception::USER_INVALID_TOKEN); @@ -209,27 +212,36 @@ $createSession = function (string $userId, string $secret, Request $request, Res $user->setAttributes($userFromRequest->getArrayCopy()); - $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG; $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); - $sessionSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION); + $sessionSecret = $proofForToken->generate(); $factor = (match ($verifiedToken->getAttribute('type')) { - Auth::TOKEN_TYPE_MAGIC_URL, - Auth::TOKEN_TYPE_OAUTH2, - Auth::TOKEN_TYPE_EMAIL => Type::EMAIL, - Auth::TOKEN_TYPE_PHONE => Type::PHONE, - Auth::TOKEN_TYPE_GENERIC => 'token', + TOKEN_TYPE_MAGIC_URL, + TOKEN_TYPE_OAUTH2, + TOKEN_TYPE_EMAIL => Type::EMAIL, + TOKEN_TYPE_PHONE => Type::PHONE, + TOKEN_TYPE_GENERIC => 'token', default => throw new Exception(Exception::USER_INVALID_TOKEN) }); + $provider = match ($verifiedToken->getAttribute('type')) { + TOKEN_TYPE_VERIFICATION, + TOKEN_TYPE_RECOVERY, + TOKEN_TYPE_INVITE => SESSION_PROVIDER_EMAIL, + TOKEN_TYPE_MAGIC_URL => SESSION_PROVIDER_MAGIC_URL, + TOKEN_TYPE_PHONE => SESSION_PROVIDER_PHONE, + TOKEN_TYPE_OAUTH2 => SESSION_PROVIDER_OAUTH2, + default => SESSION_PROVIDER_TOKEN, + }; $session = new Document(array_merge( [ '$id' => ID::unique(), 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), - 'provider' => Auth::getSessionProviderByTokenType($verifiedToken->getAttribute('type')), - 'secret' => Auth::hash($sessionSecret), // One way hash encryption to protect DB leak + 'provider' => $provider, + 'secret' => $proofForToken->hash($sessionSecret), // One way hash encryption to protect DB leak 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), 'factors' => [$factor], @@ -254,11 +266,11 @@ $createSession = function (string $userId, string $secret, Request $request, Res $dbForProject->purgeCachedDocument('users', $user->getId()); // Magic URL + Email OTP - if ($verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_MAGIC_URL || $verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_EMAIL) { + if ($verifiedToken->getAttribute('type') === TOKEN_TYPE_MAGIC_URL || $verifiedToken->getAttribute('type') === TOKEN_TYPE_EMAIL) { $user->setAttribute('emailVerification', true); } - if ($verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_PHONE) { + if ($verifiedToken->getAttribute('type') === TOKEN_TYPE_PHONE) { $user->setAttribute('phoneVerification', true); } @@ -269,8 +281,8 @@ $createSession = function (string $userId, string $secret, Request $request, Res } $isAllowedTokenType = match ($verifiedToken->getAttribute('type')) { - Auth::TOKEN_TYPE_MAGIC_URL, - Auth::TOKEN_TYPE_EMAIL => false, + TOKEN_TYPE_MAGIC_URL, + TOKEN_TYPE_EMAIL => false, default => true }; @@ -290,16 +302,21 @@ $createSession = function (string $userId, string $secret, Request $request, Res ->setParam('userId', $user->getId()) ->setParam('sessionId', $session->getId()); + $encoded = $store + ->setProperty('id', $user->getId()) + ->setProperty('secret', $sessionSecret) + ->encode(); + if (!Config::getParam('domainVerification')) { - $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $sessionSecret)])); + $response->addHeader('X-Fallback-Cookies', \json_encode([$store->getKey() => $encoded])); } $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $protocol = $request->getProtocol(); $response - ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $sessionSecret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $sessionSecret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie($store->getKey() . '_legacy', $encoded, (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($store->getKey(), $encoded, (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ->setStatusCode(Response::STATUS_CODE_CREATED); $countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); @@ -308,7 +325,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res ->setAttribute('current', true) ->setAttribute('countryName', $countryName) ->setAttribute('expire', $expire) - ->setAttribute('secret', Auth::encodeSession($user->getId(), $sessionSecret)) + ->setAttribute('secret', $encoded) ; $response->dynamic($session, Response::MODEL_SESSION); @@ -395,7 +412,8 @@ App::post('/v1/account') $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, true]); $passwordHistory = $project->getAttribute('auths', [])['passwordHistory'] ?? 0; - $password = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS); + $proof = new ProofsPassword(); + $hash = $proof->hash($password); try { $emailCanonical = new Email($email); @@ -415,11 +433,11 @@ App::post('/v1/account') 'email' => $email, 'emailVerification' => false, 'status' => true, - 'password' => $password, - 'passwordHistory' => $passwordHistory > 0 ? [$password] : [], + 'password' => $hash, + 'passwordHistory' => $passwordHistory > 0 ? [$hash] : [], 'passwordUpdate' => DateTime::now(), - 'hash' => Auth::DEFAULT_ALGO, - 'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS, + 'hash' => $proof->getHash()->getName(), + 'hashOptions' => $proof->getHash()->getOptions(), 'registration' => DateTime::now(), 'reset' => false, 'name' => $name, @@ -580,12 +598,13 @@ App::get('/v1/account/sessions') ->inject('response') ->inject('user') ->inject('locale') - ->inject('project') - ->action(function (Response $response, Document $user, Locale $locale, Document $project) { + ->inject('store') + ->inject('proofForToken') + ->action(function (Response $response, User $user, Locale $locale, Store $store, ProofsToken $proofForToken) { $sessions = $user->getAttribute('sessions', []); - $current = Auth::sessionVerify($sessions, Auth::$secret); + $current = $user->sessionVerify($store->getProperty('secret', ''), $proofForToken); foreach ($sessions as $key => $session) {/** @var Document $session */ $countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); @@ -632,7 +651,9 @@ App::delete('/v1/account/sessions') ->inject('locale') ->inject('queueForEvents') ->inject('queueForDeletes') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes) { + ->inject('store') + ->inject('proofForToken') + ->action(function (Request $request, Response $response, User $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes, Store $store, ProofsToken $proofForToken) { $protocol = $request->getProtocol(); $sessions = $user->getAttribute('sessions', []); @@ -648,13 +669,13 @@ App::delete('/v1/account/sessions') ->setAttribute('current', false) ->setAttribute('countryName', $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'))); - if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { + if ($proofForToken->verify($store->getProperty('secret', ''), $session->getAttribute('secret'))) { $session->setAttribute('current', true); // If current session delete the cookies too $response - ->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); + ->addCookie($store->getKey() . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($store->getKey(), '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); // Use current session for events. $queueForEvents @@ -698,12 +719,13 @@ App::get('/v1/account/sessions/:sessionId') ->inject('response') ->inject('user') ->inject('locale') - ->inject('project') - ->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Document $project) { + ->inject('store') + ->inject('proofForToken') + ->action(function (?string $sessionId, Response $response, User $user, Locale $locale, Store $store, ProofsToken $proofForToken) { $sessions = $user->getAttribute('sessions', []); $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) + ? $user->sessionVerify($store->getProperty('secret', ''), $proofForToken) : $sessionId; foreach ($sessions as $session) {/** @var Document $session */ @@ -711,7 +733,7 @@ App::get('/v1/account/sessions/:sessionId') $countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); $session - ->setAttribute('current', ($session->getAttribute('secret') == Auth::hash(Auth::$secret))) + ->setAttribute('current', ($proofForToken->verify($store->getProperty('secret', ''), $session->getAttribute('secret')))) ->setAttribute('countryName', $countryName) ->setAttribute('secret', $session->getAttribute('secret', '')) ; @@ -754,12 +776,13 @@ App::delete('/v1/account/sessions/:sessionId') ->inject('locale') ->inject('queueForEvents') ->inject('queueForDeletes') - ->inject('project') - ->action(function (?string $sessionId, ?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes, Document $project) { + ->inject('store') + ->inject('proofForToken') + ->action(function (?string $sessionId, ?\DateTime $requestTimestamp, Request $request, Response $response, User $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Delete $queueForDeletes, Store $store, ProofsToken $proofForToken) { $protocol = $request->getProtocol(); $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) + ? $user->sessionVerify($store->getProperty('secret', ''), $proofForToken) : $sessionId; $sessions = $user->getAttribute('sessions', []); @@ -776,7 +799,7 @@ App::delete('/v1/account/sessions/:sessionId') $session->setAttribute('current', false); - if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + if ($proofForToken->verify($store->getProperty('secret', ''), $session->getAttribute('secret'))) { // If current session delete the cookies too $session ->setAttribute('current', true) ->setAttribute('countryName', $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'))); @@ -786,8 +809,8 @@ App::delete('/v1/account/sessions/:sessionId') } $response - ->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); + ->addCookie($store->getKey() . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($store->getKey(), '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); } $dbForProject->purgeCachedDocument('users', $user->getId()); @@ -838,10 +861,12 @@ App::patch('/v1/account/sessions/:sessionId') ->inject('dbForProject') ->inject('project') ->inject('queueForEvents') - ->action(function (?string $sessionId, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents) { + ->inject('store') + ->inject('proofForToken') + ->action(function (?string $sessionId, Response $response, User $user, Database $dbForProject, Document $project, Event $queueForEvents, Store $store, ProofsToken $proofForToken) { $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) + ? $user->sessionVerify($store->getProperty('secret', ''), $proofForToken) : $sessionId; $sessions = $user->getAttribute('sessions', []); @@ -858,13 +883,17 @@ App::patch('/v1/account/sessions/:sessionId') } // Extend session - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $authDuration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG; $session->setAttribute('expire', DateTime::addSeconds(new \DateTime(), $authDuration)); // Refresh OAuth access token $provider = $session->getAttribute('provider', ''); $refreshToken = $session->getAttribute('providerRefreshToken', ''); - $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider); + $oAuthProviders = Config::getParam('oAuthProviders'); + $className = $oAuthProviders[$provider]['class']; + if (!\class_exists($className)) { + throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED); + } if (!empty($provider) && \class_exists($className)) { $appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? ''; @@ -930,8 +959,11 @@ App::post('/v1/account/sessions/email') ->inject('queueForEvents') ->inject('queueForMails') ->inject('hooks') + ->inject('store') + ->inject('proofForPassword') + ->inject('proofForToken') ->inject('authorization') - ->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Authorization $authorization) { + ->action(function (string $email, string $password, Request $request, Response $response, User $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) { $email = \strtolower($email); $protocol = $request->getProtocol(); @@ -939,7 +971,9 @@ App::post('/v1/account/sessions/email') Query::equal('email', [$email]), ]); - if ($profile->isEmpty() || empty($profile->getAttribute('passwordUpdate')) || !Auth::passwordVerify($password, $profile->getAttribute('password'), $profile->getAttribute('hash'), $profile->getAttribute('hashOptions'))) { + $userProofForPassword = ProofsPassword::createHash($profile->getAttribute('hash', $proofForPassword->getHash()->getName()), $profile->getAttribute('hashOptions', $proofForPassword->getHash()->getOptions())); + + if ($profile->isEmpty() || empty($profile->getAttribute('passwordUpdate')) || !$userProofForPassword->verify($password, $profile->getAttribute('password'))) { throw new Exception(Exception::USER_INVALID_CREDENTIALS); } @@ -951,18 +985,18 @@ App::post('/v1/account/sessions/email') $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]); - $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG; $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); - $secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION); + $secret = $proofForToken->generate(); $session = new Document(array_merge( [ '$id' => ID::unique(), 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), - 'provider' => Auth::SESSION_PROVIDER_EMAIL, + 'provider' => SESSION_PROVIDER_EMAIL, 'providerUid' => $email, - 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak + 'secret' => $proofForToken->hash($secret), // One way hash encryption to protect DB leak 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), 'factors' => ['password'], @@ -977,11 +1011,12 @@ App::post('/v1/account/sessions/email') $authorization->addRole(Role::user($user->getId())->toString()); // Re-hash if not using recommended algo - if ($user->getAttribute('hash') !== Auth::DEFAULT_ALGO) { + if ($user->getAttribute('hash') !== $proofForPassword->getHash()->getName()) { + $proofForPasswordUpdated = new ProofsPassword(); $user - ->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS)) - ->setAttribute('hash', Auth::DEFAULT_ALGO) - ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS); + ->setAttribute('password', $proofForPasswordUpdated->hash($password)) + ->setAttribute('hash', $proofForPasswordUpdated->getHash()->getName()) + ->setAttribute('hashOptions', $proofForPasswordUpdated->getHash()->getOptions()); $dbForProject->updateDocument('users', $user->getId(), $user); } @@ -993,17 +1028,20 @@ App::post('/v1/account/sessions/email') Permission::delete(Role::user($user->getId())), ])); + $encoded = $store + ->setProperty('id', $user->getId()) + ->setProperty('secret', $secret) + ->encode(); + if (!Config::getParam('domainVerification')) { - $response - ->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])) - ; + $response->addHeader('X-Fallback-Cookies', \json_encode([$store->getKey() => $encoded])); } $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $response - ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie($store->getKey() . '_legacy', $encoded, (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($store->getKey(), $encoded, (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ->setStatusCode(Response::STATUS_CODE_CREATED) ; @@ -1012,7 +1050,7 @@ App::post('/v1/account/sessions/email') $session ->setAttribute('current', true) ->setAttribute('countryName', $countryName) - ->setAttribute('secret', Auth::encodeSession($user->getId(), $secret)) + ->setAttribute('secret', $encoded) ; $queueForEvents @@ -1066,8 +1104,11 @@ App::post('/v1/account/sessions/anonymous') ->inject('dbForProject') ->inject('geodb') ->inject('queueForEvents') + ->inject('store') + ->inject('proofForPassword') + ->inject('proofForToken') ->inject('authorization') - ->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization) { + ->action(function (Request $request, Response $response, Locale $locale, User $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) { $protocol = $request->getProtocol(); if ('console' === $project->getId()) { @@ -1096,8 +1137,8 @@ App::post('/v1/account/sessions/anonymous') 'emailVerification' => false, 'status' => true, 'password' => null, - 'hash' => Auth::DEFAULT_ALGO, - 'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS, + 'hash' => $proofForPassword->getHash()->getName(), + 'hashOptions' => $proofForPassword->getHash()->getOptions(), 'passwordUpdate' => null, 'registration' => DateTime::now(), 'reset' => false, @@ -1115,18 +1156,18 @@ App::post('/v1/account/sessions/anonymous') $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); // Create session token - $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG; $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); - $secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION); + $secret = $proofForToken->generate(); $session = new Document(array_merge( [ '$id' => ID::unique(), 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), - 'provider' => Auth::SESSION_PROVIDER_ANONYMOUS, - 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak + 'provider' => SESSION_PROVIDER_ANONYMOUS, + 'secret' => $proofForToken->hash($secret), // One way hash encryption to protect DB leak 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), 'factors' => ['anonymous'], @@ -1153,15 +1194,20 @@ App::post('/v1/account/sessions/anonymous') ->setParam('sessionId', $session->getId()) ; + $encoded = $store + ->setProperty('id', $user->getId()) + ->setProperty('secret', $secret) + ->encode(); + if (!Config::getParam('domainVerification')) { - $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])); + $response->addHeader('X-Fallback-Cookies', \json_encode([$store->getKey() => $encoded])); } $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $response - ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie($store->getKey() . '_legacy', $encoded, (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($store->getKey(), $encoded, (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ->setStatusCode(Response::STATUS_CODE_CREATED) ; @@ -1170,7 +1216,7 @@ App::post('/v1/account/sessions/anonymous') $session ->setAttribute('current', true) ->setAttribute('countryName', $countryName) - ->setAttribute('secret', Auth::encodeSession($user->getId(), $secret)) + ->setAttribute('secret', $encoded) ; $response->dynamic($session, Response::MODEL_SESSION); @@ -1211,7 +1257,10 @@ App::post('/v1/account/sessions/token') ->inject('geodb') ->inject('queueForEvents') ->inject('queueForMails') - ->inject('authorization') + ->inject('store') + ->inject('proofForToken') + ->inject('proofForCode') +->inject('authorization') ->action($createSession); App::get('/v1/account/sessions/oauth2/:provider') @@ -1404,8 +1453,11 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('dbForProject') ->inject('geodb') ->inject('queueForEvents') + ->inject('store') + ->inject('proofForPassword') + ->inject('proofForToken') ->inject('authorization') - ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, array $platforms, Document $devKey, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Authorization $authorization) use ($oauthDefaultSuccess) { + ->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, array $platforms, Document $devKey, User $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) use ($oauthDefaultSuccess) { $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https'; $port = $request->getPort(); $callbackBase = $protocol . '://' . $request->getHostname(); @@ -1421,8 +1473,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}'; $providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false; - $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider); - + $oAuthProviders = Config::getParam('oAuthProviders'); + $className = $oAuthProviders[$provider]['class']; if (!\class_exists($className)) { throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED); } @@ -1548,8 +1600,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $sessionUpgrade = true; } - $sessions = $user->getAttribute('sessions', []); - $current = Auth::sessionVerify($sessions, Auth::$secret); + $current = $user->sessionVerify($store->getProperty('secret', ''), $proofForToken); if ($current) { // Delete current session of new one. $currentDocument = $dbForProject->getDocument('sessions', $current); @@ -1636,8 +1687,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'emailVerification' => true, 'status' => true, // Email should already be authenticated by OAuth2 provider 'password' => null, - 'hash' => Auth::DEFAULT_ALGO, - 'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS, + 'hash' => $proofForPassword->getHash()->getName(), + 'hashOptions' => $proofForPassword->getHash()->getOptions(), 'passwordUpdate' => null, 'registration' => DateTime::now(), 'reset' => false, @@ -1754,18 +1805,20 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $state['success'] = URLParser::parse($state['success']); $query = URLParser::parseQuery($state['success']['query']); - $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG; $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); + $proofForTokenOAuth2 = new ProofsToken(TOKEN_LENGTH_OAUTH2); + $proofForTokenOAuth2->setHash(new Sha()); // If the `token` param is set, we will return the token in the query string if ($state['token']) { - $secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_OAUTH2); + $secret = $proofForTokenOAuth2->generate(); $token = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), - 'type' => Auth::TOKEN_TYPE_OAUTH2, - 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak + 'type' => TOKEN_TYPE_OAUTH2, + 'secret' => $proofForTokenOAuth2->hash($secret), // One way hash encryption to protect DB leak 'expire' => $expire, 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), @@ -1793,7 +1846,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') } else { $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); - $secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION); + $secret = $proofForToken->generate(); $session = new Document(array_merge([ '$id' => ID::unique(), @@ -1803,8 +1856,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'providerUid' => $oauth2ID, 'providerAccessToken' => $accessToken, 'providerRefreshToken' => $refreshToken, - 'providerAccessTokenExpiry' => DateTime::addSeconds(new \DateTime(), (int) $accessTokenExpiry), - 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak + 'providerAccessTokenExpiry' => DateTime::addSeconds(new \DateTime(), (int)$accessTokenExpiry), + 'secret' => $proofForToken->hash($secret), // One way hash encryption to protect DB leak 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), 'factors' => [TYPE::EMAIL, 'oauth2'], // include a special oauth2 factor to bypass MFA checks @@ -1820,8 +1873,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $session->setAttribute('expire', $expire); + $encoded = $store + ->setProperty('id', $user->getId()) + ->setProperty('secret', $secret) + ->encode(); + if (!Config::getParam('domainVerification')) { - $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])); + $response->addHeader('X-Fallback-Cookies', \json_encode([$store->getKey() => $encoded])); } $queueForEvents @@ -1834,13 +1892,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') if ($state['success']['path'] == $oauthDefaultSuccess) { $query['project'] = $project->getId(); $query['domain'] = Config::getParam('cookieDomain'); - $query['key'] = Auth::$cookieName; - $query['secret'] = Auth::encodeSession($user->getId(), $secret); + $query['key'] = $store->getKey(); + $query['secret'] = $encoded; } $response - ->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); + ->addCookie($store->getKey() . '_legacy', $encoded, (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($store->getKey(), $encoded, (new \DateTime($expire))->getTimestamp(), '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')); } if (isset($sessionUpgrade) && $sessionUpgrade) { @@ -1927,8 +1985,8 @@ App::get('/v1/account/tokens/oauth2/:provider') throw new Exception(Exception::PROJECT_PROVIDER_DISABLED, 'This provider is disabled. Please configure the provider app ID and app secret key from your ' . APP_NAME . ' console to continue.'); } - $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider); - + $oAuthProviders = Config::getParam('oAuthProviders'); + $className = $oAuthProviders[$provider]['class']; if (!\class_exists($className)) { throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED); } @@ -1998,8 +2056,9 @@ App::post('/v1/account/tokens/magic-url') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') + ->inject('proofForPassword') ->inject('authorization') - ->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { + ->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword, Authorization $authorization) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); } @@ -2051,8 +2110,8 @@ App::post('/v1/account/tokens/magic-url') 'emailVerification' => false, 'status' => true, 'password' => null, - 'hash' => Auth::DEFAULT_ALGO, - 'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS, + 'hash' => $proofForPassword->getHash()->getName(), + 'hashOptions' => $proofForPassword->getHash()->getOptions(), 'passwordUpdate' => null, 'registration' => DateTime::now(), 'reset' => false, @@ -2075,15 +2134,18 @@ App::post('/v1/account/tokens/magic-url') $user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user)); } - $tokenSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_MAGIC_URL); - $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM)); + $proofForToken = new ProofsToken(TOKEN_LENGTH_MAGIC_URL); + $proofForToken->setHash(new Sha()); + + $tokenSecret = $proofForToken->generate(); + $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_CONFIRM)); $token = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), - 'type' => Auth::TOKEN_TYPE_MAGIC_URL, - 'secret' => Auth::hash($tokenSecret), // One way hash encryption to protect DB leak + 'type' => TOKEN_TYPE_MAGIC_URL, + 'secret' => $proofForToken->hash($tokenSecret), // One way hash encryption to protect DB leak 'expire' => $expire, 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), @@ -2262,8 +2324,10 @@ App::post('/v1/account/tokens/email') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') + ->inject('proofForPassword') + ->inject('proofForCode') ->inject('authorization') - ->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { + ->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, User $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword, ProofsCode $proofForCode, Authorization $authorization) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); } @@ -2313,8 +2377,8 @@ App::post('/v1/account/tokens/email') 'emailVerification' => false, 'status' => true, 'password' => null, - 'hash' => Auth::DEFAULT_ALGO, - 'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS, + 'hash' => $proofForPassword->getHash()->getName(), + 'hashOptions' => $proofForPassword->getHash()->getOptions(), 'passwordUpdate' => null, 'registration' => DateTime::now(), 'reset' => false, @@ -2358,15 +2422,15 @@ App::post('/v1/account/tokens/email') $dbForProject->purgeCachedDocument('users', $user->getId()); } - $tokenSecret = Auth::codeGenerator(6); - $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_OTP)); + $tokenSecret = $proofForCode->generate(); + $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_OTP)); $token = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), - 'type' => Auth::TOKEN_TYPE_EMAIL, - 'secret' => Auth::hash($tokenSecret), // One way hash encryption to protect DB leak + 'type' => TOKEN_TYPE_EMAIL, + 'secret' => $proofForCode->hash($tokenSecret), // One way hash encryption to protect DB leak 'expire' => $expire, 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), @@ -2553,8 +2617,14 @@ App::put('/v1/account/sessions/magic-url') ->inject('geodb') ->inject('queueForEvents') ->inject('queueForMails') + ->inject('store') + ->inject('proofForCode') ->inject('authorization') - ->action($createSession); + ->action(function ($userId, $secret, $request, $response, $user, $dbForProject, $project, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForCode, $authorization) use ($createSession) { + $proofForToken = new ProofsToken(TOKEN_LENGTH_MAGIC_URL); + $proofForToken->setHash(new Sha()); + $createSession($userId, $secret, $request, $response, $user, $dbForProject, $project, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForToken, $proofForCode, $authorization); + }); App::put('/v1/account/sessions/phone') ->desc('Update phone session') @@ -2595,6 +2665,9 @@ App::put('/v1/account/sessions/phone') ->inject('geodb') ->inject('queueForEvents') ->inject('queueForMails') + ->inject('store') + ->inject('proofForToken') + ->inject('proofForCode') ->inject('authorization') ->action($createSession); @@ -2636,8 +2709,10 @@ App::post('/v1/account/tokens/phone') ->inject('timelimit') ->inject('queueForStatsUsage') ->inject('plan') + ->inject('store') + ->inject('proofForCode') ->inject('authorization') - ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Authorization $authorization) { + ->action(function (string $userId, string $phone, Request $request, Response $response, User $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Store $store, ProofsCode $proofForCode, Authorization $authorization) { if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -2721,15 +2796,15 @@ App::post('/v1/account/tokens/phone') } } - $secret ??= Auth::codeGenerator(); - $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_OTP)); + $secret ??= $proofForCode->generate(); + $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_OTP)); $token = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), - 'type' => Auth::TOKEN_TYPE_PHONE, - 'secret' => Auth::hash($secret), + 'type' => TOKEN_TYPE_PHONE, + 'secret' => $proofForCode->hash($secret), 'expire' => $expire, 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), @@ -2804,7 +2879,11 @@ App::post('/v1/account/tokens/phone') ->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']); // Encode secret for clients - $token->setAttribute('secret', Auth::encodeSession($user->getId(), $secret)); + $encoded = $store + ->setProperty('id', $user->getId()) + ->setProperty('secret', $secret) + ->encode(); + $token->setAttribute('secret', $encoded); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -2835,20 +2914,12 @@ App::post('/v1/account/jwts') ->label('abuse-key', 'url:{url},userId:{userId}') ->inject('response') ->inject('user') - ->inject('dbForProject') - ->action(function (Response $response, Document $user, Database $dbForProject) { + ->inject('store') + ->inject('proofForToken') + ->action(function (Response $response, User $user, Store $store, ProofsToken $proofForToken) { + $sessionId = $user->sessionVerify($store->getProperty('secret', ''), $proofForToken); - - $sessions = $user->getAttribute('sessions', []); - $current = new Document(); - - foreach ($sessions as $session) { /** @var Utopia\Database\Document $session */ - if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too - $current = $session; - } - } - - if ($current->isEmpty()) { + if (!$sessionId) { throw new Exception(Exception::USER_SESSION_NOT_FOUND); } @@ -2859,7 +2930,7 @@ App::post('/v1/account/jwts') ->dynamic(new Document([ 'jwt' => $jwt->encode([ 'userId' => $user->getId(), - 'sessionId' => $current->getId(), + 'sessionId' => $sessionId, ]) ]), Response::MODEL_JWT); }); @@ -2988,12 +3059,11 @@ App::patch('/v1/account/name') contentType: ContentType::JSON )) ->param('name', '', new Text(128), 'User name. Max length: 128 chars.') - ->inject('requestTimestamp') ->inject('response') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $name, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->action(function (string $name, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { $user->setAttribute('name', $name); @@ -3029,25 +3099,29 @@ App::patch('/v1/account/password') ->label('abuse-limit', 10) ->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary']) ->param('oldPassword', '', new Password(), 'Current user password. Must be at least 8 chars.', true) - ->inject('requestTimestamp') ->inject('response') ->inject('user') ->inject('project') ->inject('dbForProject') ->inject('queueForEvents') ->inject('hooks') - ->action(function (string $password, string $oldPassword, ?\DateTime $requestTimestamp, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks) { - + ->inject('store') + ->inject('proofForPassword') + ->inject('proofForToken') + ->action(function (string $password, string $oldPassword, Response $response, User $user, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken) { + $userProofForPassword = ProofsPassword::createHash($user->getAttribute('hash'), $user->getAttribute('hashOptions')); // Check old password only if its an existing user. - if (!empty($user->getAttribute('passwordUpdate')) && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions'))) { // Double check user password + if (!empty($user->getAttribute('passwordUpdate')) && !$userProofForPassword->verify($oldPassword, $user->getAttribute('password'))) { // Double check user password throw new Exception(Exception::USER_INVALID_CREDENTIALS); } - $newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS); + $newPassword = $proofForPassword->hash($password); $historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0; + $hash = ProofsPassword::createHash($user->getAttribute('hash'), $user->getAttribute('hashOptions')); $history = $user->getAttribute('passwordHistory', []); + if ($historyLimit > 0) { - $validator = new PasswordHistory($history, $user->getAttribute('hash'), $user->getAttribute('hashOptions')); + $validator = new PasswordHistory($history, $hash); if (!$validator->isValid($password)) { throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED); } @@ -3069,11 +3143,13 @@ App::patch('/v1/account/password') ->setAttribute('password', $newPassword) ->setAttribute('passwordHistory', $history) ->setAttribute('passwordUpdate', DateTime::now()) - ->setAttribute('hash', Auth::DEFAULT_ALGO) - ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS); + ->setAttribute('hash', $proofForPassword->getHash()->getName()) + ->setAttribute('hashOptions', $proofForPassword->getHash()->getOptions()); $sessions = $user->getAttribute('sessions', []); - $current = Auth::sessionVerify($sessions, Auth::$secret); + + $current = $user->sessionVerify($store->getProperty('secret', ''), $proofForToken); + $invalidate = $project->getAttribute('auths', default: [])['invalidateSessions'] ?? false; if ($invalidate && !empty($current)) { foreach ($sessions as $session) { @@ -3121,14 +3197,17 @@ App::patch('/v1/account/email') ->inject('queueForEvents') ->inject('project') ->inject('hooks') - ->inject('authorization') - ->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $authorization) { + ->inject('proofForPassword') + ->inject('authorization') + ->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, ProofsPassword $proofForPassword, Authorization $authorization) { // passwordUpdate will be empty if the user has never set a password $passwordUpdate = $user->getAttribute('passwordUpdate'); + $userProofForPassword = ProofsPassword::createHash($user->getAttribute('hash'), $user->getAttribute('hashOptions')); + if ( !empty($passwordUpdate) && - !Auth::passwordVerify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions')) + !$userProofForPassword->verify($password, $user->getAttribute('password')) ) { // Double check user password throw new Exception(Exception::USER_INVALID_CREDENTIALS); } @@ -3166,9 +3245,9 @@ App::patch('/v1/account/email') if (empty($passwordUpdate)) { $user - ->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS)) - ->setAttribute('hash', Auth::DEFAULT_ALGO) - ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS) + ->setAttribute('password', $proofForPassword->hash($password)) + ->setAttribute('hash', $proofForPassword->getHash()->getName()) + ->setAttribute('hashOptions', $proofForPassword->getHash()->getOptions()) ->setAttribute('passwordUpdate', DateTime::now()); } @@ -3223,21 +3302,23 @@ App::patch('/v1/account/phone') )) ->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') - ->inject('requestTimestamp') ->inject('response') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') ->inject('project') ->inject('hooks') - ->inject('authorization') - ->action(function (string $phone, string $password, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, Authorization $authorization) { + ->inject('proofForPassword') +->inject('authorization') + ->action(function (string $phone, string $password, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, ProofsPassword $proofForPassword, Authorization $authorization) { // passwordUpdate will be empty if the user has never set a password $passwordUpdate = $user->getAttribute('passwordUpdate'); + $userProofForPassword = ProofsPassword::createHash($user->getAttribute('hash'), $user->getAttribute('hashOptions')); + if ( !empty($passwordUpdate) && - !Auth::passwordVerify($password, $user->getAttribute('password'), $user->getAttribute('hash'), $user->getAttribute('hashOptions')) + !$userProofForPassword->verify($password, $user->getAttribute('password')) ) { // Double check user password throw new Exception(Exception::USER_INVALID_CREDENTIALS); } @@ -3261,9 +3342,9 @@ App::patch('/v1/account/phone') if (empty($passwordUpdate)) { $user - ->setAttribute('password', Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS)) - ->setAttribute('hash', Auth::DEFAULT_ALGO) - ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS) + ->setAttribute('password', $proofForPassword->hash($password)) + ->setAttribute('hash', $proofForPassword->getHash()->getName()) + ->setAttribute('hashOptions', $proofForPassword->getHash()->getOptions()) ->setAttribute('passwordUpdate', DateTime::now()); } @@ -3346,13 +3427,13 @@ App::patch('/v1/account/status') ], contentType: ContentType::JSON, )) - ->inject('requestTimestamp') ->inject('request') ->inject('response') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (?\DateTime $requestTimestamp, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { + ->inject('store') + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Store $store) { $user->setAttribute('status', false); @@ -3368,8 +3449,8 @@ App::patch('/v1/account/status') $protocol = $request->getProtocol(); $response - ->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) - ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) + ->addCookie($store->getKey() . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) + ->addCookie($store->getKey(), '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ; $response->dynamic($user, Response::MODEL_ACCOUNT); @@ -3409,8 +3490,9 @@ App::post('/v1/account/recovery') ->inject('locale') ->inject('queueForMails') ->inject('queueForEvents') + ->inject('proofForToken') ->inject('authorization') - ->action(function (string $email, string $url, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents, Authorization $authorization) { + ->action(function (string $email, string $url, Request $request, Response $response, User $user, Database $dbForProject, Document $project, Locale $locale, Mail $queueForMails, Event $queueForEvents, ProofsToken $proofForToken, Authorization $authorization) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -3432,15 +3514,15 @@ App::post('/v1/account/recovery') throw new Exception(Exception::USER_BLOCKED); } - $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_RECOVERY)); + $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_RECOVERY)); - $secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_RECOVERY); + $secret = $proofForToken->generate(); $recovery = new Document([ '$id' => ID::unique(), 'userId' => $profile->getId(), 'userInternalId' => $profile->getSequence(), - 'type' => Auth::TOKEN_TYPE_RECOVERY, - 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak + 'type' => TOKEN_TYPE_RECOVERY, + 'secret' => $proofForToken->hash($secret), // One way hash encryption to protect DB leak 'expire' => $expire, 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), @@ -3588,16 +3670,18 @@ App::put('/v1/account/recovery') ->inject('project') ->inject('queueForEvents') ->inject('hooks') - ->inject('authorization') - ->action(function (string $userId, string $secret, string $password, Response $response, Document $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, Authorization $authorization) { + ->inject('proofForPassword') + ->inject('proofForToken') +->inject('authorization') + ->action(function (string $userId, string $secret, string $password, Response $response, User $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) { + /** @var Appwrite\Utopia\Database\Documents\User $profile */ $profile = $dbForProject->getDocument('users', $userId); if ($profile->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } - $tokens = $profile->getAttribute('tokens', []); - $verifiedToken = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_RECOVERY, $secret); + $verifiedToken = $profile->tokenVerify(TOKEN_TYPE_RECOVERY, $secret, $proofForToken); if (!$verifiedToken) { throw new Exception(Exception::USER_INVALID_TOKEN); @@ -3605,12 +3689,14 @@ App::put('/v1/account/recovery') $authorization->addRole(Role::user($profile->getId())->toString()); - $newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS); + $newPassword = $proofForPassword->hash($password); + $hash = ProofsPassword::createHash($profile->getAttribute('hash'), $profile->getAttribute('hashOptions')); $historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0; $history = $profile->getAttribute('passwordHistory', []); + if ($historyLimit > 0) { - $validator = new PasswordHistory($history, $profile->getAttribute('hash'), $profile->getAttribute('hashOptions')); + $validator = new PasswordHistory($history, $hash); if (!$validator->isValid($password)) { throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED); } @@ -3622,12 +3708,12 @@ App::put('/v1/account/recovery') $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, true]); $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile - ->setAttribute('password', $newPassword) - ->setAttribute('passwordHistory', $history) - ->setAttribute('passwordUpdate', DateTime::now()) - ->setAttribute('hash', Auth::DEFAULT_ALGO) - ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS) - ->setAttribute('emailVerification', true)); + ->setAttribute('password', $newPassword) + ->setAttribute('passwordHistory', $history) + ->setAttribute('passwordUpdate', DateTime::now()) + ->setAttribute('hash', $proofForPassword->getHash()->getName()) + ->setAttribute('hashOptions', $proofForPassword->getHash()->getOptions()) + ->setAttribute('emailVerification', true)); $user->setAttributes($profile->getArrayCopy()); @@ -3701,8 +3787,9 @@ App::post('/v1/account/verifications/email') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') + ->inject('proofForToken') ->inject('authorization') - ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, Authorization $authorization) { + ->action(function (string $url, Request $request, Response $response, Document $project, User $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsToken $proofForToken, Authorization $authorization) { if (empty(System::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); @@ -3717,15 +3804,15 @@ App::post('/v1/account/verifications/email') throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED); } - $verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION); - $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM)); + $verificationSecret = $proofForToken->generate(); + $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_CONFIRM)); $verification = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), - 'type' => Auth::TOKEN_TYPE_VERIFICATION, - 'secret' => Auth::hash($verificationSecret), // One way hash encryption to protect DB leak + 'type' => TOKEN_TYPE_VERIFICATION, + 'secret' => $proofForToken->hash($verificationSecret), // One way hash encryption to protect DB leak 'expire' => $expire, 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), @@ -3914,17 +4001,17 @@ App::put('/v1/account/verifications/email') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') + ->inject('proofForToken') ->inject('authorization') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { - + ->action(function (string $userId, string $secret, Response $response, User $user, Database $dbForProject, Event $queueForEvents, ProofsToken $proofForToken, Authorization $authorization) { + /** @var Appwrite\Utopia\Database\Documents\User $profile */ $profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } - $tokens = $profile->getAttribute('tokens', []); - $verifiedToken = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_VERIFICATION, $secret); + $verifiedToken = $profile->tokenVerify(TOKEN_TYPE_VERIFICATION, $secret, $proofForToken); if (!$verifiedToken) { throw new Exception(Exception::USER_INVALID_TOKEN); @@ -3989,8 +4076,9 @@ App::post('/v1/account/verifications/phone') ->inject('timelimit') ->inject('queueForStatsUsage') ->inject('plan') - ->inject('authorization') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Authorization $authorization) { + ->inject('proofForCode') + ->inject('authorization') + ->action(function (Request $request, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, ProofsCode $proofForCode, Authorization $authorization) { if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -4015,15 +4103,15 @@ App::post('/v1/account/verifications/phone') } } - $secret ??= Auth::codeGenerator(); - $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM)); + $secret ??= $proofForCode->generate(); + $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_CONFIRM)); $verification = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), - 'type' => Auth::TOKEN_TYPE_PHONE, - 'secret' => Auth::hash($secret), + 'type' => TOKEN_TYPE_PHONE, + 'secret' => $proofForCode->hash($secret), 'expire' => $expire, 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), @@ -4134,16 +4222,17 @@ App::put('/v1/account/verifications/phone') ->inject('user') ->inject('dbForProject') ->inject('queueForEvents') + ->inject('proofForCode') ->inject('authorization') - ->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Authorization $authorization) { - + ->action(function (string $userId, string $secret, Response $response, User $user, Database $dbForProject, Event $queueForEvents, ProofsCode $proofForCode, Authorization $authorization) { + /** @var Appwrite\Utopia\Database\Documents\User $profile */ $profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } - $verifiedToken = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_PHONE, $secret); + $verifiedToken = $profile->tokenVerify(TOKEN_TYPE_PHONE, $secret, $proofForCode); if (!$verifiedToken) { throw new Exception(Exception::USER_INVALID_TOKEN); @@ -4171,943 +4260,6 @@ App::put('/v1/account/verifications/phone') $response->dynamic($verificationDocument, Response::MODEL_TOKEN); }); -App::patch('/v1/account/mfa') - ->desc('Update MFA') - ->groups(['api', 'account']) - ->label('event', 'users.[userId].update.mfa') - ->label('scope', 'account') - ->label('audits.event', 'user.update') - ->label('audits.resource', 'user/{response.$id}') - ->label('audits.userId', '{response.$id}') - ->label('sdk', new Method( - namespace: 'account', - group: 'mfa', - name: 'updateMFA', - description: '/docs/references/account/update-mfa.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_USER, - ) - ], - contentType: ContentType::JSON - )) - ->param('mfa', null, new Boolean(), 'Enable or disable MFA.') - ->inject('requestTimestamp') - ->inject('response') - ->inject('user') - ->inject('session') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (bool $mfa, ?\DateTime $requestTimestamp, Response $response, Document $user, Document $session, Database $dbForProject, Event $queueForEvents) { - - $user->setAttribute('mfa', $mfa); - - $user = $dbForProject->updateDocument('users', $user->getId(), $user); - - if ($mfa) { - $factors = $session->getAttribute('factors', []); - $totp = TOTP::getAuthenticatorFromUser($user); - if ($totp !== null && $totp->getAttribute('verified', false)) { - $factors[] = Type::TOTP; - } - if ($user->getAttribute('email', false) && $user->getAttribute('emailVerification', false)) { - $factors[] = Type::EMAIL; - } - if ($user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false)) { - $factors[] = Type::PHONE; - } - $factors = \array_values(\array_unique($factors)); - - $session->setAttribute('factors', $factors); - $dbForProject->updateDocument('sessions', $session->getId(), $session); - } - - $queueForEvents->setParam('userId', $user->getId()); - - $response->dynamic($user, Response::MODEL_ACCOUNT); - }); - -App::get('/v1/account/mfa/factors') - ->desc('List factors') - ->groups(['api', 'account', 'mfa']) - ->label('scope', 'account') - ->label('sdk', [ - new Method( - namespace: 'account', - group: 'mfa', - name: 'listMfaFactors', - description: '/docs/references/account/list-mfa-factors.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_MFA_FACTORS, - ) - ], - contentType: ContentType::JSON, - deprecated: new Deprecated( - since: '1.8.0', - replaceWith: 'account.listMFAFactors', - ), - ), - new Method( - namespace: 'account', - group: 'mfa', - name: 'listMFAFactors', - description: '/docs/references/account/list-mfa-factors.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_MFA_FACTORS, - ) - ], - contentType: ContentType::JSON - ) - ]) - ->inject('response') - ->inject('user') - ->action(function (Response $response, Document $user) { - - $mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []); - $recoveryCodeEnabled = \is_array($mfaRecoveryCodes) && \count($mfaRecoveryCodes) > 0; - - $totp = TOTP::getAuthenticatorFromUser($user); - - $factors = new Document([ - Type::TOTP => $totp !== null && $totp->getAttribute('verified', false), - Type::EMAIL => $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false), - Type::PHONE => $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false), - Type::RECOVERY_CODE => $recoveryCodeEnabled - ]); - - $response->dynamic($factors, Response::MODEL_MFA_FACTORS); - }); - -App::post('/v1/account/mfa/authenticators/:type') - ->desc('Create authenticator') - ->groups(['api', 'account']) - ->label('event', 'users.[userId].update.mfa') - ->label('scope', 'account') - ->label('audits.event', 'user.update') - ->label('audits.resource', 'user/{response.$id}') - ->label('audits.userId', '{response.$id}') - ->label('sdk', [ - new Method( - namespace: 'account', - group: 'mfa', - name: 'createMfaAuthenticator', - description: '/docs/references/account/create-mfa-authenticator.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_MFA_TYPE, - ) - ], - contentType: ContentType::JSON, - deprecated: new Deprecated( - since: '1.8.0', - replaceWith: 'account.createMFAAuthenticator', - ), - ), - new Method( - namespace: 'account', - group: 'mfa', - name: 'createMFAAuthenticator', - description: '/docs/references/account/create-mfa-authenticator.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_MFA_TYPE, - ) - ], - contentType: ContentType::JSON - ) - ]) - ->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator. Must be `' . Type::TOTP . '`') - ->inject('requestTimestamp') - ->inject('response') - ->inject('project') - ->inject('user') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $type, ?\DateTime $requestTimestamp, Response $response, Document $project, Document $user, Database $dbForProject, Event $queueForEvents) { - - $otp = (match ($type) { - Type::TOTP => new TOTP(), - default => throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Unknown type.') // Ideally never happens if param validator stays always in sync - }); - - $otp->setLabel($user->getAttribute('email')); - $otp->setIssuer($project->getAttribute('name')); - - $authenticator = TOTP::getAuthenticatorFromUser($user); - - if ($authenticator) { - if ($authenticator->getAttribute('verified')) { - throw new Exception(Exception::USER_AUTHENTICATOR_ALREADY_VERIFIED); - } - $dbForProject->deleteDocument('authenticators', $authenticator->getId()); - } - - $authenticator = new Document([ - '$id' => ID::unique(), - 'userId' => $user->getId(), - 'userInternalId' => $user->getSequence(), - 'type' => Type::TOTP, - 'verified' => false, - 'data' => [ - 'secret' => $otp->getSecret(), - ], - '$permissions' => [ - Permission::read(Role::user($user->getId())), - Permission::update(Role::user($user->getId())), - Permission::delete(Role::user($user->getId())), - ] - ]); - - $model = new Document([ - 'secret' => $otp->getSecret(), - 'uri' => $otp->getProvisioningUri() - ]); - - $authenticator = $dbForProject->createDocument('authenticators', $authenticator); - $dbForProject->purgeCachedDocument('users', $user->getId()); - - $queueForEvents->setParam('userId', $user->getId()); - - $response->dynamic($model, Response::MODEL_MFA_TYPE); - }); - -App::put('/v1/account/mfa/authenticators/:type') - ->desc('Update authenticator (confirmation)') - ->groups(['api', 'account']) - ->label('event', 'users.[userId].update.mfa') - ->label('scope', 'account') - ->label('audits.event', 'user.update') - ->label('audits.resource', 'user/{response.$id}') - ->label('audits.userId', '{response.$id}') - ->label('sdk', [ - new Method( - namespace: 'account', - group: 'mfa', - name: 'updateMfaAuthenticator', - description: '/docs/references/account/update-mfa-authenticator.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_USER, - ) - ], - contentType: ContentType::JSON, - deprecated: new Deprecated( - since: '1.8.0', - replaceWith: 'account.updateMFAAuthenticator', - ), - ), - new Method( - namespace: 'account', - group: 'mfa', - name: 'updateMFAAuthenticator', - description: '/docs/references/account/update-mfa-authenticator.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_USER, - ) - ], - contentType: ContentType::JSON - ) - ]) - ->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator.') - ->param('otp', '', new Text(256), 'Valid verification token.') - ->inject('response') - ->inject('user') - ->inject('session') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $type, string $otp, Response $response, Document $user, Document $session, Database $dbForProject, Event $queueForEvents) { - - $authenticator = (match ($type) { - Type::TOTP => TOTP::getAuthenticatorFromUser($user), - default => null - }); - - if ($authenticator === null) { - throw new Exception(Exception::USER_AUTHENTICATOR_NOT_FOUND); - } - - if ($authenticator->getAttribute('verified')) { - throw new Exception(Exception::USER_AUTHENTICATOR_ALREADY_VERIFIED); - } - - $success = (match ($type) { - Type::TOTP => Challenge\TOTP::verify($user, $otp), - default => false - }); - - if (!$success) { - throw new Exception(Exception::USER_INVALID_TOKEN); - } - - $authenticator->setAttribute('verified', true); - - $dbForProject->updateDocument('authenticators', $authenticator->getId(), $authenticator); - $dbForProject->purgeCachedDocument('users', $user->getId()); - - $factors = $session->getAttribute('factors', []); - $factors[] = $type; - $factors = \array_values(\array_unique($factors)); - - $session->setAttribute('factors', $factors); - $dbForProject->updateDocument('sessions', $session->getId(), $session); - - $queueForEvents->setParam('userId', $user->getId()); - - $response->dynamic($user, Response::MODEL_ACCOUNT); - }); - -App::post('/v1/account/mfa/recovery-codes') - ->desc('Create MFA recovery codes') - ->groups(['api', 'account']) - ->label('event', 'users.[userId].update.mfa') - ->label('scope', 'account') - ->label('audits.event', 'user.update') - ->label('audits.resource', 'user/{response.$id}') - ->label('audits.userId', '{response.$id}') - ->label('sdk', [ - new Method( - namespace: 'account', - group: 'mfa', - name: 'createMfaRecoveryCodes', - description: '/docs/references/account/create-mfa-recovery-codes.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_CREATED, - model: Response::MODEL_MFA_RECOVERY_CODES, - ) - ], - contentType: ContentType::JSON, - deprecated: new Deprecated( - since: '1.8.0', - replaceWith: 'account.createMFARecoveryCodes', - ), - ), - new Method( - namespace: 'account', - group: 'mfa', - name: 'createMFARecoveryCodes', - description: '/docs/references/account/create-mfa-recovery-codes.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_CREATED, - model: Response::MODEL_MFA_RECOVERY_CODES, - ) - ], - contentType: ContentType::JSON - ) - ]) - ->inject('response') - ->inject('user') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { - - $mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []); - - if (!empty($mfaRecoveryCodes)) { - throw new Exception(Exception::USER_RECOVERY_CODES_ALREADY_EXISTS); - } - - $mfaRecoveryCodes = Type::generateBackupCodes(); - $user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes); - $dbForProject->updateDocument('users', $user->getId(), $user); - - $queueForEvents->setParam('userId', $user->getId()); - - $document = new Document([ - 'recoveryCodes' => $mfaRecoveryCodes - ]); - - $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); - }); - -App::patch('/v1/account/mfa/recovery-codes') - ->desc('Update MFA recovery codes (regenerate)') - ->groups(['api', 'account', 'mfaProtected']) - ->label('event', 'users.[userId].update.mfa') - ->label('scope', 'account') - ->label('audits.event', 'user.update') - ->label('audits.resource', 'user/{response.$id}') - ->label('audits.userId', '{response.$id}') - ->label('sdk', [ - new Method( - namespace: 'account', - group: 'mfa', - name: 'updateMfaRecoveryCodes', - description: '/docs/references/account/update-mfa-recovery-codes.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_MFA_RECOVERY_CODES, - ) - ], - contentType: ContentType::JSON, - deprecated: new Deprecated( - since: '1.8.0', - replaceWith: 'account.updateMFARecoveryCodes', - ), - ), - new Method( - namespace: 'account', - group: 'mfa', - name: 'updateMFARecoveryCodes', - description: '/docs/references/account/update-mfa-recovery-codes.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_MFA_RECOVERY_CODES, - ) - ], - contentType: ContentType::JSON - ) - ]) - ->inject('dbForProject') - ->inject('response') - ->inject('user') - ->inject('queueForEvents') - ->action(function (Database $dbForProject, Response $response, Document $user, Event $queueForEvents) { - - $mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []); - if (empty($mfaRecoveryCodes)) { - throw new Exception(Exception::USER_RECOVERY_CODES_NOT_FOUND); - } - - $mfaRecoveryCodes = Type::generateBackupCodes(); - $user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes); - $dbForProject->updateDocument('users', $user->getId(), $user); - - $queueForEvents->setParam('userId', $user->getId()); - - $document = new Document([ - 'recoveryCodes' => $mfaRecoveryCodes - ]); - - $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); - }); - -App::get('/v1/account/mfa/recovery-codes') - ->desc('List MFA recovery codes') - ->groups(['api', 'account', 'mfaProtected']) - ->label('scope', 'account') - ->label('sdk', [ - new Method( - namespace: 'account', - group: 'mfa', - name: 'getMfaRecoveryCodes', - description: '/docs/references/account/get-mfa-recovery-codes.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_MFA_RECOVERY_CODES, - ) - ], - contentType: ContentType::JSON, - deprecated: new Deprecated( - since: '1.8.0', - replaceWith: 'account.getMFARecoveryCodes', - ), - ), - new Method( - namespace: 'account', - group: 'mfa', - name: 'getMFARecoveryCodes', - description: '/docs/references/account/get-mfa-recovery-codes.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_MFA_RECOVERY_CODES, - ) - ], - contentType: ContentType::JSON - ) - ]) - ->inject('response') - ->inject('user') - ->action(function (Response $response, Document $user) { - - $mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []); - - if (empty($mfaRecoveryCodes)) { - throw new Exception(Exception::USER_RECOVERY_CODES_NOT_FOUND); - } - - $document = new Document([ - 'recoveryCodes' => $mfaRecoveryCodes - ]); - - $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); - }); - -App::delete('/v1/account/mfa/authenticators/:type') - ->desc('Delete authenticator') - ->groups(['api', 'account', 'mfaProtected']) - ->label('event', 'users.[userId].delete.mfa') - ->label('scope', 'account') - ->label('audits.event', 'user.update') - ->label('audits.resource', 'user/{response.$id}') - ->label('audits.userId', '{response.$id}') - ->label('sdk', [ - new Method( - namespace: 'account', - group: 'mfa', - name: 'deleteMfaAuthenticator', - description: '/docs/references/account/delete-mfa-authenticator.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_NOCONTENT, - model: Response::MODEL_NONE, - ) - ], - contentType: ContentType::NONE, - deprecated: new Deprecated( - since: '1.8.0', - replaceWith: 'account.deleteMFAAuthenticator', - ), - ), - new Method( - namespace: 'account', - group: 'mfa', - name: 'deleteMFAAuthenticator', - description: '/docs/references/account/delete-mfa-authenticator.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_NOCONTENT, - model: Response::MODEL_NONE, - ) - ], - contentType: ContentType::NONE - ) - ]) - ->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator.') - ->inject('response') - ->inject('user') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $type, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) { - - $authenticator = (match ($type) { - Type::TOTP => TOTP::getAuthenticatorFromUser($user), - default => null - }); - - if (!$authenticator) { - throw new Exception(Exception::USER_AUTHENTICATOR_NOT_FOUND); - } - - $dbForProject->deleteDocument('authenticators', $authenticator->getId()); - $dbForProject->purgeCachedDocument('users', $user->getId()); - - $queueForEvents->setParam('userId', $user->getId()); - - $response->noContent(); - }); - -App::post('/v1/account/mfa/challenge') - ->desc('Create MFA challenge') - ->groups(['api', 'account', 'mfa']) - ->label('scope', 'account') - ->label('event', 'users.[userId].challenges.[challengeId].create') - ->label('audits.event', 'challenge.create') - ->label('audits.resource', 'user/{response.userId}') - ->label('audits.userId', '{response.userId}') - ->label('sdk', [ - new Method( - namespace: 'account', - group: 'mfa', - name: 'createMfaChallenge', - description: '/docs/references/account/create-mfa-challenge.md', - auth: [], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_CREATED, - model: Response::MODEL_MFA_CHALLENGE, - ) - ], - contentType: ContentType::JSON, - deprecated: new Deprecated( - since: '1.8.0', - replaceWith: 'account.createMFAChallenge', - ), - ), - new Method( - namespace: 'account', - group: 'mfa', - name: 'createMFAChallenge', - description: '/docs/references/account/create-mfa-challenge.md', - auth: [], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_CREATED, - model: Response::MODEL_MFA_CHALLENGE, - ) - ], - contentType: ContentType::JSON - ) - ]) - ->label('abuse-limit', 10) - ->label('abuse-key', 'url:{url},userId:{userId}') - ->param('factor', '', new WhiteList([Type::EMAIL, Type::PHONE, Type::TOTP, Type::RECOVERY_CODE]), 'Factor used for verification. Must be one of following: `' . Type::EMAIL . '`, `' . Type::PHONE . '`, `' . Type::TOTP . '`, `' . Type::RECOVERY_CODE . '`.') - ->inject('response') - ->inject('dbForProject') - ->inject('user') - ->inject('locale') - ->inject('project') - ->inject('request') - ->inject('queueForEvents') - ->inject('queueForMessaging') - ->inject('queueForMails') - ->inject('timelimit') - ->inject('queueForStatsUsage') - ->inject('plan') - ->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Locale $locale, Document $project, Request $request, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) { - - $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM)); - $code = Auth::codeGenerator(); - $challenge = new Document([ - 'userId' => $user->getId(), - 'userInternalId' => $user->getSequence(), - 'type' => $factor, - 'token' => Auth::tokenGenerator(), - 'code' => $code, - 'expire' => $expire, - '$permissions' => [ - Permission::read(Role::user($user->getId())), - Permission::update(Role::user($user->getId())), - Permission::delete(Role::user($user->getId())), - ], - ]); - - $challenge = $dbForProject->createDocument('challenges', $challenge); - - switch ($factor) { - case Type::PHONE: - if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { - throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); - } - if (empty($user->getAttribute('phone'))) { - throw new Exception(Exception::USER_PHONE_NOT_FOUND); - } - if (!$user->getAttribute('phoneVerification')) { - throw new Exception(Exception::USER_PHONE_NOT_VERIFIED); - } - - $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl'); - - $customTemplate = $project->getAttribute('templates', [])['sms.mfaChallenge-' . $locale->default] ?? []; - if (!empty($customTemplate)) { - $message = $customTemplate['message'] ?? $message; - } - - $messageContent = Template::fromString($locale->getText("sms.verification.body")); - $messageContent - ->setParam('{{project}}', $project->getAttribute('name')) - ->setParam('{{secret}}', $code); - $messageContent = \strip_tags($messageContent->render()); - $message = $message->setParam('{{token}}', $messageContent); - - $message = $message->render(); - - $phone = $user->getAttribute('phone'); - $queueForMessaging - ->setType(MESSAGE_SEND_TYPE_INTERNAL) - ->setMessage(new Document([ - '$id' => $challenge->getId(), - 'data' => [ - 'content' => $code, - ], - ])) - ->setRecipients([$phone]) - ->setProviderType(MESSAGE_TYPE_SMS); - - if (isset($plan['authPhone'])) { - $timelimit = $timelimit('organization:{organizationId}', $plan['authPhone'], 30 * 24 * 60 * 60); // 30 days - $timelimit - ->setParam('{organizationId}', $project->getAttribute('teamId')); - - $abuse = new Abuse($timelimit); - if ($abuse->check() && System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') { - $helper = PhoneNumberUtil::getInstance(); - $countryCode = $helper->parse($phone)->getCountryCode(); - - if (!empty($countryCode)) { - $queueForStatsUsage - ->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1); - } - } - $queueForStatsUsage - ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) - ->setProject($project) - ->trigger(); - } - break; - case Type::EMAIL: - if (empty(System::getEnv('_APP_SMTP_HOST'))) { - throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); - } - if (empty($user->getAttribute('email'))) { - throw new Exception(Exception::USER_EMAIL_NOT_FOUND); - } - if (!$user->getAttribute('emailVerification')) { - throw new Exception(Exception::USER_EMAIL_NOT_VERIFIED); - } - - $subject = $locale->getText("emails.mfaChallenge.subject"); - $preview = $locale->getText("emails.mfaChallenge.preview"); - $heading = $locale->getText("emails.mfaChallenge.heading"); - - $customTemplate = $project->getAttribute('templates', [])['email.mfaChallenge-' . $locale->default] ?? []; - $smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base'); - - $validator = new FileName(); - if (!$validator->isValid($smtpBaseTemplate)) { - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid template path'); - } - - $bodyTemplate = __DIR__ . '/../../config/locale/templates/' . $smtpBaseTemplate . '.tpl'; - - $detector = new Detector($request->getUserAgent('UNKNOWN')); - $agentOs = $detector->getOS(); - $agentClient = $detector->getClient(); - $agentDevice = $detector->getDevice(); - - $message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-mfa-challenge.tpl'); - $message - ->setParam('{{hello}}', $locale->getText("emails.mfaChallenge.hello")) - ->setParam('{{description}}', $locale->getText("emails.mfaChallenge.description")) - ->setParam('{{clientInfo}}', $locale->getText("emails.mfaChallenge.clientInfo")) - ->setParam('{{thanks}}', $locale->getText("emails.mfaChallenge.thanks")) - ->setParam('{{signature}}', $locale->getText("emails.mfaChallenge.signature")); - - $body = $message->render(); - - $smtp = $project->getAttribute('smtp', []); - $smtpEnabled = $smtp['enabled'] ?? false; - - $senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); - $senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); - $replyTo = ""; - - if ($smtpEnabled) { - if (!empty($smtp['senderEmail'])) { - $senderEmail = $smtp['senderEmail']; - } - if (!empty($smtp['senderName'])) { - $senderName = $smtp['senderName']; - } - if (!empty($smtp['replyTo'])) { - $replyTo = $smtp['replyTo']; - } - - $queueForMails - ->setSmtpHost($smtp['host'] ?? '') - ->setSmtpPort($smtp['port'] ?? '') - ->setSmtpUsername($smtp['username'] ?? '') - ->setSmtpPassword($smtp['password'] ?? '') - ->setSmtpSecure($smtp['secure'] ?? ''); - - if (!empty($customTemplate)) { - if (!empty($customTemplate['senderEmail'])) { - $senderEmail = $customTemplate['senderEmail']; - } - if (!empty($customTemplate['senderName'])) { - $senderName = $customTemplate['senderName']; - } - if (!empty($customTemplate['replyTo'])) { - $replyTo = $customTemplate['replyTo']; - } - - $body = $customTemplate['message'] ?? ''; - $subject = $customTemplate['subject'] ?? $subject; - } - - $queueForMails - ->setSmtpReplyTo($replyTo) - ->setSmtpSenderEmail($senderEmail) - ->setSmtpSenderName($senderName); - } - - $emailVariables = [ - 'heading' => $heading, - 'direction' => $locale->getText('settings.direction'), - // {{user}}, {{project}} and {{otp}} are required in the templates - 'user' => $user->getAttribute('name'), - 'project' => $project->getAttribute('name'), - 'otp' => $code, - 'agentDevice' => $agentDevice['deviceBrand'] ?? $agentDevice['deviceBrand'] ?? 'UNKNOWN', - 'agentClient' => $agentClient['clientName'] ?? 'UNKNOWN', - 'agentOs' => $agentOs['osName'] ?? 'UNKNOWN', - ]; - - if ($smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE) { - $emailVariables = array_merge($emailVariables, [ - 'accentColor' => APP_EMAIL_ACCENT_COLOR, - 'logoUrl' => APP_EMAIL_LOGO_URL, - 'twitterUrl' => APP_SOCIAL_TWITTER, - 'discordUrl' => APP_SOCIAL_DISCORD, - 'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE, - 'termsUrl' => APP_EMAIL_TERMS_URL, - 'privacyUrl' => APP_EMAIL_PRIVACY_URL, - ]); - } - - $queueForMails - ->setSubject($subject) - ->setPreview($preview) - ->setBody($body) - ->setBodyTemplate($bodyTemplate) - ->setVariables($emailVariables) - ->setRecipient($user->getAttribute('email')) - ->trigger(); - break; - } - - $queueForEvents - ->setParam('userId', $user->getId()) - ->setParam('challengeId', $challenge->getId()); - - $response->dynamic($challenge, Response::MODEL_MFA_CHALLENGE); - }); - -App::put('/v1/account/mfa/challenge') - ->desc('Update MFA challenge (confirmation)') - ->groups(['api', 'account', 'mfa']) - ->label('scope', 'account') - ->label('event', 'users.[userId].sessions.[sessionId].create') - ->label('audits.event', 'challenges.update') - ->label('audits.resource', 'user/{response.userId}') - ->label('audits.userId', '{response.userId}') - ->label('sdk', [ - new Method( - namespace: 'account', - group: 'mfa', - name: 'updateMfaChallenge', - description: '/docs/references/account/update-mfa-challenge.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_SESSION, - ) - ], - contentType: ContentType::JSON, - deprecated: new Deprecated( - since: '1.8.0', - replaceWith: 'account.updateMFAChallenge', - ), - ), - new Method( - namespace: 'account', - group: 'mfa', - name: 'updateMFAChallenge', - description: '/docs/references/account/update-mfa-challenge.md', - auth: [AuthType::SESSION, AuthType::JWT], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_OK, - model: Response::MODEL_SESSION, - ) - ], - contentType: ContentType::JSON - ) - ]) - ->label('abuse-limit', 10) - ->label('abuse-key', 'url:{url},challengeId:{param-challengeId}') - ->param('challengeId', '', new Text(256), 'ID of the challenge.') - ->param('otp', '', new Text(256), 'Valid verification token.') - ->inject('project') - ->inject('response') - ->inject('user') - ->inject('session') - ->inject('dbForProject') - ->inject('queueForEvents') - ->action(function (string $challengeId, string $otp, Document $project, Response $response, Document $user, Document $session, Database $dbForProject, Event $queueForEvents) { - - $challenge = $dbForProject->getDocument('challenges', $challengeId); - - if ($challenge->isEmpty()) { - throw new Exception(Exception::USER_INVALID_TOKEN); - } - - $type = $challenge->getAttribute('type'); - - $recoveryCodeChallenge = function (Document $challenge, Document $user, string $otp) use ($dbForProject) { - if ( - $challenge->isSet('type') && - $challenge->getAttribute('type') === \strtolower(Type::RECOVERY_CODE) - ) { - $mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []); - if (in_array($otp, $mfaRecoveryCodes)) { - $mfaRecoveryCodes = array_diff($mfaRecoveryCodes, [$otp]); - $mfaRecoveryCodes = array_values($mfaRecoveryCodes); - $user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes); - $dbForProject->updateDocument('users', $user->getId(), $user); - - return true; - } - - return false; - } - - return false; - }; - - $success = (match ($type) { - Type::TOTP => Challenge\TOTP::challenge($challenge, $user, $otp), - Type::PHONE => Challenge\Phone::challenge($challenge, $user, $otp), - Type::EMAIL => Challenge\Email::challenge($challenge, $user, $otp), - \strtolower(Type::RECOVERY_CODE) => $recoveryCodeChallenge($challenge, $user, $otp), - default => false - }); - - if (!$success) { - throw new Exception(Exception::USER_INVALID_TOKEN); - } - - $dbForProject->deleteDocument('challenges', $challengeId); - $dbForProject->purgeCachedDocument('users', $user->getId()); - - $factors = $session->getAttribute('factors', []); - $factors[] = $type; - $factors = \array_values(\array_unique($factors)); - - $session - ->setAttribute('factors', $factors) - ->setAttribute('mfaUpdatedAt', DateTime::now()); - - $dbForProject->updateDocument('sessions', $session->getId(), $session); - - $queueForEvents - ->setParam('userId', $user->getId()) - ->setParam('sessionId', $session->getId()); - - $response->dynamic($session, Response::MODEL_SESSION); - }); - App::post('/v1/account/targets/push') ->desc('Create push target') ->groups(['api', 'account']) @@ -5137,8 +4289,10 @@ App::post('/v1/account/targets/push') ->inject('request') ->inject('response') ->inject('dbForProject') + ->inject('store') + ->inject('proofForToken') ->inject('authorization') - ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) { + ->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, User $user, Request $request, Response $response, Database $dbForProject, Store $store, ProofsToken $proofForToken, Authorization $authorization) { $targetId = $targetId == 'unique()' ? ID::unique() : $targetId; $provider = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId)); @@ -5154,7 +4308,7 @@ App::post('/v1/account/targets/push') $device = $detector->getDevice(); - $sessionId = Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret); + $sessionId = $user->sessionVerify($store->getProperty('secret', ''), $proofForToken); $session = $dbForProject->getDocument('sessions', $sessionId); try { @@ -5334,7 +4488,7 @@ App::get('/v1/account/identities') ->inject('response') ->inject('user') ->inject('dbForProject') - ->action(function (array $queries, bool $includeTotal, Response $response, Document $user, Database $dbForProject) { + ->action(function (array $queries, bool $includeTotal, Response $response, User $user, Database $dbForProject) { try { $queries = Query::parseQueries($queries); diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 544e22a0fa..e5b01cde09 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -96,8 +96,8 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro $appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? ''; $appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}'; - $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider); - + $oAuthProviders = Config::getParam('oAuthProviders'); + $className = $oAuthProviders[$provider]['class']; if (!\class_exists($className)) { throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED); } @@ -662,26 +662,26 @@ App::get('/v1/avatars/screenshots') ], contentType: ContentType::IMAGE_PNG )) - ->param('url', '', new URL(['http', 'https']), 'Website URL which you want to capture.') - ->param('headers', [], new Assoc(), 'HTTP headers to send with the browser request. Defaults to empty.', true) - ->param('viewportWidth', 1280, new Range(1, 1920), 'Browser viewport width. Pass an integer between 1 to 1920. Defaults to 1280.', true) - ->param('viewportHeight', 720, new Range(1, 1080), 'Browser viewport height. Pass an integer between 1 to 1080. Defaults to 720.', true) - ->param('scale', 1, new Range(0.1, 3, Range::TYPE_FLOAT), 'Browser scale factor. Pass a number between 0.1 to 3. Defaults to 1.', true) - ->param('theme', 'light', new WhiteList(['light', 'dark']), 'Browser theme. Pass "light" or "dark". Defaults to "light".', true) - ->param('userAgent', '', new Text(512), 'Custom user agent string. Defaults to browser default.', true) - ->param('fullpage', false, new Boolean(true), 'Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.', true) - ->param('locale', '', new Text(10), 'Browser locale (e.g., "en-US", "fr-FR"). Defaults to browser default.', true) - ->param('timezone', '', new WhiteList(timezone_identifiers_list()), 'IANA timezone identifier (e.g., "America/New_York", "Europe/London"). Defaults to browser default.', true) - ->param('latitude', 0, new Range(-90, 90, Range::TYPE_FLOAT), 'Geolocation latitude. Pass a number between -90 to 90. Defaults to 0.', true) - ->param('longitude', 0, new Range(-180, 180, Range::TYPE_FLOAT), 'Geolocation longitude. Pass a number between -180 to 180. Defaults to 0.', true) - ->param('accuracy', 0, new Range(0, 100000, Range::TYPE_FLOAT), 'Geolocation accuracy in meters. Pass a number between 0 to 100000. Defaults to 0.', true) - ->param('touch', false, new Boolean(true), 'Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.', true) - ->param('permissions', [], new ArrayList(new WhiteList(['geolocation', 'camera', 'microphone', 'notifications', 'midi', 'push', 'clipboard-read', 'clipboard-write', 'payment-handler', 'usb', 'bluetooth', 'accelerometer', 'gyroscope', 'magnetometer', 'ambient-light-sensor', 'background-sync', 'persistent-storage', 'screen-wake-lock', 'web-share', 'xr-spatial-tracking'])), 'Browser permissions to grant. Pass an array of permission names like ["geolocation", "camera", "microphone"]. Defaults to empty.', true) - ->param('sleep', 0, new Range(0, 10), 'Wait time in seconds before taking the screenshot. Pass an integer between 0 to 10. Defaults to 0.', true) - ->param('width', 0, new Range(0, 2000), 'Output image width. Pass 0 to use original width, or an integer between 1 to 2000. Defaults to 0 (original width).', true) - ->param('height', 0, new Range(0, 2000), 'Output image height. Pass 0 to use original height, or an integer between 1 to 2000. Defaults to 0 (original height).', true) - ->param('quality', -1, new Range(-1, 100), 'Screenshot quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true) - ->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true) + ->param('url', '', new URL(['http', 'https']), 'Website URL which you want to capture.', example: 'https://example.com') + ->param('headers', [], new Assoc(), 'HTTP headers to send with the browser request. Defaults to empty.', true, example: '{"Authorization":"Bearer token123","X-Custom-Header":"value"}') + ->param('viewportWidth', 1280, new Range(1, 1920), 'Browser viewport width. Pass an integer between 1 to 1920. Defaults to 1280.', true, example: '1920') + ->param('viewportHeight', 720, new Range(1, 1080), 'Browser viewport height. Pass an integer between 1 to 1080. Defaults to 720.', true, example: '1080') + ->param('scale', 1, new Range(0.1, 3, Range::TYPE_FLOAT), 'Browser scale factor. Pass a number between 0.1 to 3. Defaults to 1.', true, example: '2') + ->param('theme', 'light', new WhiteList(['light', 'dark']), 'Browser theme. Pass "light" or "dark". Defaults to "light".', true, example: 'dark') + ->param('userAgent', '', new Text(512), 'Custom user agent string. Defaults to browser default.', true, example: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15') + ->param('fullpage', false, new Boolean(true), 'Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.', true, example: 'true') + ->param('locale', '', new Text(10), 'Browser locale (e.g., "en-US", "fr-FR"). Defaults to browser default.', true, example: 'en-US') + ->param('timezone', '', new WhiteList(timezone_identifiers_list()), 'IANA timezone identifier (e.g., "America/New_York", "Europe/London"). Defaults to browser default.', true, example: 'america/new_york') + ->param('latitude', 0, new Range(-90, 90, Range::TYPE_FLOAT), 'Geolocation latitude. Pass a number between -90 to 90. Defaults to 0.', true, example: '37.7749') + ->param('longitude', 0, new Range(-180, 180, Range::TYPE_FLOAT), 'Geolocation longitude. Pass a number between -180 to 180. Defaults to 0.', true, example: '-122.4194') + ->param('accuracy', 0, new Range(0, 100000, Range::TYPE_FLOAT), 'Geolocation accuracy in meters. Pass a number between 0 to 100000. Defaults to 0.', true, example: '100') + ->param('touch', false, new Boolean(true), 'Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.', true, example: 'true') + ->param('permissions', [], new ArrayList(new WhiteList(['geolocation', 'camera', 'microphone', 'notifications', 'midi', 'push', 'clipboard-read', 'clipboard-write', 'payment-handler', 'usb', 'bluetooth', 'accelerometer', 'gyroscope', 'magnetometer', 'ambient-light-sensor', 'background-sync', 'persistent-storage', 'screen-wake-lock', 'web-share', 'xr-spatial-tracking'])), 'Browser permissions to grant. Pass an array of permission names like ["geolocation", "camera", "microphone"]. Defaults to empty.', true, example: '["geolocation","notifications"]') + ->param('sleep', 0, new Range(0, 10), 'Wait time in seconds before taking the screenshot. Pass an integer between 0 to 10. Defaults to 0.', true, example: '3') + ->param('width', 0, new Range(0, 2000), 'Output image width. Pass 0 to use original width, or an integer between 1 to 2000. Defaults to 0 (original width).', true, example: '800') + ->param('height', 0, new Range(0, 2000), 'Output image height. Pass 0 to use original height, or an integer between 1 to 2000. Defaults to 0 (original height).', true, example: '600') + ->param('quality', -1, new Range(-1, 100), 'Screenshot quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true, example: '85') + ->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true, example: 'jpeg') ->inject('response') ->inject('queueForStatsUsage') ->action(function (string $url, array $headers, int $viewportWidth, int $viewportHeight, float $scale, string $theme, string $userAgent, bool $fullpage, string $locale, string $timezone, float $latitude, float $longitude, float $accuracy, bool $touch, array $permissions, int $sleep, int $width, int $height, int $quality, string $output, Response $response, StatsUsage $queueForStatsUsage) { @@ -854,7 +854,7 @@ App::get('/v1/cards/cloud') $email = $user->getAttribute('email', ''); $createdAt = new \DateTime($user->getCreatedAt()); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger, $authorization); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $authorization, $logger); $githubName = $gitHub['name'] ?? ''; $githubId = $gitHub['id'] ?? ''; @@ -1061,7 +1061,7 @@ App::get('/v1/cards/cloud-back') $userId = $user->getId(); $email = $user->getAttribute('email', ''); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger, $authorization); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $authorization, $logger); $githubId = $gitHub['id'] ?? ''; $isHero = \array_key_exists($email, $heroes); @@ -1145,7 +1145,7 @@ App::get('/v1/cards/cloud-og') $email = $user->getAttribute('email', ''); $createdAt = new \DateTime($user->getCreatedAt()); - $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger, $authorization); + $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $authorization, $logger); $githubName = $gitHub['name'] ?? ''; $githubId = $gitHub['id'] ?? ''; diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index d0480225da..93e0a74ba8 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -1,6 +1,5 @@ getAttribute('apis', [])) && !$project->getAttribute('apis', [])['graphql'] - && !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles())) + && !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles())) ) { throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 765688e1ee..a7da3f1ac6 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -120,7 +120,7 @@ App::get('/v1/health/db') $output[] = new Document([ 'name' => $key . " ($database)", 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) + 'ping' => \round((\microtime(true) - $checkStart) * 1000) ]); } else { $failures[] = $database; @@ -131,6 +131,8 @@ App::get('/v1/health/db') } } + // Only throw error if ALL databases failed (no successful pings) + // This allows partial failures in environments where not all DBs are ready if (!empty($failures)) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'DB failure on: ' . implode(", ", $failures)); } @@ -180,7 +182,7 @@ App::get('/v1/health/cache') $output[] = new Document([ 'name' => $key . " ($cache)", 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) + 'ping' => \round((\microtime(true) - $checkStart) * 1000) ]); } else { $failures[] = $cache; @@ -240,7 +242,7 @@ App::get('/v1/health/pubsub') $output[] = new Document([ 'name' => $key . " ($pubsub)", 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) + 'ping' => \round((\microtime(true) - $checkStart) * 1000) ]); } else { $failures[] = $pubsub; @@ -822,7 +824,7 @@ App::get('/v1/health/storage/local') $output = [ 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) + 'ping' => \round((\microtime(true) - $checkStart) * 1000) ]; $response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS); @@ -874,7 +876,7 @@ App::get('/v1/health/storage') $output = [ 'status' => 'pass', - 'ping' => \round((\microtime(true) - $checkStart) / 1000) + 'ping' => \round((\microtime(true) - $checkStart) * 1000) ]; $response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS); diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index f9d134db05..f9f0b8b219 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -1,6 +1,5 @@ inject('deviceForMigrations') ->inject('queueForEvents') ->inject('queueForMigrations') - ->action(function (string $bucketId, string $fileId, string $resourceId, bool $internalFile, Response $response, Database $dbForProject, Database $dbForPlatform, Authorization $authorization, Document $project, Device $deviceForFiles, Device $deviceForMigrations, Event $queueForEvents, Migration $queueForMigrations) { - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - if ($internalFile && !$isPrivilegedUser) { - throw new Exception(Exception::USER_UNAUTHORIZED); - } + ->action(function ( + string $bucketId, + string $fileId, + string $resourceId, + bool $internalFile, + Response $response, + Database $dbForProject, + Database $dbForPlatform, + Authorization $authorization, + Document $project, + Device $deviceForFiles, + Device $deviceForMigrations, + Event $queueForEvents, + Migration $queueForMigrations + ) { $bucket = $authorization->skip(function () use ($internalFile, $dbForPlatform, $dbForProject, $bucketId) { if ($internalFile) { return $dbForPlatform->getDocument('buckets', 'default'); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 760a4f23a6..b8761c2da9 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1,7 +1,6 @@ APP_LIMIT_USER_SESSIONS_DEFAULT, 'passwordHistory' => 0, 'passwordDictionary' => false, - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, + 'duration' => TOKEN_EXPIRATION_LOGIN_LONG, 'personalDataCheck' => false, 'mockNumbers' => [], 'sessionAlerts' => false, diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index c2adc1e195..cd35805644 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -2,7 +2,6 @@ use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; -use Appwrite\Auth\Auth; use Appwrite\ClamAV\Network; use Appwrite\Event\Delete; use Appwrite\Event\Event; @@ -13,6 +12,7 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\MethodType; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\Queries\Buckets; use Appwrite\Utopia\Database\Validator\Queries\Files; @@ -439,8 +439,8 @@ App::post('/v1/storage/buckets/:bucketId/files') $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -471,7 +471,7 @@ App::post('/v1/storage/buckets/:bucketId/files') // Users can only manage their own roles, API keys and Admin users can manage any $roles = $authorization->getRoles(); - if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { + if (!User::isApp($roles) && !User::isPrivileged($roles)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { $permission = Permission::parse($permission); @@ -682,7 +682,13 @@ App::post('/v1/storage/buckets/:bucketId/files') 'metadata' => $metadata, ]); - $file = $dbForProject->createDocument('bucket_' . $bucket->getSequence(), $doc); + try { + $file = $dbForProject->createDocument('bucket_' . $bucket->getSequence(), $doc); + } catch (DuplicateException) { + throw new Exception(Exception::STORAGE_FILE_ALREADY_EXISTS); + } catch (NotFoundException) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } } else { $file = $file ->setAttribute('$permissions', $permissions) @@ -731,6 +737,8 @@ App::post('/v1/storage/buckets/:bucketId/files') try { $file = $dbForProject->createDocument('bucket_' . $bucket->getSequence(), $doc); + } catch (DuplicateException) { + throw new Exception(Exception::STORAGE_FILE_ALREADY_EXISTS); } catch (NotFoundException) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } @@ -799,8 +807,8 @@ App::get('/v1/storage/buckets/:bucketId/files') ->action(function (string $bucketId, array $queries, string $search, bool $includeTotal, Response $response, Database $dbForProject, Authorization $authorization, string $mode) { $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -898,8 +906,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Authorization $authorization, string $mode) { $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -980,8 +988,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') /* @type Document $bucket */ $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1006,7 +1014,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') } if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) { - throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED); } if ($file->isEmpty()) { @@ -1124,7 +1132,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg']; //Do not update transformedAt if it's a console user - if (!Auth::isPrivilegedUser($authorization->getRoles())) { + if (!User::isPrivileged($authorization->getRoles())) { $transformedAt = $file->getAttribute('transformedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) { $file->setAttribute('transformedAt', DateTime::now()); @@ -1176,8 +1184,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') /* @type Document $bucket */ $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1337,8 +1345,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') /* @type Document $bucket */ $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1495,7 +1503,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') try { $decoded = $decoder->decode($jwt); } catch (JWTException) { - throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED); } if ( @@ -1503,14 +1511,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') $decoded['bucketId'] !== $bucketId || $decoded['fileId'] !== $fileId ) { - throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription()); + throw new Exception(Exception::USER_UNAUTHORIZED); } $isInternal = $decoded['internal'] ?? false; + $disposition = $decoded['disposition'] ?? 'inline'; $dbForProject = $isInternal ? $dbForPlatform : $dbForProject; - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { @@ -1562,7 +1571,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') ->setContentType($contentType) ->addHeader('Content-Security-Policy', 'script-src none;') ->addHeader('X-Content-Type-Options', 'nosniff') - ->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"') + ->addHeader('Content-Disposition', $disposition . '; filename="' . $file->getAttribute('name', '') . '"') ->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days ->addHeader('X-Peak', \memory_get_peak_usage()); @@ -1666,8 +1675,8 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -1695,7 +1704,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') // Users can only manage their own roles, API keys and Admin users can manage any $roles = $authorization->getRoles(); - if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles) && !\is_null($permissions)) { + if (!User::isApp($roles) && !User::isPrivileged($roles) && !\is_null($permissions)) { foreach (Database::PERMISSIONS as $type) { foreach ($permissions as $permission) { $permission = Permission::parse($permission); @@ -1780,8 +1789,8 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes, Authorization $authorization) { $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 4b8c543e5d..2d38400f64 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -1,6 +1,5 @@ inject('queueForEvents') ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Authorization $authorization, Event $queueForEvents) { - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - $isAppUser = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAppUser = User::isApp($authorization->getRoles()); $teamId = $teamId == 'unique()' ? ID::unique() : $teamId; @@ -177,6 +180,7 @@ App::get('/v1/teams') ->inject('dbForProject') ->action(function (array $queries, string $search, bool $includeTotal, Response $response, Database $dbForProject) { + try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { @@ -475,10 +479,9 @@ App::post('/v1/teams/:teamId/memberships') ->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true) ->param('roles', [], function (Document $project) { if ($project->getId() === 'console') { - ; $roles = array_keys(Config::getParam('roles', [])); - array_filter($roles, function ($role) { - return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]); + $roles = array_filter($roles, function ($role) { + return !in_array($role, [User::ROLE_APPS, User::ROLE_GUESTS, User::ROLE_USERS]); }); return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE); } @@ -498,9 +501,11 @@ App::post('/v1/teams/:teamId/memberships') ->inject('timelimit') ->inject('queueForStatsUsage') ->inject('plan') - ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Authorization $authorization, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) { - $isAppUser = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + ->inject('proofForPassword') + ->inject('proofForToken') + ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Authorization $authorization, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Password $proofForPassword, Token $proofForToken) { + $isAppUser = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); $url = htmlentities($url); if (empty($url)) { @@ -570,49 +575,54 @@ App::post('/v1/teams/:teamId/memberships') } try { + $userId = ID::unique(); + $hash = $proofForPassword->hash($proofForPassword->generate()); $emailCanonical = new Email($email); } catch (Throwable) { $emailCanonical = null; } + $userId = ID::unique(); + + $userDocument = new Document([ + '$id' => $userId, + '$permissions' => [ + Permission::read(Role::any()), + Permission::read(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ], + 'email' => empty($email) ? null : $email, + 'phone' => empty($phone) ? null : $phone, + 'emailVerification' => false, + 'status' => true, + // TODO: Set password empty? + 'password' => $hash, + 'hash' => $proofForPassword->getHash()->getName(), + 'hashOptions' => $proofForPassword->getHash()->getOptions(), + /** + * Set the password update time to 0 for users created using + * team invite and OAuth to allow password updates without an + * old password + */ + 'passwordUpdate' => null, + 'registration' => DateTime::now(), + 'reset' => false, + 'name' => $name, + 'prefs' => new \stdClass(), + 'sessions' => null, + 'tokens' => null, + 'memberships' => null, + 'search' => implode(' ', [$userId, $email, $name]), + 'emailCanonical' => $emailCanonical?->getCanonical(), + 'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(), + 'emailIsCorporate' => $emailCanonical?->isCorporate(), + 'emailIsDisposable' => $emailCanonical?->isDisposable(), + 'emailIsFree' => $emailCanonical?->isFree(), + ]); + try { - $userId = ID::unique(); - $invitee = $authorization->skip(fn () => $dbForProject->createDocument('users', new Document([ - '$id' => $userId, - '$permissions' => [ - Permission::read(Role::any()), - Permission::read(Role::user($userId)), - Permission::update(Role::user($userId)), - Permission::delete(Role::user($userId)), - ], - 'email' => empty($email) ? null : $email, - 'phone' => empty($phone) ? null : $phone, - 'emailVerification' => false, - 'status' => true, - // TODO: Set password empty? - 'password' => Auth::passwordHash(Auth::passwordGenerator(), Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS), - 'hash' => Auth::DEFAULT_ALGO, - 'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS, - /** - * Set the password update time to 0 for users created using - * team invite and OAuth to allow password updates without an - * old password - */ - 'passwordUpdate' => null, - 'registration' => DateTime::now(), - 'reset' => false, - 'name' => $name, - 'prefs' => new \stdClass(), - 'sessions' => null, - 'tokens' => null, - 'memberships' => null, - 'search' => implode(' ', [$userId, $email, $name]), - 'emailCanonical' => $emailCanonical?->getCanonical(), - 'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(), - 'emailIsCorporate' => $emailCanonical?->isCorporate(), - 'emailIsDisposable' => $emailCanonical?->isDisposable(), - 'emailIsFree' => $emailCanonical?->isFree(), - ]))); + $invitee = $authorization->skip(fn () => $dbForProject->createDocument('users', $userDocument)); } catch (Duplicate $th) { throw new Exception(Exception::USER_ALREADY_EXISTS); } @@ -629,7 +639,7 @@ App::post('/v1/teams/:teamId/memberships') Query::equal('teamInternalId', [$team->getSequence()]), ]); - $secret = Auth::tokenGenerator(); + $secret = $proofForToken->generate(); if ($membership->isEmpty()) { $membershipId = ID::unique(); $membership = new Document([ @@ -649,7 +659,7 @@ App::post('/v1/teams/:teamId/memberships') 'invited' => DateTime::now(), 'joined' => ($isPrivilegedUser || $isAppUser) ? DateTime::now() : null, 'confirm' => ($isPrivilegedUser || $isAppUser), - 'secret' => Auth::hash($secret), + 'secret' => $proofForToken->hash($secret), 'search' => implode(' ', [$membershipId, $invitee->getId()]) ]); @@ -660,9 +670,8 @@ App::post('/v1/teams/:teamId/memberships') if ($isPrivilegedUser || $isAppUser) { $authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); } - } elseif ($membership->getAttribute('confirm') === false) { - $membership->setAttribute('secret', Auth::hash($secret)); + $membership->setAttribute('secret', $proofForToken->hash($secret)); $membership->setAttribute('invited', DateTime::now()); if ($isPrivilegedUser || $isAppUser) { @@ -765,7 +774,6 @@ App::post('/v1/teams/:teamId/memberships') ->setName($invitee->getAttribute('name', '')) ->setVariables($emailVariables) ->trigger(); - } elseif (!empty($phone)) { if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); @@ -930,8 +938,8 @@ App::get('/v1/teams/:teamId/memberships') ]; $roles = $authorization->getRoles(); - $isPrivilegedUser = Auth::isPrivilegedUser($roles); - $isAppUser = Auth::isAppUser($roles); + $isPrivilegedUser = User::isPrivileged($roles); + $isAppUser = User::isApp($roles); $membershipsPrivacy = array_map(function ($privacy) use ($isPrivilegedUser, $isAppUser) { return $privacy || $isPrivilegedUser || $isAppUser; @@ -1022,8 +1030,8 @@ App::get('/v1/teams/:teamId/memberships/:membershipId') ]; $roles = $authorization->getRoles(); - $isPrivilegedUser = Auth::isPrivilegedUser($roles); - $isAppUser = Auth::isAppUser($roles); + $isPrivilegedUser = User::isPrivileged($roles); + $isAppUser = User::isApp($roles); $membershipsPrivacy = array_map(function ($privacy) use ($isPrivilegedUser, $isAppUser) { return $privacy || $isPrivilegedUser || $isAppUser; @@ -1088,8 +1096,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') ->param('roles', [], function (Document $project) { if ($project->getId() === 'console') { $roles = array_keys(Config::getParam('roles', [])); - array_filter($roles, function ($role) { - return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]); + $roles = array_filter($roles, function ($role) { + return !in_array($role, [User::ROLE_APPS, User::ROLE_GUESTS, User::ROLE_USERS]); }); return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE); } @@ -1119,8 +1127,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') throw new Exception(Exception::USER_NOT_FOUND); } - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - $isAppUser = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAppUser = User::isApp($authorization->getRoles()); $isOwner = $authorization->hasRole('team:' . $team->getId() . '/owner'); if ($project->getId() === 'console') { @@ -1206,7 +1214,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->inject('project') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Authorization $authorization, Document $project, Reader $geodb, Event $queueForEvents) { + ->inject('store') + ->inject('proofForToken') + ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Authorization $authorization, $project, Reader $geodb, Event $queueForEvents, Store $store, Token $proofForToken) { $protocol = $request->getProtocol(); $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -1225,7 +1235,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH); } - if (Auth::hash($secret) !== $membership->getAttribute('secret')) { + if (!$proofForToken->verify($secret, $membership->getAttribute('secret'))) { throw new Exception(Exception::TEAM_INVALID_SECRET); } @@ -1259,9 +1269,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); - $authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $authDuration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG; $expire = DateTime::addSeconds(new \DateTime(), $authDuration); - $secret = Auth::tokenGenerator(); + $secret = $proofForToken->generate(); $session = new Document(array_merge([ '$id' => ID::unique(), '$permissions' => [ @@ -1271,9 +1281,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ], 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), - 'provider' => Auth::SESSION_PROVIDER_EMAIL, + 'provider' => SESSION_PROVIDER_EMAIL, 'providerUid' => $user->getAttribute('email'), - 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak + 'secret' => $proofForToken->hash($secret), // One way hash encryption to protect DB leak 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), 'factors' => ['email'], @@ -1285,14 +1295,19 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') $authorization->addRole(Role::user($userId)->toString()); + $encoded = $store + ->setProperty('id', $user->getId()) + ->setProperty('secret', $secret) + ->encode(); + if (!Config::getParam('domainVerification')) { - $response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])); + $response->addHeader('X-Fallback-Cookies', \json_encode([$store->getKey() => $encoded])); } $response ->addCookie( - name: Auth::$cookieName . '_legacy', - value: Auth::encodeSession($user->getId(), $secret), + name: $store->getKey() . '_legacy', + value: $encoded, expire: (new \DateTime($expire))->getTimestamp(), path: '/', domain: Config::getParam('cookieDomain'), @@ -1300,8 +1315,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') httponly: true ) ->addCookie( - name: Auth::$cookieName, - value: Auth::encodeSession($user->getId(), $secret), + name: $store->getKey(), + value: $encoded, expire: (new \DateTime($expire))->getTimestamp(), path: '/', domain: Config::getParam('cookieDomain'), diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 3fd570b03f..b1c23ce02f 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -1,7 +1,6 @@ getAttribute('auths', [])['passwordHistory'] ?? 0; if (!empty($email)) { @@ -104,8 +114,24 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e } catch (Throwable) { $emailCanonical = null; } + $hashedPassword = null; + + $isHashed = !$hash instanceof Plaintext; + + $defaultHash = new ProofsPassword(); + if (!empty($password)) { + if (!$isHashed) { // Password was never hashed, hash it with the default hash + $hashedPassword = $defaultHash->hash($password); + $hash = $defaultHash->getHash(); + } else { + $hashedPassword = $password; + } + } else { + // when password is not provided, plaintext was set as the default hash causing the issue + $hash = $defaultHash->getHash(); + $isHashed = !$hash instanceof Plaintext; + } - $password = (!empty($password)) ? ($hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password) : null; $user = new Document([ '$id' => $userId, '$permissions' => [ @@ -119,11 +145,11 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e 'phoneVerification' => false, 'status' => true, 'labels' => [], - 'password' => $password, - 'passwordHistory' => is_null($password) || $passwordHistory === 0 ? [] : [$password], - 'passwordUpdate' => (!empty($password)) ? DateTime::now() : null, - 'hash' => $hash === 'plaintext' ? Auth::DEFAULT_ALGO : $hash, - 'hashOptions' => $hash === 'plaintext' ? Auth::DEFAULT_ALGO_OPTIONS : $hashOptionsObject + ['type' => $hash], + 'password' => $hashedPassword, + 'passwordHistory' => is_null($hashedPassword) || $passwordHistory === 0 ? [] : [$hashedPassword], + 'passwordUpdate' => (!empty($hashedPassword)) ? DateTime::now() : null, + 'hash' => $hash->getName(), + 'hashOptions' => $hash->getOptions(), 'registration' => DateTime::now(), 'reset' => false, 'name' => $name, @@ -139,7 +165,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e 'emailIsFree' => $emailCanonical?->isFree(), ]); - if ($hash === 'plaintext') { + if (!$isHashed && !empty($password)) { $hooks->trigger('passwordValidator', [$dbForProject, $project, $plaintextPassword, &$user, true]); } @@ -230,7 +256,9 @@ App::post('/v1/users') ->inject('dbForProject') ->inject('hooks') ->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) { - $user = createUser('plaintext', '{}', $userId, $email, $password, $phone, $name, $project, $dbForProject, $hooks); + $plaintext = new Plaintext(); + + $user = createUser($plaintext, $userId, $email, $password, $phone, $name, $project, $dbForProject, $hooks); $response ->setStatusCode(Response::STATUS_CODE_CREATED) ->dynamic($user, Response::MODEL_USER); @@ -264,7 +292,10 @@ App::post('/v1/users/bcrypt') ->inject('dbForProject') ->inject('hooks') ->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) { - $user = createUser('bcrypt', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); + $bcrypt = new Bcrypt(); + $bcrypt->setCost(8); // Default cost + + $user = createUser($bcrypt, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -299,7 +330,9 @@ App::post('/v1/users/md5') ->inject('dbForProject') ->inject('hooks') ->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) { - $user = createUser('md5', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); + $md5 = new MD5(); + + $user = createUser($md5, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -334,7 +367,9 @@ App::post('/v1/users/argon2') ->inject('dbForProject') ->inject('hooks') ->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) { - $user = createUser('argon2', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); + $argon2 = new Argon2(); + + $user = createUser($argon2, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -370,13 +405,12 @@ App::post('/v1/users/sha') ->inject('dbForProject') ->inject('hooks') ->action(function (string $userId, string $email, string $password, string $passwordVersion, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) { - $options = '{}'; - + $sha = new Sha(); if (!empty($passwordVersion)) { - $options = '{"version":"' . $passwordVersion . '"}'; + $sha->setVersion($passwordVersion); } - $user = createUser('sha', $options, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); + $user = createUser($sha, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -411,7 +445,9 @@ App::post('/v1/users/phpass') ->inject('dbForProject') ->inject('hooks') ->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) { - $user = createUser('phpass', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); + $phpass = new PHPass(); + + $user = createUser($phpass, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -451,15 +487,15 @@ App::post('/v1/users/scrypt') ->inject('dbForProject') ->inject('hooks') ->action(function (string $userId, string $email, string $password, string $passwordSalt, int $passwordCpu, int $passwordMemory, int $passwordParallel, int $passwordLength, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) { - $options = [ - 'salt' => $passwordSalt, - 'costCpu' => $passwordCpu, - 'costMemory' => $passwordMemory, - 'costParallel' => $passwordParallel, - 'length' => $passwordLength - ]; + $scrypt = new Scrypt(); + $scrypt + ->setSalt($passwordSalt) + ->setCpuCost($passwordCpu) + ->setMemoryCost($passwordMemory) + ->setParallelCost($passwordParallel) + ->setLength($passwordLength); - $user = createUser('scrypt', \json_encode($options), $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); + $user = createUser($scrypt, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -497,7 +533,13 @@ App::post('/v1/users/scrypt-modified') ->inject('dbForProject') ->inject('hooks') ->action(function (string $userId, string $email, string $password, string $passwordSalt, string $passwordSaltSeparator, string $passwordSignerKey, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) { - $user = createUser('scryptMod', '{"signerKey":"' . $passwordSignerKey . '","saltSeparator":"' . $passwordSaltSeparator . '","salt":"' . $passwordSalt . '"}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); + $scryptModified = new ScryptModified(); + $scryptModified + ->setSalt($passwordSalt) + ->setSaltSeparator($passwordSaltSeparator) + ->setSignerKey($passwordSignerKey); + + $user = createUser($scryptModified, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks); $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -809,16 +851,12 @@ App::get('/v1/users/:userId/sessions') if ($user->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } - $sessions = $user->getAttribute('sessions', []); - foreach ($sessions as $key => $session) { /** @var Document $session */ - $countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); $session->setAttribute('countryName', $countryName); $session->setAttribute('current', false); - $sessions[$key] = $session; } @@ -858,28 +896,22 @@ App::get('/v1/users/:userId/memberships') if ($user->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } - try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - if (!empty($search)) { $queries[] = Query::search('search', $search); } - // Set internal queries $queries[] = Query::equal('userInternalId', [$user->getSequence()]); - $memberships = array_map(function ($membership) use ($dbForProject, $user) { $team = $dbForProject->getDocument('teams', $membership->getAttribute('teamId')); - $membership ->setAttribute('teamName', $team->getAttribute('name')) ->setAttribute('userName', $user->getAttribute('name')) ->setAttribute('userEmail', $user->getAttribute('email')); - return $membership; }, $dbForProject->find('memberships', $queries)); @@ -920,35 +952,26 @@ App::get('/v1/users/:userId/logs') if ($user->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } - try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - // Temp fix for logs $queries[] = Query::or([ Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))), Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))), ]); - $audit = new Audit($dbForProject); - $logs = $audit->getLogsByUser($user->getSequence(), $queries); - $output = []; - foreach ($logs as $i => &$log) { $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN'; - $detector = new Detector($log['userAgent']); $detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then) - $os = $detector->getOS(); $client = $detector->getClient(); $device = $detector->getDevice(); - $output[$i] = new Document([ 'event' => $log['event'], 'userId' => ID::custom($log['data']['userId']), @@ -969,9 +992,7 @@ App::get('/v1/users/:userId/logs') 'deviceBrand' => $device['deviceBrand'], 'deviceModel' => $device['deviceModel'] ]); - $record = $geodb->get($log['ip']); - if ($record) { $output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--'; $output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); @@ -1015,15 +1036,12 @@ App::get('/v1/users/:userId/targets') if ($user->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } - try { $queries = Query::parseQueries($queries); } catch (QueryException $e) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - $queries[] = Query::equal('userId', [$userId]); - /** * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries */ @@ -1031,20 +1049,16 @@ App::get('/v1/users/:userId/targets') return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]); }); $cursor = reset($cursor); - if ($cursor) { $validator = new Cursor(); if (!$validator->isValid($cursor)) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); } - $targetId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('targets', $targetId); - if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Target '{$targetId}' for the 'cursor' value not found."); } - $cursor->setValue($cursorDocument); } try { @@ -1092,7 +1106,6 @@ App::get('/v1/users/identities') if (!empty($search)) { $queries[] = Query::search('search', $search); } - /** * Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries */ @@ -1102,19 +1115,15 @@ App::get('/v1/users/identities') $cursor = reset($cursor); if ($cursor) { /** @var Query $cursor */ - $validator = new Cursor(); if (!$validator->isValid($cursor)) { throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); } - $identityId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('identities', $identityId); - if ($cursorDocument->isEmpty()) { throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "User '{$identityId}' for the 'cursor' value not found."); } - $cursor->setValue($cursorDocument); } @@ -1354,12 +1363,17 @@ App::patch('/v1/users/:userId/password') $hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, true]); - $newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS); + // Create Argon2 hasher with default settings + $hasher = new Argon2(); + $newPassword = $hasher->hash($password); + + $hash = ProofsPassword::createHash($user->getAttribute('hash'), $user->getAttribute('hashOptions')); $historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0; $history = $user->getAttribute('passwordHistory', []); + if ($historyLimit > 0) { - $validator = new PasswordHistory($history, $user->getAttribute('hash'), $user->getAttribute('hashOptions')); + $validator = new PasswordHistory($history, $hash); if (!$validator->isValid($password)) { throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED); } @@ -1372,8 +1386,8 @@ App::patch('/v1/users/:userId/password') ->setAttribute('password', $newPassword) ->setAttribute('passwordHistory', $history) ->setAttribute('passwordUpdate', DateTime::now()) - ->setAttribute('hash', Auth::DEFAULT_ALGO) - ->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS); + ->setAttribute('hash', $hasher->getName()) + ->setAttribute('hashOptions', $hasher->getOptions()); $user = $dbForProject->updateDocument('users', $user->getId(), $user); @@ -2197,17 +2211,19 @@ App::post('/v1/users/:userId/sessions') ->inject('locale') ->inject('geodb') ->inject('queueForEvents') - ->action(function (string $userId, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) { + ->inject('store') + ->inject('proofForToken') + ->action(function (string $userId, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Store $store, Token $proofForToken) { $user = $dbForProject->getDocument('users', $userId); if ($user->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } - $secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION); + $secret = $proofForToken->generate(); $detector = new Detector($request->getUserAgent('UNKNOWN')); $record = $geodb->get($request->getIP()); - $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG; $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $session = new Document(array_merge( @@ -2215,8 +2231,8 @@ App::post('/v1/users/:userId/sessions') '$id' => ID::unique(), 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), - 'provider' => Auth::SESSION_PROVIDER_SERVER, - 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak + 'provider' => SESSION_PROVIDER_SERVER, + 'secret' => $proofForToken->hash($secret), // One way hash encryption to protect DB leak 'userAgent' => $request->getUserAgent('UNKNOWN'), 'factors' => ['server'], 'ip' => $request->getIP(), @@ -2240,8 +2256,13 @@ App::post('/v1/users/:userId/sessions') $dbForProject->purgeCachedDocument('users', $user->getId()); + $encoded = $store + ->setProperty('id', $user->getId()) + ->setProperty('secret', $secret) + ->encode(); + $session - ->setAttribute('secret', Auth::encodeSession($user->getId(), $secret)) + ->setAttribute('secret', $encoded) ->setAttribute('countryName', $countryName); $queueForEvents @@ -2276,7 +2297,7 @@ App::post('/v1/users/:userId/tokens') )) ->param('userId', '', new UID(), 'User ID.') ->param('length', 6, new Range(4, 128), 'Token length in characters. The default length is 6 characters', true) - ->param('expire', Auth::TOKEN_EXPIRATION_GENERIC, new Range(60, Auth::TOKEN_EXPIRATION_LOGIN_LONG), 'Token expiration period in seconds. The default expiration is 15 minutes.', true) + ->param('expire', TOKEN_EXPIRATION_GENERIC, new Range(60, TOKEN_EXPIRATION_LOGIN_LONG), 'Token expiration period in seconds. The default expiration is 15 minutes.', true) ->inject('request') ->inject('response') ->inject('dbForProject') @@ -2288,15 +2309,17 @@ App::post('/v1/users/:userId/tokens') throw new Exception(Exception::USER_NOT_FOUND); } - $secret = Auth::tokenGenerator($length); + $proofForToken = new Token($length); + $proofForToken->setHash(new Sha()); + $secret = $proofForToken->generate(); $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $expire)); $token = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), 'userInternalId' => $user->getSequence(), - 'type' => Auth::TOKEN_TYPE_GENERIC, - 'secret' => Auth::hash($secret), + 'type' => TOKEN_TYPE_GENERIC, + 'secret' => $proofForToken->hash($secret), 'expire' => $expire, 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP() @@ -2609,7 +2632,8 @@ App::post('/v1/users/:userId/jwts') $session = \count($sessions) > 0 ? $sessions[\count($sessions) - 1] : new Document(); } else { // Find by ID - foreach ($sessions as $loopSession) { /** @var Utopia\Database\Document $loopSession */ + foreach ($sessions as $loopSession) { + /** @var Utopia\Database\Document $loopSession */ if ($loopSession->getId() == $sessionId) { $session = $loopSession; break; diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 4703e3a296..f97dc12ba6 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -17,7 +17,9 @@ use Appwrite\Vcs\Comment; use Swoole\Coroutine\WaitGroup; use Utopia\App; use Utopia\CLI\Console; +use Utopia\Config\Adapters\Dotenv as ConfigDotenv; use Utopia\Config\Config; +use Utopia\Config\Exceptions\Parse; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -29,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; @@ -978,14 +983,14 @@ App::post('/v1/vcs/github/installations/:installationId/detections') $contentResponse = $github->getRepositoryContent($owner, $repositoryName, \rtrim($providerRootDirectory, '/') . '/' . $file); $envFile = $contentResponse['content'] ?? ''; - $envLines = \explode("\n", $envFile); - foreach ($envLines as $line) { - $parts = \explode('=', $line, 2); - $envName = \trim($parts[0] ?? ''); - $envValue = \trim($parts[1] ?? ''); - if (!empty($envName)) { + $configAdapter = new ConfigDotenv(); + try { + $envObject = $configAdapter->parse($envFile); + foreach ($envObject as $envName => $envValue) { $envs[$envName] = $envValue; } + } catch (Parse $err) { + // Silence error, so rest of endpoint can return } } finally { $wg->done(); @@ -1031,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 = ""; } @@ -1050,11 +1056,20 @@ 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 = 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->getValue() : 4; + $offset = !empty($offsetQuery) ? $offsetQuery->getValue() : 0; + + 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); - $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'] ?? ''); @@ -1192,14 +1207,14 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories') $contentResponse = $github->getRepositoryContent($repo['organization'], $repo['name'], $file); $envFile = $contentResponse['content'] ?? ''; - $envLines = \explode("\n", $envFile); - foreach ($envLines as $line) { - $parts = \explode('=', $line, 2); - $envName = \trim($parts[0] ?? ''); - $envValue = \trim($parts[1] ?? ''); - if (!empty($envName)) { + $configAdapter = new ConfigDotenv(); + try { + $envObject = $configAdapter->parse($envFile); + foreach ($envObject as $envName => $envValue) { $envs[$envName] = $envValue; } + } catch (Parse) { + // Silence error, so rest of endpoint can return } } finally { $wg->done(); @@ -1226,7 +1241,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); }); @@ -1783,7 +1798,8 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor throw new Exception(Exception::INSTALLATION_NOT_FOUND); } - $repository = $authorization->skip(fn () => $dbForPlatform->getDocument('repositories', $repositoryId, [ + $repository = $authorization->skip(fn () => $dbForPlatform->findOne('repositories', [ + Query::equal('$id', [$repositoryId]), Query::equal('projectInternalId', [$project->getSequence()]) ])); diff --git a/app/controllers/general.php b/app/controllers/general.php index 6790d3c67d..f08896b4c4 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -4,7 +4,6 @@ require_once __DIR__ . '/../init.php'; use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; -use Appwrite\Auth\Auth; use Appwrite\Auth\Key; use Appwrite\Event\Certificate; use Appwrite\Event\Event; @@ -17,6 +16,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Transformation\Adapter\Preview; use Appwrite\Transformation\Transformation; +use Appwrite\Utopia\Database\Documents\User as DBUser; use Appwrite\Utopia\Request; use Appwrite\Utopia\Request\Filters\V16 as RequestV16; use Appwrite\Utopia\Request\Filters\V17 as RequestV17; @@ -223,7 +223,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw */ $requirePreview = \is_null($apiKey) || !$apiKey->isPreviewAuthDisabled(); if ($isPreview && $requirePreview) { - $cookie = $request->getCookie(Auth::$cookieNamePreview, ''); + $cookie = $request->getCookie(COOKIE_NAME_PREVIEW, ''); $authorized = false; // Security checks to mark authorized true @@ -1263,7 +1263,7 @@ App::error() * If not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php */ if (!$publish && $project->getId() !== 'console') { - if (!Auth::isPrivilegedUser($authorization->getRoles())) { + if (!DBUser::isPrivileged($authorization->getRoles())) { $fileSize = 0; $file = $request->getFiles('file'); if (!empty($file)) { @@ -1623,7 +1623,7 @@ App::get('/_appwrite/authorize') $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration)); $response - ->addCookie(Auth::$cookieNamePreview, $jwt, (new \DateTime($expire))->getTimestamp(), '/', $host, ('https' === $protocol), true, null) + ->addCookie(COOKIE_NAME_PREVIEW, $jwt, (new \DateTime($expire))->getTimestamp(), '/', $host, ('https' === $protocol), true, null) ->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') ->addHeader('Pragma', 'no-cache') ->redirect($protocol . '://' . $host . $path); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 911c8b110d..6f01db12b6 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -1,6 +1,5 @@ $match) { $find = $matches[0][$pos]; @@ -236,42 +235,95 @@ App::init() ->inject('authorization') ->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey, Authorization $authorization) { $route = $utopia->getRoute(); - if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted' && str_starts_with($route->getPath(), '/v1/backups')) { - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Database Backups are available on Appwrite Cloud'); - } + + /** + * Handle user authentication and session validation. + * + * This function follows a series of steps to determine the appropriate user session + * based on cookies, headers, and JWT tokens. + * + * Process: + * + * Project & Role Validation: + * 1. Check if the project is empty. If so, throw an exception. + * 2. Get the roles configuration. + * 3. Determine the role for the user based on the user document. + * 4. Get the scopes for the role. + * + * API Key Authentication: + * 5. If there is an API key: + * - Verify no user session exists simultaneously + * - Check if key is expired + * - Set role and scopes from API key + * - Handle special app role case + * - For standard keys, update last accessed time + * + * User Activity: + * 6. If the project is not the console and user is not admin: + * - Update user's last activity timestamp + * + * Access Control: + * 7. Get the method from the route + * 8. Validate namespace permissions + * 9. Validate scope permissions + * 10. Check if user is blocked + * + * Security Checks: + * 11. Verify password status (check if reset required) + * 12. Validate MFA requirements: + * - Check if MFA is enabled + * - Verify email status + * - Verify phone status + * - Verify authenticator status + * 13. Handle Multi-Factor Authentication: + * - Check remaining required factors + * - Validate factor completion + * - Throw exception if factors incomplete + */ + + // Step 1: Check if project is empty if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } + // Step 2: Get roles configuration $roles = Config::getParam('roles', []); + // Step 3: Determine role for user + // TODO get scopes from the identity instead of the user roles config. The identity will containn the scopes the user authorized for the access token. + $role = $user->isEmpty() ? Role::guests()->toString() : Role::users()->toString(); + // Step 4: Get scopes for the role $scopes = $roles[$role]['scopes']; - // API Key authentication + // Step 5: API Key Authentication if (!empty($apiKey)) { + // Verify no user session exists simultaneously if (!$user->isEmpty()) { throw new Exception(Exception::USER_API_KEY_AND_SESSION_SET); } + // Check if key is expired if ($apiKey->isExpired()) { throw new Exception(Exception::PROJECT_KEY_EXPIRED); } + // Set role and scopes from API key $role = $apiKey->getRole(); $scopes = $apiKey->getScopes(); - if ($apiKey->getRole() === Auth::USER_ROLE_APPS) { + // Handle special app role case + if ($apiKey->getRole() === User::ROLE_APPS) { // Disable authorization checks for API keys $authorization->setDefaultStatus(false); - $user = new Document([ + $user = new User([ '$id' => '', 'status' => true, - 'type' => Auth::ACTIVITY_TYPE_APP, + 'type' => ACTIVITY_TYPE_APP, 'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(), 'password' => '', 'name' => $apiKey->getName(), @@ -280,6 +332,7 @@ App::init() $queueForAudits->setUser($user); } + // For standard keys, update last accessed time if ($apiKey->getType() === API_KEY_STANDARD) { $dbKey = $project->find( key: 'secret', @@ -345,11 +398,11 @@ App::init() $scopes = \array_unique($scopes); $authorization->addRole($role); - foreach (Auth::getRoles($user, $authorization) as $authRole) { + foreach ($user->getRoles($authorization) as $authRole) { $authorization->addRole($authRole); } - // Update project last activity + // Step 6: Update project and user last activity if (!$project->isEmpty() && $project->getId() !== 'console') { $accessedAt = $project->getAttribute('accessedAt', 0); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) { @@ -358,7 +411,6 @@ App::init() } } - // Update user last activity if (!empty($user->getId())) { $accessedAt = $user->getAttribute('accessedAt', 0); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCESS)) > $accessedAt) { @@ -372,6 +424,7 @@ App::init() } } + // Steps 7-9: Access Control - Method, Namespace and Scope Validation /** * @var ?Method $method */ @@ -389,27 +442,29 @@ App::init() if ( array_key_exists($namespace, $project->getAttribute('services', [])) && !$project->getAttribute('services', [])[$namespace] - && !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles())) + && !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles())) ) { throw new Exception(Exception::GENERAL_SERVICE_DISABLED); } } - // Do now allow access if scope is not allowed + // Step 9: Validate scope permissions $allowed = (array)$route->getLabel('scope', 'none'); if (empty(\array_intersect($allowed, $scopes))) { throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scopes (' . \json_encode($allowed) . ')'); } - // Do not allow access to blocked accounts + // Step 10: Check if user is blocked if (false === $user->getAttribute('status')) { // Account is blocked throw new Exception(Exception::USER_BLOCKED); } + // Step 11: Verify password status if ($user->getAttribute('reset')) { throw new Exception(Exception::USER_PASSWORD_RESET_REQUIRED); } + // Step 12: Validate MFA requirements $mfaEnabled = $user->getAttribute('mfa', false); $hasVerifiedEmail = $user->getAttribute('emailVerification', false); $hasVerifiedPhone = $user->getAttribute('phoneVerification', false); @@ -417,6 +472,7 @@ App::init() $hasMoreFactors = $hasVerifiedEmail || $hasVerifiedPhone || $hasVerifiedAuthenticator; $minimumFactors = ($mfaEnabled && $hasMoreFactors) ? 2 : 1; + // Step 13: Handle Multi-Factor Authentication if (!in_array('mfa', $route->getGroups())) { if ($session && \count($session->getAttribute('factors', [])) < $minimumFactors) { throw new Exception(Exception::USER_MORE_FACTORS_REQUIRED); @@ -457,7 +513,7 @@ App::init() if ( array_key_exists('rest', $project->getAttribute('apis', [])) && !$project->getAttribute('apis', [])['rest'] - && !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles())) + && !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles())) ) { throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } @@ -488,8 +544,8 @@ App::init() $closestLimit = null; $roles = $authorization->getRoles(); - $isPrivilegedUser = Auth::isPrivilegedUser($roles); - $isAppUser = Auth::isAppUser($roles); + $isPrivilegedUser = User::isPrivileged($roles); + $isAppUser = User::isApp($roles); foreach ($timeLimitArray as $timeLimit) { foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys @@ -544,7 +600,7 @@ App::init() if (!$user->isEmpty()) { $userClone = clone $user; // $user doesn't support `type` and can cause unintended effects. - $userClone->setAttribute('type', Auth::ACTIVITY_TYPE_USER); + $userClone->setAttribute('type', ACTIVITY_TYPE_USER); $queueForAudits->setUser($userClone); } @@ -587,7 +643,7 @@ App::init() if ($useCache) { $route = $utopia->match($request); $isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview'; - $isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !Auth::isPrivilegedUser($authorization->getRoles()); + $isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !User::isPrivileged($authorization->getRoles()); $key = $request->cacheIdentifier(); $cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key)); @@ -610,7 +666,7 @@ App::init() $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence(); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); @@ -643,7 +699,7 @@ App::init() throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } //Do not update transformedAt if it's a console user - if (!Auth::isPrivilegedUser($authorization->getRoles())) { + if (!User::isPrivileged($authorization->getRoles())) { $transformedAt = $file->getAttribute('transformedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) { $file->setAttribute('transformedAt', DateTime::now()); @@ -792,7 +848,7 @@ App::shutdown() if (!$user->isEmpty()) { $userClone = clone $user; // $user doesn't support `type` and can cause unintended effects. - $userClone->setAttribute('type', Auth::ACTIVITY_TYPE_USER); + $userClone->setAttribute('type', ACTIVITY_TYPE_USER); $queueForAudits->setUser($userClone); } elseif ($queueForAudits->getUser() === null || $queueForAudits->getUser()->isEmpty()) { /** @@ -803,10 +859,10 @@ App::shutdown() * * Therefore, we consider this an anonymous request and create a relevant user. */ - $user = new Document([ + $user = new User([ '$id' => '', 'status' => true, - 'type' => Auth::ACTIVITY_TYPE_GUEST, + 'type' => ACTIVITY_TYPE_GUEST, 'email' => 'guest.' . $project->getId() . '@service.' . $request->getHostname(), 'password' => '', 'name' => 'Guest', @@ -896,7 +952,7 @@ App::shutdown() } if ($project->getId() !== 'console') { - if (!Auth::isPrivilegedUser($authorization->getRoles())) { + if (!User::isPrivileged($authorization->getRoles())) { $fileSize = 0; $file = $request->getFiles('file'); if (!empty($file)) { diff --git a/app/controllers/shared/api/auth.php b/app/controllers/shared/api/auth.php index 0e3c89c19a..c0f7494125 100644 --- a/app/controllers/shared/api/auth.php +++ b/app/controllers/shared/api/auth.php @@ -1,7 +1,7 @@ getAttribute('mfaUpdatedAt'); if (!empty($lastUpdate)) { $now = DateTime::now(); - $maxAllowedDate = DateTime::addSeconds(new \DateTime($lastUpdate), Auth::MFA_RECENT_DURATION); // Maximum date until session is considered safe before asking for another challenge + $maxAllowedDate = DateTime::addSeconds(new \DateTime($lastUpdate), MFA_RECENT_DURATION); // Maximum date until session is considered safe before asking for another challenge $isSessionFresh = DateTime::formatTz($maxAllowedDate) >= DateTime::formatTz($now); } @@ -50,8 +50,8 @@ App::init() $route = $utopia->match($request); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); - $isAppUser = Auth::isAppUser($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); + $isAppUser = User::isApp($authorization->getRoles()); if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs return; diff --git a/app/init/configs.php b/app/init/configs.php index 7572302919..6fa3b576d5 100644 --- a/app/init/configs.php +++ b/app/init/configs.php @@ -1,42 +1,45 @@ find('attributes', [ + $attributes = $database->getAuthorization()->skip(fn () => $database->find('attributes', [ Query::equal('collectionInternalId', [$document->getSequence()]), Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), Query::limit($database->getLimitForAttributes()), - ]); + ])); foreach ($attributes as $attribute) { $attributeType = $attribute->getAttribute('type'); @@ -104,12 +104,12 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - return $database + return $database->getAuthorization()->skip(fn () => $database ->find('indexes', [ Query::equal('collectionInternalId', [$document->getSequence()]), Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]), Query::limit($database->getLimitForIndexes()), - ]); + ])); } ); @@ -119,11 +119,11 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - return $database + return $database->getAuthorization()->skip(fn () => $database ->find('platforms', [ Query::equal('projectInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), - ]); + ])); } ); @@ -133,11 +133,11 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - return $database + return $database->getAuthorization()->skip(fn () => $database ->find('keys', [ Query::equal('projectInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), - ]); + ])); } ); @@ -147,11 +147,11 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - return $database + return $database->getAuthorization()->skip(fn () => $database ->find('devKeys', [ Query::equal('projectInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), - ]); + ])); } ); @@ -161,11 +161,11 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - return $database + return $database->getAuthorization()->skip(fn () => $database ->find('webhooks', [ Query::equal('projectInternalId', [$document->getSequence()]), Query::limit(APP_LIMIT_SUBQUERY), - ]); + ])); } ); @@ -250,14 +250,14 @@ Database::addFilter( default => ['function', 'site'] }; - return $database + return $database->getAuthorization()->skip(fn () => $database ->find('variables', [ Query::equal('resourceInternalId', [$document->getSequence()]), Query::equal('resourceType', $resourceType), Query::orderAsc('resourceType'), Query::orderAsc(), Query::limit(APP_LIMIT_SUBQUERY), - ]); + ])); } ); @@ -293,11 +293,11 @@ Database::addFilter( return; }, function (mixed $value, Document $document, Database $database) { - return $database + return $database->getAuthorization()->skip(fn () => $database ->find('variables', [ Query::equal('resourceType', ['project']), Query::limit(APP_LIMIT_SUBQUERY) - ]); + ])); } ); diff --git a/app/init/registers.php b/app/init/registers.php index 3dc0e22dba..be2009449e 100644 --- a/app/init/registers.php +++ b/app/init/registers.php @@ -39,6 +39,7 @@ if (!App::isProduction()) { PublicDomain::allow(['request-catcher-sms']); PublicDomain::allow(['request-catcher-webhook']); } + $register->set('logger', function () { // Register error logger $providerName = System::getEnv('_APP_LOGGING_PROVIDER', ''); @@ -97,6 +98,51 @@ $register->set('logger', function () { return new Logger($adapter); }); +$register->set('realtimeLogger', function () { + // Register error logger for realtime, falls back to default logging config + $providerConfig = System::getEnv('_APP_LOGGING_CONFIG_REALTIME', '') + ?: System::getEnv('_APP_LOGGING_CONFIG', ''); + + if (empty($providerConfig)) { + return; + } + + $loggingProvider = new DSN($providerConfig); + $providerName = $loggingProvider->getScheme(); + $providerConfig = match ($providerName) { + 'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()], + 'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()], + default => ['key' => $loggingProvider->getHost()], + }; + + if (empty($providerName) || empty($providerConfig)) { + return; + } + + if (!Logger::hasProvider($providerName)) { + throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); + } + + try { + $adapter = match ($providerName) { + 'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']), + 'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']), + 'raygun' => new Raygun($providerConfig['key']), + 'appsignal' => new AppSignal($providerConfig['key']), + default => null + }; + } catch (Throwable $th) { + $adapter = null; + } + + if ($adapter === null) { + Console::error("Logging provider not supported. Logging is disabled"); + return; + } + + return new Logger($adapter); +}); + $register->set('pools', function () { $group = new Group(); @@ -326,7 +372,7 @@ $register->set('smtp', function () { return $mail; }); $register->set('geodb', function () { - return new Reader(__DIR__ . '/../assets/dbip/dbip-country-lite-2024-09.mmdb'); + return new Reader(__DIR__ . '/../assets/dbip/dbip-country-lite-2025-12.mmdb'); }); $register->set('passwordsDictionary', function () { $content = \file_get_contents(__DIR__ . '/../assets/security/10k-common-passwords'); diff --git a/app/init/resources.php b/app/init/resources.php index 8b111e950c..49f726d48c 100644 --- a/app/init/resources.php +++ b/app/init/resources.php @@ -2,7 +2,6 @@ use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; -use Appwrite\Auth\Auth; use Appwrite\Auth\Key; use Appwrite\Databases\TransactionState; use Appwrite\Event\Audit; @@ -23,10 +22,18 @@ use Appwrite\Extend\Exception; use Appwrite\GraphQL\Schema; use Appwrite\Network\Platform; use Appwrite\Network\Validator\Origin; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Request; +use Appwrite\Utopia\Response; use Executor\Executor; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; use Utopia\App; +use Utopia\Auth\Hashes\Argon2; +use Utopia\Auth\Hashes\Sha; +use Utopia\Auth\Proofs\Code; +use Utopia\Auth\Proofs\Password; +use Utopia\Auth\Proofs\Token; +use Utopia\Auth\Store; use Utopia\Cache\Adapter\Pool as CachePool; use Utopia\Cache\Adapter\Sharding; use Utopia\Cache\Cache; @@ -226,77 +233,91 @@ App::setResource('platforms', function (Request $request, Document $console, Doc ]; }, ['request', 'console', 'project', 'dbForPlatform', 'authorization']); -App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform, $authorization) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $project */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Utopia\Database\Database $dbForPlatform */ - /** @var Utopia\Database\Authorization $authorization */ - /** @var string $mode */ +App::setResource('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Store $store, Token $proofForToken, $authorization) { + /** + * Handles user authentication and session validation. + * + * This function follows a series of steps to determine the appropriate user session + * based on cookies, headers, and JWT tokens. + * + * Process: + * 1. Checks the cookie based on mode: + * - If in admin mode, uses console project id for key. + * - Otherwise, sets the key using the project ID + * 2. If no cookie is found, attempts to retrieve the fallback header `x-fallback-cookies`. + * - If this method is used, returns the header: `X-Debug-Fallback: true`. + * 3. Fetches the user document from the appropriate database based on the mode. + * 4. If the user document is empty or the session key cannot be verified, sets an empty user document. + * 5. Regardless of the results from steps 1-4, attempts to fetch the JWT token. + * 6. If the JWT user has a valid session ID, updates the user variable with the user from `projectDB`, + * overwriting the previous value. + */ $authorization->setDefaultStatus(true); - Auth::setCookieName('a_session_' . $project->getId()); + $store->setKey('a_session_' . $project->getId()); if (APP_MODE_ADMIN === $mode) { - Auth::setCookieName('a_session_' . $console->getId()); + $store->setKey('a_session_' . $console->getId()); } - $session = Auth::decodeSession( + $store->decode( $request->getCookie( - Auth::$cookieName, // Get sessions - $request->getCookie(Auth::$cookieName . '_legacy', '') + $store->getKey(), // Get sessions + $request->getCookie($store->getKey() . '_legacy', '') ) ); // Get session from header for SSR clients - if (empty($session['id']) && empty($session['secret'])) { + if (empty($store->getProperty('id', '')) && empty($store->getProperty('secret', ''))) { $sessionHeader = $request->getHeader('x-appwrite-session', ''); if (!empty($sessionHeader)) { - $session = Auth::decodeSession($sessionHeader); + $store->decode($sessionHeader); } } // Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies - if ($response) { + if ($response) { // if in http context - add debug header $response->addHeader('X-Debug-Fallback', 'false'); } - if (empty($session['id']) && empty($session['secret'])) { + if (empty($store->getProperty('id', '')) && empty($store->getProperty('secret', ''))) { if ($response) { $response->addHeader('X-Debug-Fallback', 'true'); } $fallback = $request->getHeader('x-fallback-cookies', ''); $fallback = \json_decode($fallback, true); - $session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : '')); + $store->decode(((is_array($fallback) && isset($fallback[$store->getKey()])) ? $fallback[$store->getKey()] : '')); } - Auth::$unique = $session['id'] ?? ''; - Auth::$secret = $session['secret'] ?? ''; - - $user = new Document([]); - - if (!empty(Auth::$unique)) { - if ($mode === APP_MODE_ADMIN) { - $user = $dbForPlatform->getDocument('users', Auth::$unique); - } elseif (!$project->isEmpty()) { - if ($project->getId() === 'console') { - $user = $dbForPlatform->getDocument('users', Auth::$unique); - } else { - $user = $dbForProject->getDocument('users', Auth::$unique); + $user = null; + if (APP_MODE_ADMIN === $mode) { + /** @var User $user */ + $user = $dbForPlatform->getDocument('users', $store->getProperty('id', '')); + } else { + if ($project->isEmpty()) { + $user = new User([]); + } else { + if (!empty($store->getProperty('id', ''))) { + if ($project->getId() === 'console') { + /** @var User $user */ + $user = $dbForPlatform->getDocument('users', $store->getProperty('id', '')); + } else { + /** @var User $user */ + $user = $dbForProject->getDocument('users', $store->getProperty('id', '')); + } } } } if ( + !$user || $user->isEmpty() // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) + || !$user->sessionVerify($store->getProperty('secret', ''), $proofForToken) ) { // Validate user has valid login token - $user = new Document([]); + $user = new User([]); } - // if (APP_MODE_ADMIN === $mode) { // if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { // $authorization->setDefaultStatus(false); // Cancel security segmentation for admin users. @@ -304,18 +325,14 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons // $user = new Document([]); // } // } - $authJWT = $request->getHeader('x-appwrite-jwt', ''); - if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); - try { $payload = $jwt->decode($authJWT); } catch (JWTException $error) { throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage()); } - $jwtUserId = $payload['userId'] ?? ''; if (!empty($jwtUserId)) { if ($mode === APP_MODE_ADMIN) { @@ -324,20 +341,18 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons $user = $dbForProject->getDocument('users', $jwtUserId); } } - $jwtSessionId = $payload['sessionId'] ?? ''; if (!empty($jwtSessionId)) { if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token - $user = new Document([]); + $user = new User([]); } } } - $dbForProject->setMetadata('user', $user->getId()); $dbForPlatform->setMetadata('user', $user->getId()); return $user; -}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform', 'authorization']); +}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform', 'store', 'proofForToken', 'authorization']); App::setResource('project', function ($dbForPlatform, $request, $console, $authorization) { /** @var Appwrite\Utopia\Request $request */ @@ -355,26 +370,56 @@ App::setResource('project', function ($dbForPlatform, $request, $console, $autho return $project; }, ['dbForPlatform', 'request', 'console', 'authorization']); -App::setResource('session', function (Document $user) { +App::setResource('session', function (User $user, Store $store, Token $proofForToken) { if ($user->isEmpty()) { return; } $sessions = $user->getAttribute('sessions', []); - $sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret); + $sessionId = $user->sessionVerify($store->getProperty('secret', ''), $proofForToken); if (!$sessionId) { return; } - - foreach ($sessions as $session) {/** @var Document $session */ + foreach ($sessions as $session) { + /** @var Document $session */ if ($sessionId === $session->getId()) { return $session; } } return; -}, ['user']); +}, ['user', 'store', 'proofForToken']); + +App::setResource('store', function (): Store { + return new Store(); +}); + +App::setResource('proofForPassword', function (): Password { + $hash = new Argon2(); + $hash + ->setMemoryCost(7168) + ->setTimeCost(5) + ->setThreads(1); + + $password = new Password(); + $password + ->setHash($hash); + + return $password; +}); + +App::setResource('proofForToken', function (): Token { + $token = new Token(); + $token->setHash(new Sha()); + return $token; +}); + +App::setResource('proofForCode', function (): Code { + $code = new Code(); + $code->setHash(new Sha()); + return $code; +}); App::setResource('console', function () { return new Document(Config::getParam('console')); @@ -405,6 +450,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform ->setMetadata('project', $project->getId()) ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API) ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); + $database->setDocumentType('users', User::class); $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -436,6 +482,8 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache, Authoriz ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API) ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); + $database->setDocumentType('users', User::class); + return $database; }, ['pools', 'cache', 'authorization']); @@ -460,7 +508,9 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform ->setMetadata('host', \gethostname()) ->setMetadata('project', $project->getId()) ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API) - ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); + ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES) + ->setDocumentType('users', User::class) + ; $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); @@ -963,7 +1013,8 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request, A $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'), 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/app/realtime.php b/app/realtime.php index 75aa625348..1c4d64b6b5 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -1,13 +1,14 @@ get('pools'); @@ -65,8 +69,8 @@ if (!function_exists('getConsoleDB')) { ->setNamespace('_console') ->setMetadata('host', \gethostname()) ->setMetadata('project', '_console'); - - return $database; + $database->setDocumentType('users', User::class); + return $ctx['dbForPlatform'] = $database; } } @@ -74,14 +78,18 @@ if (!function_exists('getConsoleDB')) { if (!function_exists('getProjectDB')) { function getProjectDB(Document $project): Database { - global $register; + $ctx = Coroutine::getContext(); - static $databases = []; - - if (isset($databases[$project->getSequence()])) { - return $databases[$project->getSequence()]; + if (!isset($ctx['dbForProject'])) { + $ctx['dbForProject'] = []; } + if (isset($ctx['dbForProject'][$project->getSequence()])) { + return $ctx['dbForProject'][$project->getSequence()]; + } + + global $register; + /** @var Group $pools */ $pools = $register->get('pools'); @@ -117,7 +125,9 @@ if (!function_exists('getProjectDB')) { ->setMetadata('host', \gethostname()) ->setMetadata('project', $project->getId()); - return $databases[$project->getSequence()] = $database; + $database->setDocumentType('users', User::class); + + return $ctx['dbForProject'][$project->getSequence()] = $database; } } @@ -125,14 +135,14 @@ if (!function_exists('getProjectDB')) { if (!function_exists('getCache')) { function getCache(): Cache { - global $register; + $ctx = Coroutine::getContext(); - static $cache = null; - - if ($cache !== null) { - return $cache; + if (isset($ctx['cache'])) { + return $ctx['cache']; } + global $register; + $pools = $register->get('pools'); /** @var Group $pools */ $list = Config::getParam('pools-cache', []); @@ -142,7 +152,7 @@ if (!function_exists('getCache')) { $adapters[] = new CachePool($pools->get($value)); } - return $cache = new Cache(new Sharding($adapters)); + return $ctx['cache'] = new Cache(new Sharding($adapters)); } } @@ -150,10 +160,10 @@ if (!function_exists('getCache')) { if (!function_exists('getRedis')) { function getRedis(): \Redis { - static $redis = null; + $ctx = Coroutine::getContext(); - if ($redis !== null) { - return $redis; + if (isset($ctx['redis'])) { + return $ctx['redis']; } $host = System::getEnv('_APP_REDIS_HOST', 'localhost'); @@ -167,46 +177,46 @@ if (!function_exists('getRedis')) { } $redis->setOption(\Redis::OPT_READ_TIMEOUT, -1); - return $redis; + return $ctx['redis'] = $redis; } } if (!function_exists('getTimelimit')) { - function getTimelimit(): TimeLimitRedis + function getTimelimit(string $key = "", int $limit = 0, int $seconds = 1): TimeLimitRedis { - static $timelimit = null; + $ctx = Coroutine::getContext(); - if ($timelimit !== null) { - return $timelimit; + if (isset($ctx['timelimit'])) { + return $ctx['timelimit']; } - return $timelimit = new TimeLimitRedis("", 0, 1, getRedis()); + return $ctx['timelimit'] = new TimeLimitRedis($key, $limit, $seconds, getRedis()); } } if (!function_exists('getRealtime')) { function getRealtime(): Realtime { - static $realtime = null; + $ctx = Coroutine::getContext(); - if ($realtime !== null) { - return $realtime; + if (isset($ctx['realtime'])) { + return $ctx['realtime']; } - return $realtime = new Realtime(); + return $ctx['realtime'] = new Realtime(); } } if (!function_exists('getTelemetry')) { function getTelemetry(int $workerId): Utopia\Telemetry\Adapter { - static $telemetry = null; + $ctx = Coroutine::getContext(); - if ($telemetry !== null) { - return $telemetry; + if (isset($ctx['telemetry'])) { + return $ctx['telemetry']; } - return $telemetry = new NoTelemetry(); + return $ctx['telemetry'] = new NoTelemetry(); } } @@ -235,7 +245,7 @@ $adapter $server = new Server($adapter); $logError = function (Throwable $error, string $action) use ($register) { - $logger = $register->get('logger'); + $logger = $register->get('realtimeLogger'); if ($logger && !$error instanceof Exception) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); @@ -456,8 +466,10 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $project = $consoleDatabase->getAuthorization()->skip(fn () => $consoleDatabase->getDocument('projects', $projectId)); $database = getProjectDB($project); + /** @var Appwrite\Utopia\Database\Documents\User $user */ $user = $database->getDocument('users', $userId); - $roles = Auth::getRoles($user, $database->getAuthorization()); + + $roles = $user->getRoles($database->getAuthorization()); $channels = $realtime->connections[$connection]['channels']; $realtime->unsubscribe($connection); @@ -525,14 +537,14 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, if ( array_key_exists('realtime', $project->getAttribute('apis', [])) && !$project->getAttribute('apis', [])['realtime'] - && !(Auth::isPrivilegedUser($authorization->getRoles()) || Auth::isAppUser($authorization->getRoles())) + && !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles())) ) { throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED); } $timelimit = $app->getResource('timelimit'); $platforms = $app->getResource('platforms'); - $user = $app->getResource('user'); /** @var Document $user */ + $user = $app->getResource('user'); /** @var User $user */ /* * Abuse Check @@ -562,7 +574,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription()); } - $roles = Auth::getRoles($user, $authorization); + $roles = $user->getRoles($authorization); $channels = Realtime::convertChannels($request->getQuery('channels', []), $user->getId()); @@ -575,6 +587,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $realtime->subscribe($project->getId(), $connection, $roles, $channels); + $realtime->connections[$connection]['authorization'] = $authorization; + $user = empty($user->getId()) ? null : $response->output($user, Response::MODEL_ACCOUNT); $server->send([$connection], json_encode([ @@ -603,6 +617,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $code = 500; } + $message = $th->getMessage(); // sanitize 0 && 5xx errors @@ -632,12 +647,19 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $server->onMessage(function (int $connection, string $message) use ($server, $register, $realtime, $containerId) { try { $response = new Response(new SwooleResponse()); - $projectId = $realtime->connections[$connection]['projectId']; + $projectId = $realtime->connections[$connection]['projectId'] ?? null; + + // Get authorization from connection (stored during onOpen) + $authorization = $realtime->connections[$connection]['authorization'] ?? null; + $database = getConsoleDB(); + $database->setAuthorization($authorization); if ($projectId !== 'console') { - $project = $database->getAuthorization()->skip(fn () => $database->getDocument('projects', $projectId)); - $database = getProjectDB($project, $database->getAuthorization()); + $project = $authorization->skip(fn () => $database->getDocument('projects', $projectId)); + + $database = getProjectDB($project); + $database->setAuthorization($authorization); } else { $project = null; } @@ -677,24 +699,43 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Payload is not valid.'); } - $session = Auth::decodeSession($message['data']['session']); - Auth::$unique = $session['id'] ?? ''; - Auth::$secret = $session['secret'] ?? ''; + $store = new Store(); - $user = $database->getDocument('users', Auth::$unique); + $store->decode($message['data']['session']); + + /** @var User $user */ + $user = $database->getDocument('users', $store->getProperty('id', '')); + + /** + * TODO: + * Moving forward, we should try to use our dependency injection container + * to inject the proof for token. + * This way we will have one source of truth for the proof for token. + */ + $proofForToken = new Token(); + $proofForToken->setHash(new Sha()); if ( empty($user->getId()) // Check a document has been found in the DB - || !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) // Validate user has valid login token + || !$user->sessionVerify($store->getProperty('secret', ''), $proofForToken) // Validate user has valid login token ) { // cookie not valid throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.'); } - $roles = Auth::getRoles($user, $database->getAuthorization()); + $roles = $user->getRoles($database->getAuthorization()); $channels = Realtime::convertChannels(array_flip($realtime->connections[$connection]['channels']), $user->getId()); + + // Preserve authorization before subscribe overwrites the connection array + $authorization = $realtime->connections[$connection]['authorization'] ?? null; + $realtime->subscribe($realtime->connections[$connection]['projectId'], $connection, $roles, $channels); + // Restore authorization after subscribe + if ($authorization !== null) { + $realtime->connections[$connection]['authorization'] = $authorization; + } + $user = $response->output($user, Response::MODEL_ACCOUNT); $server->send([$connection], json_encode([ 'type' => 'response', diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 9a2669fb5c..34e0aee1ae 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -849,7 +849,7 @@ $image = $this->getParam('image', ''); - _APP_DB_PASS appwrite-assistant: - image: appwrite/assistant:0.8.3 + image: appwrite/assistant:0.8.4 container_name: appwrite-assistant <<: *x-logging restart: unless-stopped @@ -857,9 +857,9 @@ $image = $this->getParam('image', ''); - appwrite environment: - _APP_ASSISTANT_OPENAI_API_KEY - + appwrite-browser: - image: appwrite/browser:0.3.1 + image: appwrite/browser:0.3.2 container_name: appwrite-browser <<: *x-logging restart: unless-stopped diff --git a/app/worker.php b/app/worker.php index ef04ffec05..1988208984 100644 --- a/app/worker.php +++ b/app/worker.php @@ -17,6 +17,7 @@ use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; use Appwrite\Event\Webhook; use Appwrite\Platform\Appwrite; +use Appwrite\Utopia\Database\Documents\User; use Executor\Executor; use Swoole\Runtime; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; @@ -62,7 +63,9 @@ Server::setResource('dbForPlatform', function (Cache $cache, Registry $register, $dbForPlatform ->setAuthorization($authorization) - ->setNamespace('_console'); + ->setNamespace('_console') + ->setDocumentType('users', User::class) + ; return $dbForPlatform; @@ -95,6 +98,7 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register, $adapter = new DatabasePool($pools->get($dsn->getHost())); $database = new Database($adapter, $cache); + $database->setDocumentType('users', User::class); $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); diff --git a/composer.json b/composer.json index c24806bfc3..61e59f023e 100644 --- a/composer.json +++ b/composer.json @@ -48,9 +48,10 @@ "utopia-php/abuse": "1.*", "utopia-php/analytics": "0.10.*", "utopia-php/audit": "1.*", + "utopia-php/auth": "0.5.*", "utopia-php/cache": "0.13.*", "utopia-php/cli": "0.15.*", - "utopia-php/config": "0.2.*", + "utopia-php/config": "1.*.*", "utopia-php/database": "dev-cache-documents-xlist as 4.3.0", "utopia-php/detector": "0.2.*", "utopia-php/domains": "0.9.*", @@ -63,7 +64,7 @@ "utopia-php/locale": "0.8.*", "utopia-php/logger": "0.6.*", "utopia-php/messaging": "0.20.*", - "utopia-php/migration": "1.*", + "utopia-php/migration": "1.3.*", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "0.7.*", "utopia-php/pools": "0.8.*", @@ -74,7 +75,7 @@ "utopia-php/swoole": "0.8.*", "utopia-php/system": "0.9.*", "utopia-php/telemetry": "0.1.*", - "utopia-php/vcs": "0.12.*", + "utopia-php/vcs": "0.13.*", "utopia-php/websocket": "0.3.*", "matomo/device-detector": "6.4.*", "dragonmantank/cron-expression": "3.4.*", diff --git a/docker-compose.override.yml b/docker-compose.override.yml deleted file mode 100644 index 52c5058ab9..0000000000 --- a/docker-compose.override.yml +++ /dev/null @@ -1,7 +0,0 @@ -services: - appwrite: - # volumes: - # - ~/.ssh:/root/.ssh - environment: - - GH_TOKEN= - - GIT_EMAIL= diff --git a/docker-compose.yml b/docker-compose.yml index ede1011af3..6cf8070691 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -285,6 +285,7 @@ services: - _APP_DB_PASS - _APP_USAGE_STATS - _APP_LOGGING_CONFIG + - _APP_LOGGING_CONFIG_REALTIME - _APP_DATABASE_SHARED_TABLES appwrite-worker-audits: @@ -950,7 +951,7 @@ services: appwrite-assistant: container_name: appwrite-assistant - image: appwrite/assistant:0.8.3 + image: appwrite/assistant:0.8.4 networks: - appwrite environment: @@ -958,7 +959,7 @@ services: appwrite-browser: container_name: appwrite-browser - image: appwrite/browser:0.3.1 + image: appwrite/browser:0.3.2 networks: - appwrite diff --git a/docs/examples/1.5.x/client-rest/examples/account/create-challenge.md b/docs/examples/1.5.x/client-rest/examples/account/create-challenge.md index dbdbc1f16a..8d803e56a9 100644 --- a/docs/examples/1.5.x/client-rest/examples/account/create-challenge.md +++ b/docs/examples/1.5.x/client-rest/examples/account/create-challenge.md @@ -1,4 +1,4 @@ -POST /v1/account/mfa/challenge HTTP/1.1 +POST /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.5.0 diff --git a/docs/examples/1.5.x/client-rest/examples/account/create-mfa-challenge.md b/docs/examples/1.5.x/client-rest/examples/account/create-mfa-challenge.md index 2019a1e52c..0553c4b5ba 100644 --- a/docs/examples/1.5.x/client-rest/examples/account/create-mfa-challenge.md +++ b/docs/examples/1.5.x/client-rest/examples/account/create-mfa-challenge.md @@ -1,4 +1,4 @@ -POST /v1/account/mfa/challenge HTTP/1.1 +POST /v1/account/mfa/challenges HTTP/1.1 Host: <REGION>.cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.6.0 diff --git a/docs/examples/1.5.x/client-rest/examples/account/create2f-a-challenge.md b/docs/examples/1.5.x/client-rest/examples/account/create2f-a-challenge.md index dbdbc1f16a..8d803e56a9 100644 --- a/docs/examples/1.5.x/client-rest/examples/account/create2f-a-challenge.md +++ b/docs/examples/1.5.x/client-rest/examples/account/create2f-a-challenge.md @@ -1,4 +1,4 @@ -POST /v1/account/mfa/challenge HTTP/1.1 +POST /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.5.0 diff --git a/docs/examples/1.5.x/client-rest/examples/account/update-challenge.md b/docs/examples/1.5.x/client-rest/examples/account/update-challenge.md index 5e50a0e88d..00c85da373 100644 --- a/docs/examples/1.5.x/client-rest/examples/account/update-challenge.md +++ b/docs/examples/1.5.x/client-rest/examples/account/update-challenge.md @@ -1,4 +1,4 @@ -PUT /v1/account/mfa/challenge HTTP/1.1 +PUT /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.5.0 diff --git a/docs/examples/1.5.x/client-rest/examples/account/update-mfa-challenge.md b/docs/examples/1.5.x/client-rest/examples/account/update-mfa-challenge.md index 520b587562..093cc37930 100644 --- a/docs/examples/1.5.x/client-rest/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.5.x/client-rest/examples/account/update-mfa-challenge.md @@ -1,4 +1,4 @@ -PUT /v1/account/mfa/challenge HTTP/1.1 +PUT /v1/account/mfa/challenges HTTP/1.1 Host: <REGION>.cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.6.0 diff --git a/docs/examples/1.5.x/server-rest/examples/account/create-challenge.md b/docs/examples/1.5.x/server-rest/examples/account/create-challenge.md index dbdbc1f16a..8d803e56a9 100644 --- a/docs/examples/1.5.x/server-rest/examples/account/create-challenge.md +++ b/docs/examples/1.5.x/server-rest/examples/account/create-challenge.md @@ -1,4 +1,4 @@ -POST /v1/account/mfa/challenge HTTP/1.1 +POST /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.5.0 diff --git a/docs/examples/1.5.x/server-rest/examples/account/create-mfa-challenge.md b/docs/examples/1.5.x/server-rest/examples/account/create-mfa-challenge.md index 2019a1e52c..0553c4b5ba 100644 --- a/docs/examples/1.5.x/server-rest/examples/account/create-mfa-challenge.md +++ b/docs/examples/1.5.x/server-rest/examples/account/create-mfa-challenge.md @@ -1,4 +1,4 @@ -POST /v1/account/mfa/challenge HTTP/1.1 +POST /v1/account/mfa/challenges HTTP/1.1 Host: <REGION>.cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.6.0 diff --git a/docs/examples/1.5.x/server-rest/examples/account/create2f-a-challenge.md b/docs/examples/1.5.x/server-rest/examples/account/create2f-a-challenge.md index dbdbc1f16a..8d803e56a9 100644 --- a/docs/examples/1.5.x/server-rest/examples/account/create2f-a-challenge.md +++ b/docs/examples/1.5.x/server-rest/examples/account/create2f-a-challenge.md @@ -1,4 +1,4 @@ -POST /v1/account/mfa/challenge HTTP/1.1 +POST /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.5.0 diff --git a/docs/examples/1.5.x/server-rest/examples/account/update-challenge.md b/docs/examples/1.5.x/server-rest/examples/account/update-challenge.md index 5e50a0e88d..00c85da373 100644 --- a/docs/examples/1.5.x/server-rest/examples/account/update-challenge.md +++ b/docs/examples/1.5.x/server-rest/examples/account/update-challenge.md @@ -1,4 +1,4 @@ -PUT /v1/account/mfa/challenge HTTP/1.1 +PUT /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.5.0 diff --git a/docs/examples/1.5.x/server-rest/examples/account/update-mfa-challenge.md b/docs/examples/1.5.x/server-rest/examples/account/update-mfa-challenge.md index 520b587562..093cc37930 100644 --- a/docs/examples/1.5.x/server-rest/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.5.x/server-rest/examples/account/update-mfa-challenge.md @@ -1,4 +1,4 @@ -PUT /v1/account/mfa/challenge HTTP/1.1 +PUT /v1/account/mfa/challenges HTTP/1.1 Host: <REGION>.cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.6.0 diff --git a/docs/examples/1.6.x/client-rest/examples/account/create-mfa-challenge.md b/docs/examples/1.6.x/client-rest/examples/account/create-mfa-challenge.md index 95bf2c4926..c3007fc290 100644 --- a/docs/examples/1.6.x/client-rest/examples/account/create-mfa-challenge.md +++ b/docs/examples/1.6.x/client-rest/examples/account/create-mfa-challenge.md @@ -1,4 +1,4 @@ -POST /v1/account/mfa/challenge HTTP/1.1 +POST /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.6.0 diff --git a/docs/examples/1.6.x/client-rest/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/client-rest/examples/account/update-mfa-challenge.md index 5bd401cc4e..779aeb2ecd 100644 --- a/docs/examples/1.6.x/client-rest/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/client-rest/examples/account/update-mfa-challenge.md @@ -1,4 +1,4 @@ -PUT /v1/account/mfa/challenge HTTP/1.1 +PUT /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.6.0 diff --git a/docs/examples/1.6.x/server-rest/examples/account/create-mfa-challenge.md b/docs/examples/1.6.x/server-rest/examples/account/create-mfa-challenge.md index 95bf2c4926..c3007fc290 100644 --- a/docs/examples/1.6.x/server-rest/examples/account/create-mfa-challenge.md +++ b/docs/examples/1.6.x/server-rest/examples/account/create-mfa-challenge.md @@ -1,4 +1,4 @@ -POST /v1/account/mfa/challenge HTTP/1.1 +POST /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.6.0 diff --git a/docs/examples/1.6.x/server-rest/examples/account/update-mfa-challenge.md b/docs/examples/1.6.x/server-rest/examples/account/update-mfa-challenge.md index 5bd401cc4e..779aeb2ecd 100644 --- a/docs/examples/1.6.x/server-rest/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.6.x/server-rest/examples/account/update-mfa-challenge.md @@ -1,4 +1,4 @@ -PUT /v1/account/mfa/challenge HTTP/1.1 +PUT /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.6.0 diff --git a/docs/examples/1.7.x/client-rest/examples/account/create-mfa-challenge.md b/docs/examples/1.7.x/client-rest/examples/account/create-mfa-challenge.md index 9a84c0ef69..bda2de889d 100644 --- a/docs/examples/1.7.x/client-rest/examples/account/create-mfa-challenge.md +++ b/docs/examples/1.7.x/client-rest/examples/account/create-mfa-challenge.md @@ -1,4 +1,4 @@ -POST /v1/account/mfa/challenge HTTP/1.1 +POST /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.7.0 diff --git a/docs/examples/1.7.x/client-rest/examples/account/update-mfa-challenge.md b/docs/examples/1.7.x/client-rest/examples/account/update-mfa-challenge.md index ddc27ae334..506059dc3d 100644 --- a/docs/examples/1.7.x/client-rest/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.7.x/client-rest/examples/account/update-mfa-challenge.md @@ -1,4 +1,4 @@ -PUT /v1/account/mfa/challenge HTTP/1.1 +PUT /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.7.0 diff --git a/docs/examples/1.7.x/server-rest/examples/account/create-mfa-challenge.md b/docs/examples/1.7.x/server-rest/examples/account/create-mfa-challenge.md index 9a84c0ef69..bda2de889d 100644 --- a/docs/examples/1.7.x/server-rest/examples/account/create-mfa-challenge.md +++ b/docs/examples/1.7.x/server-rest/examples/account/create-mfa-challenge.md @@ -1,4 +1,4 @@ -POST /v1/account/mfa/challenge HTTP/1.1 +POST /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.7.0 diff --git a/docs/examples/1.7.x/server-rest/examples/account/update-mfa-challenge.md b/docs/examples/1.7.x/server-rest/examples/account/update-mfa-challenge.md index ddc27ae334..506059dc3d 100644 --- a/docs/examples/1.7.x/server-rest/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.7.x/server-rest/examples/account/update-mfa-challenge.md @@ -1,4 +1,4 @@ -PUT /v1/account/mfa/challenge HTTP/1.1 +PUT /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.7.0 diff --git a/docs/examples/1.8.x/client-android/java/account/create-o-auth-2-session.md b/docs/examples/1.8.x/client-android/java/account/create-o-auth-2-session.md index 4420859ce3..5ada9368ea 100644 --- a/docs/examples/1.8.x/client-android/java/account/create-o-auth-2-session.md +++ b/docs/examples/1.8.x/client-android/java/account/create-o-auth-2-session.md @@ -13,7 +13,7 @@ account.createOAuth2Session( OAuthProvider.AMAZON, // provider "https://example.com", // success (optional) "https://example.com", // failure (optional) - listOf(), // scopes (optional) + List.of(), // scopes (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/account/create-o-auth-2-token.md b/docs/examples/1.8.x/client-android/java/account/create-o-auth-2-token.md index e5590c8ceb..f1122dc8f3 100644 --- a/docs/examples/1.8.x/client-android/java/account/create-o-auth-2-token.md +++ b/docs/examples/1.8.x/client-android/java/account/create-o-auth-2-token.md @@ -13,7 +13,7 @@ account.createOAuth2Token( OAuthProvider.AMAZON, // provider "https://example.com", // success (optional) "https://example.com", // failure (optional) - listOf(), // scopes (optional) + List.of(), // scopes (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/account/list-identities.md b/docs/examples/1.8.x/client-android/java/account/list-identities.md index 327fe39537..e5dd44d9b7 100644 --- a/docs/examples/1.8.x/client-android/java/account/list-identities.md +++ b/docs/examples/1.8.x/client-android/java/account/list-identities.md @@ -9,7 +9,7 @@ Client client = new Client(context) Account account = new Account(client); account.listIdentities( - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/account/list-logs.md b/docs/examples/1.8.x/client-android/java/account/list-logs.md index 4562ecc3ba..8b6ec85067 100644 --- a/docs/examples/1.8.x/client-android/java/account/list-logs.md +++ b/docs/examples/1.8.x/client-android/java/account/list-logs.md @@ -9,7 +9,7 @@ Client client = new Client(context) Account account = new Account(client); account.listLogs( - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/account/update-prefs.md b/docs/examples/1.8.x/client-android/java/account/update-prefs.md index 4bd6940c61..2682fe3aeb 100644 --- a/docs/examples/1.8.x/client-android/java/account/update-prefs.md +++ b/docs/examples/1.8.x/client-android/java/account/update-prefs.md @@ -9,10 +9,10 @@ Client client = new Client(context) Account account = new Account(client); account.updatePrefs( - mapOf( - "language" to "en", - "timezone" to "UTC", - "darkTheme" to true + Map.of( + "language", "en", + "timezone", "UTC", + "darkTheme", true ), // prefs new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/avatars/get-screenshot.md b/docs/examples/1.8.x/client-android/java/avatars/get-screenshot.md index 48babdca20..ce8372dd37 100644 --- a/docs/examples/1.8.x/client-android/java/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/client-android/java/avatars/get-screenshot.md @@ -13,24 +13,27 @@ Avatars avatars = new Avatars(client); avatars.getScreenshot( "https://example.com", // url - mapOf( "a" to "b" ), // headers (optional) - 1, // viewportWidth (optional) - 1, // viewportHeight (optional) - 0.1, // scale (optional) + Map.of( + "Authorization", "Bearer token123", + "X-Custom-Header", "value" + ), // headers (optional) + 1920, // viewportWidth (optional) + 1080, // viewportHeight (optional) + 2, // scale (optional) Theme.LIGHT, // theme (optional) - "", // userAgent (optional) - false, // fullpage (optional) - "", // locale (optional) + "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15", // userAgent (optional) + true, // fullpage (optional) + "en-US", // locale (optional) Timezone.AFRICA_ABIDJAN, // timezone (optional) - -90, // latitude (optional) - -180, // longitude (optional) - 0, // accuracy (optional) - false, // touch (optional) - listOf(), // permissions (optional) - 0, // sleep (optional) - 0, // width (optional) - 0, // height (optional) - -1, // quality (optional) + 37.7749, // latitude (optional) + -122.4194, // longitude (optional) + 100, // accuracy (optional) + true, // touch (optional) + List.of("geolocation", "notifications"), // permissions (optional) + 3, // sleep (optional) + 800, // width (optional) + 600, // height (optional) + 85, // quality (optional) Output.JPG, // output (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/databases/create-document.md b/docs/examples/1.8.x/client-android/java/databases/create-document.md index bfd4601ab4..d95db8a9b0 100644 --- a/docs/examples/1.8.x/client-android/java/databases/create-document.md +++ b/docs/examples/1.8.x/client-android/java/databases/create-document.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Databases; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Databases; Client client = new Client(context) .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -14,14 +14,14 @@ databases.createDocument( "", // databaseId "", // collectionId "", // documentId - mapOf( - "username" to "walter.obrien", - "email" to "walter.obrien@example.com", - "fullName" to "Walter O'Brien", - "age" to 30, - "isAdmin" to false + Map.of( + "username", "walter.obrien", + "email", "walter.obrien@example.com", + "fullName", "Walter O'Brien", + "age", 30, + "isAdmin", false ), // data - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/databases/create-operations.md b/docs/examples/1.8.x/client-android/java/databases/create-operations.md index def58af773..a8635d81e9 100644 --- a/docs/examples/1.8.x/client-android/java/databases/create-operations.md +++ b/docs/examples/1.8.x/client-android/java/databases/create-operations.md @@ -10,17 +10,15 @@ Databases databases = new Databases(client); databases.createOperations( "", // transactionId - listOf( - { - "action": "create", - "databaseId": "", - "collectionId": "", - "documentId": "", - "data": { - "name": "Walter O'Brien" - } - } - ), // operations (optional) + List.of(Map.of( + "action", "create", + "databaseId", "", + "collectionId", "", + "documentId", "", + "data", Map.of( + "name", "Walter O'Brien" + ) + )), // operations (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/databases/get-document.md b/docs/examples/1.8.x/client-android/java/databases/get-document.md index 92d6b5ed23..85f9bb9b13 100644 --- a/docs/examples/1.8.x/client-android/java/databases/get-document.md +++ b/docs/examples/1.8.x/client-android/java/databases/get-document.md @@ -12,7 +12,7 @@ databases.getDocument( "", // databaseId "", // collectionId "", // documentId - listOf(), // queries (optional) + List.of(), // queries (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/databases/list-documents.md b/docs/examples/1.8.x/client-android/java/databases/list-documents.md index e4102ec542..5440f786cc 100644 --- a/docs/examples/1.8.x/client-android/java/databases/list-documents.md +++ b/docs/examples/1.8.x/client-android/java/databases/list-documents.md @@ -11,7 +11,7 @@ Databases databases = new Databases(client); databases.listDocuments( "", // databaseId "", // collectionId - listOf(), // queries (optional) + List.of(), // queries (optional) "", // transactionId (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/client-android/java/databases/list-transactions.md b/docs/examples/1.8.x/client-android/java/databases/list-transactions.md index 39f58f3490..899a3ea181 100644 --- a/docs/examples/1.8.x/client-android/java/databases/list-transactions.md +++ b/docs/examples/1.8.x/client-android/java/databases/list-transactions.md @@ -9,7 +9,7 @@ Client client = new Client(context) Databases databases = new Databases(client); databases.listTransactions( - listOf(), // queries (optional) + List.of(), // queries (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/databases/update-document.md b/docs/examples/1.8.x/client-android/java/databases/update-document.md index d3a3967d5b..0f13f74e08 100644 --- a/docs/examples/1.8.x/client-android/java/databases/update-document.md +++ b/docs/examples/1.8.x/client-android/java/databases/update-document.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Databases; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Databases; Client client = new Client(context) .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -14,8 +14,8 @@ databases.updateDocument( "", // databaseId "", // collectionId "", // documentId - mapOf( "a" to "b" ), // data (optional) - listOf(Permission.read(Role.any())), // permissions (optional) + Map.of("a", "b"), // data (optional) + List.of(Permission.read(Role.any())), // permissions (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/databases/upsert-document.md b/docs/examples/1.8.x/client-android/java/databases/upsert-document.md index e46afa10a9..8c72734f98 100644 --- a/docs/examples/1.8.x/client-android/java/databases/upsert-document.md +++ b/docs/examples/1.8.x/client-android/java/databases/upsert-document.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Databases; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Databases; Client client = new Client(context) .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -14,8 +14,8 @@ databases.upsertDocument( "", // databaseId "", // collectionId "", // documentId - mapOf( "a" to "b" ), // data - listOf(Permission.read(Role.any())), // permissions (optional) + Map.of("a", "b"), // data + List.of(Permission.read(Role.any())), // permissions (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/functions/create-execution.md b/docs/examples/1.8.x/client-android/java/functions/create-execution.md index 758881d13c..4aea0929f1 100644 --- a/docs/examples/1.8.x/client-android/java/functions/create-execution.md +++ b/docs/examples/1.8.x/client-android/java/functions/create-execution.md @@ -15,7 +15,7 @@ functions.createExecution( false, // async (optional) "", // path (optional) ExecutionMethod.GET, // method (optional) - mapOf( "a" to "b" ), // headers (optional) + Map.of("a", "b"), // headers (optional) "", // scheduledAt (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/functions/list-executions.md b/docs/examples/1.8.x/client-android/java/functions/list-executions.md index c9a1df107e..893d098998 100644 --- a/docs/examples/1.8.x/client-android/java/functions/list-executions.md +++ b/docs/examples/1.8.x/client-android/java/functions/list-executions.md @@ -10,7 +10,7 @@ Functions functions = new Functions(client); functions.listExecutions( "", // functionId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/graphql/mutation.md b/docs/examples/1.8.x/client-android/java/graphql/mutation.md index 25f095e1b1..445195275a 100644 --- a/docs/examples/1.8.x/client-android/java/graphql/mutation.md +++ b/docs/examples/1.8.x/client-android/java/graphql/mutation.md @@ -9,7 +9,7 @@ Client client = new Client(context) Graphql graphql = new Graphql(client); graphql.mutation( - mapOf( "a" to "b" ), // query + Map.of("a", "b"), // query new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/graphql/query.md b/docs/examples/1.8.x/client-android/java/graphql/query.md index 6b2a04d0b6..b39f128165 100644 --- a/docs/examples/1.8.x/client-android/java/graphql/query.md +++ b/docs/examples/1.8.x/client-android/java/graphql/query.md @@ -9,7 +9,7 @@ Client client = new Client(context) Graphql graphql = new Graphql(client); graphql.query( - mapOf( "a" to "b" ), // query + Map.of("a", "b"), // query new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/storage/create-file.md b/docs/examples/1.8.x/client-android/java/storage/create-file.md index 8de00099b0..518bbc9d29 100644 --- a/docs/examples/1.8.x/client-android/java/storage/create-file.md +++ b/docs/examples/1.8.x/client-android/java/storage/create-file.md @@ -1,9 +1,9 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; import io.appwrite.models.InputFile; -import io.appwrite.services.Storage; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Storage; Client client = new Client(context) .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,7 +15,7 @@ storage.createFile( "", // bucketId "", // fileId InputFile.fromPath("file.png"), // file - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/storage/list-files.md b/docs/examples/1.8.x/client-android/java/storage/list-files.md index 178027cc5d..05292a6465 100644 --- a/docs/examples/1.8.x/client-android/java/storage/list-files.md +++ b/docs/examples/1.8.x/client-android/java/storage/list-files.md @@ -10,7 +10,7 @@ Storage storage = new Storage(client); storage.listFiles( "", // bucketId - listOf(), // queries (optional) + List.of(), // queries (optional) "", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/client-android/java/storage/update-file.md b/docs/examples/1.8.x/client-android/java/storage/update-file.md index 1e21b3fae1..40cc61b1bf 100644 --- a/docs/examples/1.8.x/client-android/java/storage/update-file.md +++ b/docs/examples/1.8.x/client-android/java/storage/update-file.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Storage; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Storage; Client client = new Client(context) .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -14,7 +14,7 @@ storage.updateFile( "", // bucketId "", // fileId "", // name (optional) - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/create-operations.md b/docs/examples/1.8.x/client-android/java/tablesdb/create-operations.md index 7ca288c2ae..0e8cb4b067 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/create-operations.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/create-operations.md @@ -10,17 +10,15 @@ TablesDB tablesDB = new TablesDB(client); tablesDB.createOperations( "", // transactionId - listOf( - { - "action": "create", - "databaseId": "", - "tableId": "", - "rowId": "", - "data": { - "name": "Walter O'Brien" - } - } - ), // operations (optional) + List.of(Map.of( + "action", "create", + "databaseId", "", + "tableId", "", + "rowId", "", + "data", Map.of( + "name", "Walter O'Brien" + ) + )), // operations (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/create-row.md b/docs/examples/1.8.x/client-android/java/tablesdb/create-row.md index f7aa10e5c7..b3e4f310db 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/create-row.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/create-row.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.TablesDB; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.TablesDB; Client client = new Client(context) .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -14,14 +14,14 @@ tablesDB.createRow( "", // databaseId "", // tableId "", // rowId - mapOf( - "username" to "walter.obrien", - "email" to "walter.obrien@example.com", - "fullName" to "Walter O'Brien", - "age" to 30, - "isAdmin" to false + Map.of( + "username", "walter.obrien", + "email", "walter.obrien@example.com", + "fullName", "Walter O'Brien", + "age", 30, + "isAdmin", false ), // data - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/get-row.md b/docs/examples/1.8.x/client-android/java/tablesdb/get-row.md index 45efc6b061..2bf847284a 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/get-row.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/get-row.md @@ -12,7 +12,7 @@ tablesDB.getRow( "", // databaseId "", // tableId "", // rowId - listOf(), // queries (optional) + List.of(), // queries (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/list-rows.md b/docs/examples/1.8.x/client-android/java/tablesdb/list-rows.md index 3bd1e1c77a..0833929eb1 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/list-rows.md @@ -11,7 +11,7 @@ TablesDB tablesDB = new TablesDB(client); tablesDB.listRows( "", // databaseId "", // tableId - listOf(), // queries (optional) + List.of(), // queries (optional) "", // transactionId (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/list-transactions.md b/docs/examples/1.8.x/client-android/java/tablesdb/list-transactions.md index c37ccf1931..ce0147b036 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/list-transactions.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/list-transactions.md @@ -9,7 +9,7 @@ Client client = new Client(context) TablesDB tablesDB = new TablesDB(client); tablesDB.listTransactions( - listOf(), // queries (optional) + List.of(), // queries (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/update-row.md b/docs/examples/1.8.x/client-android/java/tablesdb/update-row.md index 3abaf90603..89f2646d9d 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/update-row.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/update-row.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.TablesDB; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.TablesDB; Client client = new Client(context) .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -14,8 +14,8 @@ tablesDB.updateRow( "", // databaseId "", // tableId "", // rowId - mapOf( "a" to "b" ), // data (optional) - listOf(Permission.read(Role.any())), // permissions (optional) + Map.of("a", "b"), // data (optional) + List.of(Permission.read(Role.any())), // permissions (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/tablesdb/upsert-row.md b/docs/examples/1.8.x/client-android/java/tablesdb/upsert-row.md index 6f979fc126..c6cfe6d1bc 100644 --- a/docs/examples/1.8.x/client-android/java/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/client-android/java/tablesdb/upsert-row.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.TablesDB; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.TablesDB; Client client = new Client(context) .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -14,8 +14,8 @@ tablesDB.upsertRow( "", // databaseId "", // tableId "", // rowId - mapOf( "a" to "b" ), // data (optional) - listOf(Permission.read(Role.any())), // permissions (optional) + Map.of("a", "b"), // data (optional) + List.of(Permission.read(Role.any())), // permissions (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/client-android/java/teams/create-membership.md b/docs/examples/1.8.x/client-android/java/teams/create-membership.md index bb5293ef63..e5eee207dc 100644 --- a/docs/examples/1.8.x/client-android/java/teams/create-membership.md +++ b/docs/examples/1.8.x/client-android/java/teams/create-membership.md @@ -10,7 +10,7 @@ Teams teams = new Teams(client); teams.createMembership( "", // teamId - listOf(), // roles + List.of(), // roles "email@example.com", // email (optional) "", // userId (optional) "+12065550100", // phone (optional) diff --git a/docs/examples/1.8.x/client-android/java/teams/create.md b/docs/examples/1.8.x/client-android/java/teams/create.md index ae2fdf32c8..232d3b38ee 100644 --- a/docs/examples/1.8.x/client-android/java/teams/create.md +++ b/docs/examples/1.8.x/client-android/java/teams/create.md @@ -11,7 +11,7 @@ Teams teams = new Teams(client); teams.create( "", // teamId "", // name - listOf(), // roles (optional) + List.of(), // roles (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/teams/list-memberships.md b/docs/examples/1.8.x/client-android/java/teams/list-memberships.md index ae5cc69b4d..c8d3b610dc 100644 --- a/docs/examples/1.8.x/client-android/java/teams/list-memberships.md +++ b/docs/examples/1.8.x/client-android/java/teams/list-memberships.md @@ -10,7 +10,7 @@ Teams teams = new Teams(client); teams.listMemberships( "", // teamId - listOf(), // queries (optional) + List.of(), // queries (optional) "", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/client-android/java/teams/list.md b/docs/examples/1.8.x/client-android/java/teams/list.md index fff14e2992..1db3e67a2e 100644 --- a/docs/examples/1.8.x/client-android/java/teams/list.md +++ b/docs/examples/1.8.x/client-android/java/teams/list.md @@ -9,7 +9,7 @@ Client client = new Client(context) Teams teams = new Teams(client); teams.list( - listOf(), // queries (optional) + List.of(), // queries (optional) "", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/client-android/java/teams/update-membership.md b/docs/examples/1.8.x/client-android/java/teams/update-membership.md index 481be43107..f8adcf1fb8 100644 --- a/docs/examples/1.8.x/client-android/java/teams/update-membership.md +++ b/docs/examples/1.8.x/client-android/java/teams/update-membership.md @@ -11,7 +11,7 @@ Teams teams = new Teams(client); teams.updateMembership( "", // teamId "", // membershipId - listOf(), // roles + List.of(), // roles new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/java/teams/update-prefs.md b/docs/examples/1.8.x/client-android/java/teams/update-prefs.md index 5a0186ff31..9ea1487bd4 100644 --- a/docs/examples/1.8.x/client-android/java/teams/update-prefs.md +++ b/docs/examples/1.8.x/client-android/java/teams/update-prefs.md @@ -10,7 +10,7 @@ Teams teams = new Teams(client); teams.updatePrefs( "", // teamId - mapOf( "a" to "b" ), // prefs + Map.of("a", "b"), // prefs new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/client-android/kotlin/avatars/get-screenshot.md b/docs/examples/1.8.x/client-android/kotlin/avatars/get-screenshot.md index 2638e9e6a5..d7cd6cda24 100644 --- a/docs/examples/1.8.x/client-android/kotlin/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/client-android/kotlin/avatars/get-screenshot.md @@ -13,23 +13,26 @@ val avatars = Avatars(client) val result = avatars.getScreenshot( url = "https://example.com", - headers = mapOf( "a" to "b" ), // (optional) - viewportWidth = 1, // (optional) - viewportHeight = 1, // (optional) - scale = 0.1, // (optional) + headers = mapOf( + "Authorization" to "Bearer token123", + "X-Custom-Header" to "value" + ), // (optional) + viewportWidth = 1920, // (optional) + viewportHeight = 1080, // (optional) + scale = 2, // (optional) theme = theme.LIGHT, // (optional) - userAgent = "", // (optional) - fullpage = false, // (optional) - locale = "", // (optional) + userAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15", // (optional) + fullpage = true, // (optional) + locale = "en-US", // (optional) timezone = timezone.AFRICA_ABIDJAN, // (optional) - latitude = -90, // (optional) - longitude = -180, // (optional) - accuracy = 0, // (optional) - touch = false, // (optional) - permissions = listOf(), // (optional) - sleep = 0, // (optional) - width = 0, // (optional) - height = 0, // (optional) - quality = -1, // (optional) + latitude = 37.7749, // (optional) + longitude = -122.4194, // (optional) + accuracy = 100, // (optional) + touch = true, // (optional) + permissions = listOf("geolocation", "notifications"), // (optional) + sleep = 3, // (optional) + width = 800, // (optional) + height = 600, // (optional) + quality = 85, // (optional) output = output.JPG, // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/databases/create-operations.md b/docs/examples/1.8.x/client-android/kotlin/databases/create-operations.md index 62c93351e7..f3a419448b 100644 --- a/docs/examples/1.8.x/client-android/kotlin/databases/create-operations.md +++ b/docs/examples/1.8.x/client-android/kotlin/databases/create-operations.md @@ -10,15 +10,13 @@ val databases = Databases(client) val result = databases.createOperations( transactionId = "", - operations = listOf( - { - "action": "create", - "databaseId": "", - "collectionId": "", - "documentId": "", - "data": { - "name": "Walter O'Brien" - } - } - ), // (optional) + operations = listOf(mapOf( + "action" to "create", + "databaseId" to "", + "collectionId" to "", + "documentId" to "", + "data" to mapOf( + "name" to "Walter O'Brien" + ) + )), // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-operations.md b/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-operations.md index 3073a00bca..7807102f27 100644 --- a/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-operations.md +++ b/docs/examples/1.8.x/client-android/kotlin/tablesdb/create-operations.md @@ -10,15 +10,13 @@ val tablesDB = TablesDB(client) val result = tablesDB.createOperations( transactionId = "", - operations = listOf( - { - "action": "create", - "databaseId": "", - "tableId": "", - "rowId": "", - "data": { - "name": "Walter O'Brien" - } - } - ), // (optional) + operations = listOf(mapOf( + "action" to "create", + "databaseId" to "", + "tableId" to "", + "rowId" to "", + "data" to mapOf( + "name" to "Walter O'Brien" + ) + )), // (optional) ) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-apple/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/client-apple/examples/avatars/get-screenshot.md index 7f4ef5da5c..20d635a8fa 100644 --- a/docs/examples/1.8.x/client-apple/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/client-apple/examples/avatars/get-screenshot.md @@ -9,24 +9,27 @@ let avatars = Avatars(client) let bytes = try await avatars.getScreenshot( url: "https://example.com", - headers: [:], // optional - viewportWidth: 1, // optional - viewportHeight: 1, // optional - scale: 0.1, // optional + headers: [ + "Authorization": "Bearer token123", + "X-Custom-Header": "value" + ], // optional + viewportWidth: 1920, // optional + viewportHeight: 1080, // optional + scale: 2, // optional theme: .light, // optional - userAgent: "", // optional - fullpage: false, // optional - locale: "", // optional + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15", // optional + fullpage: true, // optional + locale: "en-US", // optional timezone: .africaAbidjan, // optional - latitude: -90, // optional - longitude: -180, // optional - accuracy: 0, // optional - touch: false, // optional - permissions: [], // optional - sleep: 0, // optional - width: 0, // optional - height: 0, // optional - quality: -1, // optional + latitude: 37.7749, // optional + longitude: -122.4194, // optional + accuracy: 100, // optional + touch: true, // optional + permissions: ["geolocation","notifications"], // optional + sleep: 3, // optional + width: 800, // optional + height: 600, // optional + quality: 85, // optional output: .jpg // optional ) diff --git a/docs/examples/1.8.x/client-flutter/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/client-flutter/examples/avatars/get-screenshot.md index 42a5ca7230..0dc9b76e96 100644 --- a/docs/examples/1.8.x/client-flutter/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/client-flutter/examples/avatars/get-screenshot.md @@ -9,24 +9,27 @@ Avatars avatars = Avatars(client); // Downloading file Uint8List bytes = await avatars.getScreenshot( url: 'https://example.com', - headers: {}, // optional - viewportWidth: 1, // optional - viewportHeight: 1, // optional - scale: 0.1, // optional + headers: { + "Authorization": "Bearer token123", + "X-Custom-Header": "value" + }, // optional + viewportWidth: 1920, // optional + viewportHeight: 1080, // optional + scale: 2, // optional theme: Theme.light, // optional - userAgent: '', // optional - fullpage: false, // optional - locale: '', // optional + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15', // optional + fullpage: true, // optional + locale: 'en-US', // optional timezone: Timezone.africaAbidjan, // optional - latitude: -90, // optional - longitude: -180, // optional - accuracy: 0, // optional - touch: false, // optional - permissions: [], // optional - sleep: 0, // optional - width: 0, // optional - height: 0, // optional - quality: -1, // optional + latitude: 37.7749, // optional + longitude: -122.4194, // optional + accuracy: 100, // optional + touch: true, // optional + permissions: ["geolocation","notifications"], // optional + sleep: 3, // optional + width: 800, // optional + height: 600, // optional + quality: 85, // optional output: Output.jpg, // optional ) @@ -37,24 +40,27 @@ file.writeAsBytesSync(bytes); FutureBuilder( future: avatars.getScreenshot( url:'https://example.com' , - headers:{} , // optional - viewportWidth:1 , // optional - viewportHeight:1 , // optional - scale:0.1 , // optional + headers:{ + "Authorization": "Bearer token123", + "X-Custom-Header": "value" + } , // optional + viewportWidth:1920 , // optional + viewportHeight:1080 , // optional + scale:2 , // optional theme: Theme.light, // optional - userAgent:'' , // optional - fullpage:false , // optional - locale:'' , // optional + userAgent:'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15' , // optional + fullpage:true , // optional + locale:'en-US' , // optional timezone: Timezone.africaAbidjan, // optional - latitude:-90 , // optional - longitude:-180 , // optional - accuracy:0 , // optional - touch:false , // optional - permissions:[] , // optional - sleep:0 , // optional - width:0 , // optional - height:0 , // optional - quality:-1 , // optional + latitude:37.7749 , // optional + longitude:-122.4194 , // optional + accuracy:100 , // optional + touch:true , // optional + permissions:["geolocation","notifications"] , // optional + sleep:3 , // optional + width:800 , // optional + height:600 , // optional + quality:85 , // optional output: Output.jpg, // optional ), // Works for both public file and private file, for private files you need to be logged in builder: (context, snapshot) { diff --git a/docs/examples/1.8.x/client-react-native/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/client-react-native/examples/avatars/get-screenshot.md index 918ab667d6..2e4b84dec8 100644 --- a/docs/examples/1.8.x/client-react-native/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/client-react-native/examples/avatars/get-screenshot.md @@ -8,24 +8,27 @@ const avatars = new Avatars(client); const result = avatars.getScreenshot({ url: 'https://example.com', - headers: {}, // optional - viewportWidth: 1, // optional - viewportHeight: 1, // optional - scale: 0.1, // optional + headers: { + "Authorization": "Bearer token123", + "X-Custom-Header": "value" + }, // optional + viewportWidth: 1920, // optional + viewportHeight: 1080, // optional + scale: 2, // optional theme: Theme.Light, // optional - userAgent: '', // optional - fullpage: false, // optional - locale: '', // optional + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15', // optional + fullpage: true, // optional + locale: 'en-US', // optional timezone: Timezone.AfricaAbidjan, // optional - latitude: -90, // optional - longitude: -180, // optional - accuracy: 0, // optional - touch: false, // optional - permissions: [], // optional - sleep: 0, // optional - width: 0, // optional - height: 0, // optional - quality: -1, // optional + latitude: 37.7749, // optional + longitude: -122.4194, // optional + accuracy: 100, // optional + touch: true, // optional + permissions: ["geolocation","notifications"], // optional + sleep: 3, // optional + width: 800, // optional + height: 600, // optional + quality: 85, // optional output: Output.Jpg // optional }); diff --git a/docs/examples/1.8.x/client-rest/examples/account/create-mfa-challenge.md b/docs/examples/1.8.x/client-rest/examples/account/create-mfa-challenge.md index dd5ef4c731..e5a5b0ea05 100644 --- a/docs/examples/1.8.x/client-rest/examples/account/create-mfa-challenge.md +++ b/docs/examples/1.8.x/client-rest/examples/account/create-mfa-challenge.md @@ -1,4 +1,4 @@ -POST /v1/account/mfa/challenge HTTP/1.1 +POST /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/client-rest/examples/account/update-mfa-challenge.md b/docs/examples/1.8.x/client-rest/examples/account/update-mfa-challenge.md index b6a7e92b28..df2cd9a1e8 100644 --- a/docs/examples/1.8.x/client-rest/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.8.x/client-rest/examples/account/update-mfa-challenge.md @@ -1,4 +1,4 @@ -PUT /v1/account/mfa/challenge HTTP/1.1 +PUT /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/client-web/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/client-web/examples/avatars/get-screenshot.md index 75d6bcf80c..033458f43a 100644 --- a/docs/examples/1.8.x/client-web/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/client-web/examples/avatars/get-screenshot.md @@ -8,24 +8,27 @@ const avatars = new Avatars(client); const result = avatars.getScreenshot({ url: 'https://example.com', - headers: {}, // optional - viewportWidth: 1, // optional - viewportHeight: 1, // optional - scale: 0.1, // optional + headers: { + "Authorization": "Bearer token123", + "X-Custom-Header": "value" + }, // optional + viewportWidth: 1920, // optional + viewportHeight: 1080, // optional + scale: 2, // optional theme: Theme.Light, // optional - userAgent: '', // optional - fullpage: false, // optional - locale: '', // optional + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15', // optional + fullpage: true, // optional + locale: 'en-US', // optional timezone: Timezone.AfricaAbidjan, // optional - latitude: -90, // optional - longitude: -180, // optional - accuracy: 0, // optional - touch: false, // optional - permissions: [], // optional - sleep: 0, // optional - width: 0, // optional - height: 0, // optional - quality: -1, // optional + latitude: 37.7749, // optional + longitude: -122.4194, // optional + accuracy: 100, // optional + touch: true, // optional + permissions: ["geolocation","notifications"], // optional + sleep: 3, // optional + width: 800, // optional + height: 600, // optional + quality: 85, // optional output: Output.Jpg // optional }); diff --git a/docs/examples/1.8.x/console-web/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/console-web/examples/avatars/get-screenshot.md index 25ccc040f0..821f60e2ee 100644 --- a/docs/examples/1.8.x/console-web/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/console-web/examples/avatars/get-screenshot.md @@ -8,24 +8,27 @@ const avatars = new Avatars(client); const result = avatars.getScreenshot({ url: 'https://example.com', - headers: {}, // optional - viewportWidth: 1, // optional - viewportHeight: 1, // optional - scale: 0.1, // optional + headers: { + "Authorization": "Bearer token123", + "X-Custom-Header": "value" + }, // optional + viewportWidth: 1920, // optional + viewportHeight: 1080, // optional + scale: 2, // optional theme: Theme.Light, // optional - userAgent: '', // optional - fullpage: false, // optional - locale: '', // optional + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15', // optional + fullpage: true, // optional + locale: 'en-US', // optional timezone: Timezone.AfricaAbidjan, // optional - latitude: -90, // optional - longitude: -180, // optional - accuracy: 0, // optional - touch: false, // optional - permissions: [], // optional - sleep: 0, // optional - width: 0, // optional - height: 0, // optional - quality: -1, // optional + latitude: 37.7749, // optional + longitude: -122.4194, // optional + accuracy: 100, // optional + touch: true, // optional + permissions: ["geolocation","notifications"], // optional + sleep: 3, // optional + width: 800, // optional + height: 600, // optional + quality: 85, // optional output: Output.Jpg // optional }); diff --git a/docs/examples/1.8.x/console-web/examples/functions/create-template-deployment.md b/docs/examples/1.8.x/console-web/examples/functions/create-template-deployment.md index 5bf812c1bc..414a0d0bfb 100644 --- a/docs/examples/1.8.x/console-web/examples/functions/create-template-deployment.md +++ b/docs/examples/1.8.x/console-web/examples/functions/create-template-deployment.md @@ -1,4 +1,4 @@ -import { Client, Functions, } from "@appwrite.io/console"; +import { Client, Functions, TemplateReferenceType } from "@appwrite.io/console"; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint @@ -11,7 +11,7 @@ const result = await functions.createTemplateDeployment({ repository: '', owner: '', rootDirectory: '', - type: .Commit, + type: TemplateReferenceType.Commit, reference: '', activate: false // optional }); diff --git a/docs/examples/1.8.x/console-web/examples/functions/create-vcs-deployment.md b/docs/examples/1.8.x/console-web/examples/functions/create-vcs-deployment.md index 33da9bfd70..d6f4e765e3 100644 --- a/docs/examples/1.8.x/console-web/examples/functions/create-vcs-deployment.md +++ b/docs/examples/1.8.x/console-web/examples/functions/create-vcs-deployment.md @@ -1,4 +1,4 @@ -import { Client, Functions, VCSDeploymentType } from "@appwrite.io/console"; +import { Client, Functions, VCSReferenceType } from "@appwrite.io/console"; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint @@ -8,7 +8,7 @@ const functions = new Functions(client); const result = await functions.createVcsDeployment({ functionId: '', - type: VCSDeploymentType.Branch, + type: VCSReferenceType.Branch, reference: '', activate: false // optional }); diff --git a/docs/examples/1.8.x/console-web/examples/sites/create-template-deployment.md b/docs/examples/1.8.x/console-web/examples/sites/create-template-deployment.md index 4f1d0184f7..1bfaeb6a7e 100644 --- a/docs/examples/1.8.x/console-web/examples/sites/create-template-deployment.md +++ b/docs/examples/1.8.x/console-web/examples/sites/create-template-deployment.md @@ -1,4 +1,4 @@ -import { Client, Sites, } from "@appwrite.io/console"; +import { Client, Sites, TemplateReferenceType } from "@appwrite.io/console"; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint @@ -11,7 +11,7 @@ const result = await sites.createTemplateDeployment({ repository: '', owner: '', rootDirectory: '', - type: .Branch, + type: TemplateReferenceType.Branch, reference: '', activate: false // optional }); diff --git a/docs/examples/1.8.x/console-web/examples/sites/create-vcs-deployment.md b/docs/examples/1.8.x/console-web/examples/sites/create-vcs-deployment.md index cc1fd1d301..80d9403ccb 100644 --- a/docs/examples/1.8.x/console-web/examples/sites/create-vcs-deployment.md +++ b/docs/examples/1.8.x/console-web/examples/sites/create-vcs-deployment.md @@ -1,4 +1,4 @@ -import { Client, Sites, VCSDeploymentType } from "@appwrite.io/console"; +import { Client, Sites, VCSReferenceType } from "@appwrite.io/console"; const client = new Client() .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint @@ -8,7 +8,7 @@ const sites = new Sites(client); const result = await sites.createVcsDeployment({ siteId: '', - type: VCSDeploymentType.Branch, + type: VCSReferenceType.Branch, reference: '', activate: false // optional }); diff --git a/docs/examples/1.8.x/console-web/examples/vcs/list-repositories.md b/docs/examples/1.8.x/console-web/examples/vcs/list-repositories.md index cae640097e..85334c43e1 100644 --- a/docs/examples/1.8.x/console-web/examples/vcs/list-repositories.md +++ b/docs/examples/1.8.x/console-web/examples/vcs/list-repositories.md @@ -9,7 +9,8 @@ const vcs = new Vcs(client); const result = await vcs.listRepositories({ installationId: '', type: VCSDetectionType.Runtime, - search: '' // optional + search: '', // optional + queries: [] // optional }); console.log(result); diff --git a/docs/examples/1.8.x/server-dart/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-dart/examples/avatars/get-screenshot.md index c787a82298..da80f30bee 100644 --- a/docs/examples/1.8.x/server-dart/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/server-dart/examples/avatars/get-screenshot.md @@ -9,23 +9,26 @@ Avatars avatars = Avatars(client); Uint8List result = await avatars.getScreenshot( url: 'https://example.com', - headers: {}, // (optional) - viewportWidth: 1, // (optional) - viewportHeight: 1, // (optional) - scale: 0.1, // (optional) + headers: { + "Authorization": "Bearer token123", + "X-Custom-Header": "value" + }, // (optional) + viewportWidth: 1920, // (optional) + viewportHeight: 1080, // (optional) + scale: 2, // (optional) theme: Theme.light, // (optional) - userAgent: '', // (optional) - fullpage: false, // (optional) - locale: '', // (optional) + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15', // (optional) + fullpage: true, // (optional) + locale: 'en-US', // (optional) timezone: Timezone.africaAbidjan, // (optional) - latitude: -90, // (optional) - longitude: -180, // (optional) - accuracy: 0, // (optional) - touch: false, // (optional) - permissions: [], // (optional) - sleep: 0, // (optional) - width: 0, // (optional) - height: 0, // (optional) - quality: -1, // (optional) + latitude: 37.7749, // (optional) + longitude: -122.4194, // (optional) + accuracy: 100, // (optional) + touch: true, // (optional) + permissions: ["geolocation","notifications"], // (optional) + sleep: 3, // (optional) + width: 800, // (optional) + height: 600, // (optional) + quality: 85, // (optional) output: Output.jpg, // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/functions/create-template-deployment.md b/docs/examples/1.8.x/server-dart/examples/functions/create-template-deployment.md index bfd94b1963..f5b6cdeb1f 100644 --- a/docs/examples/1.8.x/server-dart/examples/functions/create-template-deployment.md +++ b/docs/examples/1.8.x/server-dart/examples/functions/create-template-deployment.md @@ -12,7 +12,7 @@ Deployment result = await functions.createTemplateDeployment( repository: '', owner: '', rootDirectory: '', - type: .commit, + type: TemplateReferenceType.commit, reference: '', activate: false, // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/functions/create-vcs-deployment.md b/docs/examples/1.8.x/server-dart/examples/functions/create-vcs-deployment.md index ed315a54e3..0c12315ffc 100644 --- a/docs/examples/1.8.x/server-dart/examples/functions/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-dart/examples/functions/create-vcs-deployment.md @@ -9,7 +9,7 @@ Functions functions = Functions(client); Deployment result = await functions.createVcsDeployment( functionId: '', - type: VCSDeploymentType.branch, + type: VCSReferenceType.branch, reference: '', activate: false, // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/sites/create-template-deployment.md b/docs/examples/1.8.x/server-dart/examples/sites/create-template-deployment.md index 93c9b1d283..8826b1f462 100644 --- a/docs/examples/1.8.x/server-dart/examples/sites/create-template-deployment.md +++ b/docs/examples/1.8.x/server-dart/examples/sites/create-template-deployment.md @@ -12,7 +12,7 @@ Deployment result = await sites.createTemplateDeployment( repository: '', owner: '', rootDirectory: '', - type: .branch, + type: TemplateReferenceType.branch, reference: '', activate: false, // (optional) ); diff --git a/docs/examples/1.8.x/server-dart/examples/sites/create-vcs-deployment.md b/docs/examples/1.8.x/server-dart/examples/sites/create-vcs-deployment.md index 50f65b9603..52133a535e 100644 --- a/docs/examples/1.8.x/server-dart/examples/sites/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-dart/examples/sites/create-vcs-deployment.md @@ -9,7 +9,7 @@ Sites sites = Sites(client); Deployment result = await sites.createVcsDeployment( siteId: '', - type: VCSDeploymentType.branch, + type: VCSReferenceType.branch, reference: '', activate: false, // (optional) ); diff --git a/docs/examples/1.8.x/server-dotnet/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-dotnet/examples/avatars/get-screenshot.md index e2e5462484..58e6e4267a 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/server-dotnet/examples/avatars/get-screenshot.md @@ -12,23 +12,26 @@ Avatars avatars = new Avatars(client); byte[] result = await avatars.GetScreenshot( url: "https://example.com", - headers: [object], // optional - viewportWidth: 1, // optional - viewportHeight: 1, // optional - scale: 0.1, // optional + headers: new { + Authorization = "Bearer token123", + X-Custom-Header = "value" + }, // optional + viewportWidth: 1920, // optional + viewportHeight: 1080, // optional + scale: 2, // optional theme: Theme.Light, // optional - userAgent: "", // optional - fullpage: false, // optional - locale: "", // optional + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15", // optional + fullpage: true, // optional + locale: "en-US", // optional timezone: Timezone.AfricaAbidjan, // optional - latitude: -90, // optional - longitude: -180, // optional - accuracy: 0, // optional - touch: false, // optional - permissions: new List(), // optional - sleep: 0, // optional - width: 0, // optional - height: 0, // optional - quality: -1, // optional + latitude: 37.7749, // optional + longitude: -122.4194, // optional + accuracy: 100, // optional + touch: true, // optional + permissions: ["geolocation","notifications"], // optional + sleep: 3, // optional + width: 800, // optional + height: 600, // optional + quality: 85, // optional output: Output.Jpg // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/functions/create-template-deployment.md b/docs/examples/1.8.x/server-dotnet/examples/functions/create-template-deployment.md index 125eeda5cc..6fcf2398dd 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/functions/create-template-deployment.md +++ b/docs/examples/1.8.x/server-dotnet/examples/functions/create-template-deployment.md @@ -15,7 +15,7 @@ Deployment result = await functions.CreateTemplateDeployment( repository: "", owner: "", rootDirectory: "", - type: .Commit, + type: TemplateReferenceType.Commit, reference: "", activate: false // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/functions/create-vcs-deployment.md b/docs/examples/1.8.x/server-dotnet/examples/functions/create-vcs-deployment.md index 9651365912..a7403ff116 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/functions/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-dotnet/examples/functions/create-vcs-deployment.md @@ -12,7 +12,7 @@ Functions functions = new Functions(client); Deployment result = await functions.CreateVcsDeployment( functionId: "", - type: VCSDeploymentType.Branch, + type: VCSReferenceType.Branch, reference: "", activate: false // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/sites/create-template-deployment.md b/docs/examples/1.8.x/server-dotnet/examples/sites/create-template-deployment.md index 9775cb659f..5353b004b8 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/sites/create-template-deployment.md +++ b/docs/examples/1.8.x/server-dotnet/examples/sites/create-template-deployment.md @@ -15,7 +15,7 @@ Deployment result = await sites.CreateTemplateDeployment( repository: "", owner: "", rootDirectory: "", - type: .Branch, + type: TemplateReferenceType.Branch, reference: "", activate: false // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-dotnet/examples/sites/create-vcs-deployment.md b/docs/examples/1.8.x/server-dotnet/examples/sites/create-vcs-deployment.md index 4d3e685176..229549befa 100644 --- a/docs/examples/1.8.x/server-dotnet/examples/sites/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-dotnet/examples/sites/create-vcs-deployment.md @@ -12,7 +12,7 @@ Sites sites = new Sites(client); Deployment result = await sites.CreateVcsDeployment( siteId: "", - type: VCSDeploymentType.Branch, + type: VCSReferenceType.Branch, reference: "", activate: false // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-go/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-go/examples/avatars/get-screenshot.md index ac425fbc4f..9191ad8875 100644 --- a/docs/examples/1.8.x/server-go/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/server-go/examples/avatars/get-screenshot.md @@ -16,23 +16,26 @@ service := avatars.New(client) response, error := service.GetScreenshot( "https://example.com", - avatars.WithGetScreenshotHeaders(map[string]interface{}{}), - avatars.WithGetScreenshotViewportWidth(1), - avatars.WithGetScreenshotViewportHeight(1), - avatars.WithGetScreenshotScale(0.1), - avatars.WithGetScreenshotTheme("light"), - avatars.WithGetScreenshotUserAgent(""), - avatars.WithGetScreenshotFullpage(false), - avatars.WithGetScreenshotLocale(""), - avatars.WithGetScreenshotTimezone("africa/abidjan"), - avatars.WithGetScreenshotLatitude(-90), - avatars.WithGetScreenshotLongitude(-180), - avatars.WithGetScreenshotAccuracy(0), - avatars.WithGetScreenshotTouch(false), - avatars.WithGetScreenshotPermissions([]interface{}{}), - avatars.WithGetScreenshotSleep(0), - avatars.WithGetScreenshotWidth(0), - avatars.WithGetScreenshotHeight(0), - avatars.WithGetScreenshotQuality(-1), - avatars.WithGetScreenshotOutput("jpg"), + avatars.WithGetScreenshotHeaders(map[string]interface{}{ + "Authorization": "Bearer token123", + "X-Custom-Header": "value" + }), + avatars.WithGetScreenshotViewportWidth(1920), + avatars.WithGetScreenshotViewportHeight(1080), + avatars.WithGetScreenshotScale(2), + avatars.WithGetScreenshotTheme("dark"), + avatars.WithGetScreenshotUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15"), + avatars.WithGetScreenshotFullpage(true), + avatars.WithGetScreenshotLocale("en-US"), + avatars.WithGetScreenshotTimezone("America/New_York"), + avatars.WithGetScreenshotLatitude(37.7749), + avatars.WithGetScreenshotLongitude(-122.4194), + avatars.WithGetScreenshotAccuracy(100), + avatars.WithGetScreenshotTouch(true), + avatars.WithGetScreenshotPermissions(interface{}{"geolocation","notifications"}), + avatars.WithGetScreenshotSleep(3), + avatars.WithGetScreenshotWidth(800), + avatars.WithGetScreenshotHeight(600), + avatars.WithGetScreenshotQuality(85), + avatars.WithGetScreenshotOutput("jpeg"), ) diff --git a/docs/examples/1.8.x/server-kotlin/java/account/create-o-auth-2-token.md b/docs/examples/1.8.x/server-kotlin/java/account/create-o-auth-2-token.md index 5b325f5c61..376d943533 100644 --- a/docs/examples/1.8.x/server-kotlin/java/account/create-o-auth-2-token.md +++ b/docs/examples/1.8.x/server-kotlin/java/account/create-o-auth-2-token.md @@ -13,7 +13,7 @@ account.createOAuth2Token( OAuthProvider.AMAZON, // provider "https://example.com", // success (optional) "https://example.com", // failure (optional) - listOf(), // scopes (optional) + List.of(), // scopes (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/account/list-identities.md b/docs/examples/1.8.x/server-kotlin/java/account/list-identities.md index 8d204d5920..97cdf99b62 100644 --- a/docs/examples/1.8.x/server-kotlin/java/account/list-identities.md +++ b/docs/examples/1.8.x/server-kotlin/java/account/list-identities.md @@ -10,7 +10,7 @@ Client client = new Client() Account account = new Account(client); account.listIdentities( - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/account/list-logs.md b/docs/examples/1.8.x/server-kotlin/java/account/list-logs.md index 4b301a11e0..6c41c7f073 100644 --- a/docs/examples/1.8.x/server-kotlin/java/account/list-logs.md +++ b/docs/examples/1.8.x/server-kotlin/java/account/list-logs.md @@ -10,7 +10,7 @@ Client client = new Client() Account account = new Account(client); account.listLogs( - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/account/update-prefs.md b/docs/examples/1.8.x/server-kotlin/java/account/update-prefs.md index 0b6893916b..4fc1cb5439 100644 --- a/docs/examples/1.8.x/server-kotlin/java/account/update-prefs.md +++ b/docs/examples/1.8.x/server-kotlin/java/account/update-prefs.md @@ -10,10 +10,10 @@ Client client = new Client() Account account = new Account(client); account.updatePrefs( - mapOf( - "language" to "en", - "timezone" to "UTC", - "darkTheme" to true + Map.of( + "language", "en", + "timezone", "UTC", + "darkTheme", true ), // prefs new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/avatars/get-screenshot.md b/docs/examples/1.8.x/server-kotlin/java/avatars/get-screenshot.md index 0f1cb9c2f0..5eea09678b 100644 --- a/docs/examples/1.8.x/server-kotlin/java/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/server-kotlin/java/avatars/get-screenshot.md @@ -14,24 +14,27 @@ Avatars avatars = new Avatars(client); avatars.getScreenshot( "https://example.com", // url - mapOf( "a" to "b" ), // headers (optional) - 1, // viewportWidth (optional) - 1, // viewportHeight (optional) - 0.1, // scale (optional) + Map.of( + "Authorization", "Bearer token123", + "X-Custom-Header", "value" + ), // headers (optional) + 1920, // viewportWidth (optional) + 1080, // viewportHeight (optional) + 2, // scale (optional) Theme.LIGHT, // theme (optional) - "", // userAgent (optional) - false, // fullpage (optional) - "", // locale (optional) + "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15", // userAgent (optional) + true, // fullpage (optional) + "en-US", // locale (optional) Timezone.AFRICA_ABIDJAN, // timezone (optional) - -90, // latitude (optional) - -180, // longitude (optional) - 0, // accuracy (optional) - false, // touch (optional) - listOf(), // permissions (optional) - 0, // sleep (optional) - 0, // width (optional) - 0, // height (optional) - -1, // quality (optional) + 37.7749, // latitude (optional) + -122.4194, // longitude (optional) + 100, // accuracy (optional) + true, // touch (optional) + List.of("geolocation", "notifications"), // permissions (optional) + 3, // sleep (optional) + 800, // width (optional) + 600, // height (optional) + 85, // quality (optional) Output.JPG, // output (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-collection.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-collection.md index 10eed04a4f..eea55558cc 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/create-collection.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-collection.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Databases; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Databases; Client client = new Client() .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,7 +15,7 @@ databases.createCollection( "", // databaseId "", // collectionId "", // name - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) false, // documentSecurity (optional) false, // enabled (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-document.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-document.md index aa6c9ea203..7ff195f613 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/create-document.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-document.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Databases; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Databases; Client client = new Client() .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,14 +15,14 @@ databases.createDocument( "", // databaseId "", // collectionId "", // documentId - mapOf( - "username" to "walter.obrien", - "email" to "walter.obrien@example.com", - "fullName" to "Walter O'Brien", - "age" to 30, - "isAdmin" to false + Map.of( + "username", "walter.obrien", + "email", "walter.obrien@example.com", + "fullName", "Walter O'Brien", + "age", 30, + "isAdmin", false ), // data - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-documents.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-documents.md index 3a4540974b..be695fe80b 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/create-documents.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-documents.md @@ -12,7 +12,7 @@ Databases databases = new Databases(client); databases.createDocuments( "", // databaseId "", // collectionId - listOf(), // documents + List.of(), // documents "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-enum-attribute.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-enum-attribute.md index 44202086b0..b8666666da 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/create-enum-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-enum-attribute.md @@ -13,7 +13,7 @@ databases.createEnumAttribute( "", // databaseId "", // collectionId "", // key - listOf(), // elements + List.of(), // elements false, // required "", // default (optional) false, // array (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-index.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-index.md index fe2d9bf66d..1fb1efb826 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/create-index.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-index.md @@ -15,9 +15,9 @@ databases.createIndex( "", // collectionId "", // key IndexType.KEY, // type - listOf(), // attributes - listOf(), // orders (optional) - listOf(), // lengths (optional) + List.of(), // attributes + List.of(), // orders (optional) + List.of(), // lengths (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-line-attribute.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-line-attribute.md index ad988b8773..c0daeac6c3 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/create-line-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-line-attribute.md @@ -14,7 +14,7 @@ databases.createLineAttribute( "", // collectionId "", // key false, // required - listOf([1, 2], [3, 4], [5, 6]), // default (optional) + List.of(List.of(1, 2), List.of(3, 4), List.of(5, 6)), // default (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-operations.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-operations.md index 2dad8a15ac..c935f82a9a 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/create-operations.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-operations.md @@ -11,17 +11,15 @@ Databases databases = new Databases(client); databases.createOperations( "", // transactionId - listOf( - { - "action": "create", - "databaseId": "", - "collectionId": "", - "documentId": "", - "data": { - "name": "Walter O'Brien" - } - } - ), // operations (optional) + List.of(Map.of( + "action", "create", + "databaseId", "", + "collectionId", "", + "documentId", "", + "data", Map.of( + "name", "Walter O'Brien" + ) + )), // operations (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-point-attribute.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-point-attribute.md index 89d7cc7177..c3b5d503b2 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/create-point-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-point-attribute.md @@ -14,7 +14,7 @@ databases.createPointAttribute( "", // collectionId "", // key false, // required - listOf(1, 2), // default (optional) + List.of(1, 2), // default (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/create-polygon-attribute.md b/docs/examples/1.8.x/server-kotlin/java/databases/create-polygon-attribute.md index 556fb38481..4f8fe0369e 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/create-polygon-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/create-polygon-attribute.md @@ -14,7 +14,7 @@ databases.createPolygonAttribute( "", // collectionId "", // key false, // required - listOf([[1, 2], [3, 4], [5, 6], [1, 2]]), // default (optional) + List.of(List.of(List.of(1, 2), List.of(3, 4), List.of(5, 6), List.of(1, 2))), // default (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/delete-documents.md b/docs/examples/1.8.x/server-kotlin/java/databases/delete-documents.md index 958c40c382..f535ae7780 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/delete-documents.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/delete-documents.md @@ -12,7 +12,7 @@ Databases databases = new Databases(client); databases.deleteDocuments( "", // databaseId "", // collectionId - listOf(), // queries (optional) + List.of(), // queries (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/get-document.md b/docs/examples/1.8.x/server-kotlin/java/databases/get-document.md index 489447f599..d3e8b8b3d4 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/get-document.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/get-document.md @@ -13,7 +13,7 @@ databases.getDocument( "", // databaseId "", // collectionId "", // documentId - listOf(), // queries (optional) + List.of(), // queries (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/list-attributes.md b/docs/examples/1.8.x/server-kotlin/java/databases/list-attributes.md index b1b3bd1b9c..dd883e232f 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/list-attributes.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/list-attributes.md @@ -12,7 +12,7 @@ Databases databases = new Databases(client); databases.listAttributes( "", // databaseId "", // collectionId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/list-collections.md b/docs/examples/1.8.x/server-kotlin/java/databases/list-collections.md index efb0e7f89b..ddb47c9989 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/list-collections.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/list-collections.md @@ -11,7 +11,7 @@ Databases databases = new Databases(client); databases.listCollections( "", // databaseId - listOf(), // queries (optional) + List.of(), // queries (optional) "", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/list-documents.md b/docs/examples/1.8.x/server-kotlin/java/databases/list-documents.md index 472d15ba27..b8ef0717ea 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/list-documents.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/list-documents.md @@ -12,7 +12,7 @@ Databases databases = new Databases(client); databases.listDocuments( "", // databaseId "", // collectionId - listOf(), // queries (optional) + List.of(), // queries (optional) "", // transactionId (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/list-indexes.md b/docs/examples/1.8.x/server-kotlin/java/databases/list-indexes.md index 5715af7d47..c701904157 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/list-indexes.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/list-indexes.md @@ -12,7 +12,7 @@ Databases databases = new Databases(client); databases.listIndexes( "", // databaseId "", // collectionId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/list-transactions.md b/docs/examples/1.8.x/server-kotlin/java/databases/list-transactions.md index 281fc1205b..8a6f60544f 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/list-transactions.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/list-transactions.md @@ -10,7 +10,7 @@ Client client = new Client() Databases databases = new Databases(client); databases.listTransactions( - listOf(), // queries (optional) + List.of(), // queries (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/list.md b/docs/examples/1.8.x/server-kotlin/java/databases/list.md index a3f2d51fed..32ef454a73 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/list.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/list.md @@ -10,7 +10,7 @@ Client client = new Client() Databases databases = new Databases(client); databases.list( - listOf(), // queries (optional) + List.of(), // queries (optional) "", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/update-collection.md b/docs/examples/1.8.x/server-kotlin/java/databases/update-collection.md index 24d312da8c..898f1aafe5 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/update-collection.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/update-collection.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Databases; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Databases; Client client = new Client() .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,7 +15,7 @@ databases.updateCollection( "", // databaseId "", // collectionId "", // name - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) false, // documentSecurity (optional) false, // enabled (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/update-document.md b/docs/examples/1.8.x/server-kotlin/java/databases/update-document.md index 749de99fce..0638225bfd 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/update-document.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/update-document.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Databases; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Databases; Client client = new Client() .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,8 +15,8 @@ databases.updateDocument( "", // databaseId "", // collectionId "", // documentId - mapOf( "a" to "b" ), // data (optional) - listOf(Permission.read(Role.any())), // permissions (optional) + Map.of("a", "b"), // data (optional) + List.of(Permission.read(Role.any())), // permissions (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/update-documents.md b/docs/examples/1.8.x/server-kotlin/java/databases/update-documents.md index a685ac81fc..57a82f56f9 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/update-documents.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/update-documents.md @@ -12,8 +12,8 @@ Databases databases = new Databases(client); databases.updateDocuments( "", // databaseId "", // collectionId - mapOf( "a" to "b" ), // data (optional) - listOf(), // queries (optional) + Map.of("a", "b"), // data (optional) + List.of(), // queries (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/update-enum-attribute.md b/docs/examples/1.8.x/server-kotlin/java/databases/update-enum-attribute.md index 89606806d9..8870e372a6 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/update-enum-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/update-enum-attribute.md @@ -13,7 +13,7 @@ databases.updateEnumAttribute( "", // databaseId "", // collectionId "", // key - listOf(), // elements + List.of(), // elements false, // required "", // default "", // newKey (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/update-line-attribute.md b/docs/examples/1.8.x/server-kotlin/java/databases/update-line-attribute.md index 6a4265bbda..14d83eeee5 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/update-line-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/update-line-attribute.md @@ -14,7 +14,7 @@ databases.updateLineAttribute( "", // collectionId "", // key false, // required - listOf([1, 2], [3, 4], [5, 6]), // default (optional) + List.of(List.of(1, 2), List.of(3, 4), List.of(5, 6)), // default (optional) "", // newKey (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/update-point-attribute.md b/docs/examples/1.8.x/server-kotlin/java/databases/update-point-attribute.md index 38d48c27e8..5d44d7a190 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/update-point-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/update-point-attribute.md @@ -14,7 +14,7 @@ databases.updatePointAttribute( "", // collectionId "", // key false, // required - listOf(1, 2), // default (optional) + List.of(1, 2), // default (optional) "", // newKey (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/update-polygon-attribute.md b/docs/examples/1.8.x/server-kotlin/java/databases/update-polygon-attribute.md index 6e6fd08575..2c530b9aae 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/update-polygon-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/update-polygon-attribute.md @@ -14,7 +14,7 @@ databases.updatePolygonAttribute( "", // collectionId "", // key false, // required - listOf([[1, 2], [3, 4], [5, 6], [1, 2]]), // default (optional) + List.of(List.of(List.of(1, 2), List.of(3, 4), List.of(5, 6), List.of(1, 2))), // default (optional) "", // newKey (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/upsert-document.md b/docs/examples/1.8.x/server-kotlin/java/databases/upsert-document.md index 4f156bbf8b..ec99390e15 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/upsert-document.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/upsert-document.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Databases; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Databases; Client client = new Client() .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,8 +15,8 @@ databases.upsertDocument( "", // databaseId "", // collectionId "", // documentId - mapOf( "a" to "b" ), // data - listOf(Permission.read(Role.any())), // permissions (optional) + Map.of("a", "b"), // data + List.of(Permission.read(Role.any())), // permissions (optional) "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/databases/upsert-documents.md b/docs/examples/1.8.x/server-kotlin/java/databases/upsert-documents.md index b8fcd8781a..ee4450fc29 100644 --- a/docs/examples/1.8.x/server-kotlin/java/databases/upsert-documents.md +++ b/docs/examples/1.8.x/server-kotlin/java/databases/upsert-documents.md @@ -12,7 +12,7 @@ Databases databases = new Databases(client); databases.upsertDocuments( "", // databaseId "", // collectionId - listOf(), // documents + List.of(), // documents "", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/functions/create-execution.md b/docs/examples/1.8.x/server-kotlin/java/functions/create-execution.md index 4abfe236b3..98a4d1b572 100644 --- a/docs/examples/1.8.x/server-kotlin/java/functions/create-execution.md +++ b/docs/examples/1.8.x/server-kotlin/java/functions/create-execution.md @@ -16,7 +16,7 @@ functions.createExecution( false, // async (optional) "", // path (optional) ExecutionMethod.GET, // method (optional) - mapOf( "a" to "b" ), // headers (optional) + Map.of("a", "b"), // headers (optional) "", // scheduledAt (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/functions/create-template-deployment.md b/docs/examples/1.8.x/server-kotlin/java/functions/create-template-deployment.md index 9a0642b04e..59c105dfb1 100644 --- a/docs/examples/1.8.x/server-kotlin/java/functions/create-template-deployment.md +++ b/docs/examples/1.8.x/server-kotlin/java/functions/create-template-deployment.md @@ -1,7 +1,7 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; import io.appwrite.services.Functions; -import io.appwrite.enums.Type; +import io.appwrite.enums.TemplateReferenceType; Client client = new Client() .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,7 +15,7 @@ functions.createTemplateDeployment( "", // repository "", // owner "", // rootDirectory - .COMMIT, // type + TemplateReferenceType.COMMIT, // type "", // reference false, // activate (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/functions/create-vcs-deployment.md b/docs/examples/1.8.x/server-kotlin/java/functions/create-vcs-deployment.md index 9274cd88c7..25f2f28485 100644 --- a/docs/examples/1.8.x/server-kotlin/java/functions/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-kotlin/java/functions/create-vcs-deployment.md @@ -1,7 +1,7 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; import io.appwrite.services.Functions; -import io.appwrite.enums.VCSDeploymentType; +import io.appwrite.enums.VCSReferenceType; Client client = new Client() .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint @@ -12,7 +12,7 @@ Functions functions = new Functions(client); functions.createVcsDeployment( "", // functionId - VCSDeploymentType.BRANCH, // type + VCSReferenceType.BRANCH, // type "", // reference false, // activate (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/functions/create.md b/docs/examples/1.8.x/server-kotlin/java/functions/create.md index 587fd54b9b..17e291140a 100644 --- a/docs/examples/1.8.x/server-kotlin/java/functions/create.md +++ b/docs/examples/1.8.x/server-kotlin/java/functions/create.md @@ -14,15 +14,15 @@ functions.create( "", // functionId "", // name Runtime.NODE_14_5, // runtime - listOf("any"), // execute (optional) - listOf(), // events (optional) + List.of("any"), // execute (optional) + List.of(), // events (optional) "", // schedule (optional) 1, // timeout (optional) false, // enabled (optional) false, // logging (optional) "", // entrypoint (optional) "", // commands (optional) - listOf(), // scopes (optional) + List.of(), // scopes (optional) "", // installationId (optional) "", // providerRepositoryId (optional) "", // providerBranch (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/functions/list-deployments.md b/docs/examples/1.8.x/server-kotlin/java/functions/list-deployments.md index a0ea8b68b3..a92cf0792a 100644 --- a/docs/examples/1.8.x/server-kotlin/java/functions/list-deployments.md +++ b/docs/examples/1.8.x/server-kotlin/java/functions/list-deployments.md @@ -11,7 +11,7 @@ Functions functions = new Functions(client); functions.listDeployments( "", // functionId - listOf(), // queries (optional) + List.of(), // queries (optional) "", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/functions/list-executions.md b/docs/examples/1.8.x/server-kotlin/java/functions/list-executions.md index 8026d4730b..2b97ab3be6 100644 --- a/docs/examples/1.8.x/server-kotlin/java/functions/list-executions.md +++ b/docs/examples/1.8.x/server-kotlin/java/functions/list-executions.md @@ -11,7 +11,7 @@ Functions functions = new Functions(client); functions.listExecutions( "", // functionId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/functions/list.md b/docs/examples/1.8.x/server-kotlin/java/functions/list.md index 5d0f59c3b8..712510db4c 100644 --- a/docs/examples/1.8.x/server-kotlin/java/functions/list.md +++ b/docs/examples/1.8.x/server-kotlin/java/functions/list.md @@ -10,7 +10,7 @@ Client client = new Client() Functions functions = new Functions(client); functions.list( - listOf(), // queries (optional) + List.of(), // queries (optional) "", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/functions/update.md b/docs/examples/1.8.x/server-kotlin/java/functions/update.md index 5cb560e200..4b54a6361d 100644 --- a/docs/examples/1.8.x/server-kotlin/java/functions/update.md +++ b/docs/examples/1.8.x/server-kotlin/java/functions/update.md @@ -14,15 +14,15 @@ functions.update( "", // functionId "", // name Runtime.NODE_14_5, // runtime (optional) - listOf("any"), // execute (optional) - listOf(), // events (optional) + List.of("any"), // execute (optional) + List.of(), // events (optional) "", // schedule (optional) 1, // timeout (optional) false, // enabled (optional) false, // logging (optional) "", // entrypoint (optional) "", // commands (optional) - listOf(), // scopes (optional) + List.of(), // scopes (optional) "", // installationId (optional) "", // providerRepositoryId (optional) "", // providerBranch (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/graphql/mutation.md b/docs/examples/1.8.x/server-kotlin/java/graphql/mutation.md index 778892457b..baf41a8a65 100644 --- a/docs/examples/1.8.x/server-kotlin/java/graphql/mutation.md +++ b/docs/examples/1.8.x/server-kotlin/java/graphql/mutation.md @@ -10,7 +10,7 @@ Client client = new Client() Graphql graphql = new Graphql(client); graphql.mutation( - mapOf( "a" to "b" ), // query + Map.of("a", "b"), // query new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/graphql/query.md b/docs/examples/1.8.x/server-kotlin/java/graphql/query.md index e109d523f8..381da3fb5a 100644 --- a/docs/examples/1.8.x/server-kotlin/java/graphql/query.md +++ b/docs/examples/1.8.x/server-kotlin/java/graphql/query.md @@ -10,7 +10,7 @@ Client client = new Client() Graphql graphql = new Graphql(client); graphql.query( - mapOf( "a" to "b" ), // query + Map.of("a", "b"), // query new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/create-email.md b/docs/examples/1.8.x/server-kotlin/java/messaging/create-email.md index d6ab5ee1bf..ca654e6a08 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/create-email.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/create-email.md @@ -13,12 +13,12 @@ messaging.createEmail( "", // messageId "", // subject "", // content - listOf(), // topics (optional) - listOf(), // users (optional) - listOf(), // targets (optional) - listOf(), // cc (optional) - listOf(), // bcc (optional) - listOf(), // attachments (optional) + List.of(), // topics (optional) + List.of(), // users (optional) + List.of(), // targets (optional) + List.of(), // cc (optional) + List.of(), // bcc (optional) + List.of(), // attachments (optional) false, // draft (optional) false, // html (optional) "", // scheduledAt (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/create-fcm-provider.md b/docs/examples/1.8.x/server-kotlin/java/messaging/create-fcm-provider.md index 0d67e28cf0..554ab3cbcf 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/create-fcm-provider.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/create-fcm-provider.md @@ -12,7 +12,7 @@ Messaging messaging = new Messaging(client); messaging.createFCMProvider( "", // providerId "", // name - mapOf( "a" to "b" ), // serviceAccountJSON (optional) + Map.of("a", "b"), // serviceAccountJSON (optional) false, // enabled (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/create-push.md b/docs/examples/1.8.x/server-kotlin/java/messaging/create-push.md index 94119e6a93..7ab3541a7d 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/create-push.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/create-push.md @@ -14,10 +14,10 @@ messaging.createPush( "", // messageId "", // title (optional) "<BODY>", // body (optional) - listOf(), // topics (optional) - listOf(), // users (optional) - listOf(), // targets (optional) - mapOf( "a" to "b" ), // data (optional) + List.of(), // topics (optional) + List.of(), // users (optional) + List.of(), // targets (optional) + Map.of("a", "b"), // data (optional) "<ACTION>", // action (optional) "<ID1:ID2>", // image (optional) "<ICON>", // icon (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/create-sms.md b/docs/examples/1.8.x/server-kotlin/java/messaging/create-sms.md index ca40cc33a8..5ac95079f7 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/create-sms.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/create-sms.md @@ -12,9 +12,9 @@ Messaging messaging = new Messaging(client); messaging.createSMS( "<MESSAGE_ID>", // messageId "<CONTENT>", // content - listOf(), // topics (optional) - listOf(), // users (optional) - listOf(), // targets (optional) + List.of(), // topics (optional) + List.of(), // users (optional) + List.of(), // targets (optional) false, // draft (optional) "", // scheduledAt (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/create-topic.md b/docs/examples/1.8.x/server-kotlin/java/messaging/create-topic.md index 63a24b467d..ec55053d42 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/create-topic.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/create-topic.md @@ -12,7 +12,7 @@ Messaging messaging = new Messaging(client); messaging.createTopic( "<TOPIC_ID>", // topicId "<NAME>", // name - listOf("any"), // subscribe (optional) + List.of("any"), // subscribe (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/list-message-logs.md b/docs/examples/1.8.x/server-kotlin/java/messaging/list-message-logs.md index 253299ccec..30469ffc09 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/list-message-logs.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/list-message-logs.md @@ -11,7 +11,7 @@ Messaging messaging = new Messaging(client); messaging.listMessageLogs( "<MESSAGE_ID>", // messageId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/list-messages.md b/docs/examples/1.8.x/server-kotlin/java/messaging/list-messages.md index 6535222999..8af8085d8e 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/list-messages.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/list-messages.md @@ -10,7 +10,7 @@ Client client = new Client() Messaging messaging = new Messaging(client); messaging.listMessages( - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/list-provider-logs.md b/docs/examples/1.8.x/server-kotlin/java/messaging/list-provider-logs.md index 3bce21144d..e62dedf89d 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/list-provider-logs.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/list-provider-logs.md @@ -11,7 +11,7 @@ Messaging messaging = new Messaging(client); messaging.listProviderLogs( "<PROVIDER_ID>", // providerId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/list-providers.md b/docs/examples/1.8.x/server-kotlin/java/messaging/list-providers.md index 115cd41745..b52408c02d 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/list-providers.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/list-providers.md @@ -10,7 +10,7 @@ Client client = new Client() Messaging messaging = new Messaging(client); messaging.listProviders( - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/list-subscriber-logs.md b/docs/examples/1.8.x/server-kotlin/java/messaging/list-subscriber-logs.md index e2e1e94667..317db32986 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/list-subscriber-logs.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/list-subscriber-logs.md @@ -11,7 +11,7 @@ Messaging messaging = new Messaging(client); messaging.listSubscriberLogs( "<SUBSCRIBER_ID>", // subscriberId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/list-subscribers.md b/docs/examples/1.8.x/server-kotlin/java/messaging/list-subscribers.md index 3b87ed865d..cc552179de 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/list-subscribers.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/list-subscribers.md @@ -11,7 +11,7 @@ Messaging messaging = new Messaging(client); messaging.listSubscribers( "<TOPIC_ID>", // topicId - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/list-targets.md b/docs/examples/1.8.x/server-kotlin/java/messaging/list-targets.md index db6aee1f68..b123218d22 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/list-targets.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/list-targets.md @@ -11,7 +11,7 @@ Messaging messaging = new Messaging(client); messaging.listTargets( "<MESSAGE_ID>", // messageId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/list-topic-logs.md b/docs/examples/1.8.x/server-kotlin/java/messaging/list-topic-logs.md index 800bb6b7d2..d2d8809575 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/list-topic-logs.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/list-topic-logs.md @@ -11,7 +11,7 @@ Messaging messaging = new Messaging(client); messaging.listTopicLogs( "<TOPIC_ID>", // topicId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/list-topics.md b/docs/examples/1.8.x/server-kotlin/java/messaging/list-topics.md index bd53b41d21..ea258c2759 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/list-topics.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/list-topics.md @@ -10,7 +10,7 @@ Client client = new Client() Messaging messaging = new Messaging(client); messaging.listTopics( - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/update-email.md b/docs/examples/1.8.x/server-kotlin/java/messaging/update-email.md index 56e9767861..1bed63b313 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/update-email.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/update-email.md @@ -11,17 +11,17 @@ Messaging messaging = new Messaging(client); messaging.updateEmail( "<MESSAGE_ID>", // messageId - listOf(), // topics (optional) - listOf(), // users (optional) - listOf(), // targets (optional) + List.of(), // topics (optional) + List.of(), // users (optional) + List.of(), // targets (optional) "<SUBJECT>", // subject (optional) "<CONTENT>", // content (optional) false, // draft (optional) false, // html (optional) - listOf(), // cc (optional) - listOf(), // bcc (optional) + List.of(), // cc (optional) + List.of(), // bcc (optional) "", // scheduledAt (optional) - listOf(), // attachments (optional) + List.of(), // attachments (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/update-fcm-provider.md b/docs/examples/1.8.x/server-kotlin/java/messaging/update-fcm-provider.md index dd92f9321f..2976ff5a53 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/update-fcm-provider.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/update-fcm-provider.md @@ -13,7 +13,7 @@ messaging.updateFCMProvider( "<PROVIDER_ID>", // providerId "<NAME>", // name (optional) false, // enabled (optional) - mapOf( "a" to "b" ), // serviceAccountJSON (optional) + Map.of("a", "b"), // serviceAccountJSON (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/update-push.md b/docs/examples/1.8.x/server-kotlin/java/messaging/update-push.md index 80ad6fcd30..80f70130e5 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/update-push.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/update-push.md @@ -12,12 +12,12 @@ Messaging messaging = new Messaging(client); messaging.updatePush( "<MESSAGE_ID>", // messageId - listOf(), // topics (optional) - listOf(), // users (optional) - listOf(), // targets (optional) + List.of(), // topics (optional) + List.of(), // users (optional) + List.of(), // targets (optional) "<TITLE>", // title (optional) "<BODY>", // body (optional) - mapOf( "a" to "b" ), // data (optional) + Map.of("a", "b"), // data (optional) "<ACTION>", // action (optional) "<ID1:ID2>", // image (optional) "<ICON>", // icon (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/update-sms.md b/docs/examples/1.8.x/server-kotlin/java/messaging/update-sms.md index c59505b68a..4df9588f55 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/update-sms.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/update-sms.md @@ -11,9 +11,9 @@ Messaging messaging = new Messaging(client); messaging.updateSMS( "<MESSAGE_ID>", // messageId - listOf(), // topics (optional) - listOf(), // users (optional) - listOf(), // targets (optional) + List.of(), // topics (optional) + List.of(), // users (optional) + List.of(), // targets (optional) "<CONTENT>", // content (optional) false, // draft (optional) "", // scheduledAt (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/messaging/update-topic.md b/docs/examples/1.8.x/server-kotlin/java/messaging/update-topic.md index be9c44dc23..0d651c7895 100644 --- a/docs/examples/1.8.x/server-kotlin/java/messaging/update-topic.md +++ b/docs/examples/1.8.x/server-kotlin/java/messaging/update-topic.md @@ -12,7 +12,7 @@ Messaging messaging = new Messaging(client); messaging.updateTopic( "<TOPIC_ID>", // topicId "<NAME>", // name (optional) - listOf("any"), // subscribe (optional) + List.of("any"), // subscribe (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/sites/create-template-deployment.md b/docs/examples/1.8.x/server-kotlin/java/sites/create-template-deployment.md index a501461a2c..e7fee585a7 100644 --- a/docs/examples/1.8.x/server-kotlin/java/sites/create-template-deployment.md +++ b/docs/examples/1.8.x/server-kotlin/java/sites/create-template-deployment.md @@ -1,7 +1,7 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; import io.appwrite.services.Sites; -import io.appwrite.enums.Type; +import io.appwrite.enums.TemplateReferenceType; Client client = new Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,7 +15,7 @@ sites.createTemplateDeployment( "<REPOSITORY>", // repository "<OWNER>", // owner "<ROOT_DIRECTORY>", // rootDirectory - .BRANCH, // type + TemplateReferenceType.BRANCH, // type "<REFERENCE>", // reference false, // activate (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/sites/create-vcs-deployment.md b/docs/examples/1.8.x/server-kotlin/java/sites/create-vcs-deployment.md index 754eb26419..8a1dca95cc 100644 --- a/docs/examples/1.8.x/server-kotlin/java/sites/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-kotlin/java/sites/create-vcs-deployment.md @@ -1,7 +1,7 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; import io.appwrite.services.Sites; -import io.appwrite.enums.VCSDeploymentType; +import io.appwrite.enums.VCSReferenceType; Client client = new Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -12,7 +12,7 @@ Sites sites = new Sites(client); sites.createVcsDeployment( "<SITE_ID>", // siteId - VCSDeploymentType.BRANCH, // type + VCSReferenceType.BRANCH, // type "<REFERENCE>", // reference false, // activate (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/sites/list-deployments.md b/docs/examples/1.8.x/server-kotlin/java/sites/list-deployments.md index a19f4ecf09..a1953cdf6e 100644 --- a/docs/examples/1.8.x/server-kotlin/java/sites/list-deployments.md +++ b/docs/examples/1.8.x/server-kotlin/java/sites/list-deployments.md @@ -11,7 +11,7 @@ Sites sites = new Sites(client); sites.listDeployments( "<SITE_ID>", // siteId - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/sites/list-logs.md b/docs/examples/1.8.x/server-kotlin/java/sites/list-logs.md index 85c2171aa2..095f0ae97a 100644 --- a/docs/examples/1.8.x/server-kotlin/java/sites/list-logs.md +++ b/docs/examples/1.8.x/server-kotlin/java/sites/list-logs.md @@ -11,7 +11,7 @@ Sites sites = new Sites(client); sites.listLogs( "<SITE_ID>", // siteId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/sites/list.md b/docs/examples/1.8.x/server-kotlin/java/sites/list.md index 20b5533c7a..d8c69419fe 100644 --- a/docs/examples/1.8.x/server-kotlin/java/sites/list.md +++ b/docs/examples/1.8.x/server-kotlin/java/sites/list.md @@ -10,7 +10,7 @@ Client client = new Client() Sites sites = new Sites(client); sites.list( - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/storage/create-bucket.md b/docs/examples/1.8.x/server-kotlin/java/storage/create-bucket.md index b228408933..0af282a0ca 100644 --- a/docs/examples/1.8.x/server-kotlin/java/storage/create-bucket.md +++ b/docs/examples/1.8.x/server-kotlin/java/storage/create-bucket.md @@ -1,9 +1,9 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Storage; -import io.appwrite.enums.Compression; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Storage; +import io.appwrite.enums.Compression; Client client = new Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,11 +15,11 @@ Storage storage = new Storage(client); storage.createBucket( "<BUCKET_ID>", // bucketId "<NAME>", // name - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) false, // fileSecurity (optional) false, // enabled (optional) 1, // maximumFileSize (optional) - listOf(), // allowedFileExtensions (optional) + List.of(), // allowedFileExtensions (optional) Compression.NONE, // compression (optional) false, // encryption (optional) false, // antivirus (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/storage/create-file.md b/docs/examples/1.8.x/server-kotlin/java/storage/create-file.md index 7ddf8913e1..abaeee3eca 100644 --- a/docs/examples/1.8.x/server-kotlin/java/storage/create-file.md +++ b/docs/examples/1.8.x/server-kotlin/java/storage/create-file.md @@ -1,9 +1,9 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; import io.appwrite.models.InputFile; -import io.appwrite.services.Storage; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Storage; Client client = new Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -16,7 +16,7 @@ storage.createFile( "<BUCKET_ID>", // bucketId "<FILE_ID>", // fileId InputFile.fromPath("file.png"), // file - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/storage/list-buckets.md b/docs/examples/1.8.x/server-kotlin/java/storage/list-buckets.md index 504cfb7ce2..3fde32c73c 100644 --- a/docs/examples/1.8.x/server-kotlin/java/storage/list-buckets.md +++ b/docs/examples/1.8.x/server-kotlin/java/storage/list-buckets.md @@ -10,7 +10,7 @@ Client client = new Client() Storage storage = new Storage(client); storage.listBuckets( - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/storage/list-files.md b/docs/examples/1.8.x/server-kotlin/java/storage/list-files.md index 6397ca07bc..712c420011 100644 --- a/docs/examples/1.8.x/server-kotlin/java/storage/list-files.md +++ b/docs/examples/1.8.x/server-kotlin/java/storage/list-files.md @@ -11,7 +11,7 @@ Storage storage = new Storage(client); storage.listFiles( "<BUCKET_ID>", // bucketId - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/storage/update-bucket.md b/docs/examples/1.8.x/server-kotlin/java/storage/update-bucket.md index 26758cf19a..47b322e449 100644 --- a/docs/examples/1.8.x/server-kotlin/java/storage/update-bucket.md +++ b/docs/examples/1.8.x/server-kotlin/java/storage/update-bucket.md @@ -1,9 +1,9 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Storage; -import io.appwrite.enums.Compression; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Storage; +import io.appwrite.enums.Compression; Client client = new Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,11 +15,11 @@ Storage storage = new Storage(client); storage.updateBucket( "<BUCKET_ID>", // bucketId "<NAME>", // name - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) false, // fileSecurity (optional) false, // enabled (optional) 1, // maximumFileSize (optional) - listOf(), // allowedFileExtensions (optional) + List.of(), // allowedFileExtensions (optional) Compression.NONE, // compression (optional) false, // encryption (optional) false, // antivirus (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/storage/update-file.md b/docs/examples/1.8.x/server-kotlin/java/storage/update-file.md index d534e0eb64..b394fd5672 100644 --- a/docs/examples/1.8.x/server-kotlin/java/storage/update-file.md +++ b/docs/examples/1.8.x/server-kotlin/java/storage/update-file.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.Storage; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.Storage; Client client = new Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,7 +15,7 @@ storage.updateFile( "<BUCKET_ID>", // bucketId "<FILE_ID>", // fileId "<NAME>", // name (optional) - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-enum-column.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-enum-column.md index bc31bbe79e..ea0452f250 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-enum-column.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-enum-column.md @@ -13,7 +13,7 @@ tablesDB.createEnumColumn( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId "", // key - listOf(), // elements + List.of(), // elements false, // required "<DEFAULT>", // default (optional) false, // array (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-index.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-index.md index 991bd3429b..07076d9763 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-index.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-index.md @@ -15,9 +15,9 @@ tablesDB.createIndex( "<TABLE_ID>", // tableId "", // key IndexType.KEY, // type - listOf(), // columns - listOf(), // orders (optional) - listOf(), // lengths (optional) + List.of(), // columns + List.of(), // orders (optional) + List.of(), // lengths (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-line-column.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-line-column.md index afe029ebe8..f8351cac79 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-line-column.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-line-column.md @@ -14,7 +14,7 @@ tablesDB.createLineColumn( "<TABLE_ID>", // tableId "", // key false, // required - listOf([1, 2], [3, 4], [5, 6]), // default (optional) + List.of(List.of(1, 2), List.of(3, 4), List.of(5, 6)), // default (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-operations.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-operations.md index 9504f623b3..a7c3a1bcb8 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-operations.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-operations.md @@ -11,17 +11,15 @@ TablesDB tablesDB = new TablesDB(client); tablesDB.createOperations( "<TRANSACTION_ID>", // transactionId - listOf( - { - "action": "create", - "databaseId": "<DATABASE_ID>", - "tableId": "<TABLE_ID>", - "rowId": "<ROW_ID>", - "data": { - "name": "Walter O'Brien" - } - } - ), // operations (optional) + List.of(Map.of( + "action", "create", + "databaseId", "<DATABASE_ID>", + "tableId", "<TABLE_ID>", + "rowId", "<ROW_ID>", + "data", Map.of( + "name", "Walter O'Brien" + ) + )), // operations (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-point-column.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-point-column.md index 2c9941b09c..2f6ae0e937 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-point-column.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-point-column.md @@ -14,7 +14,7 @@ tablesDB.createPointColumn( "<TABLE_ID>", // tableId "", // key false, // required - listOf(1, 2), // default (optional) + List.of(1, 2), // default (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-polygon-column.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-polygon-column.md index 58ca798381..58caa8a791 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-polygon-column.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-polygon-column.md @@ -14,7 +14,7 @@ tablesDB.createPolygonColumn( "<TABLE_ID>", // tableId "", // key false, // required - listOf([[1, 2], [3, 4], [5, 6], [1, 2]]), // default (optional) + List.of(List.of(List.of(1, 2), List.of(3, 4), List.of(5, 6), List.of(1, 2))), // default (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-row.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-row.md index 9e47167cd1..3da8c3248e 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-row.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-row.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.TablesDB; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.TablesDB; Client client = new Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,14 +15,14 @@ tablesDB.createRow( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId "<ROW_ID>", // rowId - mapOf( - "username" to "walter.obrien", - "email" to "walter.obrien@example.com", - "fullName" to "Walter O'Brien", - "age" to 30, - "isAdmin" to false + Map.of( + "username", "walter.obrien", + "email", "walter.obrien@example.com", + "fullName", "Walter O'Brien", + "age", 30, + "isAdmin", false ), // data - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) "<TRANSACTION_ID>", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-rows.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-rows.md index 956d812165..e99ea04da4 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-rows.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-rows.md @@ -12,7 +12,7 @@ TablesDB tablesDB = new TablesDB(client); tablesDB.createRows( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId - listOf(), // rows + List.of(), // rows "<TRANSACTION_ID>", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-table.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-table.md index 1f9fd10d5c..615278a2d4 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-table.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/create-table.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.TablesDB; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.TablesDB; Client client = new Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,7 +15,7 @@ tablesDB.createTable( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId "<NAME>", // name - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) false, // rowSecurity (optional) false, // enabled (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-rows.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-rows.md index 80ca0bb40f..f90789d276 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-rows.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/delete-rows.md @@ -12,7 +12,7 @@ TablesDB tablesDB = new TablesDB(client); tablesDB.deleteRows( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId - listOf(), // queries (optional) + List.of(), // queries (optional) "<TRANSACTION_ID>", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/get-row.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/get-row.md index d642ebcaf1..5b18e8f879 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/get-row.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/get-row.md @@ -13,7 +13,7 @@ tablesDB.getRow( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId "<ROW_ID>", // rowId - listOf(), // queries (optional) + List.of(), // queries (optional) "<TRANSACTION_ID>", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-columns.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-columns.md index bdf376cb33..f7e37f970b 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-columns.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-columns.md @@ -12,7 +12,7 @@ TablesDB tablesDB = new TablesDB(client); tablesDB.listColumns( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-indexes.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-indexes.md index 5b73204314..ca792c5470 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-indexes.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-indexes.md @@ -12,7 +12,7 @@ TablesDB tablesDB = new TablesDB(client); tablesDB.listIndexes( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-rows.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-rows.md index 8d7956bbc6..7903da4b32 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-rows.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-rows.md @@ -12,7 +12,7 @@ TablesDB tablesDB = new TablesDB(client); tablesDB.listRows( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId - listOf(), // queries (optional) + List.of(), // queries (optional) "<TRANSACTION_ID>", // transactionId (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-tables.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-tables.md index 646be0f4a4..9b4c88bdc0 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-tables.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-tables.md @@ -11,7 +11,7 @@ TablesDB tablesDB = new TablesDB(client); tablesDB.listTables( "<DATABASE_ID>", // databaseId - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-transactions.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-transactions.md index acc4902da4..f0b4db05db 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-transactions.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list-transactions.md @@ -10,7 +10,7 @@ Client client = new Client() TablesDB tablesDB = new TablesDB(client); tablesDB.listTransactions( - listOf(), // queries (optional) + List.of(), // queries (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list.md index 98784df806..cf23036591 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/list.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/list.md @@ -10,7 +10,7 @@ Client client = new Client() TablesDB tablesDB = new TablesDB(client); tablesDB.list( - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-enum-column.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-enum-column.md index 5a8036aebc..0f182b76f0 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-enum-column.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-enum-column.md @@ -13,7 +13,7 @@ tablesDB.updateEnumColumn( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId "", // key - listOf(), // elements + List.of(), // elements false, // required "<DEFAULT>", // default "", // newKey (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-line-column.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-line-column.md index 4c65a907f7..4ef648fbc9 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-line-column.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-line-column.md @@ -14,7 +14,7 @@ tablesDB.updateLineColumn( "<TABLE_ID>", // tableId "", // key false, // required - listOf([1, 2], [3, 4], [5, 6]), // default (optional) + List.of(List.of(1, 2), List.of(3, 4), List.of(5, 6)), // default (optional) "", // newKey (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-point-column.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-point-column.md index 56ac86a6f0..729595dfa6 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-point-column.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-point-column.md @@ -14,7 +14,7 @@ tablesDB.updatePointColumn( "<TABLE_ID>", // tableId "", // key false, // required - listOf(1, 2), // default (optional) + List.of(1, 2), // default (optional) "", // newKey (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-polygon-column.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-polygon-column.md index 189d473175..bc6ccc93b5 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-polygon-column.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-polygon-column.md @@ -14,7 +14,7 @@ tablesDB.updatePolygonColumn( "<TABLE_ID>", // tableId "", // key false, // required - listOf([[1, 2], [3, 4], [5, 6], [1, 2]]), // default (optional) + List.of(List.of(List.of(1, 2), List.of(3, 4), List.of(5, 6), List.of(1, 2))), // default (optional) "", // newKey (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-row.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-row.md index 835f63b19a..5585274cbb 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-row.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-row.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.TablesDB; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.TablesDB; Client client = new Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,8 +15,8 @@ tablesDB.updateRow( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId "<ROW_ID>", // rowId - mapOf( "a" to "b" ), // data (optional) - listOf(Permission.read(Role.any())), // permissions (optional) + Map.of("a", "b"), // data (optional) + List.of(Permission.read(Role.any())), // permissions (optional) "<TRANSACTION_ID>", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-rows.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-rows.md index 7d39e4422c..b479613856 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-rows.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-rows.md @@ -12,8 +12,8 @@ TablesDB tablesDB = new TablesDB(client); tablesDB.updateRows( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId - mapOf( "a" to "b" ), // data (optional) - listOf(), // queries (optional) + Map.of("a", "b"), // data (optional) + List.of(), // queries (optional) "<TRANSACTION_ID>", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-table.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-table.md index 257803b984..cf0c2fbc2b 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-table.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/update-table.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.TablesDB; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.TablesDB; Client client = new Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,7 +15,7 @@ tablesDB.updateTable( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId "<NAME>", // name - listOf(Permission.read(Role.any())), // permissions (optional) + List.of(Permission.read(Role.any())), // permissions (optional) false, // rowSecurity (optional) false, // enabled (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-row.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-row.md index 6ea29e3e8d..adb2095f34 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-row.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-row.md @@ -1,8 +1,8 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; -import io.appwrite.services.TablesDB; import io.appwrite.Permission; import io.appwrite.Role; +import io.appwrite.services.TablesDB; Client client = new Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,8 +15,8 @@ tablesDB.upsertRow( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId "<ROW_ID>", // rowId - mapOf( "a" to "b" ), // data (optional) - listOf(Permission.read(Role.any())), // permissions (optional) + Map.of("a", "b"), // data (optional) + List.of(Permission.read(Role.any())), // permissions (optional) "<TRANSACTION_ID>", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-rows.md b/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-rows.md index c4b2bf3857..e16ecb35c9 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-rows.md +++ b/docs/examples/1.8.x/server-kotlin/java/tablesdb/upsert-rows.md @@ -12,7 +12,7 @@ TablesDB tablesDB = new TablesDB(client); tablesDB.upsertRows( "<DATABASE_ID>", // databaseId "<TABLE_ID>", // tableId - listOf(), // rows + List.of(), // rows "<TRANSACTION_ID>", // transactionId (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/teams/create-membership.md b/docs/examples/1.8.x/server-kotlin/java/teams/create-membership.md index 89e9d96ef6..e71ea2e578 100644 --- a/docs/examples/1.8.x/server-kotlin/java/teams/create-membership.md +++ b/docs/examples/1.8.x/server-kotlin/java/teams/create-membership.md @@ -11,7 +11,7 @@ Teams teams = new Teams(client); teams.createMembership( "<TEAM_ID>", // teamId - listOf(), // roles + List.of(), // roles "email@example.com", // email (optional) "<USER_ID>", // userId (optional) "+12065550100", // phone (optional) diff --git a/docs/examples/1.8.x/server-kotlin/java/teams/create.md b/docs/examples/1.8.x/server-kotlin/java/teams/create.md index 28cc3dada1..bc82cb47e1 100644 --- a/docs/examples/1.8.x/server-kotlin/java/teams/create.md +++ b/docs/examples/1.8.x/server-kotlin/java/teams/create.md @@ -12,7 +12,7 @@ Teams teams = new Teams(client); teams.create( "<TEAM_ID>", // teamId "<NAME>", // name - listOf(), // roles (optional) + List.of(), // roles (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/teams/list-memberships.md b/docs/examples/1.8.x/server-kotlin/java/teams/list-memberships.md index bfbf519db8..5437e6048c 100644 --- a/docs/examples/1.8.x/server-kotlin/java/teams/list-memberships.md +++ b/docs/examples/1.8.x/server-kotlin/java/teams/list-memberships.md @@ -11,7 +11,7 @@ Teams teams = new Teams(client); teams.listMemberships( "<TEAM_ID>", // teamId - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/teams/list.md b/docs/examples/1.8.x/server-kotlin/java/teams/list.md index 7ff98ad86f..06f0034bfe 100644 --- a/docs/examples/1.8.x/server-kotlin/java/teams/list.md +++ b/docs/examples/1.8.x/server-kotlin/java/teams/list.md @@ -10,7 +10,7 @@ Client client = new Client() Teams teams = new Teams(client); teams.list( - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/teams/update-membership.md b/docs/examples/1.8.x/server-kotlin/java/teams/update-membership.md index d4816c57f1..2e0de4e3b0 100644 --- a/docs/examples/1.8.x/server-kotlin/java/teams/update-membership.md +++ b/docs/examples/1.8.x/server-kotlin/java/teams/update-membership.md @@ -12,7 +12,7 @@ Teams teams = new Teams(client); teams.updateMembership( "<TEAM_ID>", // teamId "<MEMBERSHIP_ID>", // membershipId - listOf(), // roles + List.of(), // roles new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/teams/update-prefs.md b/docs/examples/1.8.x/server-kotlin/java/teams/update-prefs.md index 2ef05222df..85f18aef1e 100644 --- a/docs/examples/1.8.x/server-kotlin/java/teams/update-prefs.md +++ b/docs/examples/1.8.x/server-kotlin/java/teams/update-prefs.md @@ -11,7 +11,7 @@ Teams teams = new Teams(client); teams.updatePrefs( "<TEAM_ID>", // teamId - mapOf( "a" to "b" ), // prefs + Map.of("a", "b"), // prefs new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/tokens/list.md b/docs/examples/1.8.x/server-kotlin/java/tokens/list.md index 23c51a64ef..1147f6f536 100644 --- a/docs/examples/1.8.x/server-kotlin/java/tokens/list.md +++ b/docs/examples/1.8.x/server-kotlin/java/tokens/list.md @@ -12,7 +12,7 @@ Tokens tokens = new Tokens(client); tokens.list( "<BUCKET_ID>", // bucketId "<FILE_ID>", // fileId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/users/list-identities.md b/docs/examples/1.8.x/server-kotlin/java/users/list-identities.md index fc95c8e48c..2dfa297592 100644 --- a/docs/examples/1.8.x/server-kotlin/java/users/list-identities.md +++ b/docs/examples/1.8.x/server-kotlin/java/users/list-identities.md @@ -10,7 +10,7 @@ Client client = new Client() Users users = new Users(client); users.listIdentities( - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/users/list-logs.md b/docs/examples/1.8.x/server-kotlin/java/users/list-logs.md index 4a2e549ed4..ebaa749f56 100644 --- a/docs/examples/1.8.x/server-kotlin/java/users/list-logs.md +++ b/docs/examples/1.8.x/server-kotlin/java/users/list-logs.md @@ -11,7 +11,7 @@ Users users = new Users(client); users.listLogs( "<USER_ID>", // userId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/users/list-memberships.md b/docs/examples/1.8.x/server-kotlin/java/users/list-memberships.md index 36e67ae322..3335e18bc7 100644 --- a/docs/examples/1.8.x/server-kotlin/java/users/list-memberships.md +++ b/docs/examples/1.8.x/server-kotlin/java/users/list-memberships.md @@ -11,7 +11,7 @@ Users users = new Users(client); users.listMemberships( "<USER_ID>", // userId - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/users/list-targets.md b/docs/examples/1.8.x/server-kotlin/java/users/list-targets.md index 156aaefa7e..02fd291cb5 100644 --- a/docs/examples/1.8.x/server-kotlin/java/users/list-targets.md +++ b/docs/examples/1.8.x/server-kotlin/java/users/list-targets.md @@ -11,7 +11,7 @@ Users users = new Users(client); users.listTargets( "<USER_ID>", // userId - listOf(), // queries (optional) + List.of(), // queries (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { if (error != null) { diff --git a/docs/examples/1.8.x/server-kotlin/java/users/list.md b/docs/examples/1.8.x/server-kotlin/java/users/list.md index ec038afded..65ed4b00f8 100644 --- a/docs/examples/1.8.x/server-kotlin/java/users/list.md +++ b/docs/examples/1.8.x/server-kotlin/java/users/list.md @@ -10,7 +10,7 @@ Client client = new Client() Users users = new Users(client); users.list( - listOf(), // queries (optional) + List.of(), // queries (optional) "<SEARCH>", // search (optional) false, // total (optional) new CoroutineCallback<>((result, error) -> { diff --git a/docs/examples/1.8.x/server-kotlin/java/users/update-labels.md b/docs/examples/1.8.x/server-kotlin/java/users/update-labels.md index 379200a56b..953f466290 100644 --- a/docs/examples/1.8.x/server-kotlin/java/users/update-labels.md +++ b/docs/examples/1.8.x/server-kotlin/java/users/update-labels.md @@ -11,7 +11,7 @@ Users users = new Users(client); users.updateLabels( "<USER_ID>", // userId - listOf(), // labels + List.of(), // labels new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/java/users/update-prefs.md b/docs/examples/1.8.x/server-kotlin/java/users/update-prefs.md index c5a9677a20..5a128aa716 100644 --- a/docs/examples/1.8.x/server-kotlin/java/users/update-prefs.md +++ b/docs/examples/1.8.x/server-kotlin/java/users/update-prefs.md @@ -11,7 +11,7 @@ Users users = new Users(client); users.updatePrefs( "<USER_ID>", // userId - mapOf( "a" to "b" ), // prefs + Map.of("a", "b"), // prefs new CoroutineCallback<>((result, error) -> { if (error != null) { error.printStackTrace(); diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/avatars/get-screenshot.md b/docs/examples/1.8.x/server-kotlin/kotlin/avatars/get-screenshot.md index eca5493c57..a2de2e5adc 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/avatars/get-screenshot.md @@ -14,23 +14,26 @@ val avatars = Avatars(client) val result = avatars.getScreenshot( url = "https://example.com", - headers = mapOf( "a" to "b" ), // optional - viewportWidth = 1, // optional - viewportHeight = 1, // optional - scale = 0.1, // optional - theme = "light", // optional - userAgent = "<USER_AGENT>", // optional - fullpage = false, // optional - locale = "<LOCALE>", // optional - timezone = "africa/abidjan", // optional - latitude = -90, // optional - longitude = -180, // optional - accuracy = 0, // optional - touch = false, // optional - permissions = listOf(), // optional - sleep = 0, // optional - width = 0, // optional - height = 0, // optional - quality = -1, // optional - output = "jpg" // optional + headers = mapOf( + "Authorization" to "Bearer token123", + "X-Custom-Header" to "value" + ), // optional + viewportWidth = 1920, // optional + viewportHeight = 1080, // optional + scale = 2, // optional + theme = "dark", // optional + userAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15", // optional + fullpage = true, // optional + locale = "en-US", // optional + timezone = "America/New_York", // optional + latitude = 37.7749, // optional + longitude = -122.4194, // optional + accuracy = 100, // optional + touch = true, // optional + permissions = listOf("geolocation", "notifications"), // optional + sleep = 3, // optional + width = 800, // optional + height = 600, // optional + quality = 85, // optional + output = "jpeg" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-line-attribute.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-line-attribute.md index af9a4d2425..8f1322b3fd 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-line-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-line-attribute.md @@ -14,5 +14,5 @@ val response = databases.createLineAttribute( collectionId = "<COLLECTION_ID>", key = "", required = false, - default = listOf([1, 2], [3, 4], [5, 6]) // optional + default = listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6)) // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-operations.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-operations.md index 1c741818b9..eae10ab609 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-operations.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-operations.md @@ -11,15 +11,13 @@ val databases = Databases(client) val response = databases.createOperations( transactionId = "<TRANSACTION_ID>", - operations = listOf( - { - "action": "create", - "databaseId": "<DATABASE_ID>", - "collectionId": "<COLLECTION_ID>", - "documentId": "<DOCUMENT_ID>", - "data": { - "name": "Walter O'Brien" - } - } - ) // optional + operations = listOf(mapOf( + "action" to "create", + "databaseId" to "<DATABASE_ID>", + "collectionId" to "<COLLECTION_ID>", + "documentId" to "<DOCUMENT_ID>", + "data" to mapOf( + "name" to "Walter O'Brien" + ) + )) // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-polygon-attribute.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-polygon-attribute.md index ffeb3c4398..5a3491c414 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-polygon-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/create-polygon-attribute.md @@ -14,5 +14,5 @@ val response = databases.createPolygonAttribute( collectionId = "<COLLECTION_ID>", key = "", required = false, - default = listOf([[1, 2], [3, 4], [5, 6], [1, 2]]) // optional + default = listOf(listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6), listOf(1, 2))) // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-line-attribute.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-line-attribute.md index 0d6b40a2d8..0a8b50a332 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-line-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-line-attribute.md @@ -14,6 +14,6 @@ val response = databases.updateLineAttribute( collectionId = "<COLLECTION_ID>", key = "", required = false, - default = listOf([1, 2], [3, 4], [5, 6]), // optional + default = listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6)), // optional newKey = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-polygon-attribute.md b/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-polygon-attribute.md index 66bbdea11c..37df6acee0 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-polygon-attribute.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/databases/update-polygon-attribute.md @@ -14,6 +14,6 @@ val response = databases.updatePolygonAttribute( collectionId = "<COLLECTION_ID>", key = "", required = false, - default = listOf([[1, 2], [3, 4], [5, 6], [1, 2]]), // optional + default = listOf(listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6), listOf(1, 2))), // optional newKey = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/functions/create-template-deployment.md b/docs/examples/1.8.x/server-kotlin/kotlin/functions/create-template-deployment.md index b8eb44b4fa..176620e19d 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/functions/create-template-deployment.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/functions/create-template-deployment.md @@ -1,7 +1,7 @@ import io.appwrite.Client import io.appwrite.coroutines.CoroutineCallback import io.appwrite.services.Functions -import io.appwrite.enums.Type +import io.appwrite.enums.TemplateReferenceType val client = Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,7 +15,7 @@ val response = functions.createTemplateDeployment( repository = "<REPOSITORY>", owner = "<OWNER>", rootDirectory = "<ROOT_DIRECTORY>", - type = .COMMIT, + type = TemplateReferenceType.COMMIT, reference = "<REFERENCE>", activate = false // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/functions/create-vcs-deployment.md b/docs/examples/1.8.x/server-kotlin/kotlin/functions/create-vcs-deployment.md index 08bb5a3097..4e1c21daeb 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/functions/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/functions/create-vcs-deployment.md @@ -1,7 +1,7 @@ import io.appwrite.Client import io.appwrite.coroutines.CoroutineCallback import io.appwrite.services.Functions -import io.appwrite.enums.VCSDeploymentType +import io.appwrite.enums.VCSReferenceType val client = Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -12,7 +12,7 @@ val functions = Functions(client) val response = functions.createVcsDeployment( functionId = "<FUNCTION_ID>", - type = VCSDeploymentType.BRANCH, + type = VCSReferenceType.BRANCH, reference = "<REFERENCE>", activate = false // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/sites/create-template-deployment.md b/docs/examples/1.8.x/server-kotlin/kotlin/sites/create-template-deployment.md index 1f3d7d386a..6ef7414bc4 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/sites/create-template-deployment.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/sites/create-template-deployment.md @@ -1,7 +1,7 @@ import io.appwrite.Client import io.appwrite.coroutines.CoroutineCallback import io.appwrite.services.Sites -import io.appwrite.enums.Type +import io.appwrite.enums.TemplateReferenceType val client = Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -15,7 +15,7 @@ val response = sites.createTemplateDeployment( repository = "<REPOSITORY>", owner = "<OWNER>", rootDirectory = "<ROOT_DIRECTORY>", - type = .BRANCH, + type = TemplateReferenceType.BRANCH, reference = "<REFERENCE>", activate = false // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/sites/create-vcs-deployment.md b/docs/examples/1.8.x/server-kotlin/kotlin/sites/create-vcs-deployment.md index 141cf3e658..c11e890fc0 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/sites/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/sites/create-vcs-deployment.md @@ -1,7 +1,7 @@ import io.appwrite.Client import io.appwrite.coroutines.CoroutineCallback import io.appwrite.services.Sites -import io.appwrite.enums.VCSDeploymentType +import io.appwrite.enums.VCSReferenceType val client = Client() .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint @@ -12,7 +12,7 @@ val sites = Sites(client) val response = sites.createVcsDeployment( siteId = "<SITE_ID>", - type = VCSDeploymentType.BRANCH, + type = VCSReferenceType.BRANCH, reference = "<REFERENCE>", activate = false // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-line-column.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-line-column.md index 3dd9ebd083..cede289439 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-line-column.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-line-column.md @@ -14,5 +14,5 @@ val response = tablesDB.createLineColumn( tableId = "<TABLE_ID>", key = "", required = false, - default = listOf([1, 2], [3, 4], [5, 6]) // optional + default = listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6)) // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-operations.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-operations.md index 40c98d1c81..5b05949715 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-operations.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-operations.md @@ -11,15 +11,13 @@ val tablesDB = TablesDB(client) val response = tablesDB.createOperations( transactionId = "<TRANSACTION_ID>", - operations = listOf( - { - "action": "create", - "databaseId": "<DATABASE_ID>", - "tableId": "<TABLE_ID>", - "rowId": "<ROW_ID>", - "data": { - "name": "Walter O'Brien" - } - } - ) // optional + operations = listOf(mapOf( + "action" to "create", + "databaseId" to "<DATABASE_ID>", + "tableId" to "<TABLE_ID>", + "rowId" to "<ROW_ID>", + "data" to mapOf( + "name" to "Walter O'Brien" + ) + )) // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-polygon-column.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-polygon-column.md index 218b4cba33..c4282d1b67 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-polygon-column.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/create-polygon-column.md @@ -14,5 +14,5 @@ val response = tablesDB.createPolygonColumn( tableId = "<TABLE_ID>", key = "", required = false, - default = listOf([[1, 2], [3, 4], [5, 6], [1, 2]]) // optional + default = listOf(listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6), listOf(1, 2))) // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-line-column.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-line-column.md index 571d25206c..4b975c0ee0 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-line-column.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-line-column.md @@ -14,6 +14,6 @@ val response = tablesDB.updateLineColumn( tableId = "<TABLE_ID>", key = "", required = false, - default = listOf([1, 2], [3, 4], [5, 6]), // optional + default = listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6)), // optional newKey = "" // optional ) diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-polygon-column.md b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-polygon-column.md index db3a46bc6c..4042847176 100644 --- a/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-polygon-column.md +++ b/docs/examples/1.8.x/server-kotlin/kotlin/tablesdb/update-polygon-column.md @@ -14,6 +14,6 @@ val response = tablesDB.updatePolygonColumn( tableId = "<TABLE_ID>", key = "", required = false, - default = listOf([[1, 2], [3, 4], [5, 6], [1, 2]]), // optional + default = listOf(listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6), listOf(1, 2))), // optional newKey = "" // optional ) diff --git a/docs/examples/1.8.x/server-nodejs/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-nodejs/examples/avatars/get-screenshot.md index 60a38988cb..dc63102bf9 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/server-nodejs/examples/avatars/get-screenshot.md @@ -9,23 +9,26 @@ const avatars = new sdk.Avatars(client); const result = await avatars.getScreenshot({ url: 'https://example.com', - headers: {}, // optional - viewportWidth: 1, // optional - viewportHeight: 1, // optional - scale: 0.1, // optional + headers: { + "Authorization": "Bearer token123", + "X-Custom-Header": "value" + }, // optional + viewportWidth: 1920, // optional + viewportHeight: 1080, // optional + scale: 2, // optional theme: sdk.Theme.Light, // optional - userAgent: '<USER_AGENT>', // optional - fullpage: false, // optional - locale: '<LOCALE>', // optional + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15', // optional + fullpage: true, // optional + locale: 'en-US', // optional timezone: sdk.Timezone.AfricaAbidjan, // optional - latitude: -90, // optional - longitude: -180, // optional - accuracy: 0, // optional - touch: false, // optional - permissions: [], // optional - sleep: 0, // optional - width: 0, // optional - height: 0, // optional - quality: -1, // optional + latitude: 37.7749, // optional + longitude: -122.4194, // optional + accuracy: 100, // optional + touch: true, // optional + permissions: ["geolocation","notifications"], // optional + sleep: 3, // optional + width: 800, // optional + height: 600, // optional + quality: 85, // optional output: sdk.Output.Jpg // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/functions/create-template-deployment.md b/docs/examples/1.8.x/server-nodejs/examples/functions/create-template-deployment.md index 999c029f8f..f1efd7b199 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/functions/create-template-deployment.md +++ b/docs/examples/1.8.x/server-nodejs/examples/functions/create-template-deployment.md @@ -12,7 +12,7 @@ const result = await functions.createTemplateDeployment({ repository: '<REPOSITORY>', owner: '<OWNER>', rootDirectory: '<ROOT_DIRECTORY>', - type: sdk..Commit, + type: sdk.TemplateReferenceType.Commit, reference: '<REFERENCE>', activate: false // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/functions/create-vcs-deployment.md b/docs/examples/1.8.x/server-nodejs/examples/functions/create-vcs-deployment.md index 0aabfcff8a..c648625531 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/functions/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-nodejs/examples/functions/create-vcs-deployment.md @@ -9,7 +9,7 @@ const functions = new sdk.Functions(client); const result = await functions.createVcsDeployment({ functionId: '<FUNCTION_ID>', - type: sdk.VCSDeploymentType.Branch, + type: sdk.VCSReferenceType.Branch, reference: '<REFERENCE>', activate: false // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/sites/create-template-deployment.md b/docs/examples/1.8.x/server-nodejs/examples/sites/create-template-deployment.md index dc9736fa80..3728f7f846 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/sites/create-template-deployment.md +++ b/docs/examples/1.8.x/server-nodejs/examples/sites/create-template-deployment.md @@ -12,7 +12,7 @@ const result = await sites.createTemplateDeployment({ repository: '<REPOSITORY>', owner: '<OWNER>', rootDirectory: '<ROOT_DIRECTORY>', - type: sdk..Branch, + type: sdk.TemplateReferenceType.Branch, reference: '<REFERENCE>', activate: false // optional }); diff --git a/docs/examples/1.8.x/server-nodejs/examples/sites/create-vcs-deployment.md b/docs/examples/1.8.x/server-nodejs/examples/sites/create-vcs-deployment.md index 7f7c2196ba..95cefea9ae 100644 --- a/docs/examples/1.8.x/server-nodejs/examples/sites/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-nodejs/examples/sites/create-vcs-deployment.md @@ -9,7 +9,7 @@ const sites = new sdk.Sites(client); const result = await sites.createVcsDeployment({ siteId: '<SITE_ID>', - type: sdk.VCSDeploymentType.Branch, + type: sdk.VCSReferenceType.Branch, reference: '<REFERENCE>', activate: false // optional }); diff --git a/docs/examples/1.8.x/server-php/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-php/examples/avatars/get-screenshot.md index b9dfd23862..9f91a3fafe 100644 --- a/docs/examples/1.8.x/server-php/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/server-php/examples/avatars/get-screenshot.md @@ -15,23 +15,26 @@ $avatars = new Avatars($client); $result = $avatars->getScreenshot( url: 'https://example.com', - headers: [], // optional - viewportWidth: 1, // optional - viewportHeight: 1, // optional - scale: 0.1, // optional + headers: [ + 'Authorization' => 'Bearer token123', + 'X-Custom-Header' => 'value' + ], // optional + viewportWidth: 1920, // optional + viewportHeight: 1080, // optional + scale: 2, // optional theme: Theme::LIGHT(), // optional - userAgent: '<USER_AGENT>', // optional - fullpage: false, // optional - locale: '<LOCALE>', // optional + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15', // optional + fullpage: true, // optional + locale: 'en-US', // optional timezone: Timezone::AFRICAABIDJAN(), // optional - latitude: -90, // optional - longitude: -180, // optional - accuracy: 0, // optional - touch: false, // optional - permissions: [], // optional - sleep: 0, // optional - width: 0, // optional - height: 0, // optional - quality: -1, // optional + latitude: 37.7749, // optional + longitude: -122.4194, // optional + accuracy: 100, // optional + touch: true, // optional + permissions: ["geolocation","notifications"], // optional + sleep: 3, // optional + width: 800, // optional + height: 600, // optional + quality: 85, // optional output: Output::JPG() // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/functions/create-template-deployment.md b/docs/examples/1.8.x/server-php/examples/functions/create-template-deployment.md index 5888396312..a1272523f7 100644 --- a/docs/examples/1.8.x/server-php/examples/functions/create-template-deployment.md +++ b/docs/examples/1.8.x/server-php/examples/functions/create-template-deployment.md @@ -2,7 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Functions; -use Appwrite\Enums\Type; +use Appwrite\Enums\TemplateReferenceType; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint @@ -16,7 +16,7 @@ $result = $functions->createTemplateDeployment( repository: '<REPOSITORY>', owner: '<OWNER>', rootDirectory: '<ROOT_DIRECTORY>', - type: Type::COMMIT(), + type: TemplateReferenceType::COMMIT(), reference: '<REFERENCE>', activate: false // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/functions/create-vcs-deployment.md b/docs/examples/1.8.x/server-php/examples/functions/create-vcs-deployment.md index bb4622e67a..f044219541 100644 --- a/docs/examples/1.8.x/server-php/examples/functions/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-php/examples/functions/create-vcs-deployment.md @@ -2,7 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Functions; -use Appwrite\Enums\VCSDeploymentType; +use Appwrite\Enums\VCSReferenceType; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint @@ -13,7 +13,7 @@ $functions = new Functions($client); $result = $functions->createVcsDeployment( functionId: '<FUNCTION_ID>', - type: VCSDeploymentType::BRANCH(), + type: VCSReferenceType::BRANCH(), reference: '<REFERENCE>', activate: false // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/sites/create-template-deployment.md b/docs/examples/1.8.x/server-php/examples/sites/create-template-deployment.md index 314ffdd1b9..1661bbb407 100644 --- a/docs/examples/1.8.x/server-php/examples/sites/create-template-deployment.md +++ b/docs/examples/1.8.x/server-php/examples/sites/create-template-deployment.md @@ -2,7 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Sites; -use Appwrite\Enums\Type; +use Appwrite\Enums\TemplateReferenceType; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint @@ -16,7 +16,7 @@ $result = $sites->createTemplateDeployment( repository: '<REPOSITORY>', owner: '<OWNER>', rootDirectory: '<ROOT_DIRECTORY>', - type: Type::BRANCH(), + type: TemplateReferenceType::BRANCH(), reference: '<REFERENCE>', activate: false // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/sites/create-vcs-deployment.md b/docs/examples/1.8.x/server-php/examples/sites/create-vcs-deployment.md index 7f63dffdf8..015bf09d7d 100644 --- a/docs/examples/1.8.x/server-php/examples/sites/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-php/examples/sites/create-vcs-deployment.md @@ -2,7 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Sites; -use Appwrite\Enums\VCSDeploymentType; +use Appwrite\Enums\VCSReferenceType; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint @@ -13,7 +13,7 @@ $sites = new Sites($client); $result = $sites->createVcsDeployment( siteId: '<SITE_ID>', - type: VCSDeploymentType::BRANCH(), + type: VCSReferenceType::BRANCH(), reference: '<REFERENCE>', activate: false // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-python/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-python/examples/avatars/get-screenshot.md index fe7073eceb..43755943f1 100644 --- a/docs/examples/1.8.x/server-python/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/server-python/examples/avatars/get-screenshot.md @@ -13,23 +13,26 @@ avatars = Avatars(client) result = avatars.get_screenshot( url = 'https://example.com', - headers = {}, # optional - viewport_width = 1, # optional - viewport_height = 1, # optional - scale = 0.1, # optional + headers = { + "Authorization": "Bearer token123", + "X-Custom-Header": "value" + }, # optional + viewport_width = 1920, # optional + viewport_height = 1080, # optional + scale = 2, # optional theme = Theme.LIGHT, # optional - user_agent = '<USER_AGENT>', # optional - fullpage = False, # optional - locale = '<LOCALE>', # optional + user_agent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15', # optional + fullpage = True, # optional + locale = 'en-US', # optional timezone = Timezone.AFRICA_ABIDJAN, # optional - latitude = -90, # optional - longitude = -180, # optional - accuracy = 0, # optional - touch = False, # optional - permissions = [], # optional - sleep = 0, # optional - width = 0, # optional - height = 0, # optional - quality = -1, # optional + latitude = 37.7749, # optional + longitude = -122.4194, # optional + accuracy = 100, # optional + touch = True, # optional + permissions = ["geolocation","notifications"], # optional + sleep = 3, # optional + width = 800, # optional + height = 600, # optional + quality = 85, # optional output = Output.JPG # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/functions/create-template-deployment.md b/docs/examples/1.8.x/server-python/examples/functions/create-template-deployment.md index d9f8f70a82..db2058a87a 100644 --- a/docs/examples/1.8.x/server-python/examples/functions/create-template-deployment.md +++ b/docs/examples/1.8.x/server-python/examples/functions/create-template-deployment.md @@ -1,6 +1,6 @@ from appwrite.client import Client from appwrite.services.functions import Functions -from appwrite.enums import +from appwrite.enums import TemplateReferenceType client = Client() client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint @@ -14,7 +14,7 @@ result = functions.create_template_deployment( repository = '<REPOSITORY>', owner = '<OWNER>', root_directory = '<ROOT_DIRECTORY>', - type = .COMMIT, + type = TemplateReferenceType.COMMIT, reference = '<REFERENCE>', activate = False # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/functions/create-vcs-deployment.md b/docs/examples/1.8.x/server-python/examples/functions/create-vcs-deployment.md index 4004baec27..43e198b4b8 100644 --- a/docs/examples/1.8.x/server-python/examples/functions/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-python/examples/functions/create-vcs-deployment.md @@ -1,6 +1,6 @@ from appwrite.client import Client from appwrite.services.functions import Functions -from appwrite.enums import VCSDeploymentType +from appwrite.enums import VCSReferenceType client = Client() client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint @@ -11,7 +11,7 @@ functions = Functions(client) result = functions.create_vcs_deployment( function_id = '<FUNCTION_ID>', - type = VCSDeploymentType.BRANCH, + type = VCSReferenceType.BRANCH, reference = '<REFERENCE>', activate = False # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/sites/create-template-deployment.md b/docs/examples/1.8.x/server-python/examples/sites/create-template-deployment.md index fb47cb1453..700ca44d1b 100644 --- a/docs/examples/1.8.x/server-python/examples/sites/create-template-deployment.md +++ b/docs/examples/1.8.x/server-python/examples/sites/create-template-deployment.md @@ -1,6 +1,6 @@ from appwrite.client import Client from appwrite.services.sites import Sites -from appwrite.enums import +from appwrite.enums import TemplateReferenceType client = Client() client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint @@ -14,7 +14,7 @@ result = sites.create_template_deployment( repository = '<REPOSITORY>', owner = '<OWNER>', root_directory = '<ROOT_DIRECTORY>', - type = .BRANCH, + type = TemplateReferenceType.BRANCH, reference = '<REFERENCE>', activate = False # optional ) diff --git a/docs/examples/1.8.x/server-python/examples/sites/create-vcs-deployment.md b/docs/examples/1.8.x/server-python/examples/sites/create-vcs-deployment.md index 089e6c8141..ec02f31c00 100644 --- a/docs/examples/1.8.x/server-python/examples/sites/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-python/examples/sites/create-vcs-deployment.md @@ -1,6 +1,6 @@ from appwrite.client import Client from appwrite.services.sites import Sites -from appwrite.enums import VCSDeploymentType +from appwrite.enums import VCSReferenceType client = Client() client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint @@ -11,7 +11,7 @@ sites = Sites(client) result = sites.create_vcs_deployment( site_id = '<SITE_ID>', - type = VCSDeploymentType.BRANCH, + type = VCSReferenceType.BRANCH, reference = '<REFERENCE>', activate = False # optional ) diff --git a/docs/examples/1.8.x/server-rest/examples/account/create-mfa-challenge.md b/docs/examples/1.8.x/server-rest/examples/account/create-mfa-challenge.md index dd5ef4c731..e5a5b0ea05 100644 --- a/docs/examples/1.8.x/server-rest/examples/account/create-mfa-challenge.md +++ b/docs/examples/1.8.x/server-rest/examples/account/create-mfa-challenge.md @@ -1,4 +1,4 @@ -POST /v1/account/mfa/challenge HTTP/1.1 +POST /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/server-rest/examples/account/update-mfa-challenge.md b/docs/examples/1.8.x/server-rest/examples/account/update-mfa-challenge.md index b6a7e92b28..df2cd9a1e8 100644 --- a/docs/examples/1.8.x/server-rest/examples/account/update-mfa-challenge.md +++ b/docs/examples/1.8.x/server-rest/examples/account/update-mfa-challenge.md @@ -1,4 +1,4 @@ -PUT /v1/account/mfa/challenge HTTP/1.1 +PUT /v1/account/mfa/challenges HTTP/1.1 Host: cloud.appwrite.io Content-Type: application/json X-Appwrite-Response-Format: 1.8.0 diff --git a/docs/examples/1.8.x/server-ruby/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-ruby/examples/avatars/get-screenshot.md index f66fae3e02..0271404522 100644 --- a/docs/examples/1.8.x/server-ruby/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/server-ruby/examples/avatars/get-screenshot.md @@ -12,23 +12,26 @@ avatars = Avatars.new(client) result = avatars.get_screenshot( url: 'https://example.com', - headers: {}, # optional - viewport_width: 1, # optional - viewport_height: 1, # optional - scale: 0.1, # optional + headers: { + "Authorization" => "Bearer token123", + "X-Custom-Header" => "value" + }, # optional + viewport_width: 1920, # optional + viewport_height: 1080, # optional + scale: 2, # optional theme: Theme::LIGHT, # optional - user_agent: '<USER_AGENT>', # optional - fullpage: false, # optional - locale: '<LOCALE>', # optional + user_agent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15', # optional + fullpage: true, # optional + locale: 'en-US', # optional timezone: Timezone::AFRICA_ABIDJAN, # optional - latitude: -90, # optional - longitude: -180, # optional - accuracy: 0, # optional - touch: false, # optional - permissions: [], # optional - sleep: 0, # optional - width: 0, # optional - height: 0, # optional - quality: -1, # optional + latitude: 37.7749, # optional + longitude: -122.4194, # optional + accuracy: 100, # optional + touch: true, # optional + permissions: ["geolocation","notifications"], # optional + sleep: 3, # optional + width: 800, # optional + height: 600, # optional + quality: 85, # optional output: Output::JPG # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/functions/create-template-deployment.md b/docs/examples/1.8.x/server-ruby/examples/functions/create-template-deployment.md index cfe0adc263..e4c759f3fc 100644 --- a/docs/examples/1.8.x/server-ruby/examples/functions/create-template-deployment.md +++ b/docs/examples/1.8.x/server-ruby/examples/functions/create-template-deployment.md @@ -15,7 +15,7 @@ result = functions.create_template_deployment( repository: '<REPOSITORY>', owner: '<OWNER>', root_directory: '<ROOT_DIRECTORY>', - type: ::COMMIT, + type: TemplateReferenceType::COMMIT, reference: '<REFERENCE>', activate: false # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/functions/create-vcs-deployment.md b/docs/examples/1.8.x/server-ruby/examples/functions/create-vcs-deployment.md index 75bd3c49f5..930ec6dc76 100644 --- a/docs/examples/1.8.x/server-ruby/examples/functions/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-ruby/examples/functions/create-vcs-deployment.md @@ -12,7 +12,7 @@ functions = Functions.new(client) result = functions.create_vcs_deployment( function_id: '<FUNCTION_ID>', - type: VCSDeploymentType::BRANCH, + type: VCSReferenceType::BRANCH, reference: '<REFERENCE>', activate: false # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/sites/create-template-deployment.md b/docs/examples/1.8.x/server-ruby/examples/sites/create-template-deployment.md index 9e1cc93986..4fb81779fe 100644 --- a/docs/examples/1.8.x/server-ruby/examples/sites/create-template-deployment.md +++ b/docs/examples/1.8.x/server-ruby/examples/sites/create-template-deployment.md @@ -15,7 +15,7 @@ result = sites.create_template_deployment( repository: '<REPOSITORY>', owner: '<OWNER>', root_directory: '<ROOT_DIRECTORY>', - type: ::BRANCH, + type: TemplateReferenceType::BRANCH, reference: '<REFERENCE>', activate: false # optional ) diff --git a/docs/examples/1.8.x/server-ruby/examples/sites/create-vcs-deployment.md b/docs/examples/1.8.x/server-ruby/examples/sites/create-vcs-deployment.md index 2e72b6e3f1..e0a6ff3af0 100644 --- a/docs/examples/1.8.x/server-ruby/examples/sites/create-vcs-deployment.md +++ b/docs/examples/1.8.x/server-ruby/examples/sites/create-vcs-deployment.md @@ -12,7 +12,7 @@ sites = Sites.new(client) result = sites.create_vcs_deployment( site_id: '<SITE_ID>', - type: VCSDeploymentType::BRANCH, + type: VCSReferenceType::BRANCH, reference: '<REFERENCE>', activate: false # optional ) diff --git a/docs/examples/1.8.x/server-swift/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-swift/examples/avatars/get-screenshot.md index 3aa1661093..91e08d9053 100644 --- a/docs/examples/1.8.x/server-swift/examples/avatars/get-screenshot.md +++ b/docs/examples/1.8.x/server-swift/examples/avatars/get-screenshot.md @@ -10,24 +10,27 @@ let avatars = Avatars(client) let bytes = try await avatars.getScreenshot( url: "https://example.com", - headers: [:], // optional - viewportWidth: 1, // optional - viewportHeight: 1, // optional - scale: 0.1, // optional + headers: [ + "Authorization": "Bearer token123", + "X-Custom-Header": "value" + ], // optional + viewportWidth: 1920, // optional + viewportHeight: 1080, // optional + scale: 2, // optional theme: .light, // optional - userAgent: "<USER_AGENT>", // optional - fullpage: false, // optional - locale: "<LOCALE>", // optional + userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15", // optional + fullpage: true, // optional + locale: "en-US", // optional timezone: .africaAbidjan, // optional - latitude: -90, // optional - longitude: -180, // optional - accuracy: 0, // optional - touch: false, // optional - permissions: [], // optional - sleep: 0, // optional - width: 0, // optional - height: 0, // optional - quality: -1, // optional + latitude: 37.7749, // optional + longitude: -122.4194, // optional + accuracy: 100, // optional + touch: true, // optional + permissions: ["geolocation","notifications"], // optional + sleep: 3, // optional + width: 800, // optional + height: 600, // optional + quality: 85, // optional output: .jpg // optional ) diff --git a/docs/sdks/android/CHANGELOG.md b/docs/sdks/android/CHANGELOG.md index c355a1904f..870ba79bfe 100644 --- a/docs/sdks/android/CHANGELOG.md +++ b/docs/sdks/android/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 11.4.0 + +* Add `getScreenshot` method to `Avatars` service +* Add `Theme`, `Timezone` and `Output` enums + ## 11.3.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/sdks/apple/CHANGELOG.md b/docs/sdks/apple/CHANGELOG.md index 44c40a5858..7f4141cc34 100644 --- a/docs/sdks/apple/CHANGELOG.md +++ b/docs/sdks/apple/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 13.5.0 + +* Add `getScreenshot` method to `Avatars` service +* Add `Theme`, `Timezone` and `Output` enums + ## 13.4.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/sdks/cli/CHANGELOG.md b/docs/sdks/cli/CHANGELOG.md index 8e50441769..9f1f02bbd0 100644 --- a/docs/sdks/cli/CHANGELOG.md +++ b/docs/sdks/cli/CHANGELOG.md @@ -1,5 +1,16 @@ # Change Log +## 12.0.1 + +Fix type generation for `point`, `lineString` and `polygon` columns + +## 12.0.0 + +* Change `create-deployment-template`'s `version` parameter to `type` and `reference`. eg. usage - `create-deployment-template --type tag --reference 1.0.0` +* Remove `bucket-id` parameter from `create-csv-export` command +* Allow enabling or disabling of image `transformations` in a bucket +* Fix type generation for `point`, `lineString` and `polygon` columns + ## 11.1.1 * Fix duplicate `enums` during type generation by prefixing them with table name. For example, `enum MyEnum` will now be generated as `enum MyTableMyEnum` to avoid conflicts. diff --git a/docs/sdks/dart/CHANGELOG.md b/docs/sdks/dart/CHANGELOG.md index 7fd7227f15..49d45becaf 100644 --- a/docs/sdks/dart/CHANGELOG.md +++ b/docs/sdks/dart/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 20.0.0 + +* Rename `VCSDeploymentType` enum to `VCSReferenceType` +* Change `createTemplateDeployment` method signature: replace `version` parameter with `type` (TemplateReferenceType) and `reference` parameters +* Add `Theme`, `Timezone` and `Output` enums + ## 19.4.0 * Add `getScreenshot` method to `Avatars` service diff --git a/docs/sdks/dotnet/CHANGELOG.md b/docs/sdks/dotnet/CHANGELOG.md index 1401108b9f..eabf40dac0 100644 --- a/docs/sdks/dotnet/CHANGELOG.md +++ b/docs/sdks/dotnet/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 0.23.0 + +* Rename `VCSDeploymentType` enum to `VCSReferenceType` +* Change `CreateTemplateDeployment` method signature: replace `Version` parameter with `Type` (TemplateReferenceType) and `Reference` parameters +* Add `GetScreenshot` method to `Avatars` service +* Add `Theme`, `Timezone` and `Output` enums + ## 0.22.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/sdks/flutter/CHANGELOG.md b/docs/sdks/flutter/CHANGELOG.md index 2f26f34edd..834c926977 100644 --- a/docs/sdks/flutter/CHANGELOG.md +++ b/docs/sdks/flutter/CHANGELOG.md @@ -1,8 +1,14 @@ # Change Log +## 20.3.2 + +* Fix OAuth2 browser infinite redirect issue + ## 20.3.1 * Fix passing of `null` values and stripping only non-nullable optional parameters from the request body +* Add `getScreenshot` method to `Avatars` service +* Add `Theme`, `Timezone` and `Output` enums ## 20.3.0 diff --git a/docs/sdks/go/CHANGELOG.md b/docs/sdks/go/CHANGELOG.md index 0cc395644f..c93c431eb7 100644 --- a/docs/sdks/go/CHANGELOG.md +++ b/docs/sdks/go/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## v0.15.0 + +* Rename `VCSDeploymentType` enum to `VCSReferenceType` +* Change `CreateTemplateDeployment` method signature: replace `Version` parameter with `Type` (TemplateReferenceType) and `Reference` parameters +* Add `GetScreenshot` method to `Avatars` service +* Add `Theme`, `Timezone` and `Output` enums + ## v0.14.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/sdks/kotlin/CHANGELOG.md b/docs/sdks/kotlin/CHANGELOG.md index 362c9aa979..07e9d9647f 100644 --- a/docs/sdks/kotlin/CHANGELOG.md +++ b/docs/sdks/kotlin/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 13.0.0 + +* Rename `VCSDeploymentType` enum to `VCSReferenceType` +* Change `createTemplateDeployment` method signature: replace `version` parameter with `type` (TemplateReferenceType) and `reference` parameters +* Add `getScreenshot` method to `Avatars` service +* Add `Theme`, `Timezone` and `Output` enums + ## 12.3.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/sdks/nodejs/CHANGELOG.md b/docs/sdks/nodejs/CHANGELOG.md index 419b052678..2811a060f4 100644 --- a/docs/sdks/nodejs/CHANGELOG.md +++ b/docs/sdks/nodejs/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 21.0.0 + +* Rename `VCSDeploymentType` enum to `VCSReferenceType` +* Change `createTemplateDeployment` method signature: replace `version` parameter with `type` (TemplateReferenceType) and `reference` parameters +* Add `getScreenshot` method to `Avatars` service +* Add `Theme`, `Timezone` and `Output` enums + ## 20.3.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/sdks/php/CHANGELOG.md b/docs/sdks/php/CHANGELOG.md index 14a26e441d..1ffcff2a6e 100644 --- a/docs/sdks/php/CHANGELOG.md +++ b/docs/sdks/php/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 19.0.0 + +* Rename `VCSDeploymentType` enum to `VCSReferenceType` +* Change `createTemplateDeployment` method signature: replace `version` parameter with `type` (TemplateReferenceType) and `reference` parameters +* Add `Theme`, `Timezone` and `Output` enums + ## 18.0.1 * Fix `TablesDB` service to use correct file name diff --git a/docs/sdks/python/CHANGELOG.md b/docs/sdks/python/CHANGELOG.md index 31396fa3e6..ff2ac85322 100644 --- a/docs/sdks/python/CHANGELOG.md +++ b/docs/sdks/python/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## 14.0.0 + +* Rename `VCSDeploymentType` enum to `VCSReferenceType` +* Change `create_template_deployment` method signature: replace `version` parameter with `type` (TemplateReferenceType) and `reference` parameters +* Add `get_screenshot` method to `Avatars` service +* Add `Theme`, `Timezone` and `Output` enums +* Add support for dart39 and flutter335 runtimes + ## 13.6.1 * Fix passing of `None` to nullable parameters diff --git a/docs/sdks/react-native/CHANGELOG.md b/docs/sdks/react-native/CHANGELOG.md index 757a5f0158..8ab9bcca43 100644 --- a/docs/sdks/react-native/CHANGELOG.md +++ b/docs/sdks/react-native/CHANGELOG.md @@ -1,5 +1,10 @@ # Change log +## 0.19.0 + +* Add `getScreenshot` method to `Avatars` service +* Add `Theme`, `Timezone` and `Output` enums + ## 0.18.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/sdks/ruby/CHANGELOG.md b/docs/sdks/ruby/CHANGELOG.md index c4b0df5615..272f9b6631 100644 --- a/docs/sdks/ruby/CHANGELOG.md +++ b/docs/sdks/ruby/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 20.0.0 + +* Rename `VCSDeploymentType` enum to `VCSReferenceType` +* Change `create_template_deployment` method signature: replace `version` parameter with `type` (TemplateReferenceType) and `reference` parameters +* Add `get_screenshot` method to `Avatars` service +* Add `Theme`, `Timezone` and `Output` enums + ## 19.3.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/sdks/swift/CHANGELOG.md b/docs/sdks/swift/CHANGELOG.md index 33ec214069..22ae4719a3 100644 --- a/docs/sdks/swift/CHANGELOG.md +++ b/docs/sdks/swift/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 14.0.0 + +* Rename `VCSDeploymentType` enum to `VCSReferenceType` +* Change `createTemplateDeployment` method signature: replace `version` parameter with `type` (TemplateReferenceType) and `reference` parameters +* Add `getScreenshot` method to `Avatars` service +* Add `Theme`, `Timezone` and `Output` enums + ## 13.3.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/sdks/web/CHANGELOG.md b/docs/sdks/web/CHANGELOG.md index 83ed257773..b600d67e1b 100644 --- a/docs/sdks/web/CHANGELOG.md +++ b/docs/sdks/web/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 21.5.0 + +* Add `getScreenshot` method to `Avatars` service +* Add `Theme`, `Timezone` and `Output` enums + ## 21.4.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/src/Appwrite/Auth/Auth.php b/src/Appwrite/Auth/Auth.php deleted file mode 100644 index b6676a2843..0000000000 --- a/src/Appwrite/Auth/Auth.php +++ /dev/null @@ -1,515 +0,0 @@ -<?php - -namespace Appwrite\Auth; - -use Appwrite\Auth\Hash\Argon2; -use Appwrite\Auth\Hash\Bcrypt; -use Appwrite\Auth\Hash\Md5; -use Appwrite\Auth\Hash\Phpass; -use Appwrite\Auth\Hash\Scrypt; -use Appwrite\Auth\Hash\Scryptmodified; -use Appwrite\Auth\Hash\Sha; -use Utopia\Database\DateTime; -use Utopia\Database\Document; -use Utopia\Database\Helpers\Role; -use Utopia\Database\Validator\Authorization; -use Utopia\Database\Validator\Roles; - -class Auth -{ - public const SUPPORTED_ALGOS = [ - 'argon2', - 'bcrypt', - 'md5', - 'sha', - 'phpass', - 'scrypt', - 'scryptMod', - 'plaintext' - ]; - - public const DEFAULT_ALGO = 'argon2'; - public const DEFAULT_ALGO_OPTIONS = ['type' => 'argon2', 'memoryCost' => 2048, 'timeCost' => 4, 'threads' => 3]; - - /** - * User Roles. - */ - public const USER_ROLE_ANY = 'any'; - public const USER_ROLE_GUESTS = 'guests'; - public const USER_ROLE_USERS = 'users'; - public const USER_ROLE_ADMIN = 'admin'; - public const USER_ROLE_DEVELOPER = 'developer'; - public const USER_ROLE_OWNER = 'owner'; - public const USER_ROLE_APPS = 'apps'; - public const USER_ROLE_SYSTEM = 'system'; - - /** - * Activity associated with user or the app. - */ - public const ACTIVITY_TYPE_APP = 'app'; - public const ACTIVITY_TYPE_USER = 'user'; - public const ACTIVITY_TYPE_GUEST = 'guest'; - - /** - * Token Types. - */ - public const TOKEN_TYPE_LOGIN = 1; // Deprecated - public const TOKEN_TYPE_VERIFICATION = 2; - public const TOKEN_TYPE_RECOVERY = 3; - public const TOKEN_TYPE_INVITE = 4; - public const TOKEN_TYPE_MAGIC_URL = 5; - public const TOKEN_TYPE_PHONE = 6; - public const TOKEN_TYPE_OAUTH2 = 7; - public const TOKEN_TYPE_GENERIC = 8; - public const TOKEN_TYPE_EMAIL = 9; // OTP - - /** - * Session Providers. - */ - public const SESSION_PROVIDER_EMAIL = 'email'; - public const SESSION_PROVIDER_ANONYMOUS = 'anonymous'; - public const SESSION_PROVIDER_MAGIC_URL = 'magic-url'; - public const SESSION_PROVIDER_PHONE = 'phone'; - public const SESSION_PROVIDER_OAUTH2 = 'oauth2'; - public const SESSION_PROVIDER_TOKEN = 'token'; - public const SESSION_PROVIDER_SERVER = 'server'; - - /** - * Token Expiration times. - */ - public const TOKEN_EXPIRATION_LOGIN_LONG = 31536000; /* 1 year */ - public const TOKEN_EXPIRATION_LOGIN_SHORT = 3600; /* 1 hour */ - public const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */ - public const TOKEN_EXPIRATION_CONFIRM = 3600 * 1; /* 1 hour */ - public const TOKEN_EXPIRATION_OTP = 60 * 15; /* 15 minutes */ - public const TOKEN_EXPIRATION_GENERIC = 60 * 15; /* 15 minutes */ - - /** - * Token Lengths. - */ - public const TOKEN_LENGTH_MAGIC_URL = 64; - public const TOKEN_LENGTH_VERIFICATION = 256; - public const TOKEN_LENGTH_RECOVERY = 256; - public const TOKEN_LENGTH_OAUTH2 = 64; - public const TOKEN_LENGTH_SESSION = 256; - - /** - * MFA - */ - public const MFA_RECENT_DURATION = 1800; // 30 mins - - /** - * @var string - */ - public static $cookieName = 'a_session'; - - /** - * @var string - */ - public static $cookieNamePreview = 'a_jwt_console'; - - /** - * User Unique ID. - * - * @var string - */ - public static $unique = ''; - - /** - * User Secret Key. - * - * @var string - */ - public static $secret = ''; - - /** - * Set Cookie Name. - * - * @param $string - * - * @return string - */ - public static function setCookieName($string) - { - return self::$cookieName = $string; - } - - /** - * Encode Session. - * - * @param string $id - * @param string $secret - * - * @return string - */ - public static function encodeSession($id, $secret) - { - return \base64_encode(\json_encode([ - 'id' => $id, - 'secret' => $secret, - ])); - } - - /** - * Token type to session provider mapping. - */ - public static function getSessionProviderByTokenType(int $type): string - { - switch ($type) { - case Auth::TOKEN_TYPE_VERIFICATION: - case Auth::TOKEN_TYPE_RECOVERY: - case Auth::TOKEN_TYPE_INVITE: - return Auth::SESSION_PROVIDER_EMAIL; - case Auth::TOKEN_TYPE_MAGIC_URL: - return Auth::SESSION_PROVIDER_MAGIC_URL; - case Auth::TOKEN_TYPE_PHONE: - return Auth::SESSION_PROVIDER_PHONE; - case Auth::TOKEN_TYPE_OAUTH2: - return Auth::SESSION_PROVIDER_OAUTH2; - default: - return Auth::SESSION_PROVIDER_TOKEN; - } - } - - /** - * Decode Session. - * - * @param string $session - * - * @return array - * - * @throws \Exception - */ - public static function decodeSession($session) - { - $session = \json_decode(\base64_decode($session), true); - $default = ['id' => null, 'secret' => '']; - - if (!\is_array($session)) { - return $default; - } - - return \array_merge($default, $session); - } - - /** - * Encode. - * - * One-way encryption - * - * @param $string - * - * @return string - */ - public static function hash(string $string) - { - return \hash('sha256', $string); - } - - /** - * Password Hash. - * - * One way string hashing for user passwords - * - * @param string $string - * @param string $algo hashing algorithm to use - * @param array $options algo-specific options - * - * @return bool|string|null - */ - public static function passwordHash(string $string, string $algo, array $options = []) - { - // Plain text not supported, just an alias. Switch to recommended algo - if ($algo === 'plaintext') { - $algo = Auth::DEFAULT_ALGO; - $options = Auth::DEFAULT_ALGO_OPTIONS; - } - - if (!\in_array($algo, Auth::SUPPORTED_ALGOS)) { - throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.'); - } - - switch ($algo) { - case 'argon2': - $hasher = new Argon2($options); - return $hasher->hash($string); - case 'bcrypt': - $hasher = new Bcrypt($options); - return $hasher->hash($string); - case 'md5': - $hasher = new Md5($options); - return $hasher->hash($string); - case 'sha': - $hasher = new Sha($options); - return $hasher->hash($string); - case 'phpass': - $hasher = new Phpass($options); - return $hasher->hash($string); - case 'scrypt': - $hasher = new Scrypt($options); - return $hasher->hash($string); - case 'scryptMod': - $hasher = new Scryptmodified($options); - return $hasher->hash($string); - default: - throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.'); - } - } - - /** - * Password verify. - * - * @param string $plain - * @param string $hash - * @param string $algo hashing algorithm used to hash - * @param array $options algo-specific options - * - * @return bool - */ - public static function passwordVerify(string $plain, string $hash, string $algo, array $options = []) - { - // Plain text not supported, just an alias. Switch to recommended algo - if ($algo === 'plaintext') { - $algo = Auth::DEFAULT_ALGO; - $options = Auth::DEFAULT_ALGO_OPTIONS; - } - - if (!\in_array($algo, Auth::SUPPORTED_ALGOS)) { - throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.'); - } - - switch ($algo) { - case 'argon2': - $hasher = new Argon2($options); - return $hasher->verify($plain, $hash); - case 'bcrypt': - $hasher = new Bcrypt($options); - return $hasher->verify($plain, $hash); - case 'md5': - $hasher = new Md5($options); - return $hasher->verify($plain, $hash); - case 'sha': - $hasher = new Sha($options); - return $hasher->verify($plain, $hash); - case 'phpass': - $hasher = new Phpass($options); - return $hasher->verify($plain, $hash); - case 'scrypt': - $hasher = new Scrypt($options); - return $hasher->verify($plain, $hash); - case 'scryptMod': - $hasher = new Scryptmodified($options); - return $hasher->verify($plain, $hash); - default: - throw new \Exception('Hashing algorithm \'' . $algo . '\' is not supported.'); - } - } - - /** - * Password Generator. - * - * Generate random password string - * - * @param int $length - * - * @return string - */ - public static function passwordGenerator(int $length = 20): string - { - return \bin2hex(\random_bytes($length)); - } - - /** - * Token Generator. - * - * Generate random password string - * - * @param int $length Length of returned token - * - * @return string - */ - public static function tokenGenerator(int $length = 256): string - { - if ($length <= 0) { - throw new \Exception('Token length must be greater than 0'); - } - - $bytesLength = (int) ceil($length / 2); - $token = \bin2hex(\random_bytes($bytesLength)); - - return substr($token, 0, $length); - } - - /** - * Code Generator. - * - * Generate random code string - * - * @param int $length - * - * @return string - */ - public static function codeGenerator(int $length = 6): string - { - $value = ''; - - for ($i = 0; $i < $length; $i++) { - $value .= random_int(0, 9); - } - - return $value; - } - - /** - * Verify token and check that its not expired. - * - * @param array<Document> $tokens - * @param int $type Type of token to verify, if null will verify any type - * @param string $secret - * - * @return false|Document - */ - public static function tokenVerify(array $tokens, int $type = null, string $secret): false|Document - { - foreach ($tokens as $token) { - if ( - $token->isSet('secret') && - $token->isSet('expire') && - $token->isSet('type') && - ($type === null || $token->getAttribute('type') === $type) && - $token->getAttribute('secret') === self::hash($secret) && - DateTime::formatTz($token->getAttribute('expire')) >= DateTime::formatTz(DateTime::now()) - ) { - return $token; - } - } - - return false; - } - - /** - * Verify session and check that its not expired. - * - * @param array<Document> $sessions - * @param string $secret - * - * @return bool|string - */ - public static function sessionVerify(array $sessions, string $secret) - { - foreach ($sessions as $session) { - if ( - $session->isSet('secret') && - $session->isSet('provider') && - $session->getAttribute('secret') === self::hash($secret) && - DateTime::formatTz(DateTime::format(new \DateTime($session->getAttribute('expire')))) >= DateTime::formatTz(DateTime::now()) - ) { - return $session->getId(); - } - } - - return false; - } - - /** - * Is Privileged User? - * - * @param array<string> $roles - * - * @return bool - */ - public static function isPrivilegedUser(array $roles): bool - { - if ( - in_array(self::USER_ROLE_OWNER, $roles) || - in_array(self::USER_ROLE_DEVELOPER, $roles) || - in_array(self::USER_ROLE_ADMIN, $roles) - ) { - return true; - } - - return false; - } - - /** - * Is App User? - * - * @param array<string> $roles - * - * @return bool - */ - public static function isAppUser(array $roles): bool - { - if (in_array(self::USER_ROLE_APPS, $roles)) { - return true; - } - - return false; - } - - /** - * Returns all roles for a user. - * - * @param Document $user - * @return array<string> - */ - public static function getRoles(Document $user, Authorization $authorization): array - { - $roles = []; - - if (!self::isPrivilegedUser($authorization->getRoles()) && !self::isAppUser($authorization->getRoles())) { - if ($user->getId()) { - $roles[] = Role::user($user->getId())->toString(); - $roles[] = Role::users()->toString(); - - $emailVerified = $user->getAttribute('emailVerification', false); - $phoneVerified = $user->getAttribute('phoneVerification', false); - - if ($emailVerified || $phoneVerified) { - $roles[] = Role::user($user->getId(), Roles::DIMENSION_VERIFIED)->toString(); - $roles[] = Role::users(Roles::DIMENSION_VERIFIED)->toString(); - } else { - $roles[] = Role::user($user->getId(), Roles::DIMENSION_UNVERIFIED)->toString(); - $roles[] = Role::users(Roles::DIMENSION_UNVERIFIED)->toString(); - } - } else { - return [Role::guests()->toString()]; - } - } - - foreach ($user->getAttribute('memberships', []) as $node) { - if (!isset($node['confirm']) || !$node['confirm']) { - continue; - } - - if (isset($node['$id']) && isset($node['teamId'])) { - $roles[] = Role::team($node['teamId'])->toString(); - $roles[] = Role::member($node['$id'])->toString(); - - if (isset($node['roles'])) { - foreach ($node['roles'] as $nodeRole) { // Set all team roles - $roles[] = Role::team($node['teamId'], $nodeRole)->toString(); - } - } - } - } - - foreach ($user->getAttribute('labels', []) as $label) { - $roles[] = 'label:' . $label; - } - - return $roles; - } - - /** - * Check if user is anonymous. - * - * @param Document $user - * @return bool - */ - public static function isAnonymousUser(Document $user): bool - { - return is_null($user->getAttribute('email')) - && is_null($user->getAttribute('phone')); - } -} diff --git a/src/Appwrite/Auth/Hash.php b/src/Appwrite/Auth/Hash.php deleted file mode 100644 index 7134057581..0000000000 --- a/src/Appwrite/Auth/Hash.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -namespace Appwrite\Auth; - -abstract class Hash -{ - /** - * @var array $options Hashing-algo specific options - */ - protected array $options = []; - - /** - * @param array $options Hashing-algo specific options - */ - public function __construct(array $options = []) - { - $this->setOptions($options); - } - - /** - * Set hashing algo options - * - * @param array $options Hashing-algo specific options - */ - public function setOptions(array $options): self - { - $this->options = \array_merge([], $this->getDefaultOptions(), $options); - return $this; - } - - /** - * Get hashing algo options - * - * @return array $options Hashing-algo specific options - */ - public function getOptions(): array - { - return $this->options; - } - - /** - * @param string $password Input password to hash - * - * @return string hash - */ - abstract public function hash(string $password): string; - - /** - * @param string $password Input password to validate - * @param string $hash Hash to verify password against - * - * @return boolean true if password matches hash - */ - abstract public function verify(string $password, string $hash): bool; - - /** - * Get default options for specific hashing algo - * - * @return array options named array - */ - abstract public function getDefaultOptions(): array; -} diff --git a/src/Appwrite/Auth/Hash/Argon2.php b/src/Appwrite/Auth/Hash/Argon2.php deleted file mode 100644 index c723b077b1..0000000000 --- a/src/Appwrite/Auth/Hash/Argon2.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -namespace Appwrite\Auth\Hash; - -use Appwrite\Auth\Hash; - -/* - * Argon2 accepted options: - * int threads - * int time_cost - * int memory_cost - * - * Reference: https://www.php.net/manual/en/function.password-hash.php#example-983 -*/ -class Argon2 extends Hash -{ - /** - * @param string $password Input password to hash - * - * @return string hash - */ - public function hash(string $password): string - { - return \password_hash($password, PASSWORD_ARGON2ID, $this->getOptions()); - } - - /** - * @param string $password Input password to validate - * @param string $hash Hash to verify password against - * - * @return boolean true if password matches hash - */ - public function verify(string $password, string $hash): bool - { - return \password_verify($password, $hash); - } - - /** - * Get default options for specific hashing algo - * - * @return array options named array - */ - public function getDefaultOptions(): array - { - return ['memory_cost' => 65536, 'time_cost' => 4, 'threads' => 3]; - } -} diff --git a/src/Appwrite/Auth/Hash/Bcrypt.php b/src/Appwrite/Auth/Hash/Bcrypt.php deleted file mode 100644 index 8b6177f33a..0000000000 --- a/src/Appwrite/Auth/Hash/Bcrypt.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -namespace Appwrite\Auth\Hash; - -use Appwrite\Auth\Hash; - -/* - * Bcrypt accepted options: - * int cost - * string? salt; auto-generated if empty - * - * Reference: https://www.php.net/manual/en/password.constants.php -*/ -class Bcrypt extends Hash -{ - /** - * @param string $password Input password to hash - * - * @return string hash - */ - public function hash(string $password): string - { - return \password_hash($password, PASSWORD_BCRYPT, $this->getOptions()); - } - - /** - * @param string $password Input password to validate - * @param string $hash Hash to verify password against - * - * @return boolean true if password matches hash - */ - public function verify(string $password, string $hash): bool - { - return \password_verify($password, $hash); - } - - /** - * Get default options for specific hashing algo - * - * @return array options named array - */ - public function getDefaultOptions(): array - { - return [ 'cost' => 8 ]; - } -} diff --git a/src/Appwrite/Auth/Hash/Md5.php b/src/Appwrite/Auth/Hash/Md5.php deleted file mode 100644 index 8ade3dd5e2..0000000000 --- a/src/Appwrite/Auth/Hash/Md5.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -namespace Appwrite\Auth\Hash; - -use Appwrite\Auth\Hash; - -/* - * MD5 does not accept any options. - * - * Reference: https://www.php.net/manual/en/function.md5.php -*/ -class Md5 extends Hash -{ - /** - * @param string $password Input password to hash - * - * @return string hash - */ - public function hash(string $password): string - { - return \md5($password); - } - - /** - * @param string $password Input password to validate - * @param string $hash Hash to verify password against - * - * @return boolean true if password matches hash - */ - public function verify(string $password, string $hash): bool - { - return $this->hash($password) === $hash; - } - - /** - * Get default options for specific hashing algo - * - * @return array options named array - */ - public function getDefaultOptions(): array - { - return []; - } -} diff --git a/src/Appwrite/Auth/Hash/Phpass.php b/src/Appwrite/Auth/Hash/Phpass.php deleted file mode 100644 index 988c38cc8d..0000000000 --- a/src/Appwrite/Auth/Hash/Phpass.php +++ /dev/null @@ -1,290 +0,0 @@ -<?php - -/** - * Portable PHP password hashing framework. - * source Version 0.5 / genuine. - * Written by Solar Designer <solar at openwall.com> in 2004-2017 and placed in - * the public domain. Revised in subsequent years, still public domain. - * There's absolutely no warranty. - * The homepage URL for the source framework is: http://www.openwall.com/phpass/ - * Please be sure to update the Version line if you edit this file in any way. - * It is suggested that you leave the main version number intact, but indicate - * your project name (after the slash) and add your own revision information. - * Please do not change the "private" password hashing method implemented in - * here, thereby making your hashes incompatible. However, if you must, please - * change the hash type identifier (the "$P$") to something different. - * Obviously, since this code is in the public domain, the above are not - * requirements (there can be none), but merely suggestions. - * - * @author Solar Designer <solar@openwall.com> - * @copyright Copyright (C) 2017 All rights reserved. - * @license http://www.opensource.org/licenses/mit-license.html MIT License; see LICENSE.txt - */ - -namespace Appwrite\Auth\Hash; - -use Appwrite\Auth\Hash; - -/* - * PHPass accepted options: - * int iteration_count_log2; The Logarithmic cost value used when generating hash values indicating the number of rounds used to generate hashes - * string portable_hashes - * string random_state; The cached random state - * - * Reference: https://github.com/photodude/phpass -*/ -class Phpass extends Hash -{ - /** - * Alphabet used in itoa64 conversions. - * - * @var string - * @since 0.1.0 - */ - protected string $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; - - /** - * Get default options for specific hashing algo - * - * @return array options named array - */ - public function getDefaultOptions(): array - { - $randomState = \microtime(); - if (\function_exists('getmypid')) { - $randomState .= getmypid(); - } - - return ['iteration_count_log2' => 8, 'portable_hashes' => false, 'random_state' => $randomState]; - } - - /** - * @param string $password Input password to hash - * - * @return string hash - */ - public function hash(string $password): string - { - $options = $this->getDefaultOptions(); - - $random = ''; - if (CRYPT_BLOWFISH === 1 && !$options['portable_hashes']) { - $random = $this->getRandomBytes(16, $options); - $hash = crypt($password, $this->gensaltBlowfish($random, $options)); - if (strlen($hash) === 60) { - return $hash; - } - } - if (strlen($random) < 6) { - $random = $this->getRandomBytes(6, $options); - } - $hash = $this->cryptPrivate($password, $this->gensaltPrivate($random, $options)); - if (strlen($hash) === 34) { - return $hash; - } - - /** - * Returning '*' on error is safe here, but would _not_ be safe - * in a crypt(3)-like function used _both_ for generating new - * hashes and for validating passwords against existing hashes. - */ - return '*'; - } - - /** - * @param string $password Input password to validate - * @param string $hash Hash to verify password against - * - * @return boolean true if password matches hash - */ - public function verify(string $password, string $hash): bool - { - $verificationHash = $this->cryptPrivate($password, $hash); - if ($verificationHash[0] === '*') { - $verificationHash = crypt($password, $hash); - } - - /** - * This is not constant-time. In order to keep the code simple, - * for timing safety we currently rely on the salts being - * unpredictable, which they are at least in the non-fallback - * cases (that is, when we use /dev/urandom and bcrypt). - */ - return $hash === $verificationHash; - } - - /** - * @param int $count - * - * @return String $output - * @since 0.1.0 - * @throws Exception Thows an Exception if the $count parameter is not a positive integer. - */ - protected function getRandomBytes(int $count, array $options): string - { - if (!is_int($count) || $count < 1) { - throw new \Exception('Argument count must be a positive integer'); - } - $output = ''; - if (@is_readable('/dev/urandom') && ($fh = @fopen('/dev/urandom', 'rb'))) { - $output = fread($fh, $count); - fclose($fh); - } - - if (strlen($output) < $count) { - $output = ''; - - for ($i = 0; $i < $count; $i += 16) { - $options['iteration_count_log2'] = md5(microtime() . $options['iteration_count_log2']); - $output .= md5($options['iteration_count_log2'], true); - } - - $output = substr($output, 0, $count); - } - - return $output; - } - - /** - * @param String $input - * @param int $count - * - * @return String $output - * @since 0.1.0 - * @throws Exception Thows an Exception if the $count parameter is not a positive integer. - */ - protected function encode64($input, $count) - { - if (!is_int($count) || $count < 1) { - throw new \Exception('Argument count must be a positive integer'); - } - $output = ''; - $i = 0; - do { - $value = ord($input[$i++]); - $output .= $this->itoa64[$value & 0x3f]; - if ($i < $count) { - $value |= ord($input[$i]) << 8; - } - $output .= $this->itoa64[($value >> 6) & 0x3f]; - if ($i++ >= $count) { - break; - } - if ($i < $count) { - $value |= ord($input[$i]) << 16; - } - $output .= $this->itoa64[($value >> 12) & 0x3f]; - if ($i++ >= $count) { - break; - } - $output .= $this->itoa64[($value >> 18) & 0x3f]; - } while ($i < $count); - - return $output; - } - - /** - * @param String $input - * - * @return String $output - * @since 0.1.0 - */ - private function gensaltPrivate($input, $options) - { - $output = '$P$'; - $output .= $this->itoa64[min($options['iteration_count_log2'] + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; - $output .= $this->encode64($input, 6); - - return $output; - } - - /** - * @param String $password - * @param String $setting - * - * @return String $output - * @since 0.1.0 - */ - private function cryptPrivate($password, $setting) - { - $output = '*0'; - if (substr($setting, 0, 2) === $output) { - $output = '*1'; - } - $id = substr($setting, 0, 3); - // We use "$P$", phpBB3 uses "$H$" for the same thing - if ($id !== '$P$' && $id !== '$H$') { - return $output; - } - $count_log2 = strpos($this->itoa64, $setting[3]); - if ($count_log2 < 7 || $count_log2 > 30) { - return $output; - } - $count = 1 << $count_log2; - $salt = substr($setting, 4, 8); - if (strlen($salt) !== 8) { - return $output; - } - /** - * We were kind of forced to use MD5 here since it's the only - * cryptographic primitive that was available in all versions of PHP - * in use. To implement our own low-level crypto in PHP - * would have result in much worse performance and - * consequently in lower iteration counts and hashes that are - * quicker to crack (by non-PHP code). - */ - $hash = md5($salt . $password, true); - do { - $hash = md5($hash . $password, true); - } while (--$count); - $output = substr($setting, 0, 12); - $output .= $this->encode64($hash, 16); - - return $output; - } - - /** - * @param String $input - * - * @return String $output - * @since 0.1.0 - */ - private function gensaltBlowfish($input, $options) - { - /** - * This one needs to use a different order of characters and a - * different encoding scheme from the one in encode64() above. - * We care because the last character in our encoded string will - * only represent 2 bits. While two known implementations of - * bcrypt will happily accept and correct a salt string which - * has the 4 unused bits set to non-zero, we do not want to take - * chances and we also do not want to waste an additional byte - * of entropy. - */ - $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - $output = '$2a$'; - $output .= chr(ord('0') + intval($options['iteration_count_log2'] / 10)); - $output .= chr(ord('0') + $options['iteration_count_log2'] % 10); - $output .= '$'; - $i = 0; - do { - $c1 = ord($input[$i++]); - $output .= $itoa64[$c1 >> 2]; - $c1 = ($c1 & 0x03) << 4; - if ($i >= 16) { - $output .= $itoa64[$c1]; - break; - } - $c2 = ord($input[$i++]); - $c1 |= $c2 >> 4; - $output .= $itoa64[$c1]; - $c1 = ($c2 & 0x0f) << 2; - $c2 = ord($input[$i++]); - $c1 |= $c2 >> 6; - $output .= $itoa64[$c1]; - $output .= $itoa64[$c2 & 0x3f]; - } while (1); - - return $output; - } -} diff --git a/src/Appwrite/Auth/Hash/Scrypt.php b/src/Appwrite/Auth/Hash/Scrypt.php deleted file mode 100644 index 821b1fba69..0000000000 --- a/src/Appwrite/Auth/Hash/Scrypt.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -namespace Appwrite\Auth\Hash; - -use Appwrite\Auth\Hash; - -/* - * Scrypt accepted options: - * string? salt; auto-generated if empty - * int costCpu - * int costMemory - * int costParallel - * int length - * - * Reference: https://github.com/DomBlack/php-scrypt/blob/master/scrypt.php#L112-L116 -*/ -class Scrypt extends Hash -{ - /** - * @param string $password Input password to hash - * - * @return string hash - */ - public function hash(string $password): string - { - $options = $this->getOptions(); - - return \scrypt($password, $options['salt'], $options['costCpu'], $options['costMemory'], $options['costParallel'], $options['length']); - } - - /** - * @param string $password Input password to validate - * @param string $hash Hash to verify password against - * - * @return boolean true if password matches hash - */ - public function verify(string $password, string $hash): bool - { - return $hash === $this->hash($password); - } - - /** - * Get default options for specific hashing algo - * - * @return array options named array - */ - public function getDefaultOptions(): array - { - return [ 'costCpu' => 8, 'costMemory' => 14, 'costParallel' => 1, 'length' => 64 ]; - } -} diff --git a/src/Appwrite/Auth/Hash/Scryptmodified.php b/src/Appwrite/Auth/Hash/Scryptmodified.php deleted file mode 100644 index 7717f324e5..0000000000 --- a/src/Appwrite/Auth/Hash/Scryptmodified.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -namespace Appwrite\Auth\Hash; - -use Appwrite\Auth\Hash; - -/* - * This is Scrypt hash with some additional steps added by Google. - * - * string salt - * string saltSeparator - * strin signerKey - * - * Reference: https://github.com/DomBlack/php-scrypt/blob/master/scrypt.php#L112-L116 -*/ -class Scryptmodified extends Hash -{ - /** - * @param string $password Input password to hash - * - * @return string hash - */ - public function hash(string $password): string - { - $options = $this->getOptions(); - - $derivedKeyBytes = $this->generateDerivedKey($password); - $signerKeyBytes = \base64_decode($options['signerKey']); - - $hashedPassword = $this->hashKeys($signerKeyBytes, $derivedKeyBytes); - - return \base64_encode($hashedPassword); - } - - /** - * @param string $password Input password to validate - * @param string $hash Hash to verify password against - * - * @return boolean true if password matches hash - */ - public function verify(string $password, string $hash): bool - { - return $this->hash($password) === $hash; - } - - /** - * Get default options for specific hashing algo - * - * @return array options named array - */ - public function getDefaultOptions(): array - { - return [ ]; - } - - private function generateDerivedKey(string $password) - { - $options = $this->getOptions(); - - $saltBytes = \base64_decode($options['salt']); - $saltSeparatorBytes = \base64_decode($options['saltSeparator']); - - $password = mb_convert_encoding($password, 'UTF-8'); - $derivedKey = \scrypt($password, $saltBytes . $saltSeparatorBytes, 16384, 8, 1, 64); - $derivedKey = \hex2bin($derivedKey); - - return $derivedKey; - } - - private function hashKeys($signerKeyBytes, $derivedKeyBytes): string - { - $key = \substr($derivedKeyBytes, 0, 32); - - $iv = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; - - $hash = \openssl_encrypt($signerKeyBytes, 'aes-256-ctr', $key, OPENSSL_RAW_DATA, $iv); - - return $hash; - } -} diff --git a/src/Appwrite/Auth/Hash/Sha.php b/src/Appwrite/Auth/Hash/Sha.php deleted file mode 100644 index c2ae3b52c1..0000000000 --- a/src/Appwrite/Auth/Hash/Sha.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -namespace Appwrite\Auth\Hash; - -use Appwrite\Auth\Hash; - -/* - * SHA accepted options: - * string? version. Allowed: - * - Version 1: sha1 - * - Version 2: sha224, sha256, sha384, sha512/224, sha512/256, sha512 - * - Version 3: sha3-224, sha3-256, sha3-384, sha3-512 - * - * Reference: https://www.php.net/manual/en/function.hash-algos.php -*/ -class Sha extends Hash -{ - /** - * @param string $password Input password to hash - * - * @return string hash - */ - public function hash(string $password): string - { - $algo = $this->getOptions()['version']; - - return \hash($algo, $password); - } - - /** - * @param string $password Input password to validate - * @param string $hash Hash to verify password against - * - * @return boolean true if password matches hash - */ - public function verify(string $password, string $hash): bool - { - return $this->hash($password) === $hash; - } - - /** - * Get default options for specific hashing algo - * - * @return array options named array - */ - public function getDefaultOptions(): array - { - return [ 'version' => 'sha3-512' ]; - } -} diff --git a/src/Appwrite/Auth/Key.php b/src/Appwrite/Auth/Key.php index 44a75a6ee3..b1f3836fb6 100644 --- a/src/Appwrite/Auth/Key.php +++ b/src/Appwrite/Auth/Key.php @@ -5,6 +5,7 @@ namespace Appwrite\Auth; use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; use Appwrite\Extend\Exception; +use Appwrite\Utopia\Database\Documents\User; use Utopia\Config\Config; use Utopia\Database\DateTime; use Utopia\Database\Document; @@ -110,16 +111,16 @@ class Key $secret = $key; } - $role = Auth::USER_ROLE_APPS; + $role = User::ROLE_APPS; $roles = Config::getParam('roles', []); - $scopes = $roles[Auth::USER_ROLE_APPS]['scopes'] ?? []; + $scopes = $roles[User::ROLE_APPS]['scopes'] ?? []; $expired = false; $guestKey = new Key( $project->getId(), $type, - Auth::USER_ROLE_GUESTS, - $roles[Auth::USER_ROLE_GUESTS]['scopes'] ?? [], + User::ROLE_GUESTS, + $roles[User::ROLE_GUESTS]['scopes'] ?? [], 'UNKNOWN' ); diff --git a/src/Appwrite/Auth/MFA/Type.php b/src/Appwrite/Auth/MFA/Type.php index 3516ec3780..d1e267965a 100644 --- a/src/Appwrite/Auth/MFA/Type.php +++ b/src/Appwrite/Auth/MFA/Type.php @@ -2,8 +2,8 @@ namespace Appwrite\Auth\MFA; -use Appwrite\Auth\Auth; use OTPHP\OTP; +use Utopia\Auth\Proofs\Token; abstract class Type { @@ -51,9 +51,10 @@ abstract class Type public static function generateBackupCodes(int $length = 10, int $total = 6): array { $backups = []; + $token = new Token($length); for ($i = 0; $i < $total; $i++) { - $backups[] = Auth::tokenGenerator($length); + $backups[] = $token->generate(); } return $backups; diff --git a/src/Appwrite/Auth/Validator/PasswordHistory.php b/src/Appwrite/Auth/Validator/PasswordHistory.php index f623ca180d..9b40b6a794 100644 --- a/src/Appwrite/Auth/Validator/PasswordHistory.php +++ b/src/Appwrite/Auth/Validator/PasswordHistory.php @@ -2,7 +2,7 @@ namespace Appwrite\Auth\Validator; -use Appwrite\Auth\Auth; +use Utopia\Auth\Hash; /** * Password. @@ -12,16 +12,14 @@ use Appwrite\Auth\Auth; class PasswordHistory extends Password { protected array $history; - protected string $algo; - protected array $algoOptions; + protected Hash $hash; - public function __construct(array $history, string $algo, array $algoOptions = []) + public function __construct(array $history, Hash $hash) { parent::__construct(); $this->history = $history; - $this->algo = $algo; - $this->algoOptions = $algoOptions; + $this->hash = $hash; } /** @@ -46,7 +44,7 @@ class PasswordHistory extends Password public function isValid($value): bool { foreach ($this->history as $hash) { - if (!empty($hash) && Auth::passwordVerify($value, $hash, $this->algo, $this->algoOptions)) { + if (!empty($hash) && $this->hash->verify($value, $hash)) { return false; } } diff --git a/src/Appwrite/Migration/Version/V16.php b/src/Appwrite/Migration/Version/V16.php index 9d72af9563..061ace31d7 100644 --- a/src/Appwrite/Migration/Version/V16.php +++ b/src/Appwrite/Migration/Version/V16.php @@ -2,7 +2,6 @@ namespace Appwrite\Migration\Version; -use Appwrite\Auth\Auth; use Appwrite\Migration\Migration; use Utopia\CLI\Console; use Utopia\Config\Config; @@ -118,7 +117,7 @@ class V16 extends Migration * Set default authDuration */ $document->setAttribute('auths', array_merge($document->getAttribute('auths', []), [ - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG + 'duration' => TOKEN_EXPIRATION_LOGIN_LONG ])); /** diff --git a/src/Appwrite/Migration/Version/V17.php b/src/Appwrite/Migration/Version/V17.php index fbbd4bfde0..79e2a8377d 100644 --- a/src/Appwrite/Migration/Version/V17.php +++ b/src/Appwrite/Migration/Version/V17.php @@ -2,8 +2,8 @@ namespace Appwrite\Migration\Version; -use Appwrite\Auth\Auth; use Appwrite\Migration\Migration; +use Utopia\Auth\Proofs\Password; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Document; @@ -270,7 +270,7 @@ class V17 extends Migration * Set hashOptions type */ $document->setAttribute('hashOptions', array_merge($document->getAttribute('hashOptions', []), [ - 'type' => $document->getAttribute('hash', Auth::DEFAULT_ALGO) + 'type' => $document->getAttribute('hash', (new Password())->getHash()->getName()) ])); break; } diff --git a/src/Appwrite/Migration/Version/V20.php b/src/Appwrite/Migration/Version/V20.php index 9ff041eb33..10e2706d0e 100644 --- a/src/Appwrite/Migration/Version/V20.php +++ b/src/Appwrite/Migration/Version/V20.php @@ -2,7 +2,6 @@ namespace Appwrite\Migration\Version; -use Appwrite\Auth\Auth; use Appwrite\Migration\Migration; use Exception; use PDOException; @@ -632,15 +631,15 @@ class V20 extends Migration } break; case 'sessions': - $duration = $this->project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $duration = $this->project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG; $expire = DateTime::addSeconds(new \DateTime(), $duration); $document->setAttribute('expire', $expire); $factors = match ($document->getAttribute('provider')) { - Auth::SESSION_PROVIDER_EMAIL => ['password'], - Auth::SESSION_PROVIDER_PHONE => ['phone'], - Auth::SESSION_PROVIDER_ANONYMOUS => ['anonymous'], - Auth::SESSION_PROVIDER_TOKEN => ['token'], + SESSION_PROVIDER_EMAIL => ['password'], + SESSION_PROVIDER_PHONE => ['phone'], + SESSION_PROVIDER_ANONYMOUS => ['anonymous'], + SESSION_PROVIDER_TOKEN => ['token'], default => ['email'], }; diff --git a/src/Appwrite/Platform/Appwrite.php b/src/Appwrite/Platform/Appwrite.php index a77f894be7..4aa135c4f1 100644 --- a/src/Appwrite/Platform/Appwrite.php +++ b/src/Appwrite/Platform/Appwrite.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform; +use Appwrite\Platform\Modules\Account; use Appwrite\Platform\Modules\Console; use Appwrite\Platform\Modules\Core; use Appwrite\Platform\Modules\Databases; @@ -17,6 +18,7 @@ class Appwrite extends Platform public function __construct() { parent::__construct(new Core()); + $this->addModule(new Account\Module()); $this->addModule(new Databases\Module()); $this->addModule(new Projects\Module()); $this->addModule(new Functions\Module()); diff --git a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Authenticators/Create.php b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Authenticators/Create.php new file mode 100644 index 0000000000..2d83599964 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Authenticators/Create.php @@ -0,0 +1,141 @@ +<?php + +namespace Appwrite\Platform\Modules\Account\Http\Account\MFA\Authenticators; + +use Appwrite\Auth\MFA\Type; +use Appwrite\Auth\MFA\Type\TOTP; +use Appwrite\Event\Event; +use Appwrite\Extend\Exception; +use Appwrite\SDK\AuthType; +use Appwrite\SDK\ContentType; +use Appwrite\SDK\Deprecated; +use Appwrite\SDK\Method; +use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Response; +use Utopia\Database\Database; +use Utopia\Database\Document; +use Utopia\Database\Helpers\ID; +use Utopia\Database\Helpers\Permission; +use Utopia\Database\Helpers\Role; +use Utopia\Platform\Action; +use Utopia\Platform\Scope\HTTP; +use Utopia\Validator\WhiteList; + +class Create extends Action +{ + use HTTP; + + public static function getName(): string + { + return 'createMFAAuthenticator'; + } + + public function __construct() + { + $this + ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/account/mfa/authenticators/:type') + ->desc('Create authenticator') + ->groups(['api', 'account']) + ->label('event', 'users.[userId].update.mfa') + ->label('scope', 'account') + ->label('audits.event', 'user.update') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') + ->label('sdk', [ + new Method( + namespace: 'account', + group: 'mfa', + name: 'createMfaAuthenticator', + description: '/docs/references/account/create-mfa-authenticator.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_MFA_TYPE, + ) + ], + contentType: ContentType::JSON, + deprecated: new Deprecated( + since: '1.8.0', + replaceWith: 'account.createMFAAuthenticator', + ), + ), + new Method( + namespace: 'account', + group: 'mfa', + name: 'createMFAAuthenticator', + description: '/docs/references/account/create-mfa-authenticator.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_MFA_TYPE, + ) + ], + contentType: ContentType::JSON + ) + ]) + ->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator. Must be `' . Type::TOTP . '`') + ->inject('response') + ->inject('project') + ->inject('user') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback($this->action(...)); + } + + public function action( + string $type, + Response $response, + Document $project, + Document $user, + Database $dbForProject, + Event $queueForEvents + ): void { + $otp = (match ($type) { + Type::TOTP => new TOTP(), + default => throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Unknown type.') + }); + + $otp->setLabel($user->getAttribute('email')); + $otp->setIssuer($project->getAttribute('name')); + + $authenticator = TOTP::getAuthenticatorFromUser($user); + + if ($authenticator) { + if ($authenticator->getAttribute('verified')) { + throw new Exception(Exception::USER_AUTHENTICATOR_ALREADY_VERIFIED); + } + $dbForProject->deleteDocument('authenticators', $authenticator->getId()); + } + + $authenticator = new Document([ + '$id' => ID::unique(), + 'userId' => $user->getId(), + 'userInternalId' => $user->getSequence(), + 'type' => Type::TOTP, + 'verified' => false, + 'data' => [ + 'secret' => $otp->getSecret(), + ], + '$permissions' => [ + Permission::read(Role::user($user->getId())), + Permission::update(Role::user($user->getId())), + Permission::delete(Role::user($user->getId())), + ] + ]); + + $model = new Document([ + 'secret' => $otp->getSecret(), + 'uri' => $otp->getProvisioningUri() + ]); + + $authenticator = $dbForProject->createDocument('authenticators', $authenticator); + $dbForProject->purgeCachedDocument('users', $user->getId()); + + $queueForEvents->setParam('userId', $user->getId()); + + $response->dynamic($model, Response::MODEL_MFA_TYPE); + } +} diff --git a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Authenticators/Delete.php b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Authenticators/Delete.php new file mode 100644 index 0000000000..5c92bfff5c --- /dev/null +++ b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Authenticators/Delete.php @@ -0,0 +1,107 @@ +<?php + +namespace Appwrite\Platform\Modules\Account\Http\Account\MFA\Authenticators; + +use Appwrite\Auth\MFA\Type; +use Appwrite\Auth\MFA\Type\TOTP; +use Appwrite\Event\Event; +use Appwrite\Extend\Exception; +use Appwrite\SDK\AuthType; +use Appwrite\SDK\ContentType; +use Appwrite\SDK\Deprecated; +use Appwrite\SDK\Method; +use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Response; +use Utopia\Database\Database; +use Utopia\Database\Document; +use Utopia\Platform\Action; +use Utopia\Platform\Scope\HTTP; +use Utopia\Validator\WhiteList; + +class Delete extends Action +{ + use HTTP; + + public static function getName(): string + { + return 'deleteMFAAuthenticator'; + } + + public function __construct() + { + $this + ->setHttpMethod(Action::HTTP_REQUEST_METHOD_DELETE) + ->setHttpPath('/v1/account/mfa/authenticators/:type') + ->desc('Delete authenticator') + ->groups(['api', 'account', 'mfaProtected']) + ->label('event', 'users.[userId].delete.mfa') + ->label('scope', 'account') + ->label('audits.event', 'user.update') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') + ->label('sdk', [ + new Method( + namespace: 'account', + group: 'mfa', + name: 'deleteMfaAuthenticator', + description: '/docs/references/account/delete-mfa-authenticator.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_NOCONTENT, + model: Response::MODEL_NONE, + ) + ], + contentType: ContentType::NONE, + deprecated: new Deprecated( + since: '1.8.0', + replaceWith: 'account.deleteMFAAuthenticator', + ), + ), + new Method( + namespace: 'account', + group: 'mfa', + name: 'deleteMFAAuthenticator', + description: '/docs/references/account/delete-mfa-authenticator.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_NOCONTENT, + model: Response::MODEL_NONE, + ) + ], + contentType: ContentType::NONE + ) + ]) + ->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator.') + ->inject('response') + ->inject('user') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback($this->action(...)); + } + + public function action( + string $type, + Response $response, + Document $user, + Database $dbForProject, + Event $queueForEvents + ): void { + $authenticator = (match ($type) { + Type::TOTP => TOTP::getAuthenticatorFromUser($user), + default => null + }); + + if (!$authenticator) { + throw new Exception(Exception::USER_AUTHENTICATOR_NOT_FOUND); + } + + $dbForProject->deleteDocument('authenticators', $authenticator->getId()); + $dbForProject->purgeCachedDocument('users', $user->getId()); + + $queueForEvents->setParam('userId', $user->getId()); + + $response->noContent(); + } +} diff --git a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Authenticators/Update.php b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Authenticators/Update.php new file mode 100644 index 0000000000..b68a55c20b --- /dev/null +++ b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Authenticators/Update.php @@ -0,0 +1,135 @@ +<?php + +namespace Appwrite\Platform\Modules\Account\Http\Account\MFA\Authenticators; + +use Appwrite\Auth\MFA\Challenge; +use Appwrite\Auth\MFA\Type; +use Appwrite\Auth\MFA\Type\TOTP; +use Appwrite\Event\Event; +use Appwrite\Extend\Exception; +use Appwrite\SDK\AuthType; +use Appwrite\SDK\ContentType; +use Appwrite\SDK\Deprecated; +use Appwrite\SDK\Method; +use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Response; +use Utopia\Database\Database; +use Utopia\Database\Document; +use Utopia\Platform\Action; +use Utopia\Platform\Scope\HTTP; +use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; + +class Update extends Action +{ + use HTTP; + + public static function getName(): string + { + return 'updateMFAAuthenticator'; + } + + public function __construct() + { + $this + ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PUT) + ->setHttpPath('/v1/account/mfa/authenticators/:type') + ->desc('Update authenticator (confirmation)') + ->groups(['api', 'account']) + ->label('event', 'users.[userId].update.mfa') + ->label('scope', 'account') + ->label('audits.event', 'user.update') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') + ->label('sdk', [ + new Method( + namespace: 'account', + group: 'mfa', + name: 'updateMfaAuthenticator', + description: '/docs/references/account/update-mfa-authenticator.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_USER, + ) + ], + contentType: ContentType::JSON, + deprecated: new Deprecated( + since: '1.8.0', + replaceWith: 'account.updateMFAAuthenticator', + ), + ), + new Method( + namespace: 'account', + group: 'mfa', + name: 'updateMFAAuthenticator', + description: '/docs/references/account/update-mfa-authenticator.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_USER, + ) + ], + contentType: ContentType::JSON + ) + ]) + ->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator.') + ->param('otp', '', new Text(256), 'Valid verification token.') + ->inject('response') + ->inject('user') + ->inject('session') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback($this->action(...)); + } + + public function action( + string $type, + string $otp, + Response $response, + Document $user, + Document $session, + Database $dbForProject, + Event $queueForEvents + ): void { + $authenticator = (match ($type) { + Type::TOTP => TOTP::getAuthenticatorFromUser($user), + default => null + }); + + if ($authenticator === null) { + throw new Exception(Exception::USER_AUTHENTICATOR_NOT_FOUND); + } + + if ($authenticator->getAttribute('verified')) { + throw new Exception(Exception::USER_AUTHENTICATOR_ALREADY_VERIFIED); + } + + $success = (match ($type) { + Type::TOTP => Challenge\TOTP::verify($user, $otp), + default => false + }); + + if (!$success) { + throw new Exception(Exception::USER_INVALID_TOKEN); + } + + $authenticator->setAttribute('verified', true); + + $dbForProject->updateDocument('authenticators', $authenticator->getId(), $authenticator); + $dbForProject->purgeCachedDocument('users', $user->getId()); + + $factors = $session->getAttribute('factors', []); + $factors[] = $type; + $factors = \array_values(\array_unique($factors)); + + $session->setAttribute('factors', $factors); + $dbForProject->updateDocument('sessions', $session->getId(), $session); + + $queueForEvents->setParam('userId', $user->getId()); + + $response->dynamic($user, Response::MODEL_ACCOUNT); + } +} 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 new file mode 100644 index 0000000000..10230df7af --- /dev/null +++ b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php @@ -0,0 +1,337 @@ +<?php + +namespace Appwrite\Platform\Modules\Account\Http\Account\MFA\Challenges; + +use Appwrite\Auth\MFA\Type; +use Appwrite\Detector\Detector; +use Appwrite\Event\Event; +use Appwrite\Event\Mail; +use Appwrite\Event\Messaging; +use Appwrite\Event\StatsUsage; +use Appwrite\Extend\Exception; +use Appwrite\SDK\ContentType; +use Appwrite\SDK\Deprecated; +use Appwrite\SDK\Method; +use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Template\Template; +use Appwrite\Utopia\Request; +use Appwrite\Utopia\Response; +use libphonenumber\PhoneNumberUtil; +use Utopia\Abuse\Abuse; +use Utopia\Auth\Proofs\Code as ProofsCode; +use Utopia\Auth\Proofs\Token as ProofsToken; +use Utopia\Database\Database; +use Utopia\Database\DateTime; +use Utopia\Database\Document; +use Utopia\Database\Helpers\Permission; +use Utopia\Database\Helpers\Role; +use Utopia\Locale\Locale; +use Utopia\Platform\Action; +use Utopia\Platform\Scope\HTTP; +use Utopia\Storage\Validator\FileName; +use Utopia\System\System; +use Utopia\Validator\WhiteList; + +class Create extends Action +{ + use HTTP; + + public static function getName(): string + { + return 'createMFAChallenge'; + } + + public function __construct() + { + $this + ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/account/mfa/challenges') + ->httpAlias('/v1/account/mfa/challenge') + ->desc('Create MFA challenge') + ->groups(['api', 'account', 'mfa']) + ->label('scope', 'account') + ->label('event', 'users.[userId].challenges.[challengeId].create') + ->label('audits.event', 'challenge.create') + ->label('audits.resource', 'user/{response.userId}') + ->label('audits.userId', '{response.userId}') + ->label('sdk', [ + new Method( + namespace: 'account', + group: 'mfa', + name: 'createMfaChallenge', + description: '/docs/references/account/create-mfa-challenge.md', + auth: [], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_CREATED, + model: Response::MODEL_MFA_CHALLENGE, + ) + ], + contentType: ContentType::JSON, + deprecated: new Deprecated( + since: '1.8.0', + replaceWith: 'account.createMFAChallenge', + ), + ), + new Method( + namespace: 'account', + group: 'mfa', + name: 'createMFAChallenge', + description: '/docs/references/account/create-mfa-challenge.md', + auth: [], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_CREATED, + model: Response::MODEL_MFA_CHALLENGE, + ) + ], + contentType: ContentType::JSON + ) + ]) + ->label('abuse-limit', 10) + ->label('abuse-key', 'url:{url},userId:{userId}') + ->param('factor', '', new WhiteList([Type::EMAIL, Type::PHONE, Type::TOTP, Type::RECOVERY_CODE]), 'Factor used for verification. Must be one of following: `' . Type::EMAIL . '`, `' . Type::PHONE . '`, `' . Type::TOTP . '`, `' . Type::RECOVERY_CODE . '`.') + ->inject('response') + ->inject('dbForProject') + ->inject('user') + ->inject('locale') + ->inject('project') + ->inject('request') + ->inject('queueForEvents') + ->inject('queueForMessaging') + ->inject('queueForMails') + ->inject('timelimit') + ->inject('queueForStatsUsage') + ->inject('plan') + ->inject('proofForToken') + ->inject('proofForCode') + ->callback($this->action(...)); + } + + public function action( + string $factor, + Response $response, + Database $dbForProject, + Document $user, + Locale $locale, + Document $project, + Request $request, + Event $queueForEvents, + Messaging $queueForMessaging, + Mail $queueForMails, + callable $timelimit, + StatsUsage $queueForStatsUsage, + array $plan, + ProofsToken $proofForToken, + ProofsCode $proofForCode + ): void { + $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), TOKEN_EXPIRATION_CONFIRM)); + + $code = $proofForCode->generate(); + $challenge = new Document([ + 'userId' => $user->getId(), + 'userInternalId' => $user->getSequence(), + 'type' => $factor, + 'token' => $proofForToken->generate(), + 'code' => $code, + 'expire' => $expire, + '$permissions' => [ + Permission::read(Role::user($user->getId())), + Permission::update(Role::user($user->getId())), + Permission::delete(Role::user($user->getId())), + ], + ]); + + $challenge = $dbForProject->createDocument('challenges', $challenge); + + // 9 levels up to project root + $templatesPath = \dirname(__DIR__, 9) . '/app/config/locale/templates'; + + switch ($factor) { + case Type::PHONE: + if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { + throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); + } + if (empty($user->getAttribute('phone'))) { + throw new Exception(Exception::USER_PHONE_NOT_FOUND); + } + if (!$user->getAttribute('phoneVerification')) { + throw new Exception(Exception::USER_PHONE_NOT_VERIFIED); + } + + $message = Template::fromFile($templatesPath . '/sms-base.tpl'); + + $customTemplate = $project->getAttribute('templates', [])['sms.mfaChallenge-' . $locale->default] ?? []; + if (!empty($customTemplate)) { + $message = $customTemplate['message'] ?? $message; + } + + $messageContent = Template::fromString($locale->getText("sms.verification.body")); + $messageContent + ->setParam('{{project}}', $project->getAttribute('name')) + ->setParam('{{secret}}', $code); + $messageContent = \strip_tags($messageContent->render()); + $message = $message->setParam('{{token}}', $messageContent); + + $message = $message->render(); + + $phone = $user->getAttribute('phone'); + $queueForMessaging + ->setType(MESSAGE_SEND_TYPE_INTERNAL) + ->setMessage(new Document([ + '$id' => $challenge->getId(), + 'data' => [ + 'content' => $code, + ], + ])) + ->setRecipients([$phone]) + ->setProviderType(MESSAGE_TYPE_SMS); + + if (isset($plan['authPhone'])) { + $timelimit = $timelimit('organization:{organizationId}', $plan['authPhone'], 30 * 24 * 60 * 60); // 30 days + $timelimit + ->setParam('{organizationId}', $project->getAttribute('teamId')); + + $abuse = new Abuse($timelimit); + if ($abuse->check() && System::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') { + $helper = PhoneNumberUtil::getInstance(); + $countryCode = $helper->parse($phone)->getCountryCode(); + + if (!empty($countryCode)) { + $queueForStatsUsage + ->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1); + } + } + $queueForStatsUsage + ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) + ->setProject($project) + ->trigger(); + } + break; + case Type::EMAIL: + if (empty(System::getEnv('_APP_SMTP_HOST'))) { + throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); + } + if (empty($user->getAttribute('email'))) { + throw new Exception(Exception::USER_EMAIL_NOT_FOUND); + } + if (!$user->getAttribute('emailVerification')) { + throw new Exception(Exception::USER_EMAIL_NOT_VERIFIED); + } + + $subject = $locale->getText("emails.mfaChallenge.subject"); + $preview = $locale->getText("emails.mfaChallenge.preview"); + $heading = $locale->getText("emails.mfaChallenge.heading"); + + $customTemplate = $project->getAttribute('templates', [])['email.mfaChallenge-' . $locale->default] ?? []; + $smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base'); + + $validator = new FileName(); + if (!$validator->isValid($smtpBaseTemplate)) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid template path'); + } + + $bodyTemplate = $templatesPath . '/' . $smtpBaseTemplate . '.tpl'; + + $detector = new Detector($request->getUserAgent('UNKNOWN')); + $agentOs = $detector->getOS(); + $agentClient = $detector->getClient(); + $agentDevice = $detector->getDevice(); + + $message = Template::fromFile($templatesPath . '/email-mfa-challenge.tpl'); + $message + ->setParam('{{hello}}', $locale->getText("emails.mfaChallenge.hello")) + ->setParam('{{description}}', $locale->getText("emails.mfaChallenge.description")) + ->setParam('{{clientInfo}}', $locale->getText("emails.mfaChallenge.clientInfo")) + ->setParam('{{thanks}}', $locale->getText("emails.mfaChallenge.thanks")) + ->setParam('{{signature}}', $locale->getText("emails.mfaChallenge.signature")); + + $body = $message->render(); + + $smtp = $project->getAttribute('smtp', []); + $smtpEnabled = $smtp['enabled'] ?? false; + + $senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + $senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'); + $replyTo = ""; + + if ($smtpEnabled) { + if (!empty($smtp['senderEmail'])) { + $senderEmail = $smtp['senderEmail']; + } + if (!empty($smtp['senderName'])) { + $senderName = $smtp['senderName']; + } + if (!empty($smtp['replyTo'])) { + $replyTo = $smtp['replyTo']; + } + + $queueForMails + ->setSmtpHost($smtp['host'] ?? '') + ->setSmtpPort($smtp['port'] ?? '') + ->setSmtpUsername($smtp['username'] ?? '') + ->setSmtpPassword($smtp['password'] ?? '') + ->setSmtpSecure($smtp['secure'] ?? ''); + + if (!empty($customTemplate)) { + if (!empty($customTemplate['senderEmail'])) { + $senderEmail = $customTemplate['senderEmail']; + } + if (!empty($customTemplate['senderName'])) { + $senderName = $customTemplate['senderName']; + } + if (!empty($customTemplate['replyTo'])) { + $replyTo = $customTemplate['replyTo']; + } + + $body = $customTemplate['message'] ?? ''; + $subject = $customTemplate['subject'] ?? $subject; + } + + $queueForMails + ->setSmtpReplyTo($replyTo) + ->setSmtpSenderEmail($senderEmail) + ->setSmtpSenderName($senderName); + } + + $emailVariables = [ + 'heading' => $heading, + 'direction' => $locale->getText('settings.direction'), + 'user' => $user->getAttribute('name'), + 'project' => $project->getAttribute('name'), + 'otp' => $code, + 'agentDevice' => $agentDevice['deviceBrand'] ?? 'UNKNOWN', + 'agentClient' => $agentClient['clientName'] ?? 'UNKNOWN', + 'agentOs' => $agentOs['osName'] ?? 'UNKNOWN', + ]; + + if ($smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE) { + $emailVariables = array_merge($emailVariables, [ + 'accentColor' => APP_EMAIL_ACCENT_COLOR, + 'logoUrl' => APP_EMAIL_LOGO_URL, + 'twitterUrl' => APP_SOCIAL_TWITTER, + 'discordUrl' => APP_SOCIAL_DISCORD, + 'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE, + 'termsUrl' => APP_EMAIL_TERMS_URL, + 'privacyUrl' => APP_EMAIL_PRIVACY_URL, + ]); + } + + $queueForMails + ->setSubject($subject) + ->setPreview($preview) + ->setBody($body) + ->setBodyTemplate($bodyTemplate) + ->setVariables($emailVariables) + ->setRecipient($user->getAttribute('email')) + ->trigger(); + break; + } + + $queueForEvents + ->setParam('userId', $user->getId()) + ->setParam('challengeId', $challenge->getId()); + + $response->dynamic($challenge, Response::MODEL_MFA_CHALLENGE); + } +} diff --git a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Update.php b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Update.php new file mode 100644 index 0000000000..40d17afa18 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Update.php @@ -0,0 +1,161 @@ +<?php + +namespace Appwrite\Platform\Modules\Account\Http\Account\MFA\Challenges; + +use Appwrite\Auth\MFA\Challenge; +use Appwrite\Auth\MFA\Type; +use Appwrite\Event\Event; +use Appwrite\Extend\Exception; +use Appwrite\SDK\AuthType; +use Appwrite\SDK\ContentType; +use Appwrite\SDK\Deprecated; +use Appwrite\SDK\Method; +use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Response; +use Utopia\Database\Database; +use Utopia\Database\DateTime; +use Utopia\Database\Document; +use Utopia\Platform\Action; +use Utopia\Platform\Scope\HTTP; +use Utopia\Validator\Text; + +class Update extends Action +{ + use HTTP; + + public static function getName(): string + { + return 'updateMFAChallenge'; + } + + public function __construct() + { + $this + ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PUT) + ->setHttpPath('/v1/account/mfa/challenges') + ->httpAlias('/v1/account/mfa/challenge') + ->desc('Update MFA challenge (confirmation)') + ->groups(['api', 'account', 'mfa']) + ->label('scope', 'account') + ->label('event', 'users.[userId].sessions.[sessionId].create') + ->label('audits.event', 'challenges.update') + ->label('audits.resource', 'user/{response.userId}') + ->label('audits.userId', '{response.userId}') + ->label('sdk', [ + new Method( + namespace: 'account', + group: 'mfa', + name: 'updateMfaChallenge', + description: '/docs/references/account/update-mfa-challenge.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_SESSION, + ) + ], + contentType: ContentType::JSON, + deprecated: new Deprecated( + since: '1.8.0', + replaceWith: 'account.updateMFAChallenge', + ), + ), + new Method( + namespace: 'account', + group: 'mfa', + name: 'updateMFAChallenge', + description: '/docs/references/account/update-mfa-challenge.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_SESSION, + ) + ], + contentType: ContentType::JSON + ) + ]) + ->label('abuse-limit', 10) + ->label('abuse-key', 'url:{url},challengeId:{param-challengeId}') + ->param('challengeId', '', new Text(256), 'ID of the challenge.') + ->param('otp', '', new Text(256), 'Valid verification token.') + ->inject('project') + ->inject('response') + ->inject('user') + ->inject('session') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback($this->action(...)); + } + + public function action( + string $challengeId, + string $otp, + Document $project, + Response $response, + Document $user, + Document $session, + Database $dbForProject, + Event $queueForEvents + ): void { + $challenge = $dbForProject->getDocument('challenges', $challengeId); + + if ($challenge->isEmpty()) { + throw new Exception(Exception::USER_INVALID_TOKEN); + } + + $type = $challenge->getAttribute('type'); + + $recoveryCodeChallenge = function (Document $challenge, Document $user, string $otp) use ($dbForProject) { + if ( + $challenge->isSet('type') && + $challenge->getAttribute('type') === \strtolower(Type::RECOVERY_CODE) + ) { + $mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []); + if (\in_array($otp, $mfaRecoveryCodes)) { + $mfaRecoveryCodes = \array_diff($mfaRecoveryCodes, [$otp]); + $mfaRecoveryCodes = \array_values($mfaRecoveryCodes); + $user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes); + $dbForProject->updateDocument('users', $user->getId(), $user); + + return true; + } + + return false; + } + + return false; + }; + + $success = (match ($type) { + Type::TOTP => Challenge\TOTP::challenge($challenge, $user, $otp), + Type::PHONE => Challenge\Phone::challenge($challenge, $user, $otp), + Type::EMAIL => Challenge\Email::challenge($challenge, $user, $otp), + \strtolower(Type::RECOVERY_CODE) => $recoveryCodeChallenge($challenge, $user, $otp), + default => false + }); + + if (!$success) { + throw new Exception(Exception::USER_INVALID_TOKEN); + } + + $dbForProject->deleteDocument('challenges', $challengeId); + $dbForProject->purgeCachedDocument('users', $user->getId()); + + $factors = $session->getAttribute('factors', []); + $factors[] = $type; + $factors = \array_values(\array_unique($factors)); + + $session + ->setAttribute('factors', $factors) + ->setAttribute('mfaUpdatedAt', DateTime::now()); + + $dbForProject->updateDocument('sessions', $session->getId(), $session); + + $queueForEvents + ->setParam('userId', $user->getId()) + ->setParam('sessionId', $session->getId()); + + $response->dynamic($session, Response::MODEL_SESSION); + } +} diff --git a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Factors/XList.php b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Factors/XList.php new file mode 100644 index 0000000000..c60f599cfc --- /dev/null +++ b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Factors/XList.php @@ -0,0 +1,89 @@ +<?php + +namespace Appwrite\Platform\Modules\Account\Http\Account\MFA\Factors; + +use Appwrite\Auth\MFA\Type; +use Appwrite\Auth\MFA\Type\TOTP; +use Appwrite\SDK\AuthType; +use Appwrite\SDK\ContentType; +use Appwrite\SDK\Deprecated; +use Appwrite\SDK\Method; +use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Response; +use Utopia\Database\Document; +use Utopia\Platform\Action; +use Utopia\Platform\Scope\HTTP; + +class XList extends Action +{ + use HTTP; + + public static function getName(): string + { + return 'listMFAFactors'; + } + + public function __construct() + { + $this + ->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/account/mfa/factors') + ->desc('List factors') + ->groups(['api', 'account', 'mfa']) + ->label('scope', 'account') + ->label('sdk', [ + new Method( + namespace: 'account', + group: 'mfa', + name: 'listMfaFactors', + description: '/docs/references/account/list-mfa-factors.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_MFA_FACTORS, + ) + ], + contentType: ContentType::JSON, + deprecated: new Deprecated( + since: '1.8.0', + replaceWith: 'account.listMFAFactors', + ), + ), + new Method( + namespace: 'account', + group: 'mfa', + name: 'listMFAFactors', + description: '/docs/references/account/list-mfa-factors.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_MFA_FACTORS, + ) + ], + contentType: ContentType::JSON + ) + ]) + ->inject('response') + ->inject('user') + ->callback($this->action(...)); + } + + public function action(Response $response, Document $user): void + { + $mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []); + $recoveryCodeEnabled = \is_array($mfaRecoveryCodes) && \count($mfaRecoveryCodes) > 0; + + $totp = TOTP::getAuthenticatorFromUser($user); + + $factors = new Document([ + Type::TOTP => $totp !== null && $totp->getAttribute('verified', false), + Type::EMAIL => $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false), + Type::PHONE => $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false), + Type::RECOVERY_CODE => $recoveryCodeEnabled + ]); + + $response->dynamic($factors, Response::MODEL_MFA_FACTORS); + } +} diff --git a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/RecoveryCodes/Create.php b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/RecoveryCodes/Create.php new file mode 100644 index 0000000000..fc26142991 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/RecoveryCodes/Create.php @@ -0,0 +1,105 @@ +<?php + +namespace Appwrite\Platform\Modules\Account\Http\Account\MFA\RecoveryCodes; + +use Appwrite\Auth\MFA\Type; +use Appwrite\Event\Event; +use Appwrite\Extend\Exception; +use Appwrite\SDK\AuthType; +use Appwrite\SDK\ContentType; +use Appwrite\SDK\Deprecated; +use Appwrite\SDK\Method; +use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Response; +use Utopia\Database\Database; +use Utopia\Database\Document; +use Utopia\Platform\Action; +use Utopia\Platform\Scope\HTTP; + +class Create extends Action +{ + use HTTP; + + public static function getName(): string + { + return 'createMFARecoveryCodes'; + } + + public function __construct() + { + $this + ->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/account/mfa/recovery-codes') + ->desc('Create MFA recovery codes') + ->groups(['api', 'account']) + ->label('event', 'users.[userId].update.mfa') + ->label('scope', 'account') + ->label('audits.event', 'user.update') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') + ->label('sdk', [ + new Method( + namespace: 'account', + group: 'mfa', + name: 'createMfaRecoveryCodes', + description: '/docs/references/account/create-mfa-recovery-codes.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_CREATED, + model: Response::MODEL_MFA_RECOVERY_CODES, + ) + ], + contentType: ContentType::JSON, + deprecated: new Deprecated( + since: '1.8.0', + replaceWith: 'account.createMFARecoveryCodes', + ), + ), + new Method( + namespace: 'account', + group: 'mfa', + name: 'createMFARecoveryCodes', + description: '/docs/references/account/create-mfa-recovery-codes.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_CREATED, + model: Response::MODEL_MFA_RECOVERY_CODES, + ) + ], + contentType: ContentType::JSON + ) + ]) + ->inject('response') + ->inject('user') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback($this->action(...)); + } + + public function action( + Response $response, + Document $user, + Database $dbForProject, + Event $queueForEvents + ): void { + $mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []); + + if (!empty($mfaRecoveryCodes)) { + throw new Exception(Exception::USER_RECOVERY_CODES_ALREADY_EXISTS); + } + + $mfaRecoveryCodes = Type::generateBackupCodes(); + $user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes); + $dbForProject->updateDocument('users', $user->getId(), $user); + + $queueForEvents->setParam('userId', $user->getId()); + + $document = new Document([ + 'recoveryCodes' => $mfaRecoveryCodes + ]); + + $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); + } +} diff --git a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/RecoveryCodes/Get.php b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/RecoveryCodes/Get.php new file mode 100644 index 0000000000..8a85c361ca --- /dev/null +++ b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/RecoveryCodes/Get.php @@ -0,0 +1,86 @@ +<?php + +namespace Appwrite\Platform\Modules\Account\Http\Account\MFA\RecoveryCodes; + +use Appwrite\Extend\Exception; +use Appwrite\SDK\AuthType; +use Appwrite\SDK\ContentType; +use Appwrite\SDK\Deprecated; +use Appwrite\SDK\Method; +use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Response; +use Utopia\Database\Document; +use Utopia\Platform\Action; +use Utopia\Platform\Scope\HTTP; + +class Get extends Action +{ + use HTTP; + + public static function getName(): string + { + return 'getMFARecoveryCodes'; + } + + public function __construct() + { + $this + ->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET) + ->setHttpPath('/v1/account/mfa/recovery-codes') + ->desc('List MFA recovery codes') + ->groups(['api', 'account', 'mfaProtected']) + ->label('scope', 'account') + ->label('sdk', [ + new Method( + namespace: 'account', + group: 'mfa', + name: 'getMfaRecoveryCodes', + description: '/docs/references/account/get-mfa-recovery-codes.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_MFA_RECOVERY_CODES, + ) + ], + contentType: ContentType::JSON, + deprecated: new Deprecated( + since: '1.8.0', + replaceWith: 'account.getMFARecoveryCodes', + ), + ), + new Method( + namespace: 'account', + group: 'mfa', + name: 'getMFARecoveryCodes', + description: '/docs/references/account/get-mfa-recovery-codes.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_MFA_RECOVERY_CODES, + ) + ], + contentType: ContentType::JSON + ) + ]) + ->inject('response') + ->inject('user') + ->callback($this->action(...)); + } + + public function action(Response $response, Document $user): void + { + $mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []); + + if (empty($mfaRecoveryCodes)) { + throw new Exception(Exception::USER_RECOVERY_CODES_NOT_FOUND); + } + + $document = new Document([ + 'recoveryCodes' => $mfaRecoveryCodes + ]); + + $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); + } +} diff --git a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/RecoveryCodes/Update.php b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/RecoveryCodes/Update.php new file mode 100644 index 0000000000..5cc2783e75 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/RecoveryCodes/Update.php @@ -0,0 +1,104 @@ +<?php + +namespace Appwrite\Platform\Modules\Account\Http\Account\MFA\RecoveryCodes; + +use Appwrite\Auth\MFA\Type; +use Appwrite\Event\Event; +use Appwrite\Extend\Exception; +use Appwrite\SDK\AuthType; +use Appwrite\SDK\ContentType; +use Appwrite\SDK\Deprecated; +use Appwrite\SDK\Method; +use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Response; +use Utopia\Database\Database; +use Utopia\Database\Document; +use Utopia\Platform\Action; +use Utopia\Platform\Scope\HTTP; + +class Update extends Action +{ + use HTTP; + + public static function getName(): string + { + return 'updateMFARecoveryCodes'; + } + + public function __construct() + { + $this + ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/account/mfa/recovery-codes') + ->desc('Update MFA recovery codes (regenerate)') + ->groups(['api', 'account', 'mfaProtected']) + ->label('event', 'users.[userId].update.mfa') + ->label('scope', 'account') + ->label('audits.event', 'user.update') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') + ->label('sdk', [ + new Method( + namespace: 'account', + group: 'mfa', + name: 'updateMfaRecoveryCodes', + description: '/docs/references/account/update-mfa-recovery-codes.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_MFA_RECOVERY_CODES, + ) + ], + contentType: ContentType::JSON, + deprecated: new Deprecated( + since: '1.8.0', + replaceWith: 'account.updateMFARecoveryCodes', + ), + ), + new Method( + namespace: 'account', + group: 'mfa', + name: 'updateMFARecoveryCodes', + description: '/docs/references/account/update-mfa-recovery-codes.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_MFA_RECOVERY_CODES, + ) + ], + contentType: ContentType::JSON + ) + ]) + ->inject('dbForProject') + ->inject('response') + ->inject('user') + ->inject('queueForEvents') + ->callback($this->action(...)); + } + + public function action( + Database $dbForProject, + Response $response, + Document $user, + Event $queueForEvents + ): void { + $mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []); + if (empty($mfaRecoveryCodes)) { + throw new Exception(Exception::USER_RECOVERY_CODES_NOT_FOUND); + } + + $mfaRecoveryCodes = Type::generateBackupCodes(); + $user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes); + $dbForProject->updateDocument('users', $user->getId(), $user); + + $queueForEvents->setParam('userId', $user->getId()); + + $document = new Document([ + 'recoveryCodes' => $mfaRecoveryCodes + ]); + + $response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES); + } +} diff --git a/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Update.php b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Update.php new file mode 100644 index 0000000000..00068c7441 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Update.php @@ -0,0 +1,99 @@ +<?php + +namespace Appwrite\Platform\Modules\Account\Http\Account\MFA; + +use Appwrite\Auth\MFA\Type; +use Appwrite\Auth\MFA\Type\TOTP; +use Appwrite\Event\Event; +use Appwrite\SDK\AuthType; +use Appwrite\SDK\ContentType; +use Appwrite\SDK\Method; +use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Response; +use Utopia\Database\Database; +use Utopia\Database\Document; +use Utopia\Platform\Action; +use Utopia\Platform\Scope\HTTP; +use Utopia\Validator\Boolean; + +class Update extends Action +{ + use HTTP; + + public static function getName(): string + { + return 'updateMFA'; + } + + public function __construct() + { + $this + ->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH) + ->setHttpPath('/v1/account/mfa') + ->desc('Update MFA') + ->groups(['api', 'account']) + ->label('event', 'users.[userId].update.mfa') + ->label('scope', 'account') + ->label('audits.event', 'user.update') + ->label('audits.resource', 'user/{response.$id}') + ->label('audits.userId', '{response.$id}') + ->label('sdk', new Method( + namespace: 'account', + group: 'mfa', + name: 'updateMFA', + description: '/docs/references/account/update-mfa.md', + auth: [AuthType::SESSION, AuthType::JWT], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_USER, + ) + ], + contentType: ContentType::JSON + )) + ->param('mfa', null, new Boolean(), 'Enable or disable MFA.') + ->inject('requestTimestamp') + ->inject('response') + ->inject('user') + ->inject('session') + ->inject('dbForProject') + ->inject('queueForEvents') + ->callback($this->action(...)); + } + + public function action( + bool $mfa, + ?\DateTime $requestTimestamp, + Response $response, + Document $user, + Document $session, + Database $dbForProject, + Event $queueForEvents + ): void { + $user->setAttribute('mfa', $mfa); + + $user = $dbForProject->updateDocument('users', $user->getId(), $user); + + if ($mfa) { + $factors = $session->getAttribute('factors', []); + $totp = TOTP::getAuthenticatorFromUser($user); + if ($totp !== null && $totp->getAttribute('verified', false)) { + $factors[] = Type::TOTP; + } + if ($user->getAttribute('email', false) && $user->getAttribute('emailVerification', false)) { + $factors[] = Type::EMAIL; + } + if ($user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false)) { + $factors[] = Type::PHONE; + } + $factors = \array_values(\array_unique($factors)); + + $session->setAttribute('factors', $factors); + $dbForProject->updateDocument('sessions', $session->getId(), $session); + } + + $queueForEvents->setParam('userId', $user->getId()); + + $response->dynamic($user, Response::MODEL_ACCOUNT); + } +} diff --git a/src/Appwrite/Platform/Modules/Account/Module.php b/src/Appwrite/Platform/Modules/Account/Module.php new file mode 100644 index 0000000000..3ad50d388a --- /dev/null +++ b/src/Appwrite/Platform/Modules/Account/Module.php @@ -0,0 +1,14 @@ +<?php + +namespace Appwrite\Platform\Modules\Account; + +use Appwrite\Platform\Modules\Account\Services\Http; +use Utopia\Platform; + +class Module extends Platform\Module +{ + public function __construct() + { + $this->addService('http', new Http()); + } +} diff --git a/src/Appwrite/Platform/Modules/Account/Services/Http.php b/src/Appwrite/Platform/Modules/Account/Services/Http.php new file mode 100644 index 0000000000..ae2e841636 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Account/Services/Http.php @@ -0,0 +1,34 @@ +<?php + +namespace Appwrite\Platform\Modules\Account\Services; + +use Appwrite\Platform\Modules\Account\Http\Account\MFA\Authenticators\Create as CreateAuthenticator; +use Appwrite\Platform\Modules\Account\Http\Account\MFA\Authenticators\Delete as DeleteAuthenticator; +use Appwrite\Platform\Modules\Account\Http\Account\MFA\Authenticators\Update as UpdateAuthenticator; +use Appwrite\Platform\Modules\Account\Http\Account\MFA\Challenges\Create as CreateChallenge; +use Appwrite\Platform\Modules\Account\Http\Account\MFA\Challenges\Update as UpdateChallenge; +use Appwrite\Platform\Modules\Account\Http\Account\MFA\Factors\XList as ListFactors; +use Appwrite\Platform\Modules\Account\Http\Account\MFA\RecoveryCodes\Create as CreateRecoveryCodes; +use Appwrite\Platform\Modules\Account\Http\Account\MFA\RecoveryCodes\Get as GetRecoveryCodes; +use Appwrite\Platform\Modules\Account\Http\Account\MFA\RecoveryCodes\Update as UpdateRecoveryCodes; +use Appwrite\Platform\Modules\Account\Http\Account\MFA\Update as UpdateMfa; +use Utopia\Platform\Service; + +class Http extends Service +{ + public function __construct() + { + $this->type = Service::TYPE_HTTP; + $this + ->addAction(UpdateMfa::getName(), new UpdateMfa()) + ->addAction(ListFactors::getName(), new ListFactors()) + ->addAction(CreateAuthenticator::getName(), new CreateAuthenticator()) + ->addAction(UpdateAuthenticator::getName(), new UpdateAuthenticator()) + ->addAction(DeleteAuthenticator::getName(), new DeleteAuthenticator()) + ->addAction(CreateRecoveryCodes::getName(), new CreateRecoveryCodes()) + ->addAction(UpdateRecoveryCodes::getName(), new UpdateRecoveryCodes()) + ->addAction(GetRecoveryCodes::getName(), new GetRecoveryCodes()) + ->addAction(CreateChallenge::getName(), new CreateChallenge()) + ->addAction(UpdateChallenge::getName(), new UpdateChallenge()); + } +} diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index 8ff5d88dd4..ff4766414c 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -13,6 +13,7 @@ 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; @@ -327,6 +328,8 @@ class Base extends Action } } + $this->updateEmptyManualRule($project, $site, $deployment, $dbForPlatform, $authorization); + $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($site) @@ -335,4 +338,34 @@ 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, Authorization $authorization) + { + $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) { + $authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([ + 'deploymentId' => $deployment->getId(), + 'deploymentInternalId' => $deployment->getSequence(), + ]))); + }, $queries); + } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php index b22119fad1..a148e23845 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Action.php @@ -105,4 +105,64 @@ abstract class Action extends UtopiaAction ? Exception::COLLECTION_LIMIT_EXCEEDED : Exception::TABLE_LIMIT_EXCEEDED; } + + /** + * Get the appropriate format unsupported exception. + */ + protected function getFormatUnsupportedException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_FORMAT_UNSUPPORTED + : Exception::COLUMN_FORMAT_UNSUPPORTED; + } + + /** + * Get the correct default unsupported message. + */ + protected function getDefaultUnsupportedException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED + : Exception::COLUMN_DEFAULT_UNSUPPORTED; + } + + /** + * Get the appropriate parent level not found exception. + */ + protected function getParentNotFoundException(): string + { + return $this->isCollectionsAPI() + ? Exception::COLLECTION_NOT_FOUND + : Exception::TABLE_NOT_FOUND; + } + + /** + * Get the correct invalid structure message. + */ + protected function getStructureException(): string + { + return $this->isCollectionsAPI() + ? Exception::DOCUMENT_INVALID_STRUCTURE + : Exception::ROW_INVALID_STRUCTURE; + } + + /** + * Get the exception for unknown attribute/column in index. + */ + protected function getParentUnknownException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_UNKNOWN + : Exception::COLUMN_UNKNOWN; + } + + /** + * Get the exception for invalid attribute/column type in index. + */ + protected function getParentInvalidTypeException(): string + { + return $this->isCollectionsAPI() + ? Exception::ATTRIBUTE_TYPE_INVALID + : Exception::COLUMN_TYPE_INVALID; + } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php index 93812585ea..e130f7a77a 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php @@ -9,7 +9,9 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Deprecated; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Validator\Attributes as AttributesValidator; use Appwrite\Utopia\Database\Validator\CustomId; +use Appwrite\Utopia\Database\Validator\Indexes as IndexesValidator; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; @@ -20,10 +22,13 @@ use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Validator\Authorization; +use Utopia\Database\Validator\Index as IndexValidator; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; +use Utopia\Validator\JSON; use Utopia\Validator\Nullable; use Utopia\Validator\Text; @@ -75,6 +80,8 @@ class Create extends Action ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) + ->param('attributes', [], new ArrayList(new JSON(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.', true) + ->param('indexes', [], new ArrayList(new JSON(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of attribute keys), orders (array of ASC/DESC, optional), and lengths (array of integers, optional).', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') @@ -82,7 +89,7 @@ class Create extends Action ->callback($this->action(...)); } - public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void + public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, array $attributes, array $indexes, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void { $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -114,20 +121,122 @@ class Create extends Action throw new Exception(Exception::DATABASE_NOT_FOUND); } + $collectionKey = 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(); + $databaseKey = 'database_' . $database->getSequence(); + + $attributesValidator = new AttributesValidator( + APP_LIMIT_ARRAY_PARAMS_SIZE, + $dbForProject->getAdapter()->getSupportForSpatialAttributes() + ); + + if (!$attributesValidator->isValid($attributes)) { + $dbForProject->deleteDocument($databaseKey, $collection->getId()); + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $attributesValidator->getDescription()); + } + + foreach ($attributes as $attribute) { + if (($attribute['type'] ?? '') === Database::VAR_RELATIONSHIP) { + $dbForProject->deleteDocument($databaseKey, $collection->getId()); + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Relationship attributes cannot be created inline. Use the create relationship endpoint instead.'); + } + } + + $collectionAttributes = []; + $attributeDocuments = []; + try { + foreach ($attributes as $attributeDef) { + $attrDoc = $this->buildAttributeDocument($database, $collection, $attributeDef); + $collectionAttributes[] = $attrDoc['collection']; + $attributeDocuments[] = $attrDoc['document']; + } + } catch (\Throwable $e) { + $dbForProject->deleteDocument($databaseKey, $collection->getId()); + throw $e; + } + + // Validate indexes + $indexesValidator = new IndexesValidator($dbForProject->getLimitForIndexes()); + if (!$indexesValidator->isValid($indexes)) { + $dbForProject->deleteDocument($databaseKey, $collection->getId()); + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $indexesValidator->getDescription()); + } + + $collectionIndexes = []; + $indexDocuments = []; + try { + foreach ($indexes as $indexDef) { + $idxDoc = $this->buildIndexDocument($database, $collection, $indexDef, $collectionAttributes); + $collectionIndexes[] = $idxDoc['collection']; + $indexDocuments[] = $idxDoc['document']; + } + } catch (\Throwable $e) { + $dbForProject->deleteDocument($databaseKey, $collection->getId()); + throw $e; + } + + // Validate indexes with DB adapter capabilities + $indexValidator = new IndexValidator( + $collectionAttributes, + [], + $dbForProject->getAdapter()->getMaxIndexLength(), + $dbForProject->getAdapter()->getInternalIndexesKeys(), + $dbForProject->getAdapter()->getSupportForIndexArray(), + $dbForProject->getAdapter()->getSupportForSpatialIndexNull(), + $dbForProject->getAdapter()->getSupportForSpatialIndexOrder(), + $dbForProject->getAdapter()->getSupportForVectors(), + $dbForProject->getAdapter()->getSupportForAttributes(), + $dbForProject->getAdapter()->getSupportForMultipleFulltextIndexes(), + $dbForProject->getAdapter()->getSupportForIdenticalIndexes() + ); + + foreach ($collectionIndexes as $indexDoc) { + if (!$indexValidator->isValid($indexDoc)) { + $dbForProject->deleteDocument($databaseKey, $collection->getId()); + throw new Exception($this->getInvalidIndexException(), $indexValidator->getDescription()); + } + } + try { $dbForProject->createCollection( - id: 'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), + id: $collectionKey, + attributes: $collectionAttributes, + indexes: $collectionIndexes, permissions: $permissions, documentSecurity: $documentSecurity ); } catch (DuplicateException) { + $dbForProject->deleteDocument($databaseKey, $collection->getId()); throw new Exception($this->getDuplicateException()); - } catch (IndexException) { - throw new Exception($this->getInvalidIndexException()); + } catch (IndexException $e) { + $dbForProject->deleteDocument($databaseKey, $collection->getId()); + throw new Exception($this->getInvalidIndexException(), $e->getMessage()); } catch (LimitException) { + $dbForProject->deleteDocument($databaseKey, $collection->getId()); throw new Exception($this->getLimitException()); + } catch (\Throwable $e) { + $dbForProject->deleteDocument($databaseKey, $collection->getId()); + throw $e; } + // Create documents in attributes and indexes collections + try { + if (!empty($attributeDocuments)) { + $dbForProject->createDocuments('attributes', $attributeDocuments); + } + if (!empty($indexDocuments)) { + $dbForProject->createDocuments('indexes', $indexDocuments); + } + } catch (DuplicateException) { + $this->cleanup($dbForProject, $databaseKey, $collectionKey, $collection->getId()); + throw new Exception($this->getDuplicateException()); + } catch (\Throwable $e) { + $this->cleanup($dbForProject, $databaseKey, $collectionKey, $collection->getId()); + throw $e; + } + + $dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $collection->getId()); + $dbForProject->purgeCachedCollection('database_' . $database->getSequence() . '_collection_' . $collection->getSequence()); + $queueForEvents ->setContext('database', $database) ->setParam('databaseId', $databaseId) @@ -137,4 +246,167 @@ class Create extends Action ->setStatusCode(SwooleResponse::STATUS_CODE_CREATED) ->dynamic($collection, $this->getResponseModel()); } + + /** + * Build attribute Document objects from a definition array + * + * @return array{collection: Document, document: Document} + */ + protected function buildAttributeDocument( + Document $database, + Document $collection, + array $attribute, + ): array { + $key = $attribute['key']; + $type = $attribute['type']; + $size = $attribute['size'] ?? 0; + $required = $attribute['required'] ?? false; + $signed = $attribute['signed'] ?? true; + $array = $attribute['array'] ?? false; + $format = $attribute['format'] ?? ''; + $formatOptions = []; + $filters = $attribute['filters'] ?? []; + $default = $attribute['default'] ?? null; + + if ($format === APP_DATABASE_ATTRIBUTE_ENUM && isset($attribute['elements'])) { + $formatOptions = ['elements' => $attribute['elements']]; + } + + if (isset($attribute['min']) || isset($attribute['max'])) { + $format = $type === Database::VAR_INTEGER + ? APP_DATABASE_ATTRIBUTE_INT_RANGE + : APP_DATABASE_ATTRIBUTE_FLOAT_RANGE; + + $formatOptions = [ + 'min' => $attribute['min'] ?? ($type === Database::VAR_INTEGER ? \PHP_INT_MIN : -\PHP_FLOAT_MAX), + 'max' => $attribute['max'] ?? ($type === Database::VAR_INTEGER ? \PHP_INT_MAX : \PHP_FLOAT_MAX), + ]; + } + + $collectionDoc = new Document([ + '$id' => $key, + 'key' => $key, + 'type' => $type, + 'size' => $size, + 'required' => $required, + 'signed' => $signed, + 'default' => $default, + 'array' => $array, + 'format' => $format, + 'formatOptions' => $formatOptions, + 'filters' => $filters, + ]); + + $document = new Document([ + '$id' => ID::custom($database->getSequence() . '_' . $collection->getSequence() . '_' . $key), + 'key' => $key, + 'databaseInternalId' => $database->getSequence(), + 'databaseId' => $database->getId(), + 'collectionInternalId' => $collection->getSequence(), + 'collectionId' => $collection->getId(), + 'type' => $type, + 'status' => 'available', + 'size' => $size, + 'required' => $required, + 'signed' => $signed, + 'default' => $default, + 'array' => $array, + 'format' => $format, + 'formatOptions' => $formatOptions, + 'filters' => $filters, + ]); + + return [ + 'collection' => $collectionDoc, + 'document' => $document, + ]; + } + + /** + * Build index Document objects from a definition array + * + * @return array{collection: Document, document: Document} + */ + protected function buildIndexDocument(Document $database, Document $collection, array $indexDef, array $attributeDocuments): array + { + $key = $indexDef['key']; + $type = $indexDef['type']; + $indexAttributes = $indexDef['attributes']; + $orders = $indexDef['orders'] ?? []; + $lengths = $indexDef['lengths'] ?? []; + + $attrKeys = array_map(fn ($a) => $a->getAttribute('key'), $attributeDocuments); + + // Build lengths and orders based on attribute properties + foreach ($indexAttributes as $i => $attr) { + $attrIndex = array_search($attr, $attrKeys); + if ($attrIndex !== false) { + $attrDoc = $attributeDocuments[$attrIndex]; + $attrArray = $attrDoc->getAttribute('array', false); + + if (empty($lengths[$i])) { + $lengths[$i] = null; + } + + if ($attrArray === true) { + $lengths[$i] = Database::MAX_ARRAY_INDEX_LENGTH; + $orders[$i] = null; + } + } else { + if (empty($lengths[$i])) { + $lengths[$i] = null; + } + } + } + + $collectionDoc = new Document([ + '$id' => $key, + 'key' => $key, + 'type' => $type, + 'attributes' => $indexAttributes, + 'lengths' => $lengths, + 'orders' => $orders, + ]); + + $document = new Document([ + '$id' => ID::custom($database->getSequence() . '_' . $collection->getSequence() . '_' . $key), + 'key' => $key, + 'status' => 'available', + 'databaseInternalId' => $database->getSequence(), + 'databaseId' => $database->getId(), + 'collectionInternalId' => $collection->getSequence(), + 'collectionId' => $collection->getId(), + 'type' => $type, + 'attributes' => $indexAttributes, + 'lengths' => $lengths, + 'orders' => $orders, + ]); + + return [ + 'collection' => $collectionDoc, + 'document' => $document, + ]; + } + + /** + * Cleanup on failure: delete the collection document and the underlying DB collection + */ + protected function cleanup( + Database $dbForProject, + string $databaseId, + string $collectionId, + string $collectionDocumentId + ): void { + try { + $dbForProject->deleteCollection($collectionId); + } catch (\Throwable) { + // Ignore cleanup errors for collection deletion + } + + try { + $dbForProject->deleteDocument($databaseId, $collectionDocumentId); + } catch (\Throwable) { + // Ignore cleanup errors for document deletion + } + } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index 158a44c1b3..568568c9bb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Attribute; -use Appwrite\Auth\Auth; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -12,6 +11,7 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Deprecated; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response as UtopiaResponse; use InvalidArgumentException; use Utopia\Database\Database; @@ -91,8 +91,8 @@ class Decrement extends Action public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan, Authorization $authorization): void { - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index 9045954789..7c47c10f7c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Attribute; -use Appwrite\Auth\Auth; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -12,6 +11,7 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Deprecated; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response as UtopiaResponse; use InvalidArgumentException; use Utopia\Database\Database; @@ -91,8 +91,8 @@ class Increment extends Action public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan, Authorization $authorization): void { - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index ec3db59668..0d73e8cf47 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; -use Appwrite\Auth\Auth; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -12,6 +11,7 @@ use Appwrite\SDK\Deprecated; use Appwrite\SDK\Method; use Appwrite\SDK\Parameter; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; @@ -180,8 +180,8 @@ class Create extends Action $documents = [$data]; } - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($isBulk && !$isAPIKey && !$isPrivilegedUser) { throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index 93ad7dc2a8..1701695b54 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; -use Appwrite\Auth\Auth; use Appwrite\Databases\TransactionState; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; @@ -12,6 +11,7 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Deprecated; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; @@ -103,8 +103,8 @@ class Delete extends Action ): void { $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index fa89d5fa32..a799f8e2cb 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; -use Appwrite\Auth\Auth; use Appwrite\Databases\TransactionState; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -11,6 +10,7 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Deprecated; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Exception\Query as QueryException; @@ -76,8 +76,8 @@ class Get extends Action public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState, Authorization $authorization): void { - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index 0aca4a08c3..a7ef71172d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; -use Appwrite\Auth\Auth; use Appwrite\Databases\TransactionState; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; @@ -12,6 +11,7 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Deprecated; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; @@ -101,8 +101,8 @@ class Update extends Action $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::DATABASE_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index 5ec455b947..7235ef5784 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; -use Appwrite\Auth\Auth; use Appwrite\Databases\TransactionState; use Appwrite\Event\Event; use Appwrite\Event\StatsUsage; @@ -12,6 +11,7 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Deprecated; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; @@ -107,8 +107,8 @@ class Upsert extends Action throw new Exception($this->getMissingPayloadException()); } - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index 4d48d62d6f..e76f865c08 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents; -use Appwrite\Auth\Auth; use Appwrite\Databases\TransactionState; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; @@ -11,6 +10,7 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Deprecated; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; @@ -83,8 +83,8 @@ class XList extends Action public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, bool $includeTotal, bool $cache, int $ttl, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState,Authorization $authorization): void { - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); $database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) { diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php index 98a0708f66..68f10ba8c2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Operations/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Operations; -use Appwrite\Auth\Auth; use Appwrite\Databases\TransactionState; use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Databases\Http\Databases\Transactions\Action; @@ -10,6 +9,7 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Database\Validator\Operation; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; @@ -74,8 +74,8 @@ class Create extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Operations array cannot be empty'); } - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); // API keys and admins can read any transaction, regular users need permissions $transaction = ($isAPIKey || $isPrivilegedUser) diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index ec5f0e4c04..f28823b252 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions; -use Appwrite\Auth\Auth; use Appwrite\Databases\TransactionState; use Appwrite\Event\Delete; use Appwrite\Event\Event; @@ -12,6 +11,7 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; use Utopia\Database\Document; @@ -112,8 +112,8 @@ class Update extends Action throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Cannot commit and rollback at the same time'); } - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); $transaction = ($isAPIKey || $isPrivilegedUser) ? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId)) @@ -241,13 +241,12 @@ class Update extends Action ->setType(DELETE_TYPE_DOCUMENT) ->setDocument($transaction); }); - } catch (NotFoundException $e) { $authorization->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); throw new Exception(Exception::DOCUMENT_NOT_FOUND, previous: $e); - } catch (DuplicateException|ConflictException $e) { + } catch (DuplicateException | ConflictException $e) { $authorization->skip(fn () => $dbForProject->updateDocument('transactions', $transactionId, new Document([ 'status' => 'failed', ]))); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php index 5b598b1a12..7190b31c27 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php @@ -12,7 +12,9 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; +use Utopia\Validator\JSON; use Utopia\Validator\Nullable; use Utopia\Validator\Text; @@ -60,6 +62,8 @@ class Create extends CollectionCreate ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('rowSecurity', false, new Boolean(true), 'Enables configuring permissions for individual rows. A user needs one of row or table level permissions to access a row. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(), 'Is table enabled? When set to \'disabled\', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.', true) + ->param('columns', [], new ArrayList(new JSON(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of column definitions to create. Each column should contain: key (string), type (string: string, integer, float, boolean, datetime, relationship), size (integer, required for string type), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options.', true) + ->param('indexes', [], new ArrayList(new JSON(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of column keys), orders (array of ASC/DESC, optional), and lengths (array of integers, optional).', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Create.php index 165f014320..e9d82f55ea 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Indexes/Create.php @@ -2,8 +2,6 @@ namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Indexes; -use Appwrite\Event\Database as EventDatabase; -use Appwrite\Event\Event; use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Indexes\Create as IndexCreate; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; @@ -11,7 +9,6 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Database; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; @@ -73,11 +70,4 @@ class Create extends IndexCreate ->callback($this->action(...)); } - public function action(string $databaseId, string $tableId, string $key, string $type, array $columns, array $orders, array $lengths, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void - { - // Map TablesDB parameters to Collections API parameters - // tableId -> collectionId - // columns -> attributes - parent::action($databaseId, $tableId, $key, $type, $columns, $orders, $lengths, $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization); - } } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php index d53324b27f..50a0dc1dcb 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php @@ -17,6 +17,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -88,6 +89,7 @@ class Create extends Action ->inject('deviceForLocal') ->inject('queueForBuilds') ->inject('plan') + ->inject('authorization') ->callback($this->action(...)); } @@ -105,7 +107,8 @@ class Create extends Action Device $deviceForFunctions, Device $deviceForLocal, Build $queueForBuilds, - array $plan + array $plan, + Authorization $authorization ) { $activate = \strval($activate) === 'true' || \strval($activate) === '1'; 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 00c29d6bba..06ae7566f7 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php @@ -15,6 +15,7 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; @@ -77,6 +78,7 @@ class Create extends Base ->inject('project') ->inject('queueForBuilds') ->inject('gitHub') + ->inject('authorization') ->callback($this->action(...)); } @@ -95,7 +97,8 @@ class Create extends Base Event $queueForEvents, Document $project, Build $queueForBuilds, - GitHub $github + GitHub $github, + Authorization $authorization ) { $function = $dbForProject->getDocument('functions', $functionId); @@ -127,7 +130,9 @@ class Create extends Base queueForBuilds: $queueForBuilds, template: $template, github: $github, - activate: $activate + activate: $activate, + referenceType: $type, + reference: $reference ); $queueForEvents @@ -170,6 +175,9 @@ class Create extends Base ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); + + $this->updateEmptyManualRule($project, $function, $deployment, $dbForPlatform, $authorization); + $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 0ad9852722..f9e0f72cdc 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php @@ -87,7 +87,7 @@ class Create extends Base Document $project, Event $queueForEvents, Build $queueForBuilds, - GitHub $github + GitHub $github, ) { $function = $dbForProject->getDocument('functions', $functionId); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php index 593c3dde55..99b57b570d 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Modules\Functions\Http\Executions; use Ahc\Jwt\JWT; -use Appwrite\Auth\Auth; use Appwrite\Event\Event; use Appwrite\Event\Func; use Appwrite\Event\StatsUsage; @@ -15,9 +14,12 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response; use Executor\Executor; use MaxMind\Db\Reader; +use Utopia\Auth\Proofs\Token; +use Utopia\Auth\Store; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; @@ -94,6 +96,8 @@ class Create extends Base ->inject('queueForStatsUsage') ->inject('queueForFunctions') ->inject('geodb') + ->inject('store') + ->inject('proofForToken') ->inject('executor') ->inject('authorization') ->callback($this->action(...)); @@ -117,6 +121,8 @@ class Create extends Base StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, + Store $store, + Token $proofForToken, Executor $executor, Authorization $authorization ) { @@ -158,8 +164,8 @@ class Create extends Base $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); @@ -200,7 +206,7 @@ class Create extends Base foreach ($sessions as $session) { /** @var Utopia\Database\Document $session */ - if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + if ($proofForToken->verify($store->getProperty('secret', ''), $session->getAttribute('secret'))) { // Find most recent active session for user ID and JWT headers $current = $session; } } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php index 69c4080f8a..a6cfff81a9 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Get.php @@ -2,12 +2,12 @@ namespace Appwrite\Platform\Modules\Functions\Http\Executions; -use Appwrite\Auth\Auth; use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Compute\Base; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\Validator\Authorization; @@ -65,8 +65,8 @@ class Get extends Base ) { $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php index 52562aadf8..460e361f15 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/XList.php @@ -2,12 +2,12 @@ namespace Appwrite\Platform\Modules\Functions\Http\Executions; -use Appwrite\Auth\Auth; use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Compute\Base; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Database\Validator\Queries\Executions; use Appwrite\Utopia\Response; use Utopia\Database\Database; @@ -74,8 +74,8 @@ class XList extends Base ) { $function = $authorization->skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($function->isEmpty() || (!$function->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::FUNCTION_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index 182f39e274..95a1f2d365 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -317,6 +317,7 @@ class Create extends Base template: $template, github: $github, activate: true, + authorization: $authorization, reference: $providerBranch, referenceType: 'branch' ); diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index 0a7a35d2e8..6147cacfd7 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -1106,10 +1106,6 @@ class Builds extends Action $resource = $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document(['latestDeploymentStatus' => $deployment->getAttribute('status', '')])); } - $queueForRealtime - ->setPayload($deployment->getArrayCopy()) - ->trigger(); - if ($isVcsEnabled) { $this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform, $queueForRealtime); } @@ -1197,6 +1193,11 @@ class Builds extends Action Console::log('Deployment activated'); } + // Send realtime event after updating the associated resource so that Console will have the resource's deployment details when re-fetching. + $queueForRealtime + ->setPayload($deployment->getArrayCopy()) + ->trigger(); + if ($resource->getCollection() === 'sites') { // VCS branch $branchName = $deployment->getAttribute('providerBranch'); diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php index 107c7b1389..8c2ca2d878 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php @@ -367,6 +367,8 @@ class Create extends Action } } + + $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 4da3e9b34f..4f35104ed2 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php @@ -211,6 +211,8 @@ class Create extends Base ])) ); + $this->updateEmptyManualRule($project, $site, $deployment, $dbForPlatform, $authorization); + $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($site) diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Action.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Action.php index e05418b90d..5f1bd55788 100644 --- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Action.php +++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Action.php @@ -2,8 +2,8 @@ namespace Appwrite\Platform\Modules\Tokens\Http\Tokens\Buckets\Files; -use Appwrite\Auth\Auth; use Appwrite\Extend\Exception; +use Appwrite\Utopia\Database\Documents\User; use Utopia\Database\Database; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Authorization\Input; @@ -15,8 +15,8 @@ class Action extends UtopiaAction { $bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser($authorization->getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser($authorization->getRoles()); + $isAPIKey = User::isApp($authorization->getRoles()); + $isPrivilegedUser = User::isPrivileged($authorization->getRoles()); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php index 4760fef97f..6cbaeaa915 100644 --- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php +++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Modules\Tokens\Http\Tokens\Buckets\Files; -use Appwrite\Auth\Auth; use Appwrite\Event\Event; use Appwrite\Extend\Exception; use Appwrite\SDK\AuthType; @@ -10,6 +9,7 @@ use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; +use Utopia\Auth\Proofs\Token; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Helpers\ID; @@ -92,7 +92,7 @@ class Create extends Action $token = $dbForProject->createDocument('resourceTokens', new Document([ '$id' => ID::unique(), - 'secret' => Auth::tokenGenerator(128), + 'secret' => (new Token(128))->generate(), 'resourceId' => $bucketId . ':' . $fileId, 'resourceInternalId' => $bucket->getSequence() . ':' . $file->getSequence(), 'resourceType' => TOKENS_RESOURCE_TYPE_FILES, diff --git a/src/Appwrite/Platform/Tasks/Install.php b/src/Appwrite/Platform/Tasks/Install.php index c3b4e33593..b210a020b9 100644 --- a/src/Appwrite/Platform/Tasks/Install.php +++ b/src/Appwrite/Platform/Tasks/Install.php @@ -2,10 +2,11 @@ namespace Appwrite\Platform\Tasks; -use Appwrite\Auth\Auth; use Appwrite\Docker\Compose; use Appwrite\Docker\Env; use Appwrite\Utopia\View; +use Utopia\Auth\Proofs\Password; +use Utopia\Auth\Proofs\Token; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Platform\Action; @@ -149,6 +150,8 @@ class Install extends Action $input = []; + $password = new Password(); + $token = new Token(); foreach ($vars as $var) { if (!empty($var['filter']) && ($interactive !== 'Y' || !Console::isInteractive())) { if ($data && $var['default'] !== null) { @@ -157,12 +160,12 @@ class Install extends Action } if ($var['filter'] === 'token') { - $input[$var['name']] = Auth::tokenGenerator(); + $input[$var['name']] = $token->generate(); continue; } if ($var['filter'] === 'password') { - $input[$var['name']] = Auth::passwordGenerator(); + $input[$var['name']] = $password->generate(); continue; } } diff --git a/src/Appwrite/Platform/Tasks/StatsResources.php b/src/Appwrite/Platform/Tasks/StatsResources.php index 87d4858351..6d04d2109a 100644 --- a/src/Appwrite/Platform/Tasks/StatsResources.php +++ b/src/Appwrite/Platform/Tasks/StatsResources.php @@ -44,7 +44,6 @@ class StatsResources extends Action ->inject('dbForPlatform') ->inject('logError') ->inject('queueForStatsResources') - ->inject('authorization') ->callback($this->action(...)); } diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index a88e2e641f..be542e7811 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Workers; -use Appwrite\Auth\Auth; use Exception; use Throwable; use Utopia\Audit\Audit; @@ -85,7 +84,7 @@ class Audits extends Action $userName = $user->getAttribute('name', ''); $userEmail = $user->getAttribute('email', ''); - $userType = $user->getAttribute('type', Auth::ACTIVITY_TYPE_USER); + $userType = $user->getAttribute('type', ACTIVITY_TYPE_USER); // Create event data $eventData = [ diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 808adabb24..4cd4819dfa 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -2,7 +2,6 @@ namespace Appwrite\Platform\Workers; -use Appwrite\Auth\Auth; use Appwrite\Certificates\Adapter as CertificatesAdapter; use Appwrite\Deletes\Identities; use Appwrite\Deletes\Targets; @@ -708,7 +707,7 @@ class Deletes extends Action private function deleteExpiredSessions(Document $project, callable $getProjectDB): void { $dbForProject = $getProjectDB($project); - $duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG; + $duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG; $expired = DateTime::addSeconds(new \DateTime(), -1 * $duration); // Delete Sessions diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index 08782fd825..afe63ec4cc 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -414,7 +414,7 @@ class Migrations extends Action $source?->success(); if ($migration->getAttribute('destination') === DestinationCSV::getName()) { - $this->handleCSVExportComplete($project, $migration, $queueForMails, $authorization); + $this->handleCSVExportComplete($project, $migration, $queueForMails, $queueForRealtime, $authorization); } } } @@ -436,7 +436,8 @@ class Migrations extends Action Document $project, Document $migration, Mail $queueForMails, - Authorization $authorization, + Realtime $queueForRealtime, + Authorization $authorization ): void { $options = $migration->getAttribute('options', []); $bucketId = 'default'; // Always use platform default bucket @@ -528,12 +529,16 @@ class Migrations extends Action 'fileId' => $fileId, 'projectId' => $project->getId(), 'internal' => true, + 'disposition' => 'attachment', ]); // Generate download URL with JWT $endpoint = System::getEnv('_APP_DOMAIN', ''); $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled' ? 'https' : 'http'; $downloadUrl = "{$protocol}://{$endpoint}/v1/storage/buckets/{$bucketId}/files/{$fileId}/push?project={$project->getId()}&jwt={$jwt}"; + $options['downloadUrl'] = $downloadUrl; + $migration->setAttribute('options', $options); + $this->updateMigrationDocument($migration, $project, $queueForRealtime); $this->sendCSVEmail( success: true, diff --git a/src/Appwrite/SDK/Specification/Format.php b/src/Appwrite/SDK/Specification/Format.php index c687df143a..aad832a9f4 100644 --- a/src/Appwrite/SDK/Specification/Format.php +++ b/src/Appwrite/SDK/Specification/Format.php @@ -171,6 +171,12 @@ abstract class Format return 'CreditCard'; case 'getFlag': return 'Flag'; + case 'getScreenshot': + switch ($param) { + case 'permissions': + return 'BrowserPermission'; + } + break; } break; case 'databases': @@ -263,7 +269,13 @@ abstract class Format case 'createVcsDeployment': switch ($param) { case 'type': - return 'VCSDeploymentType'; + return 'VCSReferenceType'; + } + break; + case 'createTemplateDeployment': + switch ($param) { + case 'type': + return 'TemplateReferenceType'; } break; } @@ -286,7 +298,13 @@ abstract class Format case 'createVcsDeployment': switch ($param) { case 'type': - return 'VCSDeploymentType'; + return 'VCSReferenceType'; + } + break; + case 'createTemplateDeployment': + switch ($param) { + case 'type': + return 'TemplateReferenceType'; } break; } diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index 0ab16d306b..bfa33e19f3 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -19,7 +19,6 @@ use Utopia\Validator; use Utopia\Validator\ArrayList; use Utopia\Validator\Nullable; use Utopia\Validator\Range; -use Utopia\Validator\WhiteList; class OpenAPI3 extends Format { @@ -440,6 +439,7 @@ class OpenAPI3 extends Format $subclass = \get_class($validator->getValidator()); switch ($subclass) { case 'Appwrite\Utopia\Database\Validator\Operation': + case 'Utopia\Validator\WhiteList': $class = $subclass; break; } @@ -449,23 +449,23 @@ class OpenAPI3 extends Format case 'Utopia\Database\Validator\UID': case 'Utopia\Validator\Text': $node['schema']['type'] = $validator->getType(); - $node['schema']['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; + $node['schema']['x-example'] = ($param['example'] ?? '') ?: '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; break; case 'Utopia\Validator\Boolean': $node['schema']['type'] = $validator->getType(); - $node['schema']['x-example'] = false; + $node['schema']['x-example'] = ($param['example'] ?? '') ?: false; break; case 'Appwrite\Utopia\Database\Validator\CustomId': if ($sdk->getType() === MethodType::UPLOAD) { $node['schema']['x-upload-id'] = true; } $node['schema']['type'] = $validator->getType(); - $node['schema']['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; + $node['schema']['x-example'] = ($param['example'] ?? '') ?: '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; break; case 'Utopia\Database\Validator\DatetimeValidator': $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'datetime'; - $node['schema']['x-example'] = Model::TYPE_DATETIME_EXAMPLE; + $node['schema']['x-example'] = ($param['example'] ?? '') ?: Model::TYPE_DATETIME_EXAMPLE; break; case 'Utopia\Database\Validator\Spatial': /** @var Spatial $validator */ @@ -475,7 +475,7 @@ class OpenAPI3 extends Format ['type' => 'array'] ] ]; - $node['schema']['x-example'] = match ($validator->getSpatialType()) { + $node['schema']['x-example'] = ($param['example'] ?? '') ?: match ($validator->getSpatialType()) { Database::VAR_POINT => '[1, 2]', Database::VAR_LINESTRING => '[[1, 2], [3, 4], [5, 6]]', Database::VAR_POLYGON => '[[[1, 2], [3, 4], [5, 6], [1, 2]]]', @@ -484,14 +484,14 @@ class OpenAPI3 extends Format case 'Appwrite\Network\Validator\Email': $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'email'; - $node['schema']['x-example'] = 'email@example.com'; + $node['schema']['x-example'] = ($param['example'] ?? '') ?: 'email@example.com'; break; case 'Utopia\Validator\Host': case 'Utopia\Validator\URL': case 'Appwrite\Network\Validator\Redirect': $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'url'; - $node['schema']['x-example'] = 'https://example.com'; + $node['schema']['x-example'] = ($param['example'] ?? '') ?: 'https://example.com'; break; case 'Utopia\Validator\JSON': case 'Utopia\Validator\Mock': @@ -511,6 +511,9 @@ class OpenAPI3 extends Format $node['schema']['items'] = [ 'type' => $validator->getValidator()->getType(), ]; + if (!empty($param['example'])) { + $node['schema']['x-example'] = $param['example']; + } break; case 'Appwrite\Utopia\Database\Validator\Queries\Base': case 'Appwrite\Utopia\Database\Validator\Queries\Columns': @@ -551,73 +554,115 @@ class OpenAPI3 extends Format $node['schema']['items'] = [ 'type' => 'string', ]; - $node['schema']['x-example'] = '["' . Permission::read(Role::any()) . '"]'; + $node['schema']['x-example'] = ($param['example'] ?? '') ?: '["' . Permission::read(Role::any()) . '"]'; break; case 'Utopia\Database\Validator\Roles': $node['schema']['type'] = $validator->getType(); $node['schema']['items'] = [ 'type' => 'string', ]; - $node['schema']['x-example'] = '["' . Role::any()->toString() . '"]'; + $node['schema']['x-example'] = ($param['example'] ?? '') ?: '["' . Role::any()->toString() . '"]'; break; case 'Appwrite\Auth\Validator\Password': $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'password'; - $node['schema']['x-example'] = 'password'; + $node['schema']['x-example'] = ($param['example'] ?? '') ?: 'password'; break; case 'Appwrite\Auth\Validator\Phone': $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'phone'; - $node['schema']['x-example'] = '+12065550100'; // In the US, 555 is reserved like example.com + $node['schema']['x-example'] = ($param['example'] ?? '') ?: '+12065550100'; // In the US, 555 is reserved like example.com break; case 'Utopia\Validator\Range': /** @var Range $validator */ $node['schema']['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType(); $node['schema']['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float'; - $node['schema']['x-example'] = $validator->getMin(); + $node['schema']['x-example'] = ($param['example'] ?? '') ?: $validator->getMin(); break; case 'Utopia\Validator\Integer': $node['schema']['type'] = $validator->getType(); $node['schema']['format'] = 'int32'; + if (!empty($param['example'])) { + $node['schema']['x-example'] = $param['example']; + } break; case 'Utopia\Validator\Numeric': case 'Utopia\Validator\FloatValidator': $node['schema']['type'] = 'number'; $node['schema']['format'] = 'float'; + if (!empty($param['example'])) { + $node['schema']['x-example'] = $param['example']; + } break; case 'Utopia\Validator\Length': $node['schema']['type'] = $validator->getType(); + if (!empty($param['example'])) { + $node['schema']['x-example'] = $param['example']; + } break; case 'Utopia\Validator\WhiteList': - /** @var WhiteList $validator */ - $node['schema']['type'] = $validator->getType(); - $node['schema']['x-example'] = $validator->getList()[0]; + if ($array) { + $validator = $validator->getValidator(); - // Iterate from the blackList. If it matches with the current one, then it is a blackList - // Do not add the enum - $allowed = true; - foreach ($this->enumBlacklist as $blacklist) { - if ( - $blacklist['namespace'] == $sdk->getNamespace() - && $blacklist['method'] == $methodName - && $blacklist['parameter'] == $name - ) { - $allowed = false; - break; + $node['schema']['type'] = 'array'; + $node['schema']['items'] = [ + 'type' => $validator->getType(), + ]; + if (!empty($param['example'])) { + $node['schema']['x-example'] = $param['example']; + } + + // Iterate from the blackList. If it matches with the current one, then it is a blackList + // Do not add the enum + $allowed = true; + foreach ($this->enumBlacklist as $blacklist) { + if ( + $blacklist['namespace'] == $sdk->getNamespace() + && $blacklist['method'] == $methodName + && $blacklist['parameter'] == $name + ) { + $allowed = false; + break; + } + } + if ($allowed && $validator->getType() === 'string') { + $node['schema']['items']['enum'] = \array_values($validator->getList()); + $node['schema']['items']['x-enum-name'] = $this->getRequestEnumName($sdk->getNamespace() ?? '', $methodName, $name); + $node['schema']['items']['x-enum-keys'] = $this->getRequestEnumKeys($sdk->getNamespace() ?? '', $methodName, $name); + } + if ($validator->getType() === 'integer') { + $node['schema']['items']['format'] = 'int32'; + } + } else { + $node['schema']['type'] = $validator->getType(); + $node['schema']['x-example'] = ($param['example'] ?? '') ?: $validator->getList()[0]; + + // Iterate from the blackList. If it matches with the current one, then it is a blackList + // Do not add the enum + $allowed = true; + foreach ($this->enumBlacklist as $blacklist) { + if ( + $blacklist['namespace'] == $sdk->getNamespace() + && $blacklist['method'] == $methodName + && $blacklist['parameter'] == $name + ) { + $allowed = false; + break; + } + } + if ($allowed && $validator->getType() === 'string') { + $node['schema']['enum'] = \array_values($validator->getList()); + $node['schema']['x-enum-name'] = $this->getRequestEnumName($sdk->getNamespace() ?? '', $methodName, $name); + $node['schema']['x-enum-keys'] = $this->getRequestEnumKeys($sdk->getNamespace() ?? '', $methodName, $name); + } + if ($validator->getType() === 'integer') { + $node['format'] = 'int32'; } - } - if ($allowed && $validator->getType() === 'string') { - $node['schema']['enum'] = $validator->getList(); - $node['schema']['x-enum-name'] = $this->getRequestEnumName($sdk->getNamespace() ?? '', $methodName, $name); - $node['schema']['x-enum-keys'] = $this->getRequestEnumKeys($sdk->getNamespace() ?? '', $methodName, $name); - } - if ($validator->getType() === 'integer') { - $node['format'] = 'int32'; } break; case 'Appwrite\Utopia\Database\Validator\CompoundUID': $node['schema']['type'] = $validator->getType(); - $node['schema']['x-example'] = '<ID1:ID2>'; + $node['schema']['x-example'] = ($param['example'] ?? '') ?: '<ID1:ID2>'; break; case 'Appwrite\Utopia\Database\Validator\Operation': if ($array) { @@ -633,22 +678,29 @@ class OpenAPI3 extends Format } else { $node['schema']['type'] = 'object'; } - $example = [ - 'action' => 'create', - 'databaseId' => '<DATABASE_ID>', - $collectionIdKey => '<'.\strtoupper(Template::fromCamelCaseToSnake($collectionIdKey)).'>', - $documentIdKey => '<'.\strtoupper(Template::fromCamelCaseToSnake($documentIdKey)).'>', - 'data' => [ - 'name' => 'Walter O\'Brien', - ], - ]; - if ($array) { - $example = [$example]; + if (empty($param['example'])) { + $example = [ + 'action' => 'create', + 'databaseId' => '<DATABASE_ID>', + $collectionIdKey => '<'.\strtoupper(Template::fromCamelCaseToSnake($collectionIdKey)).'>', + $documentIdKey => '<'.\strtoupper(Template::fromCamelCaseToSnake($documentIdKey)).'>', + 'data' => [ + 'name' => 'Walter O\'Brien', + ], + ]; + if ($array) { + $example = [$example]; + } + $node['schema']['x-example'] = \str_replace("\n", "\n\t", \json_encode($example, JSON_PRETTY_PRINT)); + } else { + $node['schema']['x-example'] = $param['example']; } - $node['schema']['x-example'] = \str_replace("\n", "\n\t", \json_encode($example, JSON_PRETTY_PRINT)); break; default: $node['schema']['type'] = 'string'; + if (!empty($param['example'])) { + $node['schema']['x-example'] = $param['example']; + } break; } @@ -853,13 +905,13 @@ class OpenAPI3 extends Format } if ($rule['type'] === 'enum' && !empty($rule['enum'])) { if ($rule['array']) { - $output['components']['schemas'][$model->getType()]['properties'][$name]['items']['enum'] = $rule['enum']; + $output['components']['schemas'][$model->getType()]['properties'][$name]['items']['enum'] = \array_values($rule['enum']); $enumName = $this->getResponseEnumName($model->getType(), $name); if ($enumName) { $output['components']['schemas'][$model->getType()]['properties'][$name]['items']['x-enum-name'] = $enumName; } } else { - $output['components']['schemas'][$model->getType()]['properties'][$name]['enum'] = $rule['enum']; + $output['components']['schemas'][$model->getType()]['properties'][$name]['enum'] = \array_values($rule['enum']); $enumName = $this->getResponseEnumName($model->getType(), $name); if ($enumName) { $output['components']['schemas'][$model->getType()]['properties'][$name]['x-enum-name'] = $enumName; diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index 7606c48750..b0deb034b4 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -20,7 +20,6 @@ use Utopia\Validator; use Utopia\Validator\ArrayList; use Utopia\Validator\Nullable; use Utopia\Validator\Range; -use Utopia\Validator\WhiteList; class Swagger2 extends Format { @@ -445,6 +444,7 @@ class Swagger2 extends Format $subclass = \get_class($validator->getValidator()); switch ($subclass) { case 'Appwrite\Utopia\Database\Validator\Operation': + case 'Utopia\Validator\WhiteList': $class = $subclass; break; } @@ -454,23 +454,23 @@ class Swagger2 extends Format case 'Utopia\Validator\Text': case 'Utopia\Database\Validator\UID': $node['type'] = $validator->getType(); - $node['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; + $node['x-example'] = ($param['example'] ?? '') ?: '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; break; case 'Utopia\Validator\Boolean': $node['type'] = $validator->getType(); - $node['x-example'] = false; + $node['x-example'] = ($param['example'] ?? '') ?: false; break; case 'Appwrite\Utopia\Database\Validator\CustomId': if ($sdk->getType() === MethodType::UPLOAD) { $node['x-upload-id'] = true; } $node['type'] = $validator->getType(); - $node['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; + $node['x-example'] = ($param['example'] ?? '') ?: '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; break; case 'Utopia\Database\Validator\DatetimeValidator': $node['type'] = $validator->getType(); $node['format'] = 'datetime'; - $node['x-example'] = Model::TYPE_DATETIME_EXAMPLE; + $node['x-example'] = ($param['example'] ?? '') ?: Model::TYPE_DATETIME_EXAMPLE; break; case 'Utopia\Database\Validator\Spatial': /** @var Spatial $validator */ @@ -480,7 +480,7 @@ class Swagger2 extends Format ['type' => 'array'] ] ]; - $node['x-example'] = match ($validator->getSpatialType()) { + $node['x-example'] = ($param['example'] ?? '') ?: match ($validator->getSpatialType()) { Database::VAR_POINT => '[1, 2]', Database::VAR_LINESTRING => '[[1, 2], [3, 4], [5, 6]]', Database::VAR_POLYGON => '[[[1, 2], [3, 4], [5, 6], [1, 2]]]', @@ -489,14 +489,14 @@ class Swagger2 extends Format case 'Appwrite\Network\Validator\Email': $node['type'] = $validator->getType(); $node['format'] = 'email'; - $node['x-example'] = 'email@example.com'; + $node['x-example'] = ($param['example'] ?? '') ?: 'email@example.com'; break; case 'Utopia\Validator\Host': case 'Utopia\Validator\URL': case 'Appwrite\Network\Validator\Redirect': $node['type'] = $validator->getType(); $node['format'] = 'url'; - $node['x-example'] = 'https://example.com'; + $node['x-example'] = ($param['example'] ?? '') ?: 'https://example.com'; break; case 'Utopia\Validator\ArrayList': /** @var ArrayList $validator */ @@ -505,6 +505,9 @@ class Swagger2 extends Format $node['items'] = [ 'type' => $validator->getValidator()->getType(), ]; + if (!empty($param['example'])) { + $node['x-example'] = $param['example']; + } break; case 'Utopia\Validator\JSON': case 'Utopia\Validator\Mock': @@ -539,7 +542,7 @@ class Swagger2 extends Format $node['items'] = [ 'type' => 'string', ]; - $node['x-example'] = '["' . Permission::read(Role::any()) . '"]'; + $node['x-example'] = ($param['example'] ?? '') ?: '["' . Permission::read(Role::any()) . '"]'; break; case 'Utopia\Database\Validator\Roles': $node['type'] = $validator->getType(); @@ -547,61 +550,99 @@ class Swagger2 extends Format $node['items'] = [ 'type' => 'string', ]; - $node['x-example'] = '["' . Role::any()->toString() . '"]'; + $node['x-example'] = ($param['example'] ?? '') ?: '["' . Role::any()->toString() . '"]'; break; case 'Appwrite\Auth\Validator\Password': $node['type'] = $validator->getType(); $node['format'] = 'password'; - $node['x-example'] = 'password'; + $node['x-example'] = ($param['example'] ?? '') ?: 'password'; break; case 'Appwrite\Auth\Validator\Phone': $node['type'] = $validator->getType(); $node['format'] = 'phone'; - $node['x-example'] = '+12065550100'; + $node['x-example'] = ($param['example'] ?? '') ?: '+12065550100'; break; case 'Utopia\Validator\Range': /** @var Range $validator */ $node['type'] = $validator->getType() === Validator::TYPE_FLOAT ? 'number' : $validator->getType(); $node['format'] = $validator->getType() == Validator::TYPE_INTEGER ? 'int32' : 'float'; - $node['x-example'] = $validator->getMin(); + $node['x-example'] = ($param['example'] ?? '') ?: $validator->getMin(); break; case 'Utopia\Validator\Integer': $node['type'] = $validator->getType(); $node['format'] = 'int32'; + if (!empty($param['example'])) { + $node['x-example'] = $param['example']; + } break; case 'Utopia\Validator\Numeric': case 'Utopia\Validator\FloatValidator': $node['type'] = 'number'; $node['format'] = 'float'; + if (!empty($param['example'])) { + $node['x-example'] = $param['example']; + } break; case 'Utopia\Validator\Length': $node['type'] = $validator->getType(); + if (!empty($param['example'])) { + $node['x-example'] = $param['example']; + } break; case 'Utopia\Validator\WhiteList': - /** @var WhiteList $validator */ - $node['type'] = $validator->getType(); - $node['x-example'] = $validator->getList()[0]; + if ($array) { + $validator = $validator->getValidator(); - // Iterate the blackList. If it matches with the current one, then it is blackListed - $allowed = true; - foreach ($this->enumBlacklist as $blacklist) { - if ($blacklist['namespace'] == $namespace && $blacklist['method'] == $methodName && $blacklist['parameter'] == $name) { - $allowed = false; - break; + $node['type'] = 'array'; + $node['collectionFormat'] = 'multi'; + $node['items'] = [ + 'type' => $validator->getType(), + ]; + if (!empty($param['example'])) { + $node['x-example'] = $param['example']; + } + + // Iterate the blackList. If it matches with the current one, then it is blackListed + $allowed = true; + foreach ($this->enumBlacklist as $blacklist) { + if ($blacklist['namespace'] == $namespace && $blacklist['method'] == $methodName && $blacklist['parameter'] == $name) { + $allowed = false; + break; + } + } + if ($allowed && $validator->getType() === 'string') { + $node['items']['enum'] = \array_values($validator->getList()); + $node['items']['x-enum-name'] = $this->getRequestEnumName($namespace, $methodName, $name); + $node['items']['x-enum-keys'] = $this->getRequestEnumKeys($namespace, $methodName, $name); + } + if ($validator->getType() === 'integer') { + $node['items']['format'] = 'int32'; + } + } else { + $node['type'] = $validator->getType(); + $node['x-example'] = ($param['example'] ?? '') ?: $validator->getList()[0]; + + // Iterate the blackList. If it matches with the current one, then it is blackListed + $allowed = true; + foreach ($this->enumBlacklist as $blacklist) { + if ($blacklist['namespace'] == $namespace && $blacklist['method'] == $methodName && $blacklist['parameter'] == $name) { + $allowed = false; + break; + } + } + if ($allowed && $validator->getType() === 'string') { + $node['enum'] = \array_values($validator->getList()); + $node['x-enum-name'] = $this->getRequestEnumName($namespace, $methodName, $name); + $node['x-enum-keys'] = $this->getRequestEnumKeys($namespace, $methodName, $name); + } + if ($validator->getType() === 'integer') { + $node['format'] = 'int32'; } - } - if ($allowed && $validator->getType() === 'string') { - $node['enum'] = $validator->getList(); - $node['x-enum-name'] = $this->getRequestEnumName($namespace, $methodName, $name); - $node['x-enum-keys'] = $this->getRequestEnumKeys($namespace, $methodName, $name); - } - if ($validator->getType() === 'integer') { - $node['format'] = 'int32'; } break; case 'Appwrite\Utopia\Database\Validator\CompoundUID': $node['type'] = $validator->getType(); - $node['x-example'] = '<ID1:ID2>'; + $node['x-example'] = ($param['example'] ?? '') ?: '<ID1:ID2>'; break; case 'Appwrite\Utopia\Database\Validator\Operation': if ($array) { @@ -618,22 +659,29 @@ class Swagger2 extends Format } else { $node['type'] = 'object'; } - $example = [ - 'action' => 'create', - 'databaseId' => '<DATABASE_ID>', - $collectionIdKey => '<'.\strtoupper(Template::fromCamelCaseToSnake($collectionIdKey)).'>', - $documentIdKey => '<'.\strtoupper(Template::fromCamelCaseToSnake($documentIdKey)).'>', - 'data' => [ - 'name' => 'Walter O\'Brien', - ], - ]; - if ($array) { - $example = [$example]; + if (empty($param['example'])) { + $example = [ + 'action' => 'create', + 'databaseId' => '<DATABASE_ID>', + $collectionIdKey => '<'.\strtoupper(Template::fromCamelCaseToSnake($collectionIdKey)).'>', + $documentIdKey => '<'.\strtoupper(Template::fromCamelCaseToSnake($documentIdKey)).'>', + 'data' => [ + 'name' => 'Walter O\'Brien', + ], + ]; + if ($array) { + $example = [$example]; + } + $node['x-example'] = \str_replace("\n", "\n\t", \json_encode($example, JSON_PRETTY_PRINT)); + } else { + $node['x-example'] = $param['example']; } - $node['x-example'] = \str_replace("\n", "\n\t", \json_encode($example, JSON_PRETTY_PRINT)); break; default: $node['type'] = 'string'; + if (!empty($param['example'])) { + $node['x-example'] = $param['example']; + } break; } @@ -853,13 +901,13 @@ class Swagger2 extends Format } if ($rule['type'] === 'enum' && !empty($rule['enum'])) { if ($rule['array']) { - $output['definitions'][$model->getType()]['properties'][$name]['items']['enum'] = $rule['enum']; + $output['definitions'][$model->getType()]['properties'][$name]['items']['enum'] = \array_values($rule['enum']); $enumName = $this->getResponseEnumName($model->getType(), $name); if ($enumName) { $output['definitions'][$model->getType()]['properties'][$name]['items']['x-enum-name'] = $enumName; } } else { - $output['definitions'][$model->getType()]['properties'][$name]['enum'] = $rule['enum']; + $output['definitions'][$model->getType()]['properties'][$name]['enum'] = \array_values($rule['enum']); $enumName = $this->getResponseEnumName($model->getType(), $name); if ($enumName) { $output['definitions'][$model->getType()]['properties'][$name]['x-enum-name'] = $enumName; diff --git a/src/Appwrite/Utopia/Database/Documents/User.php b/src/Appwrite/Utopia/Database/Documents/User.php new file mode 100644 index 0000000000..cbd22aaee5 --- /dev/null +++ b/src/Appwrite/Utopia/Database/Documents/User.php @@ -0,0 +1,181 @@ +<?php + +namespace Appwrite\Utopia\Database\Documents; + +use Utopia\Auth\Proof; +use Utopia\Auth\Proofs\Token; +use Utopia\Database\DateTime; +use Utopia\Database\Document; +use Utopia\Database\Helpers\Role; +use Utopia\Database\Validator\Roles; + +class User extends Document +{ + public const ROLE_ANY = 'any'; + public const ROLE_GUESTS = 'guests'; + public const ROLE_USERS = 'users'; + public const ROLE_ADMIN = 'admin'; + public const ROLE_DEVELOPER = 'developer'; + public const ROLE_OWNER = 'owner'; + public const ROLE_APPS = 'apps'; + public const ROLE_SYSTEM = 'system'; + + public function getEmail(): ?string + { + return $this->getAttribute('email'); + } + + public function getPhone(): ?string + { + return $this->getAttribute('phone'); + } + + /** + * Returns all roles for a user. + * + * @return array<string> + */ + public function getRoles($authorization): array + { + $roles = []; + + if (!$this->isPrivileged($authorization->getRoles()) && !$this->isApp($authorization->getRoles())) { + if ($this->getId()) { + $roles[] = Role::user($this->getId())->toString(); + $roles[] = Role::users()->toString(); + + $emailVerified = $this->getAttribute('emailVerification', false); + $phoneVerified = $this->getAttribute('phoneVerification', false); + + if ($emailVerified || $phoneVerified) { + $roles[] = Role::user($this->getId(), Roles::DIMENSION_VERIFIED)->toString(); + $roles[] = Role::users(Roles::DIMENSION_VERIFIED)->toString(); + } else { + $roles[] = Role::user($this->getId(), Roles::DIMENSION_UNVERIFIED)->toString(); + $roles[] = Role::users(Roles::DIMENSION_UNVERIFIED)->toString(); + } + } else { + return [Role::guests()->toString()]; + } + } + + foreach ($this->getAttribute('memberships', []) as $node) { + if (!isset($node['confirm']) || !$node['confirm']) { + continue; + } + + if (isset($node['$id']) && isset($node['teamId'])) { + $roles[] = Role::team($node['teamId'])->toString(); + $roles[] = Role::member($node['$id'])->toString(); + + if (isset($node['roles'])) { + foreach ($node['roles'] as $nodeRole) { // Set all team roles + $roles[] = Role::team($node['teamId'], $nodeRole)->toString(); + } + } + } + } + + foreach ($this->getAttribute('labels', []) as $label) { + $roles[] = 'label:' . $label; + } + + return $roles; + } + + /** + * Check if user is anonymous. + * + * @param Document $this + * @return bool + */ + public function isAnonymous(): bool + { + return is_null($this->getEmail()) + && is_null($this->getPhone()); + } + + /** + * Is Privileged User? + * + * @param array<string> $roles + * + * @return bool + */ + public static function isPrivileged(array $roles): bool + { + if ( + in_array(self::ROLE_OWNER, $roles) || + in_array(self::ROLE_DEVELOPER, $roles) || + in_array(self::ROLE_ADMIN, $roles) + ) { + return true; + } + + return false; + } + + /** + * Is App User? + * + * @param array<string> $roles + * + * @return bool + */ + public static function isApp(array $roles): bool + { + if (in_array(self::ROLE_APPS, $roles)) { + return true; + } + + return false; + } + + public function tokenVerify(int $type = null, string $secret, Proof $proofForToken): false|Document + { + $tokens = $this->getAttribute('tokens', []); + foreach ($tokens as $token) { + if ( + $token->isSet('secret') && + $token->isSet('expire') && + $token->isSet('type') && + ($type === null || $token->getAttribute('type') === $type) && + $proofForToken->verify($secret, $token->getAttribute('secret')) && + DateTime::formatTz($token->getAttribute('expire')) >= DateTime::formatTz(DateTime::now()) + ) { + return $token; + } + } + + return false; + } + + /** + * Verify session and check that its not expired. + * + * @param array<Document> $sessions + * @param string $secret + * + * @return bool|string + */ + public function sessionVerify(string $secret, Token $proofForToken) + { + $sessions = $this->getAttribute('sessions', []); + + foreach ($sessions as $session) { + if ( + $session->isSet('secret') && + $session->isSet('provider') && + $session->isSet('expire') && + $proofForToken->verify($secret, $session->getAttribute('secret')) && + DateTime::formatTz(DateTime::format(new \DateTime($session->getAttribute('expire')))) >= DateTime::formatTz(DateTime::now()) + ) { + return $session->getId(); + } + } + + return false; + + return false; + } +} diff --git a/src/Appwrite/Utopia/Database/Validator/Attributes.php b/src/Appwrite/Utopia/Database/Validator/Attributes.php new file mode 100644 index 0000000000..e9bd009217 --- /dev/null +++ b/src/Appwrite/Utopia/Database/Validator/Attributes.php @@ -0,0 +1,358 @@ +<?php + +namespace Appwrite\Utopia\Database\Validator; + +use Utopia\Database\Database; +use Utopia\Database\Validator\Datetime as DatetimeValidator; +use Utopia\Database\Validator\Key; +use Utopia\Validator; +use Utopia\Validator\Email; +use Utopia\Validator\IP; +use Utopia\Validator\Range; +use Utopia\Validator\Text; +use Utopia\Validator\URL; + +class Attributes extends Validator +{ + protected int $maxAttributes; + protected string $message = 'Invalid attributes'; + + /** + * @var array<string> Supported attribute types + */ + protected array $supportedTypes = [ + Database::VAR_STRING, + Database::VAR_INTEGER, + Database::VAR_FLOAT, + Database::VAR_BOOLEAN, + Database::VAR_DATETIME, + Database::VAR_POINT, + Database::VAR_LINESTRING, + Database::VAR_POLYGON, + ]; + + /** + * @var array<string> Supported formats for string attributes + */ + protected array $supportedFormats = [ + '', + APP_DATABASE_ATTRIBUTE_EMAIL, + APP_DATABASE_ATTRIBUTE_ENUM, + APP_DATABASE_ATTRIBUTE_IP, + APP_DATABASE_ATTRIBUTE_URL, + ]; + + /** + * @param int $maxAttributes Maximum number of attributes allowed + * @param bool $supportForSpatialAttributes Whether DB supports spatial attributes + */ + public function __construct( + int $maxAttributes = APP_LIMIT_ARRAY_PARAMS_SIZE, + protected bool $supportForSpatialAttributes = true, + ) { + $this->maxAttributes = $maxAttributes; + } + + /** + * Get Description + * + * Returns validator description + * + * @return string + */ + public function getDescription(): string + { + return $this->message; + } + + /** + * Is valid + * + * @param mixed $value + * @return bool + */ + public function isValid($value): bool + { + if (!\is_array($value)) { + $this->message = 'Attributes must be an array'; + return false; + } + + if (\count($value) > $this->maxAttributes) { + $this->message = 'Maximum of ' . $this->maxAttributes . ' attributes allowed'; + return false; + } + + $keyValidator = new Key(); + $keys = []; + + foreach ($value as $index => $attribute) { + if (!\is_array($attribute)) { + $this->message = "Attribute at index $index must be an object"; + return false; + } + + // Validate required fields + if (!isset($attribute['key'])) { + $this->message = "Attribute at index $index is missing required field 'key'"; + return false; + } + + if (!isset($attribute['type'])) { + $this->message = "Attribute at index $index is missing required field 'type'"; + return false; + } + + // Validate key + if (!$keyValidator->isValid($attribute['key'])) { + $this->message = "Invalid key for attribute at index $index: " . $keyValidator->getDescription(); + return false; + } + + // Check for duplicate keys + if (in_array($attribute['key'], $keys)) { + $this->message = "Duplicate attribute key: " . $attribute['key']; + return false; + } + $keys[] = $attribute['key']; + + // Check for reserved keys + $reservedKeys = ['$id', '$createdAt', '$updatedAt', '$permissions', '$collection']; + if (\in_array($attribute['key'], $reservedKeys)) { + $this->message = "Attribute key '" . $attribute['key'] . "' is reserved and cannot be used"; + return false; + } + + // Validate type + if (!\in_array($attribute['type'], $this->supportedTypes)) { + $this->message = "Invalid type for attribute '" . $attribute['key'] . "': " . $attribute['type']; + return false; + } + + // Validate spatial type support + if (\in_array($attribute['type'], Database::SPATIAL_TYPES) && !$this->supportForSpatialAttributes) { + $this->message = "Spatial attributes are not supported by the current database"; + return false; + } + + // Validate size for string types + if ($attribute['type'] === Database::VAR_STRING) { + if (!isset($attribute['size']) || !is_int($attribute['size']) || $attribute['size'] < 1 || $attribute['size'] > APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH) { + $this->message = "Invalid or missing size for string attribute '" . $attribute['key'] . "'. Size must be between 1 and " . APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH; + return false; + } + } + + // Validate format if provided + if (isset($attribute['format']) && $attribute['format'] !== '') { + // Format is only allowed for string type + if ($attribute['type'] !== Database::VAR_STRING) { + $this->message = "Format is only allowed for string type for attribute '" . $attribute['key'] . "'"; + return false; + } + if (!in_array($attribute['format'], $this->supportedFormats)) { + $this->message = "Invalid format for attribute '" . $attribute['key'] . "': " . $attribute['format']; + return false; + } + } + + // Validate required field if provided + if (isset($attribute['required']) && !is_bool($attribute['required'])) { + $this->message = "Invalid 'required' value for attribute '" . $attribute['key'] . "': must be a boolean"; + return false; + } + + // Validate array field if provided + if (isset($attribute['array']) && !is_bool($attribute['array'])) { + $this->message = "Invalid 'array' value for attribute '" . $attribute['key'] . "': must be a boolean"; + return false; + } + + // Validate signed field if provided + if (isset($attribute['signed']) && !is_bool($attribute['signed'])) { + $this->message = "Invalid 'signed' value for attribute '" . $attribute['key'] . "': must be a boolean"; + return false; + } + + // Validate signed only for integer/float types + if (isset($attribute['signed']) && !in_array($attribute['type'], [Database::VAR_INTEGER, Database::VAR_FLOAT])) { + $this->message = "Attribute '" . $attribute['key'] . "': 'signed' can only be used with integer or float types"; + return false; + } + + // Validate required and default conflict + if (isset($attribute['required']) && $attribute['required'] === true && isset($attribute['default']) && $attribute['default'] !== null) { + $this->message = "Attribute '" . $attribute['key'] . "' cannot have a default value when required is true"; + return false; + } + + // Validate array and default conflict + if (isset($attribute['array']) && $attribute['array'] === true && isset($attribute['default']) && $attribute['default'] !== null) { + $this->message = "Attribute '" . $attribute['key'] . "' cannot have a default value when array is true"; + return false; + } + + // Validate min/max range for integer/float + if (isset($attribute['min']) || isset($attribute['max'])) { + if (!in_array($attribute['type'], [Database::VAR_INTEGER, Database::VAR_FLOAT])) { + $this->message = "Attribute '" . $attribute['key'] . "': min/max can only be used with integer or float types"; + return false; + } + + // If both are set, validate ordering + if (isset($attribute['min']) && isset($attribute['max']) && $attribute['min'] > $attribute['max']) { + $this->message = "Attribute '" . $attribute['key'] . "': minimum value must be less than or equal to maximum value"; + return false; + } + } + + // Validate default value matches attribute type + if (isset($attribute['default'])) { + switch ($attribute['type']) { + case Database::VAR_STRING: + if (!is_string($attribute['default'])) { + $this->message = "Default value for string attribute '" . $attribute['key'] . "' must be a string"; + return false; + } + + // Validate string size + $size = $attribute['size'] ?? 0; + if ($size > 0) { + $textValidator = new Text($size, 0); + if (!$textValidator->isValid($attribute['default'])) { + $this->message = "Default value for attribute '" . $attribute['key'] . "' exceeds maximum size of $size characters"; + return false; + } + } + + // Validate format-specific defaults + $format = $attribute['format'] ?? ''; + if ($format === APP_DATABASE_ATTRIBUTE_EMAIL) { + $emailValidator = new Email(); + if (!$emailValidator->isValid($attribute['default'])) { + $this->message = "Default value for email attribute '" . $attribute['key'] . "' must be a valid email address"; + return false; + } + } elseif ($format === APP_DATABASE_ATTRIBUTE_IP) { + $ipValidator = new IP(); + if (!$ipValidator->isValid($attribute['default'])) { + $this->message = "Default value for IP attribute '" . $attribute['key'] . "' must be a valid IP address"; + return false; + } + } elseif ($format === APP_DATABASE_ATTRIBUTE_URL) { + $urlValidator = new URL(); + if (!$urlValidator->isValid($attribute['default'])) { + $this->message = "Default value for URL attribute '" . $attribute['key'] . "' must be a valid URL"; + return false; + } + } + break; + + case Database::VAR_INTEGER: + if (!is_int($attribute['default'])) { + $this->message = "Default value for integer attribute '" . $attribute['key'] . "' must be an integer"; + return false; + } + // Validate within range if min/max specified + if (isset($attribute['min']) || isset($attribute['max'])) { + $min = $attribute['min'] ?? \PHP_INT_MIN; + $max = $attribute['max'] ?? \PHP_INT_MAX; + $rangeValidator = new Range($min, $max, Database::VAR_INTEGER); + if (!$rangeValidator->isValid($attribute['default'])) { + $this->message = "Default value for integer attribute '" . $attribute['key'] . "' must be between $min and $max"; + return false; + } + } + break; + + case Database::VAR_FLOAT: + if (!is_float($attribute['default']) && !is_int($attribute['default'])) { + $this->message = "Default value for float attribute '" . $attribute['key'] . "' must be a number"; + return false; + } + // Validate within range if min/max specified + if (isset($attribute['min']) || isset($attribute['max'])) { + $min = $attribute['min'] ?? -\PHP_FLOAT_MAX; + $max = $attribute['max'] ?? \PHP_FLOAT_MAX; + $rangeValidator = new Range($min, $max, Database::VAR_FLOAT); + if (!$rangeValidator->isValid((float)$attribute['default'])) { + $this->message = "Default value for float attribute '" . $attribute['key'] . "' must be between $min and $max"; + return false; + } + } + break; + + case Database::VAR_BOOLEAN: + if (!is_bool($attribute['default'])) { + $this->message = "Default value for boolean attribute '" . $attribute['key'] . "' must be a boolean"; + return false; + } + break; + + case Database::VAR_DATETIME: + if (!is_string($attribute['default'])) { + $this->message = "Default value for datetime attribute '" . $attribute['key'] . "' must be a string in ISO 8601 format"; + return false; + } + // Basic datetime format validation + $datetimeValidator = new DatetimeValidator(); + if (!$datetimeValidator->isValid($attribute['default'])) { + $this->message = "Default value for datetime attribute '" . $attribute['key'] . "' must be in valid ISO 8601 format"; + return false; + } + break; + } + } + + // Validate enum elements if format is enum + if (isset($attribute['format']) && $attribute['format'] === APP_DATABASE_ATTRIBUTE_ENUM) { + if (!isset($attribute['elements']) || !is_array($attribute['elements']) || empty($attribute['elements'])) { + $this->message = "Attribute '" . $attribute['key'] . "' with enum format must have 'elements' array"; + return false; + } + + // Validate each enum element + foreach ($attribute['elements'] as $elementIndex => $element) { + if (!is_string($element) || empty($element)) { + $this->message = "Enum element at index $elementIndex for attribute '" . $attribute['key'] . "' must be a non-empty string"; + return false; + } + if (strlen($element) > Database::LENGTH_KEY) { + $this->message = "Enum element at index $elementIndex for attribute '" . $attribute['key'] . "' exceeds maximum length of " . Database::LENGTH_KEY . " characters"; + return false; + } + } + + // Validate default exists in elements + if (isset($attribute['default']) && $attribute['default'] !== null) { + if (!in_array($attribute['default'], $attribute['elements'], true)) { + $this->message = "Default value for enum attribute '" . $attribute['key'] . "' must be one of the provided elements"; + return false; + } + } + } + } + + return true; + } + + /** + * Is array + * + * @return bool + */ + public function isArray(): bool + { + return false; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return self::TYPE_ARRAY; + } +} diff --git a/src/Appwrite/Utopia/Database/Validator/Indexes.php b/src/Appwrite/Utopia/Database/Validator/Indexes.php new file mode 100644 index 0000000000..00603ab1df --- /dev/null +++ b/src/Appwrite/Utopia/Database/Validator/Indexes.php @@ -0,0 +1,200 @@ +<?php + +namespace Appwrite\Utopia\Database\Validator; + +use Utopia\Database\Database; +use Utopia\Database\Validator\Key; +use Utopia\Validator; + +class Indexes extends Validator +{ + protected string $message = 'Invalid indexes'; + + /** + * @var array<string> Supported index types + */ + protected array $supportedTypes = [ + Database::INDEX_KEY, + Database::INDEX_FULLTEXT, + Database::INDEX_UNIQUE, + Database::INDEX_SPATIAL, + ]; + + /** + * @var array<string> Supported orders + */ + protected array $supportedOrders = [ + Database::ORDER_ASC, + Database::ORDER_DESC, + ]; + + /** + * @param int $maxIndexes Maximum number of indexes allowed + */ + public function __construct( + protected int $maxIndexes = APP_LIMIT_ARRAY_PARAMS_SIZE, + ) { + } + + /** + * Get Description + * + * @return string + */ + public function getDescription(): string + { + return $this->message; + } + + /** + * Is valid + * + * @param mixed $value + * @return bool + */ + public function isValid($value): bool + { + if (!is_array($value)) { + $this->message = 'Indexes must be an array'; + return false; + } + + if (count($value) > $this->maxIndexes) { + $this->message = 'Maximum of ' . $this->maxIndexes . ' indexes allowed'; + return false; + } + + $keyValidator = new Key(); + $keys = []; + + foreach ($value as $i => $index) { + if (!is_array($index)) { + $this->message = "Index at position $i must be an object"; + return false; + } + + // Validate required fields + if (!isset($index['key']) || !is_string($index['key'])) { + $this->message = "Index at position $i is missing required field 'key'"; + return false; + } + + if (!isset($index['type']) || !is_string($index['type'])) { + $this->message = "Index at position $i is missing required field 'type'"; + return false; + } + + if (!isset($index['attributes']) || !is_array($index['attributes'])) { + $this->message = "Index at position $i is missing required field 'attributes' (must be an array)"; + return false; + } + + // Validate key format + if (!$keyValidator->isValid($index['key'])) { + $this->message = "Invalid key for index at position $i: " . $keyValidator->getDescription(); + return false; + } + + // Check for duplicate keys + if (in_array($index['key'], $keys)) { + $this->message = "Duplicate index key: " . $index['key']; + return false; + } + $keys[] = $index['key']; + + // Validate type + if (!in_array($index['type'], $this->supportedTypes)) { + $this->message = "Invalid type for index '" . $index['key'] . "': " . $index['type']; + return false; + } + + // Validate attributes array + if (empty($index['attributes'])) { + $this->message = "Index '" . $index['key'] . "' must have at least one attribute"; + return false; + } + + if (count($index['attributes']) > APP_LIMIT_ARRAY_PARAMS_SIZE) { + $this->message = "Index '" . $index['key'] . "' cannot have more than " . APP_LIMIT_ARRAY_PARAMS_SIZE . " attributes"; + return false; + } + + // Validate each attribute in the index + $systemAttrs = ['$id', '$createdAt', '$updatedAt']; + foreach ($index['attributes'] as $attrIndex => $attr) { + if (!is_string($attr)) { + $this->message = "Invalid attribute at position $attrIndex in index '" . $index['key'] . "': must be a string"; + return false; + } + if (!$keyValidator->isValid($attr) && !in_array($attr, $systemAttrs)) { + $this->message = "Invalid attribute name '$attr' in index '" . $index['key'] . "'"; + return false; + } + } + + $attrCount = count($index['attributes']); + + // Validate orders if provided + if (isset($index['orders'])) { + if (!is_array($index['orders'])) { + $this->message = "Index '" . $index['key'] . "' orders must be an array"; + return false; + } + + if (count($index['orders']) !== $attrCount) { + $this->message = "Index '" . $index['key'] . "': orders array length (" . count($index['orders']) . ") must match attributes array length ($attrCount)"; + return false; + } + + foreach ($index['orders'] as $order) { + if ($order !== null && $order !== '' && !in_array($order, $this->supportedOrders)) { + $this->message = "Invalid order '$order' in index '" . $index['key'] . "'. Must be 'ASC' or 'DESC'"; + return false; + } + } + } + + // Validate lengths if provided + if (isset($index['lengths'])) { + if (!is_array($index['lengths'])) { + $this->message = "Index '" . $index['key'] . "' lengths must be an array"; + return false; + } + + if (count($index['lengths']) !== $attrCount) { + $this->message = "Index '" . $index['key'] . "': lengths array length (" . count($index['lengths']) . ") must match attributes array length ($attrCount)"; + return false; + } + + foreach ($index['lengths'] as $length) { + if ($length !== null && (!is_int($length) || $length < 0)) { + $this->message = "Invalid length in index '" . $index['key'] . "': must be a non-negative integer or null"; + return false; + } + } + } + } + + return true; + } + + /** + * Is array + * + * @return bool + */ + public function isArray(): bool + { + return false; + } + + /** + * Get Type + * + * @return string + */ + public function getType(): string + { + return self::TYPE_ARRAY; + } +} diff --git a/src/Appwrite/Utopia/Request.php b/src/Appwrite/Utopia/Request.php index 76d87e2012..463cdfb216 100644 --- a/src/Appwrite/Utopia/Request.php +++ b/src/Appwrite/Utopia/Request.php @@ -2,8 +2,8 @@ namespace Appwrite\Utopia; -use Appwrite\Auth\Auth; use Appwrite\SDK\Method; +use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Request\Filter; use Swoole\Http\Request as SwooleRequest; use Utopia\Database\Validator\Authorization; @@ -199,19 +199,19 @@ class Request extends UtopiaRequest } /** - * Get User Agent - * - * Method for getting User Agent. Preferring forwarded agent for privileged users; otherwise returns default. - * - * @param string $default - * @return string - */ + * Get User Agent + * + * Method for getting User Agent. Preferring forwarded agent for privileged users; otherwise returns default. + * + * @param string $default + * @return string + */ public function getUserAgent(string $default = ''): string { $forwardedUserAgent = $this->getHeader('x-forwarded-user-agent'); if (!empty($forwardedUserAgent)) { - $roles = $this->authorization->getRoles() ?? []; - $isAppUser = Auth::isAppUser($roles); + $roles = $this->authorization->getRoles(); + $isAppUser = User::isApp($roles); if ($isAppUser) { return $forwardedUserAgent; diff --git a/src/Appwrite/Utopia/Request/Filters/V20.php b/src/Appwrite/Utopia/Request/Filters/V20.php index e130178083..e3d5fe2f79 100644 --- a/src/Appwrite/Utopia/Request/Filters/V20.php +++ b/src/Appwrite/Utopia/Request/Filters/V20.php @@ -149,10 +149,10 @@ class V20 extends Filter } try { - $collection = $dbForProject->getDocument( + $collection = $database = $dbForProject->getAuthorization()->skip(fn () => $dbForProject->getDocument( 'database_' . $database->getSequence(), $collectionId - ); + )); if ($collection->isEmpty()) { return []; } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 962ac37aa9..160bb47bb0 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -2,7 +2,7 @@ namespace Appwrite\Utopia; -use Appwrite\Auth\Auth; +use Appwrite\Utopia\Database\Documents\User as DBUser; use Appwrite\Utopia\Fetch\BodyMultipart; use Appwrite\Utopia\Response\Filter; use Appwrite\Utopia\Response\Model; @@ -146,8 +146,8 @@ use Appwrite\Utopia\Response\Model\VcsContent; use Appwrite\Utopia\Response\Model\Webhook; use Exception; use JsonException; -use Swoole\Http\Response as SwooleHTTPResponse; // Keep last +use Swoole\Http\Response as SwooleHTTPResponse; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; use Utopia\Swoole\Response as SwooleResponse; @@ -812,9 +812,9 @@ class Response extends SwooleResponse } if ($rule['sensitive']) { - $roles = $this->authorization->getRoles() ?? []; - $isPrivilegedUser = Auth::isPrivilegedUser($roles); - $isAppUser = Auth::isAppUser($roles); + $roles = $this->authorization->getRoles(); + $isPrivilegedUser = DBUser::isPrivileged($roles); + $isAppUser = DBUser::isApp($roles); if ((!$isPrivilegedUser && !$isAppUser) && !self::$showSensitive) { $data->setAttribute($key, ''); diff --git a/src/Appwrite/Utopia/Response/Model/Project.php b/src/Appwrite/Utopia/Response/Model/Project.php index abe67e7e86..65f9f7685b 100644 --- a/src/Appwrite/Utopia/Response/Model/Project.php +++ b/src/Appwrite/Utopia/Response/Model/Project.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Auth\Auth; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; use Utopia\Config\Config; @@ -105,7 +104,7 @@ class Project extends Model ->addRule('authDuration', [ 'type' => self::TYPE_INTEGER, 'description' => 'Session duration in seconds.', - 'default' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, + 'default' => TOKEN_EXPIRATION_LOGIN_LONG, 'example' => 60, ]) ->addRule('authLimit', [ @@ -372,7 +371,7 @@ class Project extends Model $auth = Config::getParam('auth', []); $document->setAttribute('authLimit', $authValues['limit'] ?? 0); - $document->setAttribute('authDuration', $authValues['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG); + $document->setAttribute('authDuration', $authValues['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG); $document->setAttribute('authSessionsLimit', $authValues['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT); $document->setAttribute('authPasswordHistory', $authValues['passwordHistory'] ?? 0); $document->setAttribute('authPasswordDictionary', $authValues['passwordDictionary'] ?? false); diff --git a/src/Appwrite/Utopia/Response/Model/ResourceToken.php b/src/Appwrite/Utopia/Response/Model/ResourceToken.php index 87ab66ab5d..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(), @@ -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/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index 9f35932700..b217608395 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -55,7 +55,7 @@ trait AccountBase 'origin' => 'http://localhost', 'content-type' => 'application/json', 'x-appwrite-project' => 'console', - 'x-forwarded-for' => '103.152.127.250' // Test IP for denied access region + 'x-forwarded-for' => '31.6.14.220' // Test IP for denied access region ]), [ 'userId' => ID::unique(), 'email' => $email, diff --git a/tests/e2e/Services/Avatars/AvatarsBase.php b/tests/e2e/Services/Avatars/AvatarsBase.php index cc626be99a..aca3af6dfa 100644 --- a/tests/e2e/Services/Avatars/AvatarsBase.php +++ b/tests/e2e/Services/Avatars/AvatarsBase.php @@ -1293,4 +1293,32 @@ trait AvatarsBase return []; } + + public function testGetScreenshotComparison(): array + { + /** + * Test screenshot comparison with stable domain (example.com) + * This test captures a screenshot of example.com and compares it + * against a reference image to ensure consistent rendering. + */ + $response = $this->client->call(Client::METHOD_GET, '/avatars/screenshots', [ + 'x-appwrite-project' => $this->getProject()['$id'], + ], [ + 'url' => 'https://example.com', + 'width' => 800, + 'height' => 600, + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals('image/png', $response['headers']['content-type']); + $this->assertNotEmpty($response['body']); + + // Compare with reference screenshot + $referencePath = \realpath(__DIR__ . '/../../../resources/avatars'); + $referenceScreenshot = $referencePath . '/screenshot-example-com.png'; + $this->assertFileExists($referenceScreenshot, 'Reference example.com screenshot not found'); + $this->assertSamePixels($referenceScreenshot, $response['body']); + + return []; + } } diff --git a/tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php b/tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php index 22ce1eb1e3..ef7091d771 100644 --- a/tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php +++ b/tests/e2e/Services/Databases/Legacy/DatabasesCustomServerTest.php @@ -6780,4 +6780,1337 @@ class DatabasesCustomServerTest extends Scope 'x-appwrite-key' => $this->getProject()['apiKey'] ])); } + + public function testCreateCollectionWithAttributesAndIndexes(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Test Multi Create', + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Test: Create collection with attributes and indexes in one call + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::custom('movies'), + 'name' => 'Movies', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'documentSecurity' => true, + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + 'required' => true, + ], + [ + 'key' => 'year', + 'type' => Database::VAR_INTEGER, + 'required' => false, + 'default' => 2024, + ], + [ + 'key' => 'rating', + 'type' => Database::VAR_FLOAT, + 'required' => false, + ], + [ + 'key' => 'active', + 'type' => Database::VAR_BOOLEAN, + 'required' => false, + 'default' => true, + ], + ], + 'indexes' => [ + [ + 'key' => 'idx_title', + 'type' => Database::INDEX_KEY, + 'attributes' => ['title'], + ], + [ + 'key' => 'idx_year', + 'type' => Database::INDEX_KEY, + 'attributes' => ['year'], + 'orders' => ['DESC'], + ], + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + $this->assertEquals('Movies', $collection['body']['name']); + $this->assertEquals('movies', $collection['body']['$id']); + + // Verify attributes were created and are available + $attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/movies/attributes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $attributes['headers']['status-code']); + $this->assertEquals(4, $attributes['body']['total']); + + $attrByKey = []; + foreach ($attributes['body']['attributes'] as $attr) { + $attrByKey[$attr['key']] = $attr; + } + + $this->assertEquals('available', $attrByKey['title']['status']); + $this->assertEquals(Database::VAR_STRING, $attrByKey['title']['type']); + $this->assertEquals(256, $attrByKey['title']['size']); + $this->assertTrue($attrByKey['title']['required']); + + $this->assertEquals('available', $attrByKey['year']['status']); + $this->assertEquals(Database::VAR_INTEGER, $attrByKey['year']['type']); + $this->assertFalse($attrByKey['year']['required']); + $this->assertEquals(2024, $attrByKey['year']['default']); + + $this->assertEquals('available', $attrByKey['rating']['status']); + $this->assertEquals(Database::VAR_FLOAT, $attrByKey['rating']['type']); + + $this->assertEquals('available', $attrByKey['active']['status']); + $this->assertEquals(Database::VAR_BOOLEAN, $attrByKey['active']['type']); + $this->assertTrue($attrByKey['active']['default']); + + // Verify indexes were created and are available + $indexes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/movies/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $indexes['headers']['status-code']); + $this->assertEquals(2, $indexes['body']['total']); + + $idxByKey = []; + foreach ($indexes['body']['indexes'] as $idx) { + $idxByKey[$idx['key']] = $idx; + } + + $this->assertEquals('available', $idxByKey['idx_title']['status']); + $this->assertEquals(Database::INDEX_KEY, $idxByKey['idx_title']['type']); + $this->assertEquals(['title'], $idxByKey['idx_title']['attributes']); + + $this->assertEquals('available', $idxByKey['idx_year']['status']); + $this->assertEquals(Database::INDEX_KEY, $idxByKey['idx_year']['type']); + $this->assertEquals(['year'], $idxByKey['idx_year']['attributes']); + $this->assertEquals(['DESC'], $idxByKey['idx_year']['orders']); + + // Verify we can create documents using the attributes + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/movies/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => ID::unique(), + 'data' => [ + 'title' => 'The Matrix', + 'year' => 1999, + 'rating' => 8.7, + 'active' => true, + ], + ]); + + $this->assertEquals(201, $document['headers']['status-code']); + $this->assertEquals('The Matrix', $document['body']['title']); + $this->assertEquals(1999, $document['body']['year']); + $this->assertEquals(8.7, $document['body']['rating']); + $this->assertTrue($document['body']['active']); + + // Test: Create document with default values + $document2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/movies/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => ID::unique(), + 'data' => [ + 'title' => 'New Movie', + ], + ]); + + $this->assertEquals(201, $document2['headers']['status-code']); + $this->assertEquals('New Movie', $document2['body']['title']); + $this->assertEquals(2024, $document2['body']['year']); // default value + $this->assertTrue($document2['body']['active']); // default value + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + } + + public function testCreateCollectionWithAttributesAndIndexesErrors(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Test Multi Create Errors', + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Test: Invalid attribute type + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Invalid Type', + 'attributes' => [ + [ + 'key' => 'test', + 'type' => 'invalid_type', + 'size' => 256, + ], + ], + ]); + + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Index referencing non-existent attribute + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Invalid Index', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + ], + 'indexes' => [ + [ + 'key' => 'idx_invalid', + 'type' => Database::INDEX_KEY, + 'attributes' => ['nonexistent'], + ], + ], + ]); + + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: String attribute without size (should fail - size is required for strings) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'No Size', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + ], + ], + ]); + + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Required attribute with default value (should fail) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Required With Default', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + 'required' => true, + 'default' => 'test', + ], + ], + ]); + + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Duplicate attribute keys + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Duplicate Keys', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 128, + ], + ], + ]); + + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Index on system attribute ($id) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'System Attr Index', + 'attributes' => [], + 'indexes' => [ + [ + 'key' => 'idx_id', + 'type' => Database::INDEX_KEY, + 'attributes' => ['$id'], + ], + ], + ]); + + // Should succeed - system attributes can be indexed + $this->assertEquals(201, $collection['headers']['status-code']); + + // Test: Relationship attributes not supported inline (rejected as invalid type) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Relationship Test', + 'attributes' => [ + [ + 'key' => 'related', + 'type' => 'relationship', + 'relatedCollection' => 'some_collection', + 'relationType' => 'oneToOne', + ], + ], + ]); + + $this->assertEquals(400, $collection['headers']['status-code']); + $this->assertStringContainsString('Invalid type', $collection['body']['message']); + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + } + + public function testCreateCollectionCleanupOnFailure(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Test Cleanup', + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + $collectionId = ID::unique(); + + // Test: Create collection with invalid index referencing non-existent attribute (should fail) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => $collectionId, + 'name' => 'Should Fail', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + ], + 'indexes' => [ + [ + 'key' => 'idx_invalid', + 'type' => Database::INDEX_KEY, + 'attributes' => ['nonexistent'], + ], + ], + ]); + + $this->assertEquals(400, $collection['headers']['status-code']); + + // Verify collection was cleaned up - creating with same ID should succeed + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => $collectionId, + 'name' => 'Should Succeed', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + } + + public function testCreateCollectionWithEnumAttribute(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Test Enum', + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Test: Create collection with enum attribute + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::custom('status_collection'), + 'name' => 'Status Collection', + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + ], + 'attributes' => [ + [ + 'key' => 'status', + 'type' => Database::VAR_STRING, + 'size' => 32, + 'required' => true, + 'format' => 'enum', + 'elements' => ['pending', 'active', 'completed', 'cancelled'], + ], + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + + // Verify attribute + $attributes = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/status_collection/attributes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(200, $attributes['headers']['status-code']); + $this->assertEquals(1, $attributes['body']['total']); + $this->assertEquals('available', $attributes['body']['attributes'][0]['status']); + $this->assertEquals('enum', $attributes['body']['attributes'][0]['format']); + $this->assertEquals(['pending', 'active', 'completed', 'cancelled'], $attributes['body']['attributes'][0]['elements']); + + // Test creating document with valid enum value + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/status_collection/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => ID::unique(), + 'data' => [ + 'status' => 'active', + ], + ]); + + $this->assertEquals(201, $document['headers']['status-code']); + $this->assertEquals('active', $document['body']['status']); + + // Test creating document with invalid enum value + $document = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/status_collection/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => ID::unique(), + 'data' => [ + 'status' => 'invalid_status', + ], + ]); + + $this->assertEquals(400, $document['headers']['status-code']); + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + } + + public function testCreateCollectionAttributeValidationEdgeCases(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Test Attribute Edge Cases', + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Test: Reserved attribute key ($id) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Reserved Key Test', + 'attributes' => [ + [ + 'key' => '$id', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Reserved attribute key ($createdAt) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Reserved Key Test 2', + 'attributes' => [ + [ + 'key' => '$createdAt', + 'type' => Database::VAR_DATETIME, + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Integer default value with wrong type (string instead of int) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Wrong Default Type', + 'attributes' => [ + [ + 'key' => 'count', + 'type' => Database::VAR_INTEGER, + 'default' => 'not_an_integer', + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Boolean default value with wrong type + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Wrong Boolean Default', + 'attributes' => [ + [ + 'key' => 'active', + 'type' => Database::VAR_BOOLEAN, + 'default' => 'yes', + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: min > max for integer + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Min Greater Than Max', + 'attributes' => [ + [ + 'key' => 'score', + 'type' => Database::VAR_INTEGER, + 'min' => 100, + 'max' => 10, + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Default value outside min/max range + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Default Out of Range', + 'attributes' => [ + [ + 'key' => 'score', + 'type' => Database::VAR_INTEGER, + 'min' => 0, + 'max' => 100, + 'default' => 150, + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: String default exceeds size + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Default Exceeds Size', + 'attributes' => [ + [ + 'key' => 'name', + 'type' => Database::VAR_STRING, + 'size' => 5, + 'default' => 'This is way too long for size 5', + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: 'signed' on non-numeric type + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Signed On String', + 'attributes' => [ + [ + 'key' => 'name', + 'type' => Database::VAR_STRING, + 'size' => 256, + 'signed' => true, + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Array attribute with default value + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Array With Default', + 'attributes' => [ + [ + 'key' => 'tags', + 'type' => Database::VAR_STRING, + 'size' => 256, + 'array' => true, + 'default' => ['tag1', 'tag2'], + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Format on non-string type (format is only allowed for strings) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Format On Integer', + 'attributes' => [ + [ + 'key' => 'count', + 'type' => Database::VAR_INTEGER, + 'format' => 'enum', + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Valid integer with min/max range and default within range (should succeed) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Valid Range', + 'attributes' => [ + [ + 'key' => 'score', + 'type' => Database::VAR_INTEGER, + 'min' => 0, + 'max' => 100, + 'default' => 50, + ], + ], + ]); + $this->assertEquals(201, $collection['headers']['status-code']); + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + } + + public function testCreateCollectionEnumValidationEdgeCases(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Test Enum Edge Cases', + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Test: Enum with empty elements array + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Empty Enum Elements', + 'attributes' => [ + [ + 'key' => 'status', + 'type' => Database::VAR_STRING, + 'size' => 32, + 'format' => 'enum', + 'elements' => [], + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Enum with empty string element + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Empty String Element', + 'attributes' => [ + [ + 'key' => 'status', + 'type' => Database::VAR_STRING, + 'size' => 32, + 'format' => 'enum', + 'elements' => ['active', '', 'inactive'], + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Enum default not in elements + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Default Not In Elements', + 'attributes' => [ + [ + 'key' => 'status', + 'type' => Database::VAR_STRING, + 'size' => 32, + 'format' => 'enum', + 'elements' => ['active', 'inactive'], + 'default' => 'pending', + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Enum with valid default in elements (should succeed) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Valid Enum Default', + 'attributes' => [ + [ + 'key' => 'status', + 'type' => Database::VAR_STRING, + 'size' => 32, + 'format' => 'enum', + 'elements' => ['active', 'inactive', 'pending'], + 'default' => 'pending', + ], + ], + ]); + $this->assertEquals(201, $collection['headers']['status-code']); + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + } + + public function testCreateCollectionIndexValidationEdgeCases(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Test Index Edge Cases', + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Test: Duplicate index keys + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Duplicate Index Keys', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + ], + 'indexes' => [ + [ + 'key' => 'idx_title', + 'type' => Database::INDEX_KEY, + 'attributes' => ['title'], + ], + [ + 'key' => 'idx_title', + 'type' => Database::INDEX_UNIQUE, + 'attributes' => ['title'], + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Invalid index type + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Invalid Index Type', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + ], + 'indexes' => [ + [ + 'key' => 'idx_title', + 'type' => 'invalid_type', + 'attributes' => ['title'], + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Empty attributes array in index + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Empty Index Attributes', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + ], + 'indexes' => [ + [ + 'key' => 'idx_empty', + 'type' => Database::INDEX_KEY, + 'attributes' => [], + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Orders array length mismatch + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Orders Length Mismatch', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + [ + 'key' => 'year', + 'type' => Database::VAR_INTEGER, + ], + ], + 'indexes' => [ + [ + 'key' => 'idx_compound', + 'type' => Database::INDEX_KEY, + 'attributes' => ['title', 'year'], + 'orders' => ['ASC'], // Only one order for two attributes + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Lengths array length mismatch + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Lengths Mismatch', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + [ + 'key' => 'description', + 'type' => Database::VAR_STRING, + 'size' => 1024, + ], + ], + 'indexes' => [ + [ + 'key' => 'idx_compound', + 'type' => Database::INDEX_KEY, + 'attributes' => ['title', 'description'], + 'lengths' => [100], // Only one length for two attributes + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Invalid order value + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Invalid Order', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + ], + 'indexes' => [ + [ + 'key' => 'idx_title', + 'type' => Database::INDEX_KEY, + 'attributes' => ['title'], + 'orders' => ['INVALID'], + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Valid compound index with proper orders/lengths (should succeed) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Valid Compound Index', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + [ + 'key' => 'year', + 'type' => Database::VAR_INTEGER, + ], + ], + 'indexes' => [ + [ + 'key' => 'idx_compound', + 'type' => Database::INDEX_KEY, + 'attributes' => ['title', 'year'], + 'orders' => ['ASC', 'DESC'], + ], + ], + ]); + $this->assertEquals(201, $collection['headers']['status-code']); + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + } + + public function testCreateCollectionDatetimeValidation(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Test Datetime Validation', + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Test: Invalid datetime default (not ISO 8601) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Invalid Datetime Default', + 'attributes' => [ + [ + 'key' => 'publishedAt', + 'type' => Database::VAR_DATETIME, + 'default' => 'not-a-date', + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Valid datetime with ISO 8601 default (should succeed) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Valid Datetime', + 'attributes' => [ + [ + 'key' => 'publishedAt', + 'type' => Database::VAR_DATETIME, + ], + ], + ]); + $this->assertEquals(201, $collection['headers']['status-code']); + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + } + + public function testCreateCollectionFloatValidation(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Test Float Validation', + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Test: Float with min > max + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Float Min Greater Max', + 'attributes' => [ + [ + 'key' => 'price', + 'type' => Database::VAR_FLOAT, + 'min' => 100.50, + 'max' => 10.25, + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Float default outside range + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Float Default Out of Range', + 'attributes' => [ + [ + 'key' => 'price', + 'type' => Database::VAR_FLOAT, + 'min' => 0.0, + 'max' => 100.0, + 'default' => 150.50, + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Valid float with range (should succeed) + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Valid Float Range', + 'attributes' => [ + [ + 'key' => 'price', + 'type' => Database::VAR_FLOAT, + 'min' => 0.0, + 'max' => 1000.0, + 'default' => 99.99, + ], + ], + ]); + $this->assertEquals(201, $collection['headers']['status-code']); + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + } + + public function testCreateCollectionMissingRequiredFields(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'Test Missing Fields', + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Test: Attribute without key + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Missing Attribute Key', + 'attributes' => [ + [ + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Attribute without type + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Missing Attribute Type', + 'attributes' => [ + [ + 'key' => 'title', + 'size' => 256, + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Index without key + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Missing Index Key', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + ], + 'indexes' => [ + [ + 'type' => Database::INDEX_KEY, + 'attributes' => ['title'], + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Index without type + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Missing Index Type', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + ], + 'indexes' => [ + [ + 'key' => 'idx_title', + 'attributes' => ['title'], + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Test: Index without attributes + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'Missing Index Attributes', + 'attributes' => [ + [ + 'key' => 'title', + 'type' => Database::VAR_STRING, + 'size' => 256, + ], + ], + 'indexes' => [ + [ + 'key' => 'idx_title', + 'type' => Database::INDEX_KEY, + ], + ], + ]); + $this->assertEquals(400, $collection['headers']['status-code']); + + // Cleanup + $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + } } diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index cc978a8e38..2f02fd92ba 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -422,7 +422,7 @@ class FunctionsCustomClientTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'useCases' => ['starter', 'ai'], - 'runtimes' => ['bun-1.0', 'dart-2.16'] + 'runtimes' => ['node-22'] ]); $this->assertEquals(200, $templates['headers']['status-code']); $this->assertGreaterThanOrEqual(3, $templates['body']['total']); @@ -436,8 +436,7 @@ class FunctionsCustomClientTest extends Scope $this->assertThat( \array_column($template['runtimes'], 'name'), $this->logicalOr( - $this->containsEqual('bun-1.0'), - $this->containsEqual('dart-2.16'), + $this->containsEqual('node-22'), ), ); } diff --git a/tests/e2e/Services/Health/HealthCustomServerTest.php b/tests/e2e/Services/Health/HealthCustomServerTest.php index 63516f624d..4b7062dc22 100644 --- a/tests/e2e/Services/Health/HealthCustomServerTest.php +++ b/tests/e2e/Services/Health/HealthCustomServerTest.php @@ -40,6 +40,7 @@ class HealthCustomServerTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); + $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals('pass', $response['body']['statuses'][0]['status']); $this->assertIsInt($response['body']['statuses'][0]['ping']); diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index f16864960e..490ac026b5 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -1315,7 +1315,7 @@ trait MigrationsBase $this->assertEquals('CSV', $response['body']['destination']); return true; - }, 30000, 500); + }, 30_000, 500); // Check that email was sent with download link $lastEmail = $this->getLastEmail(); diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 4e479344d3..91dce5c09c 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -2,7 +2,6 @@ namespace Tests\E2E\Services\Projects; -use Appwrite\Auth\Auth; use Appwrite\Extend\Exception; use Appwrite\Tests\Async; use Tests\E2E\Client; @@ -866,7 +865,7 @@ class ProjectsConsoleClientTest extends Scope ], $this->getHeaders())); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(Auth::TOKEN_EXPIRATION_LOGIN_LONG, $response['body']['authDuration']); // 1 Year + $this->assertEquals(TOKEN_EXPIRATION_LOGIN_LONG, $response['body']['authDuration']); // 1 Year /** * Test for SUCCESS @@ -1009,7 +1008,7 @@ class ProjectsConsoleClientTest extends Scope 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ - 'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, + 'duration' => TOKEN_EXPIRATION_LOGIN_LONG, ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -1022,7 +1021,7 @@ class ProjectsConsoleClientTest extends Scope ], $this->getHeaders())); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(Auth::TOKEN_EXPIRATION_LOGIN_LONG, $response['body']['authDuration']); // 1 Year + $this->assertEquals(TOKEN_EXPIRATION_LOGIN_LONG, $response['body']['authDuration']); // 1 Year return ['projectId' => $projectId]; } diff --git a/tests/e2e/Services/Sites/SitesConsoleClientTest.php b/tests/e2e/Services/Sites/SitesConsoleClientTest.php index 227e36a50e..2b75402b25 100644 --- a/tests/e2e/Services/Sites/SitesConsoleClientTest.php +++ b/tests/e2e/Services/Sites/SitesConsoleClientTest.php @@ -74,8 +74,11 @@ class SitesConsoleClientTest extends Scope $this->assertGreaterThan(1, $file['headers']['content-length']); $this->assertEquals('image/png', $file['headers']['content-type']); - $screenshotHash = \md5($file['body']); - $this->assertNotEmpty($screenshotHash); + // Compare with reference screenshots + $referencePath = \realpath(__DIR__ . '/../../../resources/sites/static-themed'); + $referenceScreenshotLight = $referencePath . '/screenshot-light.png'; + $this->assertFileExists($referenceScreenshotLight, 'Reference light screenshot not found'); + $this->assertSamePixels($referenceScreenshotLight, $file['body']); $screenshotId = $deployment['body']['screenshotDark']; $file = $this->client->call(Client::METHOD_GET, "/storage/buckets/screenshots/files/$screenshotId/view?project=console", array_merge($this->getHeaders(), [ @@ -87,10 +90,9 @@ class SitesConsoleClientTest extends Scope $this->assertGreaterThan(1, $file['headers']['content-length']); $this->assertEquals('image/png', $file['headers']['content-type']); - $screenshotDarkHash = \md5($file['body']); - $this->assertNotEmpty($screenshotDarkHash); - - $this->assertNotEquals($screenshotDarkHash, $screenshotHash); + $referenceScreenshotDark = $referencePath . '/screenshot-dark.png'; + $this->assertFileExists($referenceScreenshotDark, 'Reference dark screenshot not found'); + $this->assertSamePixels($referenceScreenshotDark, $file['body']); $screenshotId = $deployment['body']['screenshotLight']; $file = $this->client->call(Client::METHOD_GET, "/storage/buckets/screenshots/files/$screenshotId/view?project=console"); diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index 8c03ec7649..b7dc9e7334 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -2552,7 +2552,7 @@ class SitesCustomServerTest extends Scope $this->cleanupSite($siteId); } - public function testDomainForFailedDeloyment(): void + public function testDomainForFailedDeployment(): void { $siteId = $this->setupSite([ 'siteId' => ID::unique(), 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'); diff --git a/tests/e2e/Services/Users/UsersConsoleClientTest.php b/tests/e2e/Services/Users/UsersConsoleClientTest.php index 967104f5db..24e0f6868b 100644 --- a/tests/e2e/Services/Users/UsersConsoleClientTest.php +++ b/tests/e2e/Services/Users/UsersConsoleClientTest.php @@ -6,6 +6,7 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideConsole; +use Utopia\Database\Helpers\ID; class UsersConsoleClientTest extends Scope { @@ -45,4 +46,39 @@ class UsersConsoleClientTest extends Scope $this->assertIsArray($response['body']['users']); $this->assertIsArray($response['body']['sessions']); } + + public function testCreateUserWithoutPasswordThenSetPassword() + { + // Create a user with email but without password + $userId = ID::unique(); + $email = $userId . '@example.com'; + + $response = $this->client->call(Client::METHOD_POST, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'userId' => $userId, + 'email' => $email, + // no password provided + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + $this->assertEquals($userId, $response['body']['$id']); + $this->assertEquals($email, $response['body']['email']); + $this->assertEmpty($response['body']['password']); + + // Now set the password for that user (console-side) + $newPassword = 'NewPass123!'; + + $set = $this->client->call(Client::METHOD_PATCH, '/users/' . $userId . '/password', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'password' => $newPassword, + ]); + + $this->assertEquals(200, $set['headers']['status-code']); + $this->assertEquals($userId, $set['body']['$id']); + $this->assertNotEmpty($set['body']['password']); + } } diff --git a/tests/e2e/Services/VCS/VCSConsoleClientTest.php b/tests/e2e/Services/VCS/VCSConsoleClientTest.php index 13c3ddb251..963aa5a84b 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; @@ -316,6 +317,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' => Query::limit(1)->toString(), + 'offset' => Query::offset(0)->toString() + ]); + $this->assertSame(200, $repositories['headers']['status-code']); + $this->assertSame(4, $repositories['body']['total']); + $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' => Query::limit(2)->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('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'], + ], $this->getHeaders()), [ + 'type' => 'runtime', + 'limit' => Query::limit(2)->toString(), + 'offset' => Query::offset(100)->toString() + ]); + $this->assertSame(200, $repositories['headers']['status-code']); + $this->assertSame(4, $repositories['body']['total']); + $this->assertCount(0, $repositories['body']['runtimeProviderRepositories']); + // TODO: If you are about to add another check, rewrite this to @provideScenarios /** @@ -338,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()), [ diff --git a/tests/resources/avatars/screenshot-example-com.png b/tests/resources/avatars/screenshot-example-com.png new file mode 100644 index 0000000000..2f8af920e0 Binary files /dev/null and b/tests/resources/avatars/screenshot-example-com.png differ diff --git a/tests/resources/sites/static-themed/index.html b/tests/resources/sites/static-themed/index.html index 955696b473..f1a446612f 100644 --- a/tests/resources/sites/static-themed/index.html +++ b/tests/resources/sites/static-themed/index.html @@ -5,19 +5,70 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Themed website - + +
+

Themed website

+

Adaptive light and dark mode showcase

+
Appwrite Sites
+
+ diff --git a/tests/resources/sites/static-themed/screenshot-dark.png b/tests/resources/sites/static-themed/screenshot-dark.png new file mode 100644 index 0000000000..199a09b902 Binary files /dev/null and b/tests/resources/sites/static-themed/screenshot-dark.png differ diff --git a/tests/resources/sites/static-themed/screenshot-light.png b/tests/resources/sites/static-themed/screenshot-light.png new file mode 100644 index 0000000000..4eae73c5b7 Binary files /dev/null and b/tests/resources/sites/static-themed/screenshot-light.png differ diff --git a/tests/unit/Auth/AuthTest.php b/tests/unit/Auth/AuthTest.php deleted file mode 100644 index 5e883bf924..0000000000 --- a/tests/unit/Auth/AuthTest.php +++ /dev/null @@ -1,516 +0,0 @@ -authorization)) { - return $this->authorization; - } - - $this->authorization = new Authorization(); - - return $this->authorization; - } - - - /** - * Reset Roles - */ - public function setUp(): void - { - $this->getAuthorization()->cleanRoles(); - $this->getAuthorization()->addRole(Role::any()->toString()); - } - - public function testCookieName(): void - { - $name = 'cookie-name'; - - $this->assertEquals(Auth::setCookieName($name), $name); - $this->assertEquals(Auth::$cookieName, $name); - } - - public function testEncodeDecodeSession(): void - { - $id = 'id'; - $secret = 'secret'; - $session = 'eyJpZCI6ImlkIiwic2VjcmV0Ijoic2VjcmV0In0='; - - $this->assertEquals(Auth::encodeSession($id, $secret), $session); - $this->assertEquals(Auth::decodeSession($session), ['id' => $id, 'secret' => $secret]); - } - - public function testHash(): void - { - $secret = 'secret'; - $this->assertEquals(Auth::hash($secret), '2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b'); - } - - public function testPassword(): void - { - /* - General tests, using pre-defined hashes generated by online tools - */ - - // Bcrypt - Version Y - $plain = 'secret'; - $hash = '$2y$08$PDbMtV18J1KOBI9tIYabBuyUwBrtXPGhLxCy9pWP6xkldVOKLrLKy'; - $generatedHash = Auth::passwordHash($plain, 'bcrypt'); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'bcrypt')); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'bcrypt')); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'bcrypt')); - - // Bcrypt - Version A - $plain = 'test123'; - $hash = '$2a$12$3f2ZaARQ1AmhtQWx2nmQpuXcWfTj1YV2/Hl54e8uKxIzJe3IfwLiu'; - $generatedHash = Auth::passwordHash($plain, 'bcrypt'); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'bcrypt')); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'bcrypt')); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'bcrypt')); - - // Bcrypt - Cost 5 - $plain = 'hello-world'; - $hash = '$2a$05$IjrtSz6SN7UJ6Sh3l.b5jODEvEG2LMJTPAHIaLWRvlWx7if3VMkFO'; - $generatedHash = Auth::passwordHash($plain, 'bcrypt'); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'bcrypt')); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'bcrypt')); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'bcrypt')); - - // Bcrypt - Cost 15 - $plain = 'super-secret-password'; - $hash = '$2a$15$DS0ZzbsFZYumH/E4Qj5oeOHnBcM3nCCsCA2m4Goigat/0iMVQC4Na'; - $generatedHash = Auth::passwordHash($plain, 'bcrypt'); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'bcrypt')); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'bcrypt')); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'bcrypt')); - - // MD5 - Short - $plain = 'appwrite'; - $hash = '144fa7eaa4904e8ee120651997f70dcc'; - $generatedHash = Auth::passwordHash($plain, 'md5'); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'md5')); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'md5')); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'md5')); - - // MD5 - Long - $plain = 'AppwriteIsAwesomeBackendAsAServiceThatIsAlsoOpenSourced'; - $hash = '8410e96cf7ac64e0b84c3f8517a82616'; - $generatedHash = Auth::passwordHash($plain, 'md5'); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'md5')); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'md5')); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'md5')); - - // PHPass - $plain = 'pass123'; - $hash = '$P$BVKPmJBZuLch27D4oiMRTEykGLQ9tX0'; - $generatedHash = Auth::passwordHash($plain, 'phpass'); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'phpass')); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'phpass')); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'phpass')); - - // SHA - $plain = 'developersAreAwesome!'; - $hash = '2455118438cb125354b89bb5888346e9bd23355462c40df393fab514bf2220b5a08e4e2d7b85d7327595a450d0ac965cc6661152a46a157c66d681bed20a4735'; - $generatedHash = Auth::passwordHash($plain, 'sha'); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'sha')); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'sha')); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'sha')); - - // Argon2 - $plain = 'safe-argon-password'; - $hash = '$argon2id$v=19$m=2048,t=3,p=4$MWc5NWRmc2QxZzU2$41mp7rSgBZ49YxLbbxIac7aRaxfp5/e1G45ckwnK0g8'; - $generatedHash = Auth::passwordHash($plain, 'argon2'); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'argon2')); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'argon2')); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'argon2')); - - // Scrypt - $plain = 'some-scrypt-password'; - $hash = 'b448ad7ba88b653b5b56b8053a06806724932d0751988bc9cd0ef7ff059e8ba8a020e1913b7069a650d3f99a1559aba0221f2c277826919513a054e76e339028'; - $generatedHash = Auth::passwordHash($plain, 'scrypt', [ 'salt' => 'some-salt', 'length' => 64, 'costCpu' => 16384, 'costMemory' => 12, 'costParallel' => 2]); - - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'scrypt', [ 'salt' => 'some-salt', 'length' => 64, 'costCpu' => 16384, 'costMemory' => 12, 'costParallel' => 2])); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'scrypt', [ 'salt' => 'some-salt', 'length' => 64, 'costCpu' => 16384, 'costMemory' => 12, 'costParallel' => 2])); - $this->assertEquals(false, Auth::passwordVerify($plain, $hash, 'scrypt', [ 'salt' => 'some-wrong-salt', 'length' => 64, 'costCpu' => 16384, 'costMemory' => 12, 'costParallel' => 2])); - $this->assertEquals(false, Auth::passwordVerify($plain, $hash, 'scrypt', [ 'salt' => 'some-salt', 'length' => 64, 'costCpu' => 16384, 'costMemory' => 10, 'costParallel' => 2])); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'scrypt', [ 'salt' => 'some-salt', 'length' => 64, 'costCpu' => 16384, 'costMemory' => 12, 'costParallel' => 2])); - - // ScryptModified tested are in provider-specific tests below - - /* - Provider-specific tests, ensuring functionality of specific use-cases - */ - - // Provider #1 (Database) - $plain = 'example-password'; - $hash = '$2a$10$3bIGRWUes86CICsuchGLj.e.BqdCdg2/1Ud9LvBhJr0j7Dze8PBdS'; - $generatedHash = Auth::passwordHash($plain, 'bcrypt'); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'bcrypt')); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'bcrypt')); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'bcrypt')); - - // Provider #2 (Blog) - $plain = 'your-password'; - $hash = '$P$BkiNDJTpAWXtpaMhEUhUdrv7M0I1g6.'; - $generatedHash = Auth::passwordHash($plain, 'phpass'); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'phpass')); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'phpass')); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'phpass')); - - // Provider #2 (Google) - $plain = 'users-password'; - $hash = 'EPKgfALpS9Tvgr/y1ki7ubY4AEGJeWL3teakrnmOacN4XGiyD00lkzEHgqCQ71wGxoi/zb7Y9a4orOtvMV3/Jw=='; - $salt = '56dFqW+kswqktw=='; - $saltSeparator = 'Bw=='; - $signerKey = 'XyEKE9RcTDeLEsL/RjwPDBv/RqDl8fb3gpYEOQaPihbxf1ZAtSOHCjuAAa7Q3oHpCYhXSN9tizHgVOwn6krflQ=='; - - $options = [ 'salt' => $salt, 'saltSeparator' => $saltSeparator, 'signerKey' => $signerKey ]; - $generatedHash = Auth::passwordHash($plain, 'scryptMod', $options); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'scryptMod', $options)); - $this->assertEquals(true, Auth::passwordVerify($plain, $hash, 'scryptMod', $options)); - $this->assertEquals(false, Auth::passwordVerify('wrongPassword', $hash, 'scryptMod', $options)); - } - - public function testUnknownAlgo() - { - $this->expectExceptionMessage('Hashing algorithm \'md8\' is not supported.'); - - // Bcrypt - Cost 5 - $plain = 'whatIsMd8?!?'; - $generatedHash = Auth::passwordHash($plain, 'md8'); - $this->assertEquals(true, Auth::passwordVerify($plain, $generatedHash, 'md8')); - } - - public function testPasswordGenerator(): void - { - $this->assertEquals(\mb_strlen(Auth::passwordGenerator()), 40); - $this->assertEquals(\mb_strlen(Auth::passwordGenerator(5)), 10); - } - - public function testTokenGenerator(): void - { - $this->assertEquals(\strlen(Auth::tokenGenerator()), 256); - $this->assertEquals(\strlen(Auth::tokenGenerator(5)), 5); - } - - public function testCodeGenerator(): void - { - $this->assertEquals(6, \strlen(Auth::codeGenerator())); - $this->assertEquals(\mb_strlen(Auth::codeGenerator(256)), 256); - $this->assertEquals(\mb_strlen(Auth::codeGenerator(10)), 10); - $this->assertTrue(is_numeric(Auth::codeGenerator(5))); - } - - public function testSessionVerify(): void - { - $expireTime1 = 60 * 60 * 24; - - $secret = 'secret1'; - $hash = Auth::hash($secret); - $tokens1 = [ - new Document([ - '$id' => ID::custom('token1'), - 'secret' => $hash, - 'provider' => Auth::SESSION_PROVIDER_EMAIL, - 'providerUid' => 'test@example.com', - 'expire' => DateTime::addSeconds(new \DateTime(), $expireTime1), - ]), - new Document([ - '$id' => ID::custom('token2'), - 'secret' => 'secret2', - 'provider' => Auth::SESSION_PROVIDER_EMAIL, - 'providerUid' => 'test@example.com', - 'expire' => DateTime::addSeconds(new \DateTime(), $expireTime1), - ]), - ]; - - $expireTime2 = -60 * 60 * 24; - - $tokens2 = [ - new Document([ // Correct secret and type time, wrong expire time - '$id' => ID::custom('token1'), - 'secret' => $hash, - 'provider' => Auth::SESSION_PROVIDER_EMAIL, - 'providerUid' => 'test@example.com', - 'expire' => DateTime::addSeconds(new \DateTime(), $expireTime2), - ]), - new Document([ - '$id' => ID::custom('token2'), - 'secret' => 'secret2', - 'provider' => Auth::SESSION_PROVIDER_EMAIL, - 'providerUid' => 'test@example.com', - 'expire' => DateTime::addSeconds(new \DateTime(), $expireTime2), - ]), - ]; - - $this->assertEquals(Auth::sessionVerify($tokens1, $secret), 'token1'); - $this->assertEquals(Auth::sessionVerify($tokens1, 'false-secret'), false); - $this->assertEquals(Auth::sessionVerify($tokens2, $secret), false); - $this->assertEquals(Auth::sessionVerify($tokens2, 'false-secret'), false); - } - - public function testTokenVerify(): void - { - $secret = 'secret1'; - $hash = Auth::hash($secret); - $tokens1 = [ - new Document([ - '$id' => ID::custom('token1'), - 'type' => Auth::TOKEN_TYPE_RECOVERY, - 'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), 60 * 60 * 24)), - 'secret' => $hash, - ]), - new Document([ - '$id' => ID::custom('token2'), - 'type' => Auth::TOKEN_TYPE_RECOVERY, - 'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)), - 'secret' => 'secret2', - ]), - ]; - - $tokens2 = [ - new Document([ // Correct secret and type time, wrong expire time - '$id' => ID::custom('token1'), - 'type' => Auth::TOKEN_TYPE_RECOVERY, - 'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)), - 'secret' => $hash, - ]), - new Document([ - '$id' => ID::custom('token2'), - 'type' => Auth::TOKEN_TYPE_RECOVERY, - 'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)), - 'secret' => 'secret2', - ]), - ]; - - $tokens3 = [ // Correct secret and expire time, wrong type - new Document([ - '$id' => ID::custom('token1'), - 'type' => Auth::TOKEN_TYPE_INVITE, - 'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), 60 * 60 * 24)), - 'secret' => $hash, - ]), - new Document([ - '$id' => ID::custom('token2'), - 'type' => Auth::TOKEN_TYPE_RECOVERY, - 'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)), - 'secret' => 'secret2', - ]), - ]; - - $this->assertEquals(Auth::tokenVerify($tokens1, Auth::TOKEN_TYPE_RECOVERY, $secret), $tokens1[0]); - $this->assertEquals(Auth::tokenVerify($tokens1, null, $secret), $tokens1[0]); - $this->assertEquals(Auth::tokenVerify($tokens1, Auth::TOKEN_TYPE_RECOVERY, 'false-secret'), false); - $this->assertEquals(Auth::tokenVerify($tokens2, Auth::TOKEN_TYPE_RECOVERY, $secret), false); - $this->assertEquals(Auth::tokenVerify($tokens2, Auth::TOKEN_TYPE_RECOVERY, 'false-secret'), false); - $this->assertEquals(Auth::tokenVerify($tokens3, Auth::TOKEN_TYPE_RECOVERY, $secret), false); - $this->assertEquals(Auth::tokenVerify($tokens3, Auth::TOKEN_TYPE_RECOVERY, 'false-secret'), false); - } - - public function testIsPrivilegedUser(): void - { - $this->assertEquals(false, Auth::isPrivilegedUser([])); - $this->assertEquals(false, Auth::isPrivilegedUser([Role::guests()->toString()])); - $this->assertEquals(false, Auth::isPrivilegedUser([Role::users()->toString()])); - $this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_ADMIN])); - $this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_DEVELOPER])); - $this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_OWNER])); - $this->assertEquals(false, Auth::isPrivilegedUser([Auth::USER_ROLE_APPS])); - $this->assertEquals(false, Auth::isPrivilegedUser([Auth::USER_ROLE_SYSTEM])); - - $this->assertEquals(false, Auth::isPrivilegedUser([Auth::USER_ROLE_APPS, Auth::USER_ROLE_APPS])); - $this->assertEquals(false, Auth::isPrivilegedUser([Auth::USER_ROLE_APPS, Role::guests()->toString()])); - $this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_OWNER, Role::guests()->toString()])); - $this->assertEquals(true, Auth::isPrivilegedUser([Auth::USER_ROLE_OWNER, Auth::USER_ROLE_ADMIN, Auth::USER_ROLE_DEVELOPER])); - } - - public function testIsAppUser(): void - { - $this->assertEquals(false, Auth::isAppUser([])); - $this->assertEquals(false, Auth::isAppUser([Role::guests()->toString()])); - $this->assertEquals(false, Auth::isAppUser([Role::users()->toString()])); - $this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_ADMIN])); - $this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_DEVELOPER])); - $this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_OWNER])); - $this->assertEquals(true, Auth::isAppUser([Auth::USER_ROLE_APPS])); - $this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_SYSTEM])); - - $this->assertEquals(true, Auth::isAppUser([Auth::USER_ROLE_APPS, Auth::USER_ROLE_APPS])); - $this->assertEquals(true, Auth::isAppUser([Auth::USER_ROLE_APPS, Role::guests()->toString()])); - $this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_OWNER, Role::guests()->toString()])); - $this->assertEquals(false, Auth::isAppUser([Auth::USER_ROLE_OWNER, Auth::USER_ROLE_ADMIN, Auth::USER_ROLE_DEVELOPER])); - } - - public function testGuestRoles(): void - { - $user = new Document([ - '$id' => '' - ]); - - $roles = Auth::getRoles($user, new Authorization()); - $this->assertCount(1, $roles); - $this->assertContains(Role::guests()->toString(), $roles); - } - - public function testUserRoles(): void - { - $user = new Document([ - '$id' => ID::custom('123'), - 'labels' => [ - 'vip', - 'admin' - ], - 'emailVerification' => true, - 'phoneVerification' => true, - 'memberships' => [ - [ - '$id' => ID::custom('456'), - 'teamId' => ID::custom('abc'), - 'confirm' => true, - 'roles' => [ - 'administrator', - 'moderator' - ] - ], - [ - '$id' => ID::custom('abc'), - 'teamId' => ID::custom('def'), - 'confirm' => true, - 'roles' => [ - 'guest' - ] - ] - ] - ]); - - $roles = Auth::getRoles($user, $this->getAuthorization()); - - $this->assertCount(13, $roles); - $this->assertContains(Role::users()->toString(), $roles); - $this->assertContains(Role::user(ID::custom('123'))->toString(), $roles); - $this->assertContains(Role::users(Roles::DIMENSION_VERIFIED)->toString(), $roles); - $this->assertContains(Role::user(ID::custom('123'), Roles::DIMENSION_VERIFIED)->toString(), $roles); - $this->assertContains(Role::team(ID::custom('abc'))->toString(), $roles); - $this->assertContains(Role::team(ID::custom('abc'), 'administrator')->toString(), $roles); - $this->assertContains(Role::team(ID::custom('abc'), 'moderator')->toString(), $roles); - $this->assertContains(Role::team(ID::custom('def'))->toString(), $roles); - $this->assertContains(Role::team(ID::custom('def'), 'guest')->toString(), $roles); - $this->assertContains(Role::member(ID::custom('456'))->toString(), $roles); - $this->assertContains(Role::member(ID::custom('abc'))->toString(), $roles); - $this->assertContains('label:vip', $roles); - $this->assertContains('label:admin', $roles); - - // Disable all verification - $user['emailVerification'] = false; - $user['phoneVerification'] = false; - - $roles = Auth::getRoles($user, $this->getAuthorization()); - $this->assertContains(Role::users(Roles::DIMENSION_UNVERIFIED)->toString(), $roles); - $this->assertContains(Role::user(ID::custom('123'), Roles::DIMENSION_UNVERIFIED)->toString(), $roles); - - // Enable single verification type - $user['emailVerification'] = true; - - $roles = Auth::getRoles($user, $this->getAuthorization()); - $this->assertContains(Role::users(Roles::DIMENSION_VERIFIED)->toString(), $roles); - $this->assertContains(Role::user(ID::custom('123'), Roles::DIMENSION_VERIFIED)->toString(), $roles); - } - - public function testPrivilegedUserRoles(): void - { - $this->getAuthorization()->addRole(Auth::USER_ROLE_OWNER); - $user = new Document([ - '$id' => ID::custom('123'), - 'emailVerification' => true, - 'phoneVerification' => true, - 'memberships' => [ - [ - '$id' => ID::custom('def'), - 'teamId' => ID::custom('abc'), - 'confirm' => true, - 'roles' => [ - 'administrator', - 'moderator' - ] - ], - [ - '$id' => ID::custom('abc'), - 'teamId' => ID::custom('def'), - 'confirm' => true, - 'roles' => [ - 'guest' - ] - ] - ] - ]); - - $roles = Auth::getRoles($user, $this->getAuthorization()); - - $this->assertCount(7, $roles); - $this->assertNotContains(Role::users()->toString(), $roles); - $this->assertNotContains(Role::user(ID::custom('123'))->toString(), $roles); - $this->assertNotContains(Role::users(Roles::DIMENSION_VERIFIED)->toString(), $roles); - $this->assertNotContains(Role::user(ID::custom('123'), Roles::DIMENSION_VERIFIED)->toString(), $roles); - $this->assertContains(Role::team(ID::custom('abc'))->toString(), $roles); - $this->assertContains(Role::team(ID::custom('abc'), 'administrator')->toString(), $roles); - $this->assertContains(Role::team(ID::custom('abc'), 'moderator')->toString(), $roles); - $this->assertContains(Role::team(ID::custom('def'))->toString(), $roles); - $this->assertContains(Role::team(ID::custom('def'), 'guest')->toString(), $roles); - $this->assertContains(Role::member(ID::custom('def'))->toString(), $roles); - $this->assertContains(Role::member(ID::custom('abc'))->toString(), $roles); - } - - public function testAppUserRoles(): void - { - $this->getAuthorization()->addRole(Auth::USER_ROLE_APPS); - $user = new Document([ - '$id' => ID::custom('123'), - 'memberships' => [ - [ - '$id' => ID::custom('def'), - 'teamId' => ID::custom('abc'), - 'confirm' => true, - 'roles' => [ - 'administrator', - 'moderator' - ] - ], - [ - '$id' => ID::custom('abc'), - 'teamId' => ID::custom('def'), - 'confirm' => true, - 'roles' => [ - 'guest' - ] - ] - ] - ]); - - $roles = Auth::getRoles($user, $this->getAuthorization()); - - $this->assertCount(7, $roles); - $this->assertNotContains(Role::users()->toString(), $roles); - $this->assertNotContains(Role::user(ID::custom('123'))->toString(), $roles); - $this->assertContains(Role::team(ID::custom('abc'))->toString(), $roles); - $this->assertContains(Role::team(ID::custom('abc'), 'administrator')->toString(), $roles); - $this->assertContains(Role::team(ID::custom('abc'), 'moderator')->toString(), $roles); - $this->assertContains(Role::team(ID::custom('def'))->toString(), $roles); - $this->assertContains(Role::team(ID::custom('def'), 'guest')->toString(), $roles); - $this->assertContains(Role::member(ID::custom('def'))->toString(), $roles); - $this->assertContains(Role::member(ID::custom('abc'))->toString(), $roles); - } -} diff --git a/tests/unit/Auth/KeyTest.php b/tests/unit/Auth/KeyTest.php index 8ae2114697..920608e82f 100644 --- a/tests/unit/Auth/KeyTest.php +++ b/tests/unit/Auth/KeyTest.php @@ -3,8 +3,8 @@ namespace Tests\Unit\Auth; use Ahc\Jwt\JWT; -use Appwrite\Auth\Auth; use Appwrite\Auth\Key; +use Appwrite\Utopia\Database\Documents\User; use PHPUnit\Framework\TestCase; use Utopia\Config\Config; use Utopia\Database\Document; @@ -21,7 +21,7 @@ class KeyTest extends TestCase 'collections.read', 'documents.read', ]; - $roleScopes = Config::getParam('roles', [])[Auth::USER_ROLE_APPS]['scopes']; + $roleScopes = Config::getParam('roles', [])[User::ROLE_APPS]['scopes']; $key = static::generateKey($projectId, $usage, $scopes); $project = new Document(['$id' => $projectId,]); @@ -29,7 +29,7 @@ class KeyTest extends TestCase $this->assertEquals($projectId, $decoded->getProjectId()); $this->assertEquals(API_KEY_DYNAMIC, $decoded->getType()); - $this->assertEquals(Auth::USER_ROLE_APPS, $decoded->getRole()); + $this->assertEquals(User::ROLE_APPS, $decoded->getRole()); $this->assertEquals(\array_merge($scopes, $roleScopes), $decoded->getScopes()); } diff --git a/tests/unit/Messaging/MessagingChannelsTest.php b/tests/unit/Messaging/MessagingChannelsTest.php index 8fb7f3f666..7df5b8d1e6 100644 --- a/tests/unit/Messaging/MessagingChannelsTest.php +++ b/tests/unit/Messaging/MessagingChannelsTest.php @@ -2,10 +2,9 @@ namespace Tests\Unit\Messaging; -use Appwrite\Auth\Auth; use Appwrite\Messaging\Adapter\Realtime; +use Appwrite\Utopia\Database\Documents\User; use PHPUnit\Framework\TestCase; -use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; @@ -64,7 +63,7 @@ class MessagingChannelsTest extends TestCase */ for ($i = 0; $i < $this->connectionsPerChannel; $i++) { foreach ($this->allChannels as $index => $channel) { - $user = new Document([ + $user = new User([ '$id' => ID::custom('user' . $this->connectionsCount), 'memberships' => [ [ @@ -73,14 +72,14 @@ class MessagingChannelsTest extends TestCase 'confirm' => true, 'roles' => [ empty($index % 2) - ? Auth::USER_ROLE_ADMIN + ? User::ROLE_ADMIN : 'member', ] ] ] ]); - $roles = Auth::getRoles($user, $this->getAuthorization()); + $roles = $user->getRoles($this->getAuthorization()); $parsedChannels = Realtime::convertChannels([0 => $channel], $user->getId()); @@ -100,11 +99,11 @@ class MessagingChannelsTest extends TestCase */ for ($i = 0; $i < $this->connectionsPerChannel; $i++) { foreach ($this->allChannels as $index => $channel) { - $user = new Document([ + $user = new User([ '$id' => '' ]); - $roles = Auth::getRoles($user, $this->getAuthorization()); + $roles = $user->getRoles($this->getAuthorization()); $parsedChannels = Realtime::convertChannels([0 => $channel], $user->getId()); @@ -308,7 +307,7 @@ class MessagingChannelsTest extends TestCase } $role = empty($index % 2) - ? Auth::USER_ROLE_ADMIN + ? User::ROLE_ADMIN : 'member'; $permissions = [ diff --git a/tests/unit/Utopia/Database/Documents/UserTest.php b/tests/unit/Utopia/Database/Documents/UserTest.php new file mode 100644 index 0000000000..d5706e7bec --- /dev/null +++ b/tests/unit/Utopia/Database/Documents/UserTest.php @@ -0,0 +1,363 @@ +authorization)) { + return $this->authorization; + } + + $this->authorization = new Authorization(); + return $this->authorization; + } + + /** + * Reset Roles + */ + public function tearDown(): void + { + $this->getAuthorization()->cleanRoles(); + $this->getAuthorization()->addRole(Role::any()->toString()); + } + + public function testSessionVerify(): void + { + $proofForToken = new Token(); + $expireTime1 = 60 * 60 * 24; + + $secret = 'secret1'; + $hash = $proofForToken->hash($secret); + $tokens1 = [ + new Document([ + '$id' => ID::custom('token1'), + 'secret' => $hash, + 'provider' => SESSION_PROVIDER_EMAIL, + 'providerUid' => 'test@example.com', + 'expire' => DateTime::addSeconds(new \DateTime(), $expireTime1), + ]), + new Document([ + '$id' => ID::custom('token2'), + 'secret' => 'secret2', + 'provider' => SESSION_PROVIDER_EMAIL, + 'providerUid' => 'test@example.com', + 'expire' => DateTime::addSeconds(new \DateTime(), $expireTime1), + ]), + ]; + + $expireTime2 = -60 * 60 * 24; + + $tokens2 = [ + new Document([ // Correct secret and type time, wrong expire time + '$id' => ID::custom('token1'), + 'secret' => $hash, + 'provider' => SESSION_PROVIDER_EMAIL, + 'providerUid' => 'test@example.com', + 'expire' => DateTime::addSeconds(new \DateTime(), $expireTime2), + ]), + new Document([ + '$id' => ID::custom('token2'), + 'secret' => 'secret2', + 'provider' => SESSION_PROVIDER_EMAIL, + 'providerUid' => 'test@example.com', + 'expire' => DateTime::addSeconds(new \DateTime(), $expireTime2), + ]), + ]; + + $user1 = new User([ + '$id' => ID::custom('user1'), + 'sessions' => $tokens1, + + ]); + + $user2 = new User([ + '$id' => ID::custom('user2'), + 'sessions' => $tokens2, + ]); + + $this->assertEquals('token1', $user1->sessionVerify($secret, $proofForToken)); + $this->assertEquals($user1->sessionVerify('false-secret', $proofForToken), false); + $this->assertEquals($user2->sessionVerify($secret, $proofForToken), false); + $this->assertEquals($user2->sessionVerify('false-secret', $proofForToken), false); + } + + public function testTokenVerify(): void + { + $proofForToken = new Token(); + $secret = 'secret1'; + $hash = $proofForToken->hash($secret); + $tokens1 = [ + new Document([ + '$id' => ID::custom('token1'), + 'type' => TOKEN_TYPE_RECOVERY, + 'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), 60 * 60 * 24)), + 'secret' => $hash, + ]), + new Document([ + '$id' => ID::custom('token2'), + 'type' => TOKEN_TYPE_RECOVERY, + 'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)), + 'secret' => 'secret2', + ]), + ]; + + $tokens2 = [ + new Document([ // Correct secret and type time, wrong expire time + '$id' => ID::custom('token1'), + 'type' => TOKEN_TYPE_RECOVERY, + 'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)), + 'secret' => $hash, + ]), + new Document([ + '$id' => ID::custom('token2'), + 'type' => TOKEN_TYPE_RECOVERY, + 'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)), + 'secret' => 'secret2', + ]), + ]; + + $tokens3 = [ // Correct secret and expire time, wrong type + new Document([ + '$id' => ID::custom('token1'), + 'type' => TOKEN_TYPE_INVITE, + 'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), 60 * 60 * 24)), + 'secret' => $hash, + ]), + new Document([ + '$id' => ID::custom('token2'), + 'type' => TOKEN_TYPE_RECOVERY, + 'expire' => DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -60 * 60 * 24)), + 'secret' => 'secret2', + ]), + ]; + + $user1 = new User([ + '$id' => ID::custom('user1'), + 'tokens' => $tokens1, + ]); + + $user2 = new User([ + '$id' => ID::custom('user2'), + 'tokens' => $tokens2, + ]); + + $user3 = new User([ + '$id' => ID::custom('user3'), + 'tokens' => $tokens3, + ]); + + $this->assertEquals($user1->tokenVerify(TOKEN_TYPE_RECOVERY, $secret, $proofForToken), $tokens1[0]); + $this->assertEquals($user1->tokenVerify(null, $secret, $proofForToken), $tokens1[0]); + $this->assertEquals($user1->tokenVerify(TOKEN_TYPE_RECOVERY, 'false-secret', $proofForToken), false); + $this->assertEquals($user2->tokenVerify(TOKEN_TYPE_RECOVERY, $secret, $proofForToken), false); + $this->assertEquals($user2->tokenVerify(TOKEN_TYPE_RECOVERY, 'false-secret', $proofForToken), false); + $this->assertEquals($user3->tokenVerify(TOKEN_TYPE_RECOVERY, $secret, $proofForToken), false); + $this->assertEquals($user3->tokenVerify(TOKEN_TYPE_RECOVERY, 'false-secret', $proofForToken), false); + } + + public function testIsPrivilegedUser(): void + { + $this->assertEquals(false, User::isPrivileged([])); + $this->assertEquals(false, User::isPrivileged([Role::guests()->toString()])); + $this->assertEquals(false, User::isPrivileged([Role::users()->toString()])); + $this->assertEquals(true, User::isPrivileged([User::ROLE_ADMIN])); + $this->assertEquals(true, User::isPrivileged([User::ROLE_DEVELOPER])); + $this->assertEquals(true, User::isPrivileged([User::ROLE_OWNER])); + $this->assertEquals(false, User::isPrivileged([User::ROLE_APPS])); + $this->assertEquals(false, User::isPrivileged([User::ROLE_SYSTEM])); + + $this->assertEquals(false, User::isPrivileged([User::ROLE_APPS, User::ROLE_APPS])); + $this->assertEquals(false, User::isPrivileged([User::ROLE_APPS, Role::guests()->toString()])); + $this->assertEquals(true, User::isPrivileged([User::ROLE_OWNER, Role::guests()->toString()])); + $this->assertEquals(true, User::isPrivileged([User::ROLE_OWNER, User::ROLE_ADMIN, User::ROLE_DEVELOPER])); + } + + public function testIsAppUser(): void + { + $this->assertEquals(false, User::isApp([])); + $this->assertEquals(false, User::isApp([Role::guests()->toString()])); + $this->assertEquals(false, User::isApp([Role::users()->toString()])); + $this->assertEquals(false, User::isApp([User::ROLE_ADMIN])); + $this->assertEquals(false, User::isApp([User::ROLE_DEVELOPER])); + $this->assertEquals(false, User::isApp([User::ROLE_OWNER])); + $this->assertEquals(true, User::isApp([User::ROLE_APPS])); + $this->assertEquals(false, User::isApp([User::ROLE_SYSTEM])); + + $this->assertEquals(true, User::isApp([User::ROLE_APPS, User::ROLE_APPS])); + $this->assertEquals(true, User::isApp([User::ROLE_APPS, Role::guests()->toString()])); + $this->assertEquals(false, User::isApp([User::ROLE_OWNER, Role::guests()->toString()])); + $this->assertEquals(false, User::isApp([User::ROLE_OWNER, User::ROLE_ADMIN, User::ROLE_DEVELOPER])); + } + + public function testGuestRoles(): void + { + $user = new User([ + '$id' => '' + ]); + + $roles = $user->getRoles($this->getAuthorization()); + $this->assertCount(1, $roles); + $this->assertContains(Role::guests()->toString(), $roles); + } + + public function testUserRoles(): void + { + $user = new User([ + '$id' => ID::custom('123'), + 'labels' => [ + 'vip', + 'admin' + ], + 'emailVerification' => true, + 'phoneVerification' => true, + 'memberships' => [ + [ + '$id' => ID::custom('456'), + 'teamId' => ID::custom('abc'), + 'confirm' => true, + 'roles' => [ + 'administrator', + 'moderator' + ] + ], + [ + '$id' => ID::custom('abc'), + 'teamId' => ID::custom('def'), + 'confirm' => true, + 'roles' => [ + 'guest' + ] + ] + ] + ]); + + $roles = $user->getRoles($this->getAuthorization()); + + $this->assertCount(13, $roles); + $this->assertContains(Role::users()->toString(), $roles); + $this->assertContains(Role::user(ID::custom('123'))->toString(), $roles); + $this->assertContains(Role::users(Roles::DIMENSION_VERIFIED)->toString(), $roles); + $this->assertContains(Role::user(ID::custom('123'), Roles::DIMENSION_VERIFIED)->toString(), $roles); + $this->assertContains(Role::team(ID::custom('abc'))->toString(), $roles); + $this->assertContains(Role::team(ID::custom('abc'), 'administrator')->toString(), $roles); + $this->assertContains(Role::team(ID::custom('abc'), 'moderator')->toString(), $roles); + $this->assertContains(Role::team(ID::custom('def'))->toString(), $roles); + $this->assertContains(Role::team(ID::custom('def'), 'guest')->toString(), $roles); + $this->assertContains(Role::member(ID::custom('456'))->toString(), $roles); + $this->assertContains(Role::member(ID::custom('abc'))->toString(), $roles); + $this->assertContains('label:vip', $roles); + $this->assertContains('label:admin', $roles); + + // Disable all verification + $user['emailVerification'] = false; + $user['phoneVerification'] = false; + + $roles = $user->getRoles($this->getAuthorization()); + $this->assertContains(Role::users(Roles::DIMENSION_UNVERIFIED)->toString(), $roles); + $this->assertContains(Role::user(ID::custom('123'), Roles::DIMENSION_UNVERIFIED)->toString(), $roles); + + // Enable single verification type + $user['emailVerification'] = true; + + $roles = $user->getRoles($this->getAuthorization()); + $this->assertContains(Role::users(Roles::DIMENSION_VERIFIED)->toString(), $roles); + $this->assertContains(Role::user(ID::custom('123'), Roles::DIMENSION_VERIFIED)->toString(), $roles); + } + + public function testPrivilegedUserRoles(): void + { + $this->getAuthorization()->addRole(User::ROLE_OWNER); + $user = new User([ + '$id' => ID::custom('123'), + 'emailVerification' => true, + 'phoneVerification' => true, + 'memberships' => [ + [ + '$id' => ID::custom('def'), + 'teamId' => ID::custom('abc'), + 'confirm' => true, + 'roles' => [ + 'administrator', + 'moderator' + ] + ], + [ + '$id' => ID::custom('abc'), + 'teamId' => ID::custom('def'), + 'confirm' => true, + 'roles' => [ + 'guest' + ] + ] + ] + ]); + $roles = $user->getRoles($this->getAuthorization()); + + $this->assertCount(7, $roles); + $this->assertNotContains(Role::users()->toString(), $roles); + $this->assertNotContains(Role::user(ID::custom('123'))->toString(), $roles); + $this->assertNotContains(Role::users(Roles::DIMENSION_VERIFIED)->toString(), $roles); + $this->assertNotContains(Role::user(ID::custom('123'), Roles::DIMENSION_VERIFIED)->toString(), $roles); + $this->assertContains(Role::team(ID::custom('abc'))->toString(), $roles); + $this->assertContains(Role::team(ID::custom('abc'), 'administrator')->toString(), $roles); + $this->assertContains(Role::team(ID::custom('abc'), 'moderator')->toString(), $roles); + $this->assertContains(Role::team(ID::custom('def'))->toString(), $roles); + $this->assertContains(Role::team(ID::custom('def'), 'guest')->toString(), $roles); + $this->assertContains(Role::member(ID::custom('def'))->toString(), $roles); + $this->assertContains(Role::member(ID::custom('abc'))->toString(), $roles); + } + + public function testAppUserRoles(): void + { + $this->getAuthorization()->addRole(User::ROLE_APPS); + $user = new User([ + '$id' => ID::custom('123'), + 'memberships' => [ + [ + '$id' => ID::custom('def'), + 'teamId' => ID::custom('abc'), + 'confirm' => true, + 'roles' => [ + 'administrator', + 'moderator' + ] + ], + [ + '$id' => ID::custom('abc'), + 'teamId' => ID::custom('def'), + 'confirm' => true, + 'roles' => [ + 'guest' + ] + ] + ] + ]); + + $roles = $user->getRoles($this->getAuthorization()); + + $this->assertCount(7, $roles); + $this->assertNotContains(Role::users()->toString(), $roles); + $this->assertNotContains(Role::user(ID::custom('123'))->toString(), $roles); + $this->assertContains(Role::team(ID::custom('abc'))->toString(), $roles); + $this->assertContains(Role::team(ID::custom('abc'), 'administrator')->toString(), $roles); + $this->assertContains(Role::team(ID::custom('abc'), 'moderator')->toString(), $roles); + $this->assertContains(Role::team(ID::custom('def'))->toString(), $roles); + $this->assertContains(Role::team(ID::custom('def'), 'guest')->toString(), $roles); + $this->assertContains(Role::member(ID::custom('def'))->toString(), $roles); + $this->assertContains(Role::member(ID::custom('abc'))->toString(), $roles); + } +}