diff --git a/CHANGES.md b/CHANGES.md index d724ab0d06..25bf60d2d8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,17 +1,70 @@ -# Version 1.0.0 +# Version 0.12.0 ## Features -- Grouped auth related attributes in project collection. Introduced new attribute `auths` and removed all attributes related to auth methods and `usersAuthLimit` as well, all these are grouped under `auths` attribute -- Grouped oAuth related attributes in project collection. Introduced new attribute `providers` and removed all attributes related to OAuth2 providers. All OAuth2 attributes are grouped under `providers` -- Project model changed, `userAuth` => `auth` example `userAuthEmailPassword` => `authEmailPassword`, also `userOauth2...` => `provider...` example `userOauth2GithubAppid` => `providerGithubAppid` - -# Version 0.12.0 - -## Breaking Changes (Read before upgrading!) - +- Completely rewritten Database service: + - Collection rules are now attributes + - Filters for have been replaced with a new, more powerful syntax + - Custom indexes for more performant queries + - Enum Attributes + - **DEPRECATED** Nested documents has been removed + - **DEPRECATED** Wildcard rule has been removed +- You can now set custom ID’s when creating following resources: + - User + - Team + - Function + - Project + - File + - Collection + - Document +- Wildcard permissions `*` are now `role:all` +- Collections can be enabled and disabled +- Permissions are now found as top-level keys `$read` and `$write` instead of nested under `$permissions` +- Accessing collections with insufficient permissions now return a `401` isntead of `404` status code +- Added Cursor pagination to all endpoints that provide pagination by offset +- Added new Usage worker to aggregate usage statistics +- Added new Database worker to handle heavy database tasks in the background +- Added detailed Usage statistics to following services in the Console: + - Users + - Storage + - Database +- You can now disable/enable following services in the Console: + - Account + - Avatars + - Database + - Locale + - Health + - Storage + - Teams + - Users + - Functions +- Added Flutter Desktop Support +- Fixed several memory leaks in the Console +- Added pagination to account activities in the Console +- Added following events from User service to Webhooks and Functions: + - `users.update.email` + - `users.update.name` + - `users.update.password` +- Added new environment variable `_APP_USAGE_AGGREGATION_INTERVAL` to configure the usage worker interval +- Added negative rotation values to file preview endpoint - Multiple HealthAPI response models were changed to new (better) schema - Method `health.getAntiVirus()` has been renamed to `health.getAntivirus()` +- Added following langauges to the Locale service: + - Latin + - Sindhi + - Telugu +- **DEPRECATED** Tasks service + +## Bugs +- Fixed `/v1/avatars/initials` when no space in the name, will try to split by `_` +- Fixed all audit logs now saving all relevant informations +- Fixed Health endpoints for `db` and `cache` + +## Security +- Increased minimum password length to 8 and removed maximum length +- Upgraded Redis to 6.2 +- Upgraded InfluxDB to 1.4.0 +- Upgraded Telegraf to 1.3.0 # Version 0.11.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cd28fab482..1a453a3b54 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -288,7 +288,7 @@ The Runtimes for all supported cloud functions (multicore builds) can be found a For generating a new console SDK follow the next steps: -1. Update the console spec file located at `app/config/specs/0.10.x.console.json` from the dynamic version located at `https://localhost/specs/swagger2?platform=console` +1. Update the console spec file located at `app/config/specs/swagger2-0.12.x.console.json` from the dynamic version located at `https://localhost/specs/swagger2?platform=console` 2. Generate a new SDK using the command `php app/cli.php sdks` 3. Change your working dir using `cd app/sdks/console-web` 4. Build the new SDK `npm run build` diff --git a/Dockerfile b/Dockerfile index fe515f7b53..e6f1334206 100755 --- a/Dockerfile +++ b/Dockerfile @@ -259,6 +259,7 @@ RUN chmod +x /usr/local/bin/doctor && \ chmod +x /usr/local/bin/realtime && \ chmod +x /usr/local/bin/schedule && \ chmod +x /usr/local/bin/sdks && \ + chmod +x /usr/local/bin/specs && \ chmod +x /usr/local/bin/ssl && \ chmod +x /usr/local/bin/test && \ chmod +x /usr/local/bin/vars && \ diff --git a/app/cli.php b/app/cli.php index 99a77d6382..d2c6496dc3 100644 --- a/app/cli.php +++ b/app/cli.php @@ -1,6 +1,6 @@ setParam('body', $page); }); -App::get('/specs/:format') - ->groups(['web', 'home']) - ->label('scope', 'public') - ->label('docs', false) - ->label('origin', '*') - ->param('format', 'swagger2', new WhiteList(['swagger2', 'open-api3'], true), 'Spec format.', true) - ->param('platform', APP_PLATFORM_CLIENT, new WhiteList([APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER, APP_PLATFORM_CONSOLE], true), 'Choose target platform.', true) - ->param('tests', 0, function () {return new Range(0, 1);}, 'Include only test services.', true) - ->inject('utopia') - ->inject('request') - ->inject('response') - ->action(function ($format, $platform, $tests, $utopia, $request, $response) { - /** @var Utopia\App $utopia */ - /** @var Appwrite\Utopia\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - - $platforms = [ - 'client' => APP_PLATFORM_CLIENT, - 'server' => APP_PLATFORM_SERVER, - 'console' => APP_PLATFORM_CONSOLE, - ]; - - $authCounts = [ - 'client' => 1, - 'server' => 2, - 'console' => 1, - ]; - - $routes = []; - $models = []; - $services = []; - - $keys = [ - APP_PLATFORM_CLIENT => [ - 'Project' => [ - 'type' => 'apiKey', - 'name' => 'X-Appwrite-Project', - 'description' => 'Your project ID', - 'in' => 'header', - ], - 'JWT' => [ - 'type' => 'apiKey', - 'name' => 'X-Appwrite-JWT', - 'description' => 'Your secret JSON Web Token', - 'in' => 'header', - ], - 'Locale' => [ - 'type' => 'apiKey', - 'name' => 'X-Appwrite-Locale', - 'description' => '', - 'in' => 'header', - ], - ], - APP_PLATFORM_SERVER => [ - 'Project' => [ - 'type' => 'apiKey', - 'name' => 'X-Appwrite-Project', - 'description' => 'Your project ID', - 'in' => 'header', - ], - 'Key' => [ - 'type' => 'apiKey', - 'name' => 'X-Appwrite-Key', - 'description' => 'Your secret API key', - 'in' => 'header', - ], - 'JWT' => [ - 'type' => 'apiKey', - 'name' => 'X-Appwrite-JWT', - 'description' => 'Your secret JSON Web Token', - 'in' => 'header', - ], - 'Locale' => [ - 'type' => 'apiKey', - 'name' => 'X-Appwrite-Locale', - 'description' => '', - 'in' => 'header', - ], - ], - APP_PLATFORM_CONSOLE => [ - 'Project' => [ - 'type' => 'apiKey', - 'name' => 'X-Appwrite-Project', - 'description' => 'Your project ID', - 'in' => 'header', - ], - 'Key' => [ - 'type' => 'apiKey', - 'name' => 'X-Appwrite-Key', - 'description' => 'Your secret API key', - 'in' => 'header', - ], - 'JWT' => [ - 'type' => 'apiKey', - 'name' => 'X-Appwrite-JWT', - 'description' => 'Your secret JSON Web Token', - 'in' => 'header', - ], - 'Locale' => [ - 'type' => 'apiKey', - 'name' => 'X-Appwrite-Locale', - 'description' => '', - 'in' => 'header', - ], - 'Mode' => [ - 'type' => 'apiKey', - 'name' => 'X-Appwrite-Mode', - 'description' => '', - 'in' => 'header', - ], - ], - ]; - - foreach ($utopia->getRoutes() as $key => $method) { - foreach ($method as $route) { /** @var \Utopia\Route $route */ - $routeSecurity = $route->getLabel('sdk.auth', []); - $sdkPlatofrms = []; - - foreach ($routeSecurity as $value) { - switch ($value) { - case APP_AUTH_TYPE_SESSION: - $sdkPlatofrms[] = APP_PLATFORM_CLIENT; - break; - case APP_AUTH_TYPE_KEY: - $sdkPlatofrms[] = APP_PLATFORM_SERVER; - break; - case APP_AUTH_TYPE_JWT: - $sdkPlatofrms[] = APP_PLATFORM_SERVER; - break; - case APP_AUTH_TYPE_ADMIN: - $sdkPlatofrms[] = APP_PLATFORM_CONSOLE; - break; - } - } - - if(empty($routeSecurity)) { - $sdkPlatofrms[] = APP_PLATFORM_CLIENT; - } - - if (!$route->getLabel('docs', true)) { - continue; - } - - if ($route->getLabel('sdk.mock', false) && !$tests) { - continue; - } - - if (!$route->getLabel('sdk.mock', false) && $tests) { - continue; - } - - if (empty($route->getLabel('sdk.namespace', null))) { - continue; - } - - if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $sdkPlatofrms)) { - continue; - } - - $routes[] = $route; - $modelLabel = $route->getLabel('sdk.response.model', 'none'); - $model = \is_array($modelLabel) ? \array_map(function($m) use($response) { - return $response->getModel($m); - }, $modelLabel) : $response->getModel($modelLabel); - } - } - - foreach (Config::getParam('services', []) as $service) { - if(!isset($service['docs']) // Skip service if not part of the public API - || !isset($service['sdk']) - || !$service['docs'] - || !$service['sdk']) { - continue; - } - - $services[] = [ - 'name' => $service['key'] ?? '', - 'description' => $service['subtitle'] ?? '', - ]; - } - - $models = $response->getModels(); - - foreach ($models as $key => $value) { - if($platform !== APP_PLATFORM_CONSOLE && !$value->isPublic()) { - unset($models[$key]); - } - } - - switch ($format) { - case 'swagger2': - $format = new Swagger2($utopia, $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0); - break; - - case 'open-api3': - $format = new OpenAPI3($utopia, $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0); - break; - - default: - throw new Exception('Format not found', 404); - break; - } - - $specs = new Specification($format); - - $format - ->setParam('name', APP_NAME) - ->setParam('description', 'Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)') - ->setParam('endpoint', App::getEnv('_APP_HOME', $request->getProtocol().'://'.$request->getHostname()).'/v1') - ->setParam('version', APP_VERSION_STABLE) - ->setParam('terms', App::getEnv('_APP_HOME', $request->getProtocol().'://'.$request->getHostname()).'/policy/terms') - ->setParam('support.email', App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM)) - ->setParam('support.url', App::getEnv('_APP_HOME', $request->getProtocol().'://'.$request->getHostname()).'/support') - ->setParam('contact.name', APP_NAME.' Team') - ->setParam('contact.email', App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM)) - ->setParam('contact.url', App::getEnv('_APP_HOME', $request->getProtocol().'://'.$request->getHostname()).'/support') - ->setParam('license.name', 'BSD-3-Clause') - ->setParam('license.url', 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE') - ->setParam('docs.description', 'Full API docs, specs and tutorials') - ->setParam('docs.url', App::getEnv('_APP_HOME', $request->getProtocol().'://'.$request->getHostname()).'/docs') - ; - - $response - ->json($specs->parse()); - }); - App::get('/versions') ->desc('Get Version') ->groups(['web', 'home']) diff --git a/app/realtime.php b/app/realtime.php index 6db9ce718a..bef9a4ae6c 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -510,7 +510,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re } switch ($message['type']) { - /** + /** * This type is used to authenticate. */ case 'authentication': diff --git a/app/tasks/maintenance.php b/app/tasks/maintenance.php index e2fa90fcd3..a6f37ff71b 100644 --- a/app/tasks/maintenance.php +++ b/app/tasks/maintenance.php @@ -2,8 +2,6 @@ global $cli; -require_once __DIR__.'/../init.php'; - use Appwrite\Event\Event; use Utopia\App; use Utopia\CLI\Console; diff --git a/app/tasks/sdks.php b/app/tasks/sdks.php index 5fcf9efe6c..22b09a3f7b 100644 --- a/app/tasks/sdks.php +++ b/app/tasks/sdks.php @@ -30,38 +30,38 @@ $cli $production = ($git) ? (Console::confirm('Type "Appwrite" to push code to production git repos') == 'Appwrite') : false; $message = ($git) ? Console::confirm('Please enter your commit message:') : ''; - if(!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x'])) { + if (!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', 'latest'])) { throw new Exception('Unknown version given'); } - foreach($platforms as $key => $platform) { - foreach($platform['languages'] as $language) { - if($selected !== $language['key'] && $selected !== '*') { + foreach ($platforms as $key => $platform) { + foreach ($platform['languages'] as $language) { + if ($selected !== $language['key'] && $selected !== '*') { continue; } - if(!$language['enabled']) { - Console::warning($language['name'].' for '.$platform['name'] . ' is disabled'); + if (!$language['enabled']) { + Console::warning($language['name'] . ' for ' . $platform['name'] . ' is disabled'); continue; } - Console::info('Fetching API Spec for '.$language['name'].' for '.$platform['name'] . ' (version: '.$version.')'); - - $spec = file_get_contents(__DIR__.'/../config/specs/'.$version.'.'.$language['family'].'.json'); + Console::info('Fetching API Spec for ' . $language['name'] . ' for ' . $platform['name'] . ' (version: ' . $version . ')'); + + $spec = file_get_contents(__DIR__ . '/../config/specs/swagger2-' . $version . '-' . $language['family'] . '.json'); $cover = 'https://appwrite.io/images/github.png'; - $result = \realpath(__DIR__.'/..').'/sdks/'.$key.'-'.$language['key']; - $resultExamples = \realpath(__DIR__.'/../..').'/docs/examples/'.$version.'/'.$key.'-'.$language['key']; - $target = \realpath(__DIR__.'/..').'/sdks/git/'.$language['key'].'/'; - $readme = \realpath(__DIR__ . '/../../docs/sdks/'.$language['key'].'/README.md'); + $result = \realpath(__DIR__ . '/..') . '/sdks/' . $key . '-' . $language['key']; + $resultExamples = \realpath(__DIR__ . '/../..') . '/docs/examples/' . $version . '/' . $key . '-' . $language['key']; + $target = \realpath(__DIR__ . '/..') . '/sdks/git/' . $language['key'] . '/'; + $readme = \realpath(__DIR__ . '/../../docs/sdks/' . $language['key'] . '/README.md'); $readme = ($readme) ? \file_get_contents($readme) : ''; - $gettingStarted = \realpath(__DIR__ . '/../../docs/sdks/'.$language['key'].'/GETTING_STARTED.md'); + $gettingStarted = \realpath(__DIR__ . '/../../docs/sdks/' . $language['key'] . '/GETTING_STARTED.md'); $gettingStarted = ($gettingStarted) ? \file_get_contents($gettingStarted) : ''; - $examples = \realpath(__DIR__ . '/../../docs/sdks/'.$language['key'].'/EXAMPLES.md'); + $examples = \realpath(__DIR__ . '/../../docs/sdks/' . $language['key'] . '/EXAMPLES.md'); $examples = ($examples) ? \file_get_contents($examples) : ''; - $changelog = \realpath(__DIR__ . '/../../docs/sdks/'.$language['key'].'/CHANGELOG.md'); + $changelog = \realpath(__DIR__ . '/../../docs/sdks/' . $language['key'] . '/CHANGELOG.md'); $changelog = ($changelog) ? \file_get_contents($changelog) : '# Change Log'; - $warning = '**This SDK is compatible with Appwrite server version ' . $version . '. For older versions, please check [previous releases]('.$language['url'].'/releases).**'; + $warning = '**This SDK is compatible with Appwrite server version ' . $version . '. For older versions, please check [previous releases](' . $language['url'] . '/releases).**'; $license = 'BSD-3-Clause'; $licenseContent = 'Copyright (c) ' . date('Y') . ' Appwrite (https://appwrite.io) and individual contributors. All rights reserved. @@ -105,7 +105,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND $config = new Node(); $config->setNPMPackage('node-appwrite'); $config->setBowerPackage('appwrite'); - $warning = $warning."\n\n > This is the Node.js SDK for integrating with Appwrite from your Node.js server-side code. + $warning = $warning . "\n\n > This is the Node.js SDK for integrating with Appwrite from your Node.js server-side code. If you're looking to integrate from the browser, you should check [appwrite/sdk-for-web](https://github.com/appwrite/sdk-for-web)"; break; case 'deno': @@ -115,7 +115,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND $config = new Python(); $config->setPipPackage('appwrite'); $license = 'BSD License'; // license edited due to classifiers in pypi - break; + break; case 'ruby': $config = new Ruby(); $config->setGemPackage('appwrite'); @@ -131,14 +131,14 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND case 'dart': $config = new Dart(); $config->setPackageName('dart_appwrite'); - $warning = $warning."\n\n > This is the Dart SDK for integrating with Appwrite from your Dart server-side code. If you're looking for the Flutter SDK you should check [appwrite/sdk-for-flutter](https://github.com/appwrite/sdk-for-flutter)"; + $warning = $warning . "\n\n > This is the Dart SDK for integrating with Appwrite from your Dart server-side code. If you're looking for the Flutter SDK you should check [appwrite/sdk-for-flutter](https://github.com/appwrite/sdk-for-flutter)"; break; case 'go': $config = new Go(); break; case 'swift': $config = new Swift(); - $warning = $warning."\n\n > This is the Swift SDK for integrating with Appwrite from your Swift server-side code. If you're looking for the Apple SDK you should check [appwrite/sdk-for-apple](https://github.com/appwrite/sdk-for-apple)"; + $warning = $warning . "\n\n > This is the Swift SDK for integrating with Appwrite from your Swift server-side code. If you're looking for the Apple SDK you should check [appwrite/sdk-for-apple](https://github.com/appwrite/sdk-for-apple)"; break; case 'apple': $config = new SwiftClient(); @@ -152,10 +152,10 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND break; case 'kotlin': $config = new Kotlin(); - $warning = $warning."\n\n > This is the Kotlin SDK for integrating with Appwrite from your Kotlin server-side code. If you're looking for the Android SDK you should check [appwrite/sdk-for-android](https://github.com/appwrite/sdk-for-android)"; + $warning = $warning . "\n\n > This is the Kotlin SDK for integrating with Appwrite from your Kotlin server-side code. If you're looking for the Android SDK you should check [appwrite/sdk-for-android](https://github.com/appwrite/sdk-for-android)"; break; default: - throw new Exception('Language "'.$language['key'].'" not supported'); + throw new Exception('Language "' . $language['key'] . '" not supported'); break; } @@ -189,10 +189,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ->setTwitter(APP_SOCIAL_TWITTER_HANDLE) ->setDiscord(APP_SOCIAL_DISCORD_CHANNEL, APP_SOCIAL_DISCORD) ->setDefaultHeaders([ - 'X-Appwrite-Response-Format' => '0.11.0', - ]) - ; - + 'X-Appwrite-Response-Format' => '0.12.0', + ]); + try { $sdk->generate($result); } catch (Exception $exception) { @@ -204,38 +203,43 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND $gitUrl = $language['gitUrl']; $gitBranch = $language['gitBranch']; - - if(!$production) { - $gitUrl = 'git@github.com:aw-tests/'.$language['gitRepoName'].'.git'; + + if (!$production) { + $gitUrl = 'git@github.com:aw-tests/' . $language['gitRepoName'] . '.git'; } - - if($git && !empty($gitUrl)) { - \exec('rm -rf '.$target.' && \ - mkdir -p '.$target.' && \ - cd '.$target.' && \ - git init --initial-branch='.$gitBranch.' && \ - git remote add origin '.$gitUrl.' && \ + + if ($git && !empty($gitUrl)) { + \exec('rm -rf ' . $target . ' && \ + mkdir -p ' . $target . ' && \ + cd ' . $target . ' && \ + git init --initial-branch=' . $gitBranch . ' && \ + git remote add origin ' . $gitUrl . ' && \ git fetch && \ - git pull '.$gitUrl.' && \ - rm -rf '.$target.'/* && \ - cp -r '.$result.'/ '.$target.'/ && \ + git pull ' . $gitUrl . ' && \ + rm -rf ' . $target . '/* && \ + cp -r ' . $result . '/ ' . $target . '/ && \ git add . && \ - git commit -m "'.$message.'" && \ - git push -u origin '.$gitBranch.' + git commit -m "' . $message . '" && \ + git push -u origin ' . $gitBranch . ' '); Console::success("Pushed {$language['name']} SDK to {$gitUrl}"); - \exec('rm -rf '.$target); + \exec('rm -rf ' . $target); Console::success("Remove temp directory '{$target}' for {$language['name']} SDK"); } $docDirectories = $language['docDirectories'] ?? ['']; + + if($version === 'latest') { + continue; + } + foreach ($docDirectories as $languageTitle => $path) { - $languagePath = strtolower($languageTitle !== 0 ? '/'.$languageTitle : ''); + $languagePath = strtolower($languageTitle !== 0 ? '/' . $languageTitle : ''); \exec( - 'mkdir -p '.$resultExamples.$languagePath.' && \ - cp -r '.$result.'/docs/examples'.$languagePath.' '.$resultExamples + 'mkdir -p ' . $resultExamples . $languagePath . ' && \ + cp -r ' . $result . '/docs/examples' . $languagePath . ' ' . $resultExamples ); Console::success("Copied code examples for {$language['name']} SDK to: {$resultExamples}"); } diff --git a/app/tasks/specs.php b/app/tasks/specs.php new file mode 100644 index 0000000000..dc073177ca --- /dev/null +++ b/app/tasks/specs.php @@ -0,0 +1,266 @@ +task('specs') + ->param('version', 'latest', new Text(8), 'Spec version', true) + ->param('mode', 'normal', new WhiteList(['normal', 'mocks']), 'Spec Mode', true) + ->action(function ($version, $mode) use ($register) { + $db = $register->get('db'); + $redis = $register->get('cache'); + $appRoutes = App::getRoutes(); + $response = new Response(new HttpResponse()); + $mocks = ($mode === 'mocks'); + + App::setResource('request', fn() => new Request); + App::setResource('response', fn() => $response); + + App::setResource('db', fn() => $db); + App::setResource('cache', fn() => $redis); + + $platforms = [ + 'client' => APP_PLATFORM_CLIENT, + 'server' => APP_PLATFORM_SERVER, + 'console' => APP_PLATFORM_CONSOLE, + ]; + + $authCounts = [ + 'client' => 1, + 'server' => 2, + 'console' => 1, + ]; + + $keys = [ + APP_PLATFORM_CLIENT => [ + 'Project' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-Project', + 'description' => 'Your project ID', + 'in' => 'header', + ], + 'JWT' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-JWT', + 'description' => 'Your secret JSON Web Token', + 'in' => 'header', + ], + 'Locale' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-Locale', + 'description' => '', + 'in' => 'header', + ], + ], + APP_PLATFORM_SERVER => [ + 'Project' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-Project', + 'description' => 'Your project ID', + 'in' => 'header', + ], + 'Key' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-Key', + 'description' => 'Your secret API key', + 'in' => 'header', + ], + 'JWT' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-JWT', + 'description' => 'Your secret JSON Web Token', + 'in' => 'header', + ], + 'Locale' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-Locale', + 'description' => '', + 'in' => 'header', + ], + ], + APP_PLATFORM_CONSOLE => [ + 'Project' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-Project', + 'description' => 'Your project ID', + 'in' => 'header', + ], + 'Key' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-Key', + 'description' => 'Your secret API key', + 'in' => 'header', + ], + 'JWT' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-JWT', + 'description' => 'Your secret JSON Web Token', + 'in' => 'header', + ], + 'Locale' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-Locale', + 'description' => '', + 'in' => 'header', + ], + 'Mode' => [ + 'type' => 'apiKey', + 'name' => 'X-Appwrite-Mode', + 'description' => '', + 'in' => 'header', + ], + ], + ]; + + foreach (['swagger2', 'open-api3'] as $format) { + foreach ($platforms as $platform) { + + $routes = []; + $models = []; + $services = []; + + foreach ($appRoutes as $key => $method) { + foreach ($method as $route) { /** @var \Utopia\Route $route */ + $routeSecurity = $route->getLabel('sdk.auth', []); + $sdkPlatofrms = []; + + foreach ($routeSecurity as $value) { + switch ($value) { + case APP_AUTH_TYPE_SESSION: + $sdkPlatofrms[] = APP_PLATFORM_CLIENT; + break; + case APP_AUTH_TYPE_KEY: + $sdkPlatofrms[] = APP_PLATFORM_SERVER; + break; + case APP_AUTH_TYPE_JWT: + $sdkPlatofrms[] = APP_PLATFORM_SERVER; + break; + case APP_AUTH_TYPE_ADMIN: + $sdkPlatofrms[] = APP_PLATFORM_CONSOLE; + break; + } + } + + if(empty($routeSecurity)) { + $sdkPlatofrms[] = APP_PLATFORM_CLIENT; + } + + if (!$route->getLabel('docs', true)) { + continue; + } + + if ($route->getLabel('sdk.mock', false) && !$mocks) { + continue; + } + + if (!$route->getLabel('sdk.mock', false) && $mocks) { + continue; + } + + if (empty($route->getLabel('sdk.namespace', null))) { + continue; + } + + if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $sdkPlatofrms)) { + continue; + } + + $routes[] = $route; + $modelLabel = $route->getLabel('sdk.response.model', 'none'); + \is_array($modelLabel) ? \array_map(function($m) use($response) { + return $response->getModel($m); + }, $modelLabel) : $response->getModel($modelLabel); + } + } + + foreach (Config::getParam('services', []) as $service) { + if(!isset($service['docs']) // Skip service if not part of the public API + || !isset($service['sdk']) + || !$service['docs'] + || !$service['sdk']) { + continue; + } + + $services[] = [ + 'name' => $service['key'] ?? '', + 'description' => $service['subtitle'] ?? '', + ]; + } + + $models = $response->getModels(); + + foreach ($models as $key => $value) { + if($platform !== APP_PLATFORM_CONSOLE && !$value->isPublic()) { + unset($models[$key]); + } + } + + switch ($format) { + case 'swagger2': + $formatInstance = new Swagger2(new App('UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0); + break; + + case 'open-api3': + $formatInstance = new OpenAPI3(new App('UTC'), $services, $routes, $models, $keys[$platform], $authCounts[$platform] ?? 0); + break; + + default: + throw new Exception('Format not found: '.$format); + break; + } + + $specs = new Specification($formatInstance); + $endpoint = App::getEnv('_APP_HOME', '[HOSTNAME]'); + $email = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM); + + $formatInstance + ->setParam('name', APP_NAME) + ->setParam('description', 'Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)') + ->setParam('endpoint', 'https://HOSTNAME/v1') + ->setParam('version', APP_VERSION_STABLE) + ->setParam('terms', $endpoint.'/policy/terms') + ->setParam('support.email', $email) + ->setParam('support.url', $endpoint.'/support') + ->setParam('contact.name', APP_NAME.' Team') + ->setParam('contact.email', $email) + ->setParam('contact.url', $endpoint.'/support') + ->setParam('license.name', 'BSD-3-Clause') + ->setParam('license.url', 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE') + ->setParam('docs.description', 'Full API docs, specs and tutorials') + ->setParam('docs.url', $endpoint.'/docs') + ; + + if($mocks) { + $path = __DIR__.'/../config/specs/'.$format.'-mocks-'.$platform.'.json'; + + if(!file_put_contents($path, json_encode($specs->parse()))) { + throw new Exception('Failed to save mocks spec file: '.$path); + } + + Console::success('Saved mocks spec file: ' . realpath($path)); + + continue; + } + + $path = __DIR__.'/../config/specs/'.$format.'-'.$version.'-'.$platform.'.json'; + + if(!file_put_contents($path, json_encode($specs->parse()))) { + throw new Exception('Failed to save spec file: '.$path); + } + + Console::success('Saved spec file: ' . realpath($path)); + } + } + }); \ No newline at end of file diff --git a/app/tasks/usage.php b/app/tasks/usage.php index 690da5e2ff..e341dad3d8 100644 --- a/app/tasks/usage.php +++ b/app/tasks/usage.php @@ -2,8 +2,6 @@ global $cli, $register; -require_once __DIR__ . '/../init.php'; - use Utopia\App; use Utopia\Cache\Adapter\Redis; use Utopia\Cache\Cache; diff --git a/app/views/console/database/collection.phtml b/app/views/console/database/collection.phtml index 34dc84e913..50892bad1d 100644 --- a/app/views/console/database/collection.phtml +++ b/app/views/console/database/collection.phtml @@ -155,10 +155,8 @@ $logs = $this->getParam('logs', null); Attribute ID Type - Length - Default - + @@ -186,52 +184,116 @@ $logs = $this->getParam('logs', null); - - - - - - - n/a - - required -
+
-
+
  • @@ -543,22 +605,23 @@ $logs = $this->getParam('logs', null);

    Add String Attribute

    -
    + @@ -571,48 +634,25 @@ $logs = $this->getParam('logs', null);
    -   Required +   Required
    -   Array +   Array
    -
    - + + +
     
    - - -
    -   Required +   Required
    -   Array +   Array
    - + +
     
    -