diff --git a/.travis-ci/build.sh b/.travis-ci/build.sh index 27b598fd4b..050168289a 100644 --- a/.travis-ci/build.sh +++ b/.travis-ci/build.sh @@ -15,7 +15,7 @@ then exit 1 fi -if test $(find "./app/db/DBIP/dbip-country-lite-2021-02.mmdb" -mmin +259200) +if test $(find "./app/db/DBIP/dbip-country-lite-2021-06.mmdb" -mmin +259200) then printf "${RED}GEO country DB has not been updated for more than 6 months. Go to https://db-ip.com/db/download/ip-to-country-lite to download a newer version${NC}\n" fi diff --git a/CHANGES.md b/CHANGES.md index 0bedf0b12d..0f24324eeb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,28 @@ +# NOT PUBLISHED YET + +## Features + +- Added file created date to file info on the console +- Added file size to file info on the console +- Refactored Devices page in Console: + - Renamed *Devices* to *Sessions* + - Add Provider Icon to each Session + - Add Anonymous Account Placeholder +- Upgraded telegraf docker image version to v1.2.0 +- Added new environment variables to the `telegraf` service: + - _APP_INFLUXDB_HOST + - _APP_INFLUXDB_PORT + +## Bugs + +- Fixed bug when removing a project member on the Appwrite console (#1214) +- Fixed Swoole buffer output size to allow downloading files bigger than allowed size (#1189) +- Fixed ClamAV status when anti virus is not running (#1188) +- Fixed deleteSession which was removing cookieFallback from the localstorage on any logout instead of current session (#1206) +- Fixed Nepal flag (#1173) +- Fixed a bug in the Twitch OAuth adapter (#1209) +- Fixed missing session object when OAuth session creation event is triggered (#1208) + # Version 0.8.0 ## Features @@ -35,6 +60,8 @@ - Fixed incorrect audit worker event names (#1143) - Increased limit of memberships fetched in `createTeamMembership` to 2000 (#1143) - Fixed exception thrown when SSL certificate is already stored in the database (#1151) +- Fixed user delete button in the Appwrite console (#1216) +- Fixed missing placeholder for user name when empty (#1220) ## Breaking Changes (Read before upgrading!) diff --git a/README.md b/README.md index 9725943c56..0596bee316 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ [](https://appwrite.io/discord) +[](https://store.appwrite.io) [](https://hub.docker.com/r/appwrite/appwrite) [](https://travis-ci.com/appwrite/appwrite) [](https://twitter.com/appwrite_io) -[](https://stackshare.io/appwrite) [**Appwrite 0.8 has been released! Learn what's new!**](https://dev.to/appwrite/announcing-appwrite-0-8-an-open-source-self-hosted-baas-kda) diff --git a/app/config/auth.php b/app/config/auth.php index 8e718e6804..11b89dd9ec 100644 --- a/app/config/auth.php +++ b/app/config/auth.php @@ -6,7 +6,7 @@ return [ 'email-password' => [ 'name' => 'Email/Password', 'key' => 'usersAuthEmailPassword', - 'icon' => '/images/users/email-password.png', + 'icon' => '/images/users/email.png', 'docs' => 'https://appwrite.io/docs/client/account?sdk=web#accountCreateSession', 'enabled' => true, ], diff --git a/app/config/locale/templates/email-base.tpl b/app/config/locale/templates/email-base.tpl index 561ce73855..c9b730f46e 100644 --- a/app/config/locale/templates/email-base.tpl +++ b/app/config/locale/templates/email-base.tpl @@ -56,13 +56,13 @@ .main { background: {{bg-content}}; - border-radius: 3px; + border-radius: 10px; width: 100%; } .wrapper { box-sizing: border-box; - padding: 20px; + padding: 30px 30px 15px 30px; } .content-block { @@ -97,16 +97,15 @@ .btn table td { background-color: {{bg-content}}; - border-radius: 5px; + border-radius: 20px; text-align: center; } .btn a { background-color: {{bg-content}}; - border: solid 1px {{bg-cta}}; - border-radius: 5px; + border-radius: 20px; box-sizing: border-box; - color: #3498db; + color: #577590; cursor: pointer; display: inline-block; font-size: 14px; @@ -123,45 +122,17 @@ .btn-primary a { background-color: {{bg-cta}}; - border-color: {{bg-cta}}; color: {{text-cta}}; } @media only screen and (max-width: 620px) { - table[class=body] h1 { - font-size: 28px !important; - margin-bottom: 10px !important; + .container { + padding: 0; + width: 100%; } - table[class=body] p { - font-size: 16px !important; - } - - table[class=body] .wrapper { - padding: 10px !important; - } - - table[class=body] .content { - padding: 0 !important; - } - - table[class=body] .container { - padding: 0 !important; - width: 100% !important; - } - - table[class=body] .main { - border-left-width: 0 !important; - border-radius: 0 !important; - border-right-width: 0 !important; - } - - table[class=body] .btn table { - width: 100% !important; - } - - table[class=body] .btn a { - width: 100% !important; + .btn-primary a { + font-size: 13px; } } @@ -198,12 +169,11 @@ } .btn-primary table td:hover { - background-color: {{bg-cta-hover}} !important; + opacity: 0.7 !important; } .btn-primary a:hover { - background-color: {{bg-cta-hover}} !important; - border-color: {{bg-cta-hover}} !important; + opacity: 0.7 !important; } } @@ -220,15 +190,17 @@
| - {{content}} - | +{{content}} |
| + |
- - - {{redirect}} - - + + {{redirect}} + \ No newline at end of file diff --git a/app/config/locale/translations/templates/es.email.auth.confirm.tpl b/app/config/locale/translations/templates/es.email.auth.confirm.tpl index 2c728ddce1..33e69819a0 100644 --- a/app/config/locale/translations/templates/es.email.auth.confirm.tpl +++ b/app/config/locale/translations/templates/es.email.auth.confirm.tpl @@ -1,4 +1,4 @@ -+ Hola {{name}},
@@ -12,4 +12,4 @@
Gracias,
- Hola, + Hola {{name}}, Te hemos enviado este correo porque {{owner}} quiere invitarte a formar parte del equipo {{team}} en {{project}}. diff --git a/app/config/locale/translations/templates/he.email.auth.confirm.tpl b/app/config/locale/translations/templates/he.email.auth.confirm.tpl index 0c1f2a36d3..585636c1c9 100644 --- a/app/config/locale/translations/templates/he.email.auth.confirm.tpl +++ b/app/config/locale/translations/templates/he.email.auth.confirm.tpl @@ -2,7 +2,7 @@ שלום {{name}}, - נא ללחוץ על הקישור שלהלן כדי לאמת את החשבון שלך. + אנא לחץ\י על הקישור שלהלן כדי לאמת את החשבון שלך. {{cta}}diff --git a/app/config/locale/translations/templates/he.email.auth.invitation.tpl b/app/config/locale/translations/templates/he.email.auth.invitation.tpl index 9a9226a5ba..b11cf388a4 100644 --- a/app/config/locale/translations/templates/he.email.auth.invitation.tpl +++ b/app/config/locale/translations/templates/he.email.auth.invitation.tpl @@ -5,7 +5,7 @@ הודעת דוא״ל זו נשלחה אליך כי {{owner}} ביקש להזמינך להצטרף לצוות {{team}} ב־{{project}}. - כדי להצטרף לצוות {{team}}, נא ללחוץ על הקישור: + כדי להצטרף לצוות {{team}}, אנא לחץ\י על הקישור: {{cta}}diff --git a/app/config/locale/translations/templates/he.email.auth.recovery.tpl b/app/config/locale/translations/templates/he.email.auth.recovery.tpl index e46bf1990e..ec249408a7 100644 --- a/app/config/locale/translations/templates/he.email.auth.recovery.tpl +++ b/app/config/locale/translations/templates/he.email.auth.recovery.tpl @@ -2,7 +2,7 @@ שלום {{name}}, - נא ללחוץ על הקישור שלהלן כדי לאפס את הסיסמה שלך ב־{{project}}. + אנא לחץ\י על הקישור שלהלן כדי לאפס את הסיסמה שלך ב־{{project}}. {{cta}}diff --git a/app/config/platforms.php b/app/config/platforms.php index cfb21f3d82..9f83e63028 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -15,7 +15,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '3.0.4', + 'version' => '3.1.0', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -62,7 +62,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '0.6.2', + 'version' => '0.6.4', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -109,19 +109,20 @@ return [ 'gitUserName' => 'appwrite', ], [ - 'key' => 'kotlin', - 'name' => 'Kotlin', - 'url' => '', - 'package' => '', - 'enabled' => false, - 'beta' => false, + 'key' => 'android', + 'name' => 'Android', + 'version' => '0.0.0-SNAPSHOT', + 'url' => 'https://github.com/appwrite/sdk-for-android', + 'package' => 'https://repo1.maven.org/maven2/io/appwrite/sdk-for-android/', + 'enabled' => true, + 'beta' => true, 'dev' => false, 'hidden' => false, 'family' => APP_PLATFORM_CLIENT, 'prism' => 'kotlin', - 'source' => false, - 'gitUrl' => 'git@github.com:appwrite/sdk-for-kotlin.git', - 'gitRepoName' => 'sdk-for-kotlin', + 'source' => \realpath(__DIR__ . '/../sdks/client-android'), + 'gitUrl' => 'git@github.com:appwrite/sdk-for-android.git', + 'gitRepoName' => 'sdk-for-android', 'gitUserName' => 'appwrite', ], // [ @@ -178,7 +179,7 @@ return [ [ 'key' => 'nodejs', 'name' => 'Node.js', - 'version' => '2.2.2', + 'version' => '2.3.0', 'url' => 'https://github.com/appwrite/sdk-for-node', 'package' => 'https://www.npmjs.com/package/node-appwrite', 'enabled' => true, @@ -195,7 +196,7 @@ return [ [ 'key' => 'deno', 'name' => 'Deno', - 'version' => '0.2.1', + 'version' => '0.2.2', 'url' => 'https://github.com/appwrite/sdk-for-deno', 'package' => 'https://deno.land/x/appwrite', 'enabled' => true, @@ -212,7 +213,7 @@ return [ [ 'key' => 'php', 'name' => 'PHP', - 'version' => '2.1.1', + 'version' => '2.1.2', 'url' => 'https://github.com/appwrite/sdk-for-php', 'package' => 'https://packagist.org/packages/appwrite/appwrite', 'enabled' => true, @@ -229,7 +230,7 @@ return [ [ 'key' => 'python', 'name' => 'Python', - 'version' => '0.2.1', + 'version' => '0.3.0', 'url' => 'https://github.com/appwrite/sdk-for-python', 'package' => 'https://pypi.org/project/appwrite/', 'enabled' => true, @@ -246,7 +247,7 @@ return [ [ 'key' => 'ruby', 'name' => 'Ruby', - 'version' => '2.1.1', + 'version' => '2.2.0', 'url' => 'https://github.com/appwrite/sdk-for-ruby', 'package' => 'https://rubygems.org/gems/appwrite', 'enabled' => true, @@ -314,7 +315,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '0.6.1', + 'version' => '0.6.3', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, @@ -331,7 +332,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '0.10.1', + 'version' => '0.10.0', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://github.com/appwrite/sdk-for-cli', 'enabled' => true, diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 61b0e27be7..1801a26e39 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -58,15 +58,16 @@ App::post('/v1/account') /** @var Appwrite\Database\Database $projectDB */ /** @var Appwrite\Event\Event $audits */ + $email = \strtolower($email); if ('console' === $project->getId()) { - $whitlistEmails = $project->getAttribute('authWhitelistEmails'); - $whitlistIPs = $project->getAttribute('authWhitelistIPs'); + $whitelistEmails = $project->getAttribute('authWhitelistEmails'); + $whitelistIPs = $project->getAttribute('authWhitelistIPs'); - if (!empty($whitlistEmails) && !\in_array($email, $whitlistEmails)) { + if (!empty($whitelistEmails) && !\in_array($email, $whitelistEmails)) { throw new Exception('Console registration is restricted to specific emails. Contact your administrator for more information.', 401); } - if (!empty($whitlistIPs) && !\in_array($request->getIP(), $whitlistIPs)) { + if (!empty($whitelistIPs) && !\in_array($request->getIP(), $whitelistIPs)) { throw new Exception('Console registration is restricted to specific IPs. Contact your administrator for more information.', 401); } } @@ -174,6 +175,7 @@ App::post('/v1/account/sessions') /** @var MaxMind\Db\Reader $geodb */ /** @var Appwrite\Event\Event $audits */ + $email = \strtolower($email); $protocol = $request->getProtocol(); $profile = $projectDB->getCollectionFirst([ // Get user by email address 'limit' => 1, @@ -386,7 +388,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->inject('projectDB') ->inject('geodb') ->inject('audits') - ->action(function ($provider, $code, $state, $request, $response, $project, $user, $projectDB, $geodb, $audits) use ($oauthDefaultSuccess) { + ->inject('events') + ->action(function ($provider, $code, $state, $request, $response, $project, $user, $projectDB, $geodb, $audits, $events) use ($oauthDefaultSuccess) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Database\Document $project */ @@ -579,6 +582,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->setParam('data', ['provider' => $provider]) ; + $events->setParam('eventData', $response->output($session, Response::MODEL_SESSION)); + if (!Config::getParam('domainVerification')) { $response ->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])) @@ -1083,6 +1088,7 @@ App::patch('/v1/account/email') throw new Exception('Invalid credentials', 401); } + $email = \strtolower($email); $profile = $projectDB->getCollectionFirst([ // Get user by email address 'limit' => 1, 'filters' => [ @@ -1272,16 +1278,16 @@ App::delete('/v1/account/sessions/:sessionId') ->setParam('resource', '/user/'.$user->getId()) ; - if (!Config::getParam('domainVerification')) { - $response - ->addHeader('X-Fallback-Cookies', \json_encode([])) - ; - } - $session->setAttribute('current', false); - + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too $session->setAttribute('current', true); + + if (!Config::getParam('domainVerification')) { + $response + ->addHeader('X-Fallback-Cookies', \json_encode([])) + ; + } $response ->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null) @@ -1405,7 +1411,8 @@ App::post('/v1/account/recovery') $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles); $isAppUser = Auth::isAppUser(Authorization::$roles); - + + $email = \strtolower($email); $profile = $projectDB->getCollectionFirst([ // Get user by email address 'limit' => 1, 'filters' => [ @@ -1459,17 +1466,16 @@ App::post('/v1/account/recovery') $cta = new Template(__DIR__.'/../../config/locale/templates/email-cta.tpl'); $body - ->setParam('{{content}}', $content->render()) + ->setParam('{{content}}', $content->render(false)) ->setParam('{{cta}}', $cta->render()) ->setParam('{{title}}', $locale->getText('account.emails.recovery.title')) ->setParam('{{direction}}', $locale->getText('settings.direction')) ->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]'])) ->setParam('{{name}}', $profile->getAttribute('name')) ->setParam('{{redirect}}', $url) - ->setParam('{{bg-body}}', '#f6f6f6') + ->setParam('{{bg-body}}', '#f7f7f7') ->setParam('{{bg-content}}', '#ffffff') - ->setParam('{{bg-cta}}', '#3498db') - ->setParam('{{bg-cta-hover}}', '#34495e') + ->setParam('{{bg-cta}}', '#073b4c') ->setParam('{{text-content}}', '#000000') ->setParam('{{text-cta}}', '#ffffff') ; @@ -1662,17 +1668,16 @@ App::post('/v1/account/verification') $cta = new Template(__DIR__.'/../../config/locale/templates/email-cta.tpl'); $body - ->setParam('{{content}}', $content->render()) + ->setParam('{{content}}', $content->render(false)) ->setParam('{{cta}}', $cta->render()) ->setParam('{{title}}', $locale->getText('account.emails.verification.title')) ->setParam('{{direction}}', $locale->getText('settings.direction')) ->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]'])) ->setParam('{{name}}', $user->getAttribute('name')) ->setParam('{{redirect}}', $url) - ->setParam('{{bg-body}}', '#f6f6f6') + ->setParam('{{bg-body}}', '#f7f7f7') ->setParam('{{bg-content}}', '#ffffff') - ->setParam('{{bg-cta}}', '#3498db') - ->setParam('{{bg-cta-hover}}', '#34495e') + ->setParam('{{bg-cta}}', '#073b4c') ->setParam('{{text-content}}', '#000000') ->setParam('{{text-cta}}', '#ffffff') ; diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index dd1d4a0d69..dbbd59f8c3 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -267,7 +267,7 @@ App::get('/v1/health/anti-virus') 'status' => (@$antiVirus->ping()) ? 'online' : 'offline', 'version' => @$antiVirus->version(), ]); - } catch( RuntimeException $e) { + } catch( \Exception $e) { $response->json([ 'status' => 'offline', 'version' => '', diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 9d2a28f04f..a9c72f2d4f 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1305,7 +1305,7 @@ App::post('/v1/projects/:projectId/platforms') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PLATFORM) ->param('projectId', null, new UID(), 'Project unique ID.') - ->param('type', null, new WhiteList(['web', 'flutter-ios', 'flutter-android', 'ios', 'android', 'unity'], true), 'Platform type.') + ->param('type', null, new WhiteList(['web', 'flutter-ios', 'flutter-android', 'flutter-linux', 'flutter-macos', 'flutter-windows', 'ios', 'android', 'unity'], true), 'Platform type.') ->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.') ->param('key', '', new Text(256), 'Package name for android or bundle ID for iOS. Max length: 256 chars.', true) ->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true) diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index b3060eaf4b..c4fe8c8494 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -242,6 +242,7 @@ App::get('/v1/storage/files/:fileId/preview') ->param('fileId', '', new UID(), 'File unique ID') ->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true) ->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true) + ->param('gravity', Image::GRAVITY_CENTER, new WhiteList([Image::GRAVITY_CENTER, Image::GRAVITY_NORTH, Image::GRAVITY_NORTHWEST, Image::GRAVITY_NORTHEAST, Image::GRAVITY_WEST, Image::GRAVITY_EAST, Image::GRAVITY_SOUTHWEST, Image::GRAVITY_SOUTH, Image::GRAVITY_SOUTHEAST]), 'Image crop gravity', true) ->param('quality', 100, new Range(0, 100), 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.', true) ->param('borderWidth', 0, new Range(0, 100), 'Preview image border in pixels. Pass an integer between 0 to 100. Defaults to 0.', true) ->param('borderColor', '', new HexColor(), 'Preview image border color. Use a valid HEX color, no # is needed for prefix.', true) @@ -254,7 +255,7 @@ App::get('/v1/storage/files/:fileId/preview') ->inject('response') ->inject('project') ->inject('projectDB') - ->action(function ($fileId, $width, $height, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $projectDB) { + ->action(function ($fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $projectDB) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Database\Document $project */ @@ -342,7 +343,7 @@ App::get('/v1/storage/files/:fileId/preview') $image = new Image($source); - $image->crop((int) $width, (int) $height); + $image->crop((int) $width, (int) $height, $gravity); if (!empty($opacity) || $opacity==0) { $image->setOpacity($opacity); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 238e6248a1..3b3039a0fd 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -285,7 +285,8 @@ App::post('/v1/teams/:teamId/memberships') $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles); $isAppUser = Auth::isAppUser(Authorization::$roles); - + + $email = \strtolower($email); $name = (empty($name)) ? $email : $name; $team = $projectDB->getDocument($teamId); @@ -433,7 +434,7 @@ App::post('/v1/teams/:teamId/memberships') $title = \sprintf($locale->getText('account.emails.invitation.title'), $team->getAttribute('name', '[TEAM-NAME]'), $project->getAttribute('name', ['[APP-NAME]'])); $body - ->setParam('{{content}}', $content->render()) + ->setParam('{{content}}', $content->render(false)) ->setParam('{{cta}}', $cta->render()) ->setParam('{{title}}', $title) ->setParam('{{direction}}', $locale->getText('settings.direction')) @@ -441,10 +442,9 @@ App::post('/v1/teams/:teamId/memberships') ->setParam('{{team}}', $team->getAttribute('name', '[TEAM-NAME]')) ->setParam('{{owner}}', $user->getAttribute('name', '')) ->setParam('{{redirect}}', $url) - ->setParam('{{bg-body}}', '#f6f6f6') + ->setParam('{{bg-body}}', '#f7f7f7') ->setParam('{{bg-content}}', '#ffffff') - ->setParam('{{bg-cta}}', '#3498db') - ->setParam('{{bg-cta-hover}}', '#34495e') + ->setParam('{{bg-cta}}', '#073b4c') ->setParam('{{text-content}}', '#000000') ->setParam('{{text-cta}}', '#ffffff') ; @@ -784,7 +784,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') if ($membership->getAttribute('confirm')) { // Count only confirmed members $team = $projectDB->updateDocument(\array_merge($team->getArrayCopy(), [ - 'sum' => $team->getAttribute('sum', 0) - 1, + 'sum' => \max($team->getAttribute('sum', 0) - 1, 0), // Ensure that sum >= 0 ])); } diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 03543b7cd3..9dd13e34e8 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -8,6 +8,7 @@ use Utopia\Validator\WhiteList; use Appwrite\Network\Validator\Email; use Utopia\Validator\Text; use Utopia\Validator\Range; +use Utopia\Validator\Boolean; use Utopia\Audit\Audit; use Utopia\Audit\Adapters\MySQL as AuditAdapter; use Appwrite\Auth\Auth; @@ -40,6 +41,7 @@ App::post('/v1/users') /** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Database\Database $projectDB */ + $email = \strtolower($email); $profile = $projectDB->getCollectionFirst([ // Get user by email address 'limit' => 1, 'filters' => [ @@ -368,6 +370,43 @@ App::patch('/v1/users/:userId/status') $response->dynamic($user, Response::MODEL_USER); }); +App::patch('/v1/users/:userId/verification') + ->desc('Update Email Verification') + ->groups(['api', 'users']) + ->label('event', 'users.update.verification') + ->label('scope', 'users.write') + ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) + ->label('sdk.namespace', 'users') + ->label('sdk.method', 'updateVerification') + ->label('sdk.description', '/docs/references/users/update-user-verification.md') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) + ->label('sdk.response.model', Response::MODEL_USER) + ->param('userId', '', new UID(), 'User unique ID.') + ->param('emailVerification', false, new Boolean(), 'User Email Verification Status.') + ->inject('response') + ->inject('projectDB') + ->action(function ($userId, $emailVerification, $response, $projectDB) { + /** @var Appwrite\Utopia\Response $response */ + /** @var Appwrite\Database\Database $projectDB */ + + $user = $projectDB->getDocument($userId); + + if (empty($user->getId()) || Database::SYSTEM_COLLECTION_USERS != $user->getCollection()) { + throw new Exception('User not found', 404); + } + + $user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [ + 'emailVerification' => $emailVerification, + ])); + + if (false === $user) { + throw new Exception('Failed saving user to DB', 500); + } + + $response->dynamic($user, Response::MODEL_USER); + }); + App::patch('/v1/users/:userId/prefs') ->desc('Update User Preferences') ->groups(['api', 'users']) diff --git a/app/controllers/mock.php b/app/controllers/mock.php index d9cd03f288..80c4c6c427 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -6,9 +6,9 @@ use Appwrite\Database\Document; use Appwrite\Network\Validator\Host; use Appwrite\Utopia\Response; use Utopia\App; -use Utopia\Validator\Numeric; -use Utopia\Validator\Text; use Utopia\Validator\ArrayList; +use Utopia\Validator\Integer; +use Utopia\Validator\Text; use Utopia\Storage\Validator\File; App::get('/v1/mock/tests/foo') @@ -24,7 +24,7 @@ App::get('/v1/mock/tests/foo') ->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') + ->param('y', '', new Integer(), 'Sample numeric param') ->param('z', null, new ArrayList(new Text(256)), 'Sample array param') ->action(function ($x, $y, $z) { }); @@ -42,7 +42,7 @@ App::post('/v1/mock/tests/foo') ->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') + ->param('y', '', new Integer(), 'Sample numeric param') ->param('z', null, new ArrayList(new Text(256)), 'Sample array param') ->action(function ($x, $y, $z) { }); @@ -60,7 +60,7 @@ App::patch('/v1/mock/tests/foo') ->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') + ->param('y', '', new Integer(), 'Sample numeric param') ->param('z', null, new ArrayList(new Text(256)), 'Sample array param') ->action(function ($x, $y, $z) { }); @@ -78,7 +78,7 @@ App::put('/v1/mock/tests/foo') ->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') + ->param('y', '', new Integer(), 'Sample numeric param') ->param('z', null, new ArrayList(new Text(256)), 'Sample array param') ->action(function ($x, $y, $z) { }); @@ -96,7 +96,7 @@ App::delete('/v1/mock/tests/foo') ->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') + ->param('y', '', new Integer(), 'Sample numeric param') ->param('z', null, new ArrayList(new Text(256)), 'Sample array param') ->action(function ($x, $y, $z) { }); @@ -114,7 +114,7 @@ App::get('/v1/mock/tests/bar') ->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') + ->param('y', '', new Integer(), 'Sample numeric param') ->param('z', null, new ArrayList(new Text(256)), 'Sample array param') ->action(function ($x, $y, $z) { }); @@ -132,7 +132,7 @@ App::post('/v1/mock/tests/bar') ->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') + ->param('y', '', new Integer(), 'Sample numeric param') ->param('z', null, new ArrayList(new Text(256)), 'Sample array param') ->action(function ($x, $y, $z) { }); @@ -150,7 +150,7 @@ App::patch('/v1/mock/tests/bar') ->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') + ->param('y', '', new Integer(), 'Sample numeric param') ->param('z', null, new ArrayList(new Text(256)), 'Sample array param') ->action(function ($x, $y, $z) { }); @@ -168,7 +168,7 @@ App::put('/v1/mock/tests/bar') ->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') + ->param('y', '', new Integer(), 'Sample numeric param') ->param('z', null, new ArrayList(new Text(256)), 'Sample array param') ->action(function ($x, $y, $z) { }); @@ -186,11 +186,36 @@ App::delete('/v1/mock/tests/bar') ->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') + ->param('y', '', new Integer(), 'Sample numeric param') ->param('z', null, new ArrayList(new Text(256)), 'Sample array param') ->action(function ($x, $y, $z) { }); +App::get('/v1/mock/tests/general/download') + ->desc('Download File') + ->groups(['mock']) + ->label('scope', 'public') + ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) + ->label('sdk.namespace', 'general') + ->label('sdk.method', 'download') + ->label('sdk.methodType', 'location') + ->label('sdk.description', 'Mock a file download request.') + ->label('sdk.response.type', '*/*') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.mock', true) + ->inject('response') + ->action(function ($response) { + /** @var Utopia\Swoole\Request $request */ + + $response + ->setContentType('text/plain') + ->addHeader('Content-Disposition', 'attachment; filename="test.txt"') + ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache + ->addHeader('X-Peak', \memory_get_peak_usage()) + ->send("Download test passed.") + ; + }); + App::post('/v1/mock/tests/general/upload') ->desc('Upload File') ->groups(['mock']) @@ -205,7 +230,7 @@ App::post('/v1/mock/tests/general/upload') ->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') + ->param('y', '', new Integer(), 'Sample numeric param') ->param('z', null, new ArrayList(new Text(256)), 'Sample array param') ->param('file', [], new File(), 'Sample file param', false) ->inject('request') @@ -246,6 +271,7 @@ App::get('/v1/mock/tests/general/redirect') ->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.response.model', Response::MODEL_MOCK) ->label('sdk.mock', true) ->inject('response') ->action(function ($response) { @@ -369,6 +395,7 @@ App::get('/v1/mock/tests/general/502-error') ->label('sdk.description', 'Mock a 502 bad gateway.') ->label('sdk.response.code', Response::STATUS_CODE_BAD_GATEWAY) ->label('sdk.response.type', Response::CONTENT_TYPE_TEXT) + ->label('sdk.response.model', Response::MODEL_ANY) ->label('sdk.mock', true) ->inject('response') ->action(function ($response) { diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index c9d2595b42..5a1eeda8de 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -48,7 +48,9 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e //TODO make sure we get array here foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys - $timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value); + if(!empty($value)) { + $timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value); + } } $abuse = new Abuse($timeLimit); diff --git a/app/controllers/web/home.php b/app/controllers/web/home.php index d76d8da054..f3eeb71c94 100644 --- a/app/controllers/web/home.php +++ b/app/controllers/web/home.php @@ -8,6 +8,7 @@ use Utopia\App; use Utopia\View; use Utopia\Config\Config; use Utopia\Exception; +use Utopia\Validator\Boolean; use Utopia\Validator\Range; use Utopia\Validator\WhiteList; @@ -494,4 +495,4 @@ App::get('/versions') } $response->json($versions); - }); \ No newline at end of file + }); diff --git a/app/db/DBIP/dbip-country-lite-2021-02.mmdb b/app/db/DBIP/dbip-country-lite-2021-02.mmdb deleted file mode 100644 index 6434c220f9..0000000000 Binary files a/app/db/DBIP/dbip-country-lite-2021-02.mmdb and /dev/null differ diff --git a/app/db/DBIP/dbip-country-lite-2021-06.mmdb b/app/db/DBIP/dbip-country-lite-2021-06.mmdb new file mode 100644 index 0000000000..984d200d19 Binary files /dev/null and b/app/db/DBIP/dbip-country-lite-2021-06.mmdb differ diff --git a/app/http.php b/app/http.php index 154b88e5a1..7d56a9046b 100644 --- a/app/http.php +++ b/app/http.php @@ -12,26 +12,6 @@ use Swoole\Http\Request as SwooleRequest; use Swoole\Http\Response as SwooleResponse; use Utopia\App; use Utopia\CLI\Console; -use Utopia\Config\Config; -use Utopia\Domains\Domain; - -// xdebug_start_trace('/tmp/trace'); - -Files::load(__DIR__ . '/../public'); - -include __DIR__ . '/controllers/general.php'; - -$domain = App::getEnv('_APP_DOMAIN', ''); - -Console::info('Issuing a TLS certificate for the master domain ('.$domain.') in 30 seconds. - Make sure your domain points to your server IP or restart your Appwrite server to try again.'); // TODO move this to installation script - -ResqueScheduler::enqueueAt(\time() + 30, 'v1-certificates', 'CertificatesV1', [ - 'document' => [], - 'domain' => $domain, - 'validateTarget' => false, - 'validateCNAME' => false, -]); $http = new Server("0.0.0.0", App::getEnv('PORT', 80)); @@ -101,8 +81,8 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo $register->set('db', function () use (&$db) { return $db; }); - - $register->set('cache', function () use (&$redis) { // Register cache connection + + $register->set('cache', function () use (&$redis) { return $redis; }); diff --git a/app/init.php b/app/init.php index 3d4455bb23..cb87697a83 100644 --- a/app/init.php +++ b/app/init.php @@ -46,7 +46,7 @@ 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 = 146; +const APP_CACHE_BUSTER = 148; const APP_VERSION_STABLE = '0.8.0'; const APP_STORAGE_UPLOADS = '/storage/uploads'; const APP_STORAGE_FUNCTIONS = '/storage/functions'; @@ -161,19 +161,21 @@ $register->set('dbPool', function () { // Register DB connection return $pool; }); - $register->set('redisPool', function () { - $user = App::getEnv('_APP_REDIS_USER', ''); - $pass = App::getEnv('_APP_REDIS_PASS', ''); - $auth = []; - if ($user) { - $auth[] = $user; + $redisHost = App::getEnv('_APP_REDIS_HOST', ''); + $redisPort = App::getEnv('_APP_REDIS_PORT', ''); + $redisUser = App::getEnv('_APP_REDIS_USER', ''); + $redisPass = App::getEnv('_APP_REDIS_PASS', ''); + $redisAuth = []; + + if ($redisUser) { + $redisAuth[] = $redisUser; } - if ($pass) { - $auth[] = $pass; + if ($redisPass) { + $redisAuth[] = $redisPass; } - $pool = new RedisPool(10, App::getEnv('_APP_REDIS_HOST', ''), App::getEnv('_APP_REDIS_PORT', ''), $auth); + $pool = new RedisPool(10, $redisHost, $redisPort, $redisAuth); return $pool; }); @@ -228,7 +230,7 @@ $register->set('smtp', function () { return $mail; }); $register->set('geodb', function () { - return new Reader(__DIR__.'/db/DBIP/dbip-country-lite-2021-02.mmdb'); + return new Reader(__DIR__.'/db/DBIP/dbip-country-lite-2021-06.mmdb'); }); /* diff --git a/app/realtime.php b/app/realtime.php index a125118654..6d94b2cc4a 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -1,336 +1,13 @@ set([ +$config = [ 'package_max_length' => 64000 // Default maximum Package Size (64kb) -]); +]; -$subscriptions = []; -$connections = []; - -$stats = new Table(4096, 1); -$stats->column('projectId', Table::TYPE_STRING, 64); -$stats->column('connections', Table::TYPE_INT); -$stats->column('connectionsTotal', Table::TYPE_INT); -$stats->column('messages', Table::TYPE_INT); -$stats->create(); - -/** - * Sends usage stats every 10 seconds. - */ -Timer::tick(10000, function () use (&$stats) { - /** @var Table $stats */ - foreach ($stats as $projectId => $value) { - if (empty($value['connections']) && empty($value['messages'])) { - continue; - } - - $connections = $value['connections']; - $messages = $value['messages']; - - $usage = new Event('v1-usage', 'UsageV1'); - $usage - ->setParam('projectId', $projectId) - ->setParam('realtimeConnections', $connections) - ->setParam('realtimeMessages', $messages) - ->setParam('networkRequestSize', 0) - ->setParam('networkResponseSize', 0); - - $stats->set($projectId, [ - 'projectId' => $projectId, - 'messages' => 0, - 'connections' => 0 - ]); - - if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { - $usage->trigger(); - } - } -}); - -$server->on('workerStart', function ($server, $workerId) use (&$subscriptions, &$register, &$stats) { - Console::success('Worker ' . $workerId . ' started succefully'); - - $attempts = 0; - $start = time(); - $redisPool = $register->get('redisPool'); - - /** - * Sending current connections to project channels on the console project every 5 seconds. - */ - $server->tick(5000, function () use (&$server, &$subscriptions, &$stats) { - if ( - array_key_exists('console', $subscriptions) - && array_key_exists('role:member', $subscriptions['console']) - && array_key_exists('project', $subscriptions['console']['role:member']) - ) { - $payload = []; - foreach ($stats as $projectId => $value) { - $payload[$projectId] = $value['connectionsTotal']; - } - foreach ($subscriptions['console']['role:member']['project'] as $connection => $value) { - $server->push( - $connection, - json_encode([ - 'event' => 'stats.connections', - 'channels' => ['project'], - 'timestamp' => time(), - 'payload' => $payload - ]), - SWOOLE_WEBSOCKET_OPCODE_TEXT, - SWOOLE_WEBSOCKET_FLAG_FIN | SWOOLE_WEBSOCKET_FLAG_COMPRESS - ); - } - } - }); - - while ($attempts < 300) { - try { - if ($attempts > 0) { - Console::error('Pub/sub connection lost (lasted ' . (time() - $start) . ' seconds, worker: ' . $workerId . '). - Attempting restart in 5 seconds (attempt #' . $attempts . ')'); - sleep(5); // 5 sec delay between connection attempts - } - - /** @var Swoole\Coroutine\Redis $redis */ - $redis = $redisPool->get(); - - if ($redis->ping(true)) { - $attempts = 0; - Console::success('Pub/sub connection established (worker: ' . $workerId . ')'); - } else { - Console::error('Pub/sub failed (worker: ' . $workerId . ')'); - } - - $redis->subscribe(['realtime'], function ($redis, $channel, $payload) use ($server, $workerId, &$subscriptions, &$stats) { - /** - * Supported Resources: - * - Collection - * - Document - * - File - * - Account - * - Session - * - Team? (not implemented yet) - * - Membership? (not implemented yet) - * - Function - * - Execution - */ - $event = json_decode($payload, true); - - $receivers = Realtime::identifyReceivers($event, $subscriptions); - - - // Temporarily print debug logs by default for Alpha testing. - // if (App::isDevelopment() && !empty($receivers)) { - if (!empty($receivers)) { - Console::log("[Debug][Worker {$workerId}] Receivers: " . count($receivers)); - Console::log("[Debug][Worker {$workerId}] Receivers Connection IDs: " . json_encode($receivers)); - Console::log("[Debug][Worker {$workerId}] Event: " . $payload); - } - - foreach ($receivers as $receiver) { - if ($server->exist($receiver) && $server->isEstablished($receiver)) { - $server->push( - $receiver, - json_encode($event['data']), - SWOOLE_WEBSOCKET_OPCODE_TEXT, - SWOOLE_WEBSOCKET_FLAG_FIN | SWOOLE_WEBSOCKET_FLAG_COMPRESS - ); - } else { - $server->close($receiver); - } - } - if (($num = count($receivers)) > 0) { - $stats->incr($event['project'], 'messages', $num); - } - }); - } catch (\Throwable $th) { - Console::error('Pub/sub error: ' . $th->getMessage()); - $redisPool->put($redis); - $attempts++; - continue; - } - - $attempts++; - } - - Console::error('Failed to restart pub/sub...'); -}); - -$server->on('start', function (Server $server) { - Console::success('Server started succefully'); - - Console::info("Master pid {$server->master_pid}, manager pid {$server->manager_pid}"); - - // listen ctrl + c - Process::signal(2, function () use ($server) { - Console::log('Stop by Ctrl+C'); - $server->shutdown(); - }); -}); - -$server->on('open', function (Server $server, Request $request) use (&$connections, &$subscriptions, &$register, &$stats) { - $app = new App('UTC'); - $connection = $request->fd; - $request = new SwooleRequest($request); - - $db = $register->get('dbPool')->get(); - $redis = $register->get('redisPool')->get(); - - $register->set('db', function () use (&$db) { - return $db; - }); - - $register->set('cache', function () use (&$redis) { // Register cache connection - return $redis; - }); - - Console::info("Connection open (user: {$connection}, worker: {$server->getWorkerId()})"); - - App::setResource('request', function () use ($request) { - return $request; - }); - - App::setResource('response', function () { - return new Response(new SwooleResponse()); - }); - - try { - /** @var Appwrite\Database\Document $user */ - $user = $app->getResource('user'); - - /** @var Appwrite\Database\Document $project */ - $project = $app->getResource('project'); - - /** @var Appwrite\Database\Document $console */ - $console = $app->getResource('console'); - - /* - * Project Check - */ - if (empty($project->getId())) { - throw new Exception('Missing or unknown project ID', 1008); - } - - /* - * Abuse Check - * - * Abuse limits are connecting 128 times per minute and ip address. - */ - $timeLimit = new TimeLimit('url:{url},ip:{ip}', 128, 60, function () use ($db) { - return $db; - }); - $timeLimit - ->setNamespace('app_' . $project->getId()) - ->setParam('{ip}', $request->getIP()) - ->setParam('{url}', $request->getURI()); - - $abuse = new Abuse($timeLimit); - - if ($abuse->check() && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') { - throw new Exception('Too many requests', 1013); - } - - /* - * Validate Client Domain - Check to avoid CSRF attack. - * Adding Appwrite API domains to allow XDOMAIN communication. - * Skip this check for non-web platforms which are not required to send an origin header. - */ - $origin = $request->getOrigin(); - $originValidator = new Origin(\array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', []))); - - if (!$originValidator->isValid($origin) && $project->getId() !== 'console') { - throw new Exception($originValidator->getDescription(), 1008); - } - - Realtime::setUser($user); - - $roles = Realtime::getRoles(); - $channels = Realtime::parseChannels($request->getQuery('channels', [])); - - /** - * Channels Check - */ - if (empty($channels)) { - throw new Exception('Missing channels', 1008); - } - - Realtime::subscribe($project->getId(), $connection, $roles, $subscriptions, $connections, $channels); - - $server->push($connection, json_encode($channels)); - - $stats->incr($project->getId(), 'connections'); - $stats->incr($project->getId(), 'connectionsTotal'); - } catch (\Throwable $th) { - $response = [ - 'code' => $th->getCode(), - 'message' => $th->getMessage() - ]; - // Temporarily print debug logs by default for Alpha testing. - //if (App::isDevelopment()) { - Console::error("[Error] Connection Error"); - Console::error("[Error] Code: " . $response['code']); - Console::error("[Error] Message: " . $response['message']); - //} - $server->push($connection, json_encode($response)); - $server->close($connection); - } - /** - * Put used PDO and Redis Connections back into their pools. - */ - /** @var PDOPool $dbPool */ - $dbPool = $register->get('dbPool'); - $dbPool->put($db); - - /** @var RedisPool $redisPool */ - $redisPool = $register->get('redisPool'); - $redisPool->put($redis); -}); - -$server->on('message', function (Server $server, Frame $frame) { - $server->push($frame->fd, 'Sending messages is not allowed.'); - $server->close($frame->fd); -}); - -$server->on('close', function (Server $server, int $connection) use (&$connections, &$subscriptions, &$stats) { - if (array_key_exists($connection, $connections)) { - $stats->decr($connections[$connection]['projectId'], 'connectionsTotal'); - } - Realtime::unsubscribe($connection, $subscriptions, $connections); - Console::info('Connection close: ' . $connection); -}); - -$server->start(); +$realtimeServer = new Server($register, config: $config); diff --git a/app/tasks/sdks.php b/app/tasks/sdks.php index 108ce7a486..d5f858a7be 100644 --- a/app/tasks/sdks.php +++ b/app/tasks/sdks.php @@ -15,7 +15,7 @@ use Appwrite\SDK\Language\Deno; use Appwrite\SDK\Language\DotNet; use Appwrite\SDK\Language\Flutter; use Appwrite\SDK\Language\Go; -use Appwrite\SDK\Language\Java; +use Appwrite\SDK\Language\Kotlin; use Appwrite\SDK\Language\Swift; $cli @@ -134,9 +134,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND case 'go': $config = new Go(); break; - case 'java': - $config = new Java(); - break; case 'swift': $config = new Swift(); break; @@ -144,6 +141,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND $cover = ''; $config = new DotNet(); break; + case 'android': + $config = new Kotlin(); + break; default: throw new Exception('Language "'.$language['key'].'" not supported'); break; @@ -155,9 +155,8 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND $sdk ->setName($language['name']) - ->setDescription("Appwrite is an open-source backend as a service server that abstract and simplify complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. - Use the {$language['name']} SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. - For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)") + ->setNamespace('io appwrite') + ->setDescription("Appwrite is an open-source backend as a service server that abstract and simplify complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the {$language['name']} SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)") ->setShortDescription('Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API') ->setLicense($license) ->setLicenseContent($licenseContent) @@ -202,7 +201,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND \exec('rm -rf '.$target.' && \ mkdir -p '.$target.' && \ cd '.$target.' && \ - git init && \ + git init --initial-branch=master && \ git remote add origin '.$gitUrl.' && \ git fetch && \ git pull '.$gitUrl.' && \ diff --git a/app/views/console/account/index.phtml b/app/views/console/account/index.phtml index 3f1e7decc9..4ad919cc2b 100644 --- a/app/views/console/account/index.phtml +++ b/app/views/console/account/index.phtml @@ -193,8 +193,8 @@ - Devices+Sessions
-
@@ -244,11 +256,11 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
+
Unknown
on
@@ -283,7 +289,7 @@
data-failure-param-alert-text="Logout from All Sessions Failed"
data-failure-param-alert-classname="error">
-
+
+
+
-
-
+
+
+
+
+
+
+
+
+
New Flutter App (beta) New iOS App (soon) New Android App New Android App (soon) New iOS App (soon) New Unity Game (soon)
+
+
+
+
+
+Register your Android App+ + +Register your Flutter App@@ -403,6 +451,105 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);+ + + + Linux+ + +MacOS+ + +Windows+ +
- -----
+ Unknown
+ Anonymous User
|
@@ -479,7 +480,7 @@ $auth = $this->getParam('auth', []);
- |
+
escape($name); ?> sandbox beta
diff --git a/app/views/console/users/team.phtml b/app/views/console/users/team.phtml
index 912d123062..4b1efcd22b 100644
--- a/app/views/console/users/team.phtml
+++ b/app/views/console/users/team.phtml
@@ -12,7 +12,7 @@
- Unknown + Unknown diff --git a/app/views/console/users/user.phtml b/app/views/console/users/user.phtml index 048d870071..d949823501 100644 --- a/app/views/console/users/user.phtml +++ b/app/views/console/users/user.phtml @@ -12,7 +12,8 @@ - Unknown + Unknown + Anonymous User @@ -96,7 +97,7 @@ Danger Zone-
+
- This is the area where you can delete this user. By deleting this user you will lose all data associated with this user. @@ -109,7 +110,7 @@ data-analytics-event="submit" data-analytics-category="console" data-analytics-label="Delete User" - data-service="users.deleteUser" + data-service="users.delete" data-event="submit" data-param-user-id="{{router.params.id}}" data-success="alert,trigger,redirect" @@ -180,8 +181,8 @@Devices+Sessions
-
-
@@ -259,11 +265,11 @@
data-param-user-id="{{router.params.id}}"
data-event="load,logs-load">
-
+
on
+
+
-
@@ -244,7 +250,7 @@
data-failure-param-alert-text="Failed to logout all sessions"
data-failure-param-alert-classname="error">
-
+
|