diff --git a/CHANGES.md b/CHANGES.md index eba15ca01d..d0cd072bc4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,12 +2,37 @@ - Anonymous login -# Version 0.7.1 (Not Released Yet) +# Version 0.7.1 + +## Features + +- Better error logs on appwrite certificates worker +- Added option for Redis authentication +- Force adding a security email on setup +- SMTP is now disabled by default, no dummy SMTP is included in setup +- Added a new endpoint that returns the server and SDKs latest versions numbers #941 + +## Upgrades + +- Upgraded redis extenstion lib to version 5.3.3 +- Upgraded maxmind extenstion lib to version 1.10.0 +- Upgraded utopia-php/cli lib to version 0.10.0 +- Upgraded matomo/device-detector lib to version 4.1.0 +- Upgraded dragonmantank/cron-expression lib to version 3.1.0 +- Upgraded influxdb/influxdb-php lib to version 1.15.2 +- Upgraded phpmailer/phpmailer lib to version 6.3.0 +- Upgraded adhocore/jwt lib to version 1.1.2 +- Upgraded domnikl/statsd to slickdeals/statsd version 3.0 + +## Bug Fixes - Updated missing storage env vars -- Added option to for Redis authentication -- Force adding a security email on setup -- Better error logs on appwrite cretificates worker +- Fixed a bug, that Response format header was not added in the access-control-allow-header list. +- Fixed a bug where countryName is unknown on sessions (#933) + +## Security + +- Fixed an XSS vulnerability in the Appwrite console # Version 0.7.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a994337483..dd7217a9af 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -143,7 +143,6 @@ Appwrite's current structure is a combination of both [Monolithic](https://en.wi ├── docs # Docs and tutorials │ ├── examples │ ├── references -│ ├── sdks │ ├── services │ ├── specs │ └── tutorials @@ -157,13 +156,14 @@ Appwrite's current structure is a combination of both [Monolithic](https://en.wi │ └── Appwrite │ ├── Auth │ ├── Database +│ ├── Detector │ ├── Docker │ ├── Event │ ├── Extend +│ ├── Migration │ ├── Network │ ├── OpenSSL -│ ├── Resize -│ ├── Storage +│ ├── Specification │ ├── Task │ ├── Template │ ├── URL diff --git a/Dockerfile b/Dockerfile index c7316bd1d4..7cd404b7ae 100755 --- a/Dockerfile +++ b/Dockerfile @@ -14,9 +14,9 @@ RUN composer update --ignore-platform-reqs --optimize-autoloader \ FROM php:7.4-cli-alpine as step1 -ENV PHP_REDIS_VERSION=5.3.0 \ +ENV PHP_REDIS_VERSION=5.3.3 \ PHP_SWOOLE_VERSION=v4.5.8 \ - PHP_MAXMINDDB_VERSION=v1.8.0 \ + PHP_MAXMINDDB_VERSION=v1.10.0 \ PHP_XDEBUG_VERSION=sdebug_2_9-beta RUN \ @@ -90,8 +90,11 @@ ENV _APP_SERVER=swoole \ _APP_INFLUXDB_PORT=8086 \ _APP_STATSD_HOST=telegraf \ _APP_STATSD_PORT=8125 \ - _APP_SMTP_HOST=smtp \ - _APP_SMTP_PORT=25 \ + _APP_SMTP_HOST= \ + _APP_SMTP_PORT= \ + _APP_SMTP_SECURE= \ + _APP_SMTP_USERNAME= \ + _APP_SMTP_PASSWORD= \ _APP_FUNCTIONS_TIMEOUT=900 \ _APP_FUNCTIONS_CONTAINERS=10 \ _APP_FUNCTIONS_CPUS=1 \ @@ -106,9 +109,6 @@ ENV _APP_SERVER=swoole \ # 1 Day = 86400 s _APP_MAINTENANCE_RETENTION_ABUSE=86400 \ _APP_MAINTENANCE_INTERVAL=86400 -#ENV _APP_SMTP_SECURE '' -#ENV _APP_SMTP_USERNAME '' -#ENV _APP_SMTP_PASSWORD '' RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone diff --git a/README.md b/README.md index 950c5a3010..3d4bd75733 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:0.7.0 + appwrite/appwrite:0.7.1 ``` ### Windows @@ -65,7 +65,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:0.7.0 + appwrite/appwrite:0.7.1 ``` #### PowerShell @@ -75,7 +75,7 @@ docker run -it --rm , --volume /var/run/docker.sock:/var/run/docker.sock , --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw , --entrypoint="install" , - appwrite/appwrite:0.7.0 + appwrite/appwrite:0.7.1 ``` Once the Docker installation completes, go to http://localhost to access the Appwrite console from your browser. Please note that on non-linux native hosts, the server might take a few minutes to start after installation completes. diff --git a/app/config/platforms.php b/app/config/platforms.php index 399d52e74b..ab7a30fdb1 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -32,7 +32,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '0.3.0', + 'version' => '0.4.0-dev.2', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -165,7 +165,7 @@ return [ [ 'key' => 'deno', 'name' => 'Deno', - 'version' => '0.1.0', + 'version' => '0.1.1', 'url' => 'https://github.com/appwrite/sdk-for-deno', 'package' => 'https://deno.land/x/appwrite', 'enabled' => true, @@ -199,7 +199,7 @@ return [ [ 'key' => 'python', 'name' => 'Python', - 'version' => '0.1.0', + 'version' => '0.1.1', 'url' => 'https://github.com/appwrite/sdk-for-python', 'package' => 'https://pypi.org/project/appwrite/', 'enabled' => true, @@ -216,7 +216,7 @@ return [ [ 'key' => 'ruby', 'name' => 'Ruby', - 'version' => '2.0.0', + 'version' => '2.0.2', 'url' => 'https://github.com/appwrite/sdk-for-ruby', 'package' => 'https://rubygems.org/gems/appwrite', 'enabled' => true, @@ -284,12 +284,12 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '0.2.0', + 'version' => '0.4.0', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, 'beta' => true, - 'dev' => true, + 'dev' => false, 'hidden' => false, 'family' => APP_PLATFORM_SERVER, 'prism' => 'dart', @@ -301,7 +301,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '0.5.0', + 'version' => '0.6.0', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://github.com/appwrite/sdk-for-cli', 'enabled' => true, diff --git a/app/config/variables.php b/app/config/variables.php index e842283c2c..369a860fe2 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -105,9 +105,9 @@ return [ ], [ 'name' => '_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', - 'description' => 'This is the email address used to issue SSL certificates for custom domains or the user agent in webhooks. The default value is \'security@localhost.test\'.', + 'description' => 'This is the email address used to issue SSL certificates for custom domains or the user agent in your webhooks payload.', 'introduction' => '0.7.0', - 'default' => 'security@localhost.test', + 'default' => 'certs@appwrite.io', 'required' => false, 'question' => '', ], @@ -141,6 +141,22 @@ return [ 'required' => false, 'question' => '', ], + [ + 'name' => '_APP_REDIS_USER', + 'description' => 'Redis server user.', + 'introduction' => '0.7', + 'default' => '', + 'required' => false, + 'question' => '', + ], + [ + 'name' => '_APP_REDIS_PASS', + 'description' => 'Redis server password.', + 'introduction' => '0.7', + 'default' => '', + 'required' => false, + 'question' => '', + ], ], ], [ @@ -239,17 +255,17 @@ return [ 'variables' => [ [ 'name' => '_APP_SMTP_HOST', - 'description' => 'SMTP server host name address. Default value is: \'smtp\'. Pass an empty string to disable all mail sending from the server.', + 'description' => 'SMTP server host name address. Use an empty string to disable all mail sending from the server. The default value for this variable is an empty string', 'introduction' => '', - 'default' => 'smtp', + 'default' => '', 'required' => false, 'question' => '', ], [ 'name' => '_APP_SMTP_PORT', - 'description' => 'SMTP server TCP port. Default value is: \'25\'.', + 'description' => 'SMTP server TCP port. Empty by default.', 'introduction' => '', - 'default' => '25', + 'default' => '', 'required' => false, 'question' => '', ], diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index e8ae76312b..fd09aaa3d5 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -236,9 +236,11 @@ App::post('/v1/account/sessions') ->setStatusCode(Response::STATUS_CODE_CREATED) ; + $countries = $locale->getText('countries'); + $session ->setAttribute('current', true) - ->setAttribute('countryName', (isset($countries[$session->getAttribute('countryCode')])) ? $countries[$session->getAttribute('countryCode')] : $locale->getText('locale.country.unknown')) + ->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))])) ? $countries[strtoupper($session->getAttribute('countryCode'))] : $locale->getText('locale.country.unknown')) ; $response->dynamic($session, Response::MODEL_SESSION); @@ -253,8 +255,8 @@ App::get('/v1/account/sessions/oauth2/:provider') ->label('sdk.namespace', 'account') ->label('sdk.method', 'createOAuth2Session') ->label('sdk.description', '/docs/references/account/create-session-oauth2.md') - ->label('sdk.response.code', 301) - ->label('sdk.response.type', 'text/html') + ->label('sdk.response.code', Response::STATUS_CODE_MOVED_PERMANENTLY) + ->label('sdk.response.type', Response::CONTENT_TYPE_HTML) ->label('sdk.methodType', 'webAuth') ->label('abuse-limit', 50) ->label('abuse-key', 'ip:{ip}') @@ -679,8 +681,8 @@ App::get('/v1/account/sessions') continue; } - $token->setAttribute('countryName', (isset($countries[$token->getAttribute('contryCode')])) - ? $countries[$token->getAttribute('contryCode')] + $token->setAttribute('countryName', (isset($countries[strtoupper($token->getAttribute('countryCode'))])) + ? $countries[strtoupper($token->getAttribute('countryCode'))] : $locale->getText('locale.country.unknown')); $token->setAttribute('current', ($current == $token->getId()) ? true : false); diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index fe2b53f71f..a094dc672a 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -1,28 +1,28 @@ load($key, 60 * 60 * 24 * 30 * 3 /* 3 months */); + $cache = new Cache(new Filesystem(APP_STORAGE_CACHE . '/app-0')); // Limit file number or size + $data = $cache->load($key, 60 * 60 * 24 * 30 * 3/* 3 months */); if ($data) { //$output = (empty($output)) ? $type : $output; @@ -60,24 +60,23 @@ $avatarCallback = function ($type, $code, $width, $height, $quality, $response) ; } - $resize = new Resize(\file_get_contents($path)); + $image = new Image(\file_get_contents($path)); - $resize->crop((int) $width, (int) $height); + $image->crop((int) $width, (int) $height); $output = (empty($output)) ? $type : $output; - $data = $resize->output($output, $quality); - + $data = $image->output($output, $quality); + $cache->save($key, $data); - + $response ->setContentType('image/png') ->addHeader('Expires', $date) ->addHeader('X-Appwrite-Cache', 'miss') ->send($data, null); - ; - unset($resize); + unset($image); }; App::get('/v1/avatars/credit-cards/:code') @@ -91,7 +90,7 @@ App::get('/v1/avatars/credit-cards/:code') ->label('sdk.description', '/docs/references/avatars/get-credit-card.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG) - ->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-credit-cards'))), 'Credit Card Code. Possible values: '.\implode(', ', \array_keys(Config::getParam('avatar-credit-cards'))).'.') + ->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-credit-cards'))), 'Credit Card Code. Possible values: ' . \implode(', ', \array_keys(Config::getParam('avatar-credit-cards'))) . '.') ->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true) ->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true) ->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true) @@ -160,11 +159,11 @@ App::get('/v1/avatars/image') $quality = 80; $output = 'png'; - $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache - $key = \md5('/v2/avatars/images-'.$url.'-'.$width.'/'.$height.'/'.$quality); + $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT'; // 45 days cache + $key = \md5('/v2/avatars/images-' . $url . '-' . $width . '/' . $height . '/' . $quality); $type = 'png'; - $cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-0')); // Limit file number or size - $data = $cache->load($key, 60 * 60 * 24 * 7 /* 1 week */); + $cache = new Cache(new Filesystem(APP_STORAGE_CACHE . '/app-0')); // Limit file number or size + $data = $cache->load($key, 60 * 60 * 24 * 7/* 1 week */); if ($data) { return $response @@ -186,17 +185,17 @@ App::get('/v1/avatars/image') } try { - $resize = new Resize($fetch); - } catch (\Exception $exception) { + $image = new Image($fetch); + } catch (\Exception$exception) { throw new Exception('Unable to parse image', 500); } - $resize->crop((int) $width, (int) $height); + $image->crop((int) $width, (int) $height); $output = (empty($output)) ? $type : $output; - - $data = $resize->output($output, $quality); - + + $data = $image->output($output, $quality); + $cache->save($key, $data); $response @@ -206,7 +205,7 @@ App::get('/v1/avatars/image') ->send($data); ; - unset($resize); + unset($image); }); App::get('/v1/avatars/favicon') @@ -229,11 +228,11 @@ App::get('/v1/avatars/favicon') $height = 56; $quality = 80; $output = 'png'; - $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache - $key = \md5('/v2/avatars/favicon-'.$url); + $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT'; // 45 days cache + $key = \md5('/v2/avatars/favicon-' . $url); $type = 'png'; - $cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-0')); // Limit file number or size - $data = $cache->load($key, 60 * 60 * 24 * 30 * 3 /* 3 months */); + $cache = new Cache(new Filesystem(APP_STORAGE_CACHE . '/app-0')); // Limit file number or size + $data = $cache->load($key, 60 * 60 * 24 * 30 * 3/* 3 months */); if ($data) { return $response @@ -316,7 +315,7 @@ App::get('/v1/avatars/favicon') if (empty($outputHref) || empty($outputExt)) { $default = \parse_url($url); - $outputHref = $default['scheme'].'://'.$default['host'].'/favicon.ico'; + $outputHref = $default['scheme'] . '://' . $default['host'] . '/favicon.ico'; $outputExt = 'ico'; } @@ -343,13 +342,13 @@ App::get('/v1/avatars/favicon') throw new Exception('Icon not found', 404); } - $resize = new Resize($fetch); + $image = new Image($fetch); - $resize->crop((int) $width, (int) $height); + $image->crop((int) $width, (int) $height); $output = (empty($output)) ? $type : $output; - $data = $resize->output($output, $quality); + $data = $image->output($output, $quality); $cache->save($key, $data); @@ -359,7 +358,7 @@ App::get('/v1/avatars/favicon') ->addHeader('X-Appwrite-Cache', 'miss') ->send($data); - unset($resize); + unset($image); }); App::get('/v1/avatars/qr') @@ -394,14 +393,14 @@ App::get('/v1/avatars/qr') $response->addHeader('Content-Disposition', 'attachment; filename="qr.png"'); } - $resize = new Resize($qrcode->render($text)); + $image = new Image($qrcode->render($text)); - $resize->crop((int) $size, (int) $size); + $image->crop((int) $size, (int) $size); $response - ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache + ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache ->setContentType('image/png') - ->send($resize->output('png', 9)) + ->send($image->output('png', 9)) ; }); @@ -437,10 +436,10 @@ App::get('/v1/avatars/initials') ['color' => '#610038', 'background' => '#f5d1e6'], // PINK ['color' => '#386100', 'background' => '#dcf1bd'], // LIME ['color' => '#615800', 'background' => '#f1ecba'], // YELLOW - ['color' => '#610008', 'background' => '#f6d2d5'] // RED + ['color' => '#610008', 'background' => '#f6d2d5'], // RED ]; - $rand = \rand(0, \count($themes)-1); + $rand = \rand(0, \count($themes) - 1); $name = (!empty($name)) ? $name : $user->getAttribute('name', $user->getAttribute('email', '')); $words = \explode(' ', \strtoupper($name)); @@ -457,23 +456,23 @@ App::get('/v1/avatars/initials') } $length = \count($words); - $rand = \substr($code,-1); - $background = (!empty($background)) ? '#'.$background : $themes[$rand]['background']; - $color = (!empty($color)) ? '#'.$color : $themes[$rand]['color']; + $rand = \substr($code, -1); + $background = (!empty($background)) ? '#' . $background : $themes[$rand]['background']; + $color = (!empty($color)) ? '#' . $color : $themes[$rand]['color']; $image = new \Imagick(); $draw = new \ImagickDraw(); $fontSize = \min($width, $height) / 2; - - $draw->setFont(__DIR__."/../../../public/fonts/poppins-v9-latin-500.ttf"); - $image->setFont(__DIR__."/../../../public/fonts/poppins-v9-latin-500.ttf"); + + $draw->setFont(__DIR__ . "/../../../public/fonts/poppins-v9-latin-500.ttf"); + $image->setFont(__DIR__ . "/../../../public/fonts/poppins-v9-latin-500.ttf"); $draw->setFillColor(new \ImagickPixel($color)); $draw->setFontSize($fontSize); - + $draw->setTextAlignment(\Imagick::ALIGN_CENTER); $draw->annotation($width / 1.97, ($height / 2) + ($fontSize / 3), $initials); - + $image->newImage($width, $height, $background); $image->setImageFormat("png"); $image->drawImage($draw); @@ -481,7 +480,7 @@ App::get('/v1/avatars/initials') //$image->setImageCompressionQuality(9 - round(($quality / 100) * 9)); $response - ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache + ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache ->setContentType('image/png') ->send($image->getImageBlob()) ; diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index e51fdd044f..7a6b11bcda 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -294,7 +294,7 @@ App::put('/v1/functions/:functionId') } $original = $function->getAttribute('schedule', ''); - $cron = (!empty($function->getAttribute('tag', null)) && !empty($schedule)) ? CronExpression::factory($schedule) : null; + $cron = (!empty($function->getAttribute('tag', null)) && !empty($schedule)) ? new CronExpression($schedule) : null; $next = (!empty($function->getAttribute('tag', null)) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : null; $function = $projectDB->updateDocument(array_merge($function->getArrayCopy(), [ @@ -359,7 +359,7 @@ App::patch('/v1/functions/:functionId/tag') } $schedule = $function->getAttribute('schedule', ''); - $cron = (empty($function->getAttribute('tag')) && !empty($schedule)) ? CronExpression::factory($schedule) : null; + $cron = (empty($function->getAttribute('tag')) && !empty($schedule)) ? new CronExpression($schedule) : null; $next = (empty($function->getAttribute('tag')) && !empty($schedule)) ? $cron->getNextRunDate()->format('U') : null; $function = $projectDB->updateDocument(array_merge($function->getArrayCopy(), [ diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index ed0d3a4435..63c99ffaee 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -943,7 +943,7 @@ App::post('/v1/projects/:projectId/tasks') throw new Exception('Project not found', 404); } - $cron = CronExpression::factory($schedule); + $cron = new CronExpression($schedule); $next = ($status == 'play') ? $cron->getNextRunDate()->format('U') : null; $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); @@ -1093,7 +1093,7 @@ App::put('/v1/projects/:projectId/tasks/:taskId') throw new Exception('Task not found', 404); } - $cron = CronExpression::factory($schedule); + $cron = new CronExpression($schedule); $next = ($status == 'play') ? $cron->getNextRunDate()->format('U') : null; $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 98980fd6bd..d4500e95aa 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -18,7 +18,7 @@ use Utopia\Storage\Validator\File; use Utopia\Storage\Validator\FileSize; use Utopia\Storage\Validator\Upload; use Utopia\Storage\Compression\Algorithms\GZIP; -use Appwrite\Resize\Resize; +use Utopia\Image\Image; use Appwrite\OpenSSL\OpenSSL; use Appwrite\Utopia\Response; use Utopia\Config\Config; @@ -332,17 +332,17 @@ App::get('/v1/storage/files/:fileId/preview') $source = $compressor->decompress($source); } - $resize = new Resize($source); + $image = new Image($source); - $resize->crop((int) $width, (int) $height); + $image->crop((int) $width, (int) $height); if (!empty($background)) { - $resize->setBackground('#'.$background); + $image->setBackground('#'.$background); } $output = (empty($output)) ? $type : $output; - $data = $resize->output($output, $quality); + $data = $image->output($output, $quality); $cache->save($key, $data); @@ -353,7 +353,7 @@ App::get('/v1/storage/files/:fileId/preview') ->send($data) ; - unset($resize); + unset($image); }); App::get('/v1/storage/files/:fileId/download') diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 183c50b6b8..7354b1bb2c 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -207,8 +207,8 @@ App::get('/v1/users/:userId/sessions') continue; } - $token->setAttribute('countryName', (isset($countries[$token->getAttribute('contryCode')])) - ? $countries[$token->getAttribute('contryCode')] + $token->setAttribute('countryName', (isset($countries[strtoupper($token->getAttribute('contryCode'))])) + ? $countries[strtoupper($token->getAttribute('contryCode'))] : $locale->getText('locale.country.unknown')); $token->setAttribute('current', false); diff --git a/app/controllers/general.php b/app/controllers/general.php index 53be8fcf98..010295a3af 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -120,7 +120,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $user, $lo ->addHeader('Server', 'Appwrite') ->addHeader('X-Content-Type-Options', 'nosniff') ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') - ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-SDK-Version, Cache-Control, Expires, Pragma') + ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, Cache-Control, Expires, Pragma') ->addHeader('Access-Control-Expose-Headers', 'X-Fallback-Cookies') ->addHeader('Access-Control-Allow-Origin', $refDomain) ->addHeader('Access-Control-Allow-Credentials', 'true') @@ -246,7 +246,7 @@ App::options(function ($request, $response) { $response ->addHeader('Server', 'Appwrite') ->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE') - ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-SDK-Version, Cache-Control, Expires, Pragma, X-Fallback-Cookies') + ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-SDK-Version, Cache-Control, Expires, Pragma, X-Fallback-Cookies') ->addHeader('Access-Control-Expose-Headers', 'X-Fallback-Cookies') ->addHeader('Access-Control-Allow-Origin', $origin) ->addHeader('Access-Control-Allow-Credentials', 'true') diff --git a/app/controllers/mock.php b/app/controllers/mock.php index 4c9f3faf9f..7e65e2d936 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -2,8 +2,9 @@ global $utopia, $request, $response; +use Appwrite\Database\Document; +use Appwrite\Utopia\Response; use Utopia\App; -use Utopia\Response; use Utopia\Validator\Numeric; use Utopia\Validator\Text; use Utopia\Validator\ArrayList; @@ -11,13 +12,16 @@ use Utopia\Validator\Host; use Utopia\Storage\Validator\File; App::get('/v1/mock/tests/foo') - ->desc('Mock a get request for SDK tests') + ->desc('Get Foo') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'foo') ->label('sdk.method', 'get') - ->label('sdk.description', 'Mock a get request for SDK tests') + ->label('sdk.description', 'Mock a get request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -26,13 +30,16 @@ App::get('/v1/mock/tests/foo') }); App::post('/v1/mock/tests/foo') - ->desc('Mock a post request for SDK tests') + ->desc('Post Foo') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'foo') ->label('sdk.method', 'post') - ->label('sdk.description', 'Mock a post request for SDK tests') + ->label('sdk.description', 'Mock a post request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -41,13 +48,16 @@ App::post('/v1/mock/tests/foo') }); App::patch('/v1/mock/tests/foo') - ->desc('Mock a patch request for SDK tests') + ->desc('Patch Foo') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'foo') ->label('sdk.method', 'patch') - ->label('sdk.description', 'Mock a get request for SDK tests') + ->label('sdk.description', 'Mock a patch request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -56,13 +66,16 @@ App::patch('/v1/mock/tests/foo') }); App::put('/v1/mock/tests/foo') - ->desc('Mock a put request for SDK tests') + ->desc('Put Foo') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'foo') ->label('sdk.method', 'put') - ->label('sdk.description', 'Mock a put request for SDK tests') + ->label('sdk.description', 'Mock a put request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -71,13 +84,16 @@ App::put('/v1/mock/tests/foo') }); App::delete('/v1/mock/tests/foo') - ->desc('Mock a delete request for SDK tests') + ->desc('Delete Foo') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'foo') ->label('sdk.method', 'delete') - ->label('sdk.description', 'Mock a delete request for SDK tests') + ->label('sdk.description', 'Mock a delete request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -86,13 +102,16 @@ App::delete('/v1/mock/tests/foo') }); App::get('/v1/mock/tests/bar') - ->desc('Mock a get request for SDK tests') + ->desc('Get Bar') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'bar') ->label('sdk.method', 'get') - ->label('sdk.description', 'Mock a get request for SDK tests') + ->label('sdk.description', 'Mock a get request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -101,13 +120,16 @@ App::get('/v1/mock/tests/bar') }); App::post('/v1/mock/tests/bar') - ->desc('Mock a post request for SDK tests') + ->desc('Post Bar') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'bar') ->label('sdk.method', 'post') - ->label('sdk.description', 'Mock a post request for SDK tests') + ->label('sdk.description', 'Mock a post request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -116,13 +138,16 @@ App::post('/v1/mock/tests/bar') }); App::patch('/v1/mock/tests/bar') - ->desc('Mock a patch request for SDK tests') + ->desc('Patch Bar') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'bar') ->label('sdk.method', 'patch') - ->label('sdk.description', 'Mock a get request for SDK tests') + ->label('sdk.description', 'Mock a patch request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -131,13 +156,16 @@ App::patch('/v1/mock/tests/bar') }); App::put('/v1/mock/tests/bar') - ->desc('Mock a put request for SDK tests') + ->desc('Put Bar') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'bar') ->label('sdk.method', 'put') - ->label('sdk.description', 'Mock a put request for SDK tests') + ->label('sdk.description', 'Mock a put request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -146,13 +174,16 @@ App::put('/v1/mock/tests/bar') }); App::delete('/v1/mock/tests/bar') - ->desc('Mock a delete request for SDK tests') + ->desc('Delete Bar') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'bar') ->label('sdk.method', 'delete') - ->label('sdk.description', 'Mock a delete request for SDK tests') + ->label('sdk.description', 'Mock a delete request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -161,14 +192,17 @@ App::delete('/v1/mock/tests/bar') }); App::post('/v1/mock/tests/general/upload') - ->desc('Mock a post request for SDK tests') + ->desc('Upload File') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'upload') - ->label('sdk.description', 'Mock a delete request for SDK tests') + ->label('sdk.description', 'Mock a file upload request.') ->label('sdk.request.type', 'multipart/form-data') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->param('x', '', new Text(100), 'Sample string param') ->param('y', '', new Numeric(), 'Sample numeric param') @@ -203,13 +237,15 @@ App::post('/v1/mock/tests/general/upload') }); App::get('/v1/mock/tests/general/redirect') - ->desc('Mock a post request for SDK tests') + ->desc('Redirect') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'redirect') - ->label('sdk.description', 'Mock a redirect request for SDK tests') + ->label('sdk.description', 'Mock a redirect request.') + ->label('sdk.response.code', Response::STATUS_CODE_MOVED_PERMANENTLY) + ->label('sdk.response.type', Response::CONTENT_TYPE_HTML) ->label('sdk.mock', true) ->inject('response') ->action(function ($response) { @@ -219,25 +255,31 @@ App::get('/v1/mock/tests/general/redirect') }); App::get('/v1/mock/tests/general/redirect/done') - ->desc('Mock a post request for SDK tests') + ->desc('Redirected') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'redirected') - ->label('sdk.description', 'Mock a redirected request for SDK tests') + ->label('sdk.description', 'Mock a redirected request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->action(function () { }); App::get('/v1/mock/tests/general/set-cookie') - ->desc('Mock a cookie request for SDK tests') + ->desc('Set Cookie') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'setCookie') - ->label('sdk.description', 'Mock a set cookie request for SDK tests') + ->label('sdk.description', 'Mock a set cookie request.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->inject('response') ->action(function ($response) { @@ -247,13 +289,16 @@ App::get('/v1/mock/tests/general/set-cookie') }); App::get('/v1/mock/tests/general/get-cookie') - ->desc('Mock a cookie request for SDK tests') + ->desc('Get Cookie') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'getCookie') - ->label('sdk.description', 'Mock a get cookie request for SDK tests') + ->label('sdk.description', 'Mock a cookie response.') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->inject('request') ->action(function ($request) { @@ -265,13 +310,15 @@ App::get('/v1/mock/tests/general/get-cookie') }); App::get('/v1/mock/tests/general/empty') - ->desc('Mock a post request for SDK tests') + ->desc('Empty Response') ->groups(['mock']) ->label('scope', 'public') ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) ->label('sdk.namespace', 'general') ->label('sdk.method', 'empty') - ->label('sdk.description', 'Mock a redirected request for SDK tests') + ->label('sdk.description', 'Mock a an empty response.') + ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) + ->label('sdk.response.model', Response::MODEL_NONE) ->label('sdk.mock', true) ->inject('response') ->action(function ($response) { @@ -280,8 +327,40 @@ App::get('/v1/mock/tests/general/empty') $response->noContent(); }); +App::get('/v1/mock/tests/general/400-error') + ->desc('400 Error') + ->groups(['mock']) + ->label('scope', 'public') + ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) + ->label('sdk.namespace', 'general') + ->label('sdk.method', 'error400') + ->label('sdk.description', 'Mock a an 400 failed request.') + ->label('sdk.response.code', Response::STATUS_CODE_BAD_REQUEST) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_ERROR) + ->label('sdk.mock', true) + ->action(function () { + throw new Exception('Mock 400 error', 400); + }); + +App::get('/v1/mock/tests/general/500-error') + ->desc('500 Error') + ->groups(['mock']) + ->label('scope', 'public') + ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER]) + ->label('sdk.namespace', 'general') + ->label('sdk.method', 'error500') + ->label('sdk.description', 'Mock a an 500 failed request.') + ->label('sdk.response.code', Response::STATUS_CODE_INTERNAL_SERVER_ERROR) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_ERROR) + ->label('sdk.mock', true) + ->action(function () { + throw new Exception('Mock 500 error', 500); + }); + App::get('/v1/mock/tests/general/oauth2') - ->desc('Mock an OAuth2 login route') + ->desc('OAuth Login') ->groups(['mock']) ->label('scope', 'public') ->label('docs', false) @@ -298,7 +377,7 @@ App::get('/v1/mock/tests/general/oauth2') }); App::get('/v1/mock/tests/general/oauth2/token') - ->desc('Mock an OAuth2 login route') + ->desc('OAuth2 Token') ->groups(['mock']) ->label('scope', 'public') ->label('docs', false) @@ -327,7 +406,7 @@ App::get('/v1/mock/tests/general/oauth2/token') }); App::get('/v1/mock/tests/general/oauth2/user') - ->desc('Mock an OAuth2 user route') + ->desc('OAuth2 User') ->groups(['mock']) ->label('scope', 'public') ->label('docs', false) @@ -348,8 +427,9 @@ App::get('/v1/mock/tests/general/oauth2/user') }); App::get('/v1/mock/tests/general/oauth2/success') - ->label('scope', 'public') + ->desc('OAuth2 Success') ->groups(['mock']) + ->label('scope', 'public') ->label('docs', false) ->inject('response') ->action(function ($response) { @@ -361,6 +441,7 @@ App::get('/v1/mock/tests/general/oauth2/success') }); App::get('/v1/mock/tests/general/oauth2/failure') + ->desc('OAuth2 Failure') ->groups(['mock']) ->label('scope', 'public') ->label('docs', false) @@ -397,5 +478,5 @@ App::shutdown(function($utopia, $response, $request) { throw new Exception('Failed to save resutls', 500); } - $response->json(['result' => $route->getMethod() . ':' . $route->getURL() . ':passed']); + $response->dynamic(new Document(['result' => $route->getMethod() . ':' . $route->getURL() . ':passed']), Response::MODEL_MOCK); }, ['utopia', 'response', 'request'], 'mock'); \ No newline at end of file diff --git a/app/controllers/shared/web.php b/app/controllers/shared/web.php index 43eaf74d09..526a4dbb62 100644 --- a/app/controllers/shared/web.php +++ b/app/controllers/shared/web.php @@ -11,13 +11,18 @@ App::init(function ($utopia, $request, $response, $layout) { /* AJAX check */ if (!empty($request->getQuery('version', ''))) { - $layout->setPath(__DIR__.'/../../views/layouts/empty.phtml'); + $layout->setPath(__DIR__ . '/../../views/layouts/empty.phtml'); } - + + $port = $request->getPort(); + $protocol = $request->getProtocol(); + $domain = $request->getHostname(); + $layout ->setParam('title', APP_NAME) - ->setParam('protocol', $request->getProtocol()) - ->setParam('domain', $request->getHostname()) + ->setParam('protocol', $protocol) + ->setParam('domain', $domain) + ->setParam('endpoint', $protocol . '://' . $domain . ($port != 80 && $port != 443 ? ':' . $port : '')) ->setParam('home', App::getEnv('_APP_HOME')) ->setParam('setup', App::getEnv('_APP_SETUP')) ->setParam('class', 'unknown') @@ -34,10 +39,10 @@ App::init(function ($utopia, $request, $response, $layout) { $time = (60 * 60 * 24 * 45); // 45 days cache $response - ->addHeader('Cache-Control', 'public, max-age='.$time) - ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache + ->addHeader('Cache-Control', 'public, max-age=' . $time) + ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time) . ' GMT') // 45 days cache ->addHeader('X-Frame-Options', 'SAMEORIGIN') // Avoid console and homepage from showing in iframes - ->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url='.\urlencode($request->getURI())) + ->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url=' . \urlencode($request->getURI())) ->addHeader('X-UA-Compatible', 'IE=Edge') // Deny IE browsers from going into quirks mode ; diff --git a/app/controllers/web/console.php b/app/controllers/web/console.php index 6b3e105aa3..b41f1d1f86 100644 --- a/app/controllers/web/console.php +++ b/app/controllers/web/console.php @@ -144,6 +144,7 @@ App::get('/console/settings') $page ->setParam('customDomainsEnabled', ($target->isKnown() && !$target->isTest())) ->setParam('customDomainsTarget', $target->get()) + ->setParam('smtpEnabled', (!empty(App::getEnv('_APP_SMTP_HOST')))) ; $layout diff --git a/app/controllers/web/home.php b/app/controllers/web/home.php index f302f0af33..1a25dc2ee6 100644 --- a/app/controllers/web/home.php +++ b/app/controllers/web/home.php @@ -375,4 +375,40 @@ App::get('/specs/:format') $response ->json($specs->parse()); + }); + +App::get('/versions') + ->desc('Get Version') + ->groups(['web', 'home']) + ->label('scope', 'public') + ->inject('response') + ->action(function ($response) { + /** @var Appwrite\Utopia\Response $response */ + + $platforms = Config::getParam('platforms'); + + $versions = [ + 'server' => APP_VERSION_STABLE, + ]; + + foreach($platforms as $platform) { + $languages = $platform['languages'] ?? []; + + foreach ($languages as $key => $language) { + if(isset($language['dev']) && $language['dev']) { + continue; + } + + if(isset($language['enabled']) && !$language['enabled']) { + continue; + } + + $platformKey = $platform['key'] ?? ''; + $languageKey = $language['key'] ?? ''; + $version = $language['version'] ?? ''; + $versions[$platformKey . '-' . $languageKey] = $version; + } + } + + $response->json($versions); }); \ No newline at end of file diff --git a/app/init.php b/app/init.php index 14e1f802a1..c749d7de21 100644 --- a/app/init.php +++ b/app/init.php @@ -35,13 +35,13 @@ use PDO as PDONative; const APP_NAME = 'Appwrite'; const APP_DOMAIN = 'appwrite.io'; const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address -const APP_EMAIL_SECURITY = 'security@localhost.test'; // Default security email address +const APP_EMAIL_SECURITY = ''; // Default security email address const APP_USERAGENT = APP_NAME.'-Server v%s. Please report abuse at %s'; const APP_MODE_DEFAULT = 'default'; const APP_MODE_ADMIN = 'admin'; const APP_PAGING_LIMIT = 12; const APP_CACHE_BUSTER = 144; -const APP_VERSION_STABLE = '0.7.0'; +const APP_VERSION_STABLE = '0.7.1'; const APP_STORAGE_UPLOADS = '/storage/uploads'; const APP_STORAGE_FUNCTIONS = '/storage/functions'; const APP_STORAGE_CACHE = '/storage/cache'; @@ -92,9 +92,13 @@ Config::load('storage-mimes', __DIR__.'/config/storage/mimes.php'); Config::load('storage-inputs', __DIR__.'/config/storage/inputs.php'); Config::load('storage-outputs', __DIR__.'/config/storage/outputs.php'); -Resque::setBackend(App::getEnv('_APP_REDIS_HOST', '') - .':'.App::getEnv('_APP_REDIS_PORT', '')); - +$user = App::getEnv('_APP_REDIS_USER',''); +$pass = App::getEnv('_APP_REDIS_PASS',''); +if(!empty($user) || !empty($pass)) { + Resque::setBackend('redis://'.$user.':'.$pass.'@'.App::getEnv('_APP_REDIS_HOST', '').':'.App::getEnv('_APP_REDIS_PORT', '')); +} else { + Resque::setBackend(App::getEnv('_APP_REDIS_HOST', '').':'.App::getEnv('_APP_REDIS_PORT', '')); +} /** * DB Filters */ @@ -177,6 +181,18 @@ $register->set('statsd', function () { // Register DB connection $register->set('cache', function () { // Register cache connection $redis = new Redis(); $redis->pconnect(App::getEnv('_APP_REDIS_HOST', ''), App::getEnv('_APP_REDIS_PORT', '')); + $user = App::getEnv('_APP_REDIS_USER',''); + $pass = App::getEnv('_APP_REDIS_PASS',''); + $auth = []; + if(!empty($user)) { + $auth["user"] = $user; + } + if(!empty($pass)) { + $auth["pass"] = $pass; + } + if(!empty($auth)) { + $redis->auth($auth); + } $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); return $redis; diff --git a/app/preload.php b/app/preload.php index f73aaea61a..7c8ae00938 100644 --- a/app/preload.php +++ b/app/preload.php @@ -28,10 +28,9 @@ foreach ([ realpath(__DIR__ . '/../vendor/felixfbecker'), realpath(__DIR__ . '/../vendor/twig/twig'), realpath(__DIR__ . '/../vendor/guzzlehttp/guzzle'), - realpath(__DIR__ . '/../vendor/domnikl'), - realpath(__DIR__ . '/../vendor/domnikl'), + realpath(__DIR__ . '/../vendor/slickdeals'), realpath(__DIR__ . '/../vendor/psr/log'), - realpath(__DIR__ . '/../vendor/piwik'), + realpath(__DIR__ . '/../vendor/matomo'), realpath(__DIR__ . '/../vendor/symfony'), ] as $key => $value) { if($value !== false) { diff --git a/app/tasks/doctor.php b/app/tasks/doctor.php index 2918d4f1e4..0f601bb5b5 100644 --- a/app/tasks/doctor.php +++ b/app/tasks/doctor.php @@ -139,7 +139,6 @@ $cli Console::success('SMTP................connected 👍'); } catch (\Throwable $th) { Console::error('SMTP.............disconnected 👎'); - var_dump($th); } $host = App::getEnv('_APP_STATSD_HOST', 'telegraf'); diff --git a/app/tasks/sdks.php b/app/tasks/sdks.php index 6b17104f6c..f8d4ed3976 100644 --- a/app/tasks/sdks.php +++ b/app/tasks/sdks.php @@ -96,6 +96,16 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND $config = new CLI(); $config->setComposerVendor('appwrite'); $config->setComposerPackage('cli'); + $config->setExecutableName('appwrite'); + $config->setExecutableName('appwrite'); + $config->setLogo(" + _ _ _ ___ __ _____ + /_\ _ __ _ ____ ___ __(_) |_ ___ / __\ / / \_ \ + //_\\| '_ \| '_ \ \ /\ / / '__| | __/ _ \ / / / / / /\/ + / _ \ |_) | |_) \ V V /| | | | || __/ / /___/ /___/\/ /_ + \_/ \_/ .__/| .__/ \_/\_/ |_| |_|\__\___| \____/\____/\____/ + |_| |_| + "); break; case 'php': $config = new PHP(); diff --git a/app/views/console/database/collection.phtml b/app/views/console/database/collection.phtml index afe0365ee8..45d3287cf5 100644 --- a/app/views/console/database/collection.phtml +++ b/app/views/console/database/collection.phtml @@ -114,7 +114,7 @@ $maxCells = 10; - + {...} diff --git a/app/views/console/database/search/documents.phtml b/app/views/console/database/search/documents.phtml index 1b656dc4e4..6fb67feab1 100644 --- a/app/views/console/database/search/documents.phtml +++ b/app/views/console/database/search/documents.phtml @@ -72,7 +72,7 @@ $rules = $collection->getAttribute('rules', []); - + {...} diff --git a/app/views/console/database/search/files.phtml b/app/views/console/database/search/files.phtml index f8c39f4a87..0a1b4d739c 100644 --- a/app/views/console/database/search/files.phtml +++ b/app/views/console/database/search/files.phtml @@ -54,7 +54,7 @@ - + @@ -64,6 +64,7 @@ + diff --git a/app/views/console/functions/function.phtml b/app/views/console/functions/function.phtml index 49e2fffc41..4ae65f491e 100644 --- a/app/views/console/functions/function.phtml +++ b/app/views/console/functions/function.phtml @@ -117,7 +117,7 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);  
- +
getParam('usageStatsEnabled',true);

PowerShell

-
diff --git a/app/views/console/home/index.phtml b/app/views/console/home/index.phtml index e7164ebfde..1f3e95f1d9 100644 --- a/app/views/console/home/index.phtml +++ b/app/views/console/home/index.phtml @@ -97,7 +97,10 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
-
0
+
+ 0 + +
Bandwidth
@@ -117,7 +120,10 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
Documents
-
0
+
+ 0 + +
Storage
diff --git a/app/views/console/settings/index.phtml b/app/views/console/settings/index.phtml index 5eedb6388a..8cefd580cd 100644 --- a/app/views/console/settings/index.phtml +++ b/app/views/console/settings/index.phtml @@ -2,6 +2,7 @@ $customDomainsEnabled = $this->getParam('customDomainsEnabled', false); $customDomainsTarget = $this->getParam('customDomainsTarget', false); +$smtpEnabled = $this->getParam('smtpEnabled', false); ?>
@@ -135,7 +136,7 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
- +
    @@ -453,7 +454,7 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false); data-failure-param-alert-classname="error"> - + @@ -493,7 +494,7 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false); data-failure-param-alert-classname="error"> - + @@ -506,10 +507,16 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false); + +
    + SMTP connection is disabled. Learn more +
    + +
    - + disabled>Send Invite  
    diff --git a/app/views/console/storage/index.phtml b/app/views/console/storage/index.phtml index 934189f93c..6b03814ae2 100644 --- a/app/views/console/storage/index.phtml +++ b/app/views/console/storage/index.phtml @@ -113,7 +113,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0); - +
@@ -204,6 +204,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0); + diff --git a/app/views/console/users/index.phtml b/app/views/console/users/index.phtml index 8debcd9b4c..b41730baf9 100644 --- a/app/views/console/users/index.phtml +++ b/app/views/console/users/index.phtml @@ -361,7 +361,7 @@ $providers = $this->getParam('providers', []);

To complete set up, add this OAuth2 redirect URI to your escape(ucfirst($provider)); ?> app configuration.

- +
diff --git a/app/views/console/users/team.phtml b/app/views/console/users/team.phtml index 659fe3bd5a..ad6d263fac 100644 --- a/app/views/console/users/team.phtml +++ b/app/views/console/users/team.phtml @@ -145,7 +145,7 @@ data-failure-param-alert-classname="error"> - + diff --git a/app/views/home/auth/recovery.phtml b/app/views/home/auth/recovery.phtml index 575227a158..6f633b9a0c 100644 --- a/app/views/home/auth/recovery.phtml +++ b/app/views/home/auth/recovery.phtml @@ -23,7 +23,7 @@ - + diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index b98642fb94..1c0c98108f 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -56,9 +56,12 @@ services: - influxdb environment: - _APP_ENV + - _APP_CONSOLE_WHITELIST_EMAILS + - _APP_CONSOLE_WHITELIST_IPS - _APP_SYSTEM_EMAIL_NAME - _APP_SYSTEM_EMAIL_ADDRESS - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_SYSTEM_RESPONSE_FORMAT - _APP_OPTIONS_ABUSE - _APP_OPTIONS_FORCE_HTTPS - _APP_OPENSSL_KEY_V1 @@ -66,6 +69,8 @@ services: - _APP_DOMAIN_TARGET - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -104,6 +109,8 @@ services: - _APP_ENV - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_STATSD_HOST - _APP_STATSD_PORT @@ -121,6 +128,8 @@ services: - _APP_ENV - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -142,6 +151,8 @@ services: - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -163,6 +174,8 @@ services: - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -187,6 +200,8 @@ services: - _APP_ENV - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -211,6 +226,8 @@ services: - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_DOMAIN_TARGET - _APP_DB_HOST - _APP_DB_PORT @@ -236,6 +253,8 @@ services: - _APP_ENV - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -257,13 +276,14 @@ services: - appwrite depends_on: - redis - - smtp environment: - _APP_ENV - _APP_SYSTEM_EMAIL_NAME - _APP_SYSTEM_EMAIL_ADDRESS - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_SMTP_HOST - _APP_SMTP_PORT - _APP_SMTP_SECURE @@ -283,6 +303,8 @@ services: - _APP_ENV - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_MAINTENANCE_INTERVAL - _APP_MAINTENANCE_RETENTION_EXECUTION - _APP_MAINTENANCE_RETENTION_ABUSE @@ -302,6 +324,8 @@ services: - _APP_ENV - _APP_REDIS_HOST - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS mariadb: image: appwrite/mariadb:1.2.0 # fix issues when upgrading using: mysql_upgrade -u root -p @@ -318,16 +342,6 @@ services: - MYSQL_PASSWORD=${_APP_DB_PASS} command: 'mysqld --innodb-flush-method=fsync' - smtp: - image: appwrite/smtp:1.0.1 - container_name: appwrite-smtp - restart: unless-stopped - networks: - - appwrite - environment: - - MAILNAME=appwrite - - RELAY_NETWORKS=:192.168.0.0/24:10.0.0.0/16 - redis: image: redis:6.0-alpine3.12 container_name: appwrite-redis diff --git a/app/views/layouts/default.phtml b/app/views/layouts/default.phtml index 1f0c686494..b019baa0df 100644 --- a/app/views/layouts/default.phtml +++ b/app/views/layouts/default.phtml @@ -2,6 +2,7 @@ $protocol = $this->getParam('protocol', ''); $domain = $this->getParam('domain', ''); +$endpoint = $this->getParam('endpoint', ''); $platforms = $this->getParam('platforms', []); $version = $this->getParam('version', '0.0.0'); $isDev = $this->getParam('isDev', false); @@ -56,7 +57,7 @@ if(!empty($platforms)) { - +