From 518a37d2946108257a3d5fbbcbe1b2f182227a80 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Thu, 27 Apr 2023 19:10:42 -0700 Subject: [PATCH 01/11] Add a generic OIDC OAuth2 provider --- app/config/providers.php | 10 + src/Appwrite/Auth/OAuth2/Oidc.php | 296 ++++++++++++++++++++++++++++++ 2 files changed, 306 insertions(+) create mode 100644 src/Appwrite/Auth/OAuth2/Oidc.php diff --git a/app/config/providers.php b/app/config/providers.php index 62430faae1..5e4dc0aa11 100644 --- a/app/config/providers.php +++ b/app/config/providers.php @@ -201,6 +201,16 @@ return [ // Ordered by ABC. 'beta' => false, 'mock' => false, ], + 'oidc' => [ + 'name' => 'OpenID Connect', + 'developers' => 'https://openid.net/connect/faq/', + 'icon' => 'icon-oidc', + 'enabled' => true, + 'sandbox' => false, + 'form' => 'oidc.phtml', + 'beta' => false, + 'mock' => false, + ], 'okta' => [ 'name' => 'Okta', 'developers' => 'https://developer.okta.com/', diff --git a/src/Appwrite/Auth/OAuth2/Oidc.php b/src/Appwrite/Auth/OAuth2/Oidc.php new file mode 100644 index 0000000000..de2eab65c8 --- /dev/null +++ b/src/Appwrite/Auth/OAuth2/Oidc.php @@ -0,0 +1,296 @@ +getAuthorizationEndpoint() . '?' . \http_build_query([ + 'client_id' => $this->appID, + 'redirect_uri' => $this->callback, + 'state' => \json_encode($this->state), + 'scope' => \implode(' ', $this->getScopes()), + 'response_type' => 'code', + ]); + } + + /** + * @param string $code + * + * @return array + */ + protected function getTokens(string $code): array + { + if (empty($this->tokens)) { + $headers = ['Content-Type: application/x-www-form-urlencoded']; + $this->tokens = \json_decode($this->request( + 'POST', + $this->getTokenEndpoint(), + $headers, + \http_build_query([ + 'code' => $code, + 'client_id' => $this->appID, + 'client_secret' => $this->getClientSecret(), + 'redirect_uri' => $this->callback, + 'scope' => \implode(' ', $this->getScopes()), + 'grant_type' => 'authorization_code' + ]) + ), true); + } + return $this->tokens; + } + + + /** + * @param string $refreshToken + * + * @return array + */ + public function refreshTokens(string $refreshToken): array + { + $headers = ['Content-Type: application/x-www-form-urlencoded']; + $this->tokens = \json_decode($this->request( + 'POST', + $this->getTokenEndpoint(), + $headers, + \http_build_query([ + 'refresh_token' => $refreshToken, + 'client_id' => $this->appID, + 'client_secret' => $this->getClientSecret(), + 'grant_type' => 'refresh_token' + ]) + ), true); + + if (empty($this->tokens['refresh_token'])) { + $this->tokens['refresh_token'] = $refreshToken; + } + + return $this->tokens; + } + + /** + * @param string $accessToken + * + * @return string + */ + public function getUserID(string $accessToken): string + { + $user = $this->getUser($accessToken); + + if (isset($user['sub'])) { + return $user['sub']; + } + + return ''; + } + + /** + * @param string $accessToken + * + * @return string + */ + public function getUserEmail(string $accessToken): string + { + $user = $this->getUser($accessToken); + + if (isset($user['email'])) { + return $user['email']; + } + + return ''; + } + + /** + * Check if the User email is verified + * + * @param string $accessToken + * + * @return bool + */ + public function isEmailVerified(string $accessToken): bool + { + $user = $this->getUser($accessToken); + + return $user['email_verified'] ?? false; + } + + /** + * @param string $accessToken + * + * @return string + */ + public function getUserName(string $accessToken): string + { + $user = $this->getUser($accessToken); + + if (isset($user['name'])) { + return $user['name']; + } + + return ''; + } + + /** + * @param string $accessToken + * + * @return array + */ + protected function getUser(string $accessToken): array + { + if (empty($this->user)) { + $headers = ['Authorization: Bearer ' . \urlencode($accessToken)]; + $user = $this->request('GET', $this->getUserinfoEndpoint(), $headers); + $this->user = \json_decode($user, true); + } + + return $this->user; + } + + /** + * Extracts the Client Secret from the JSON stored in appSecret + * + * @return string + */ + protected function getClientSecret(): string + { + $secret = $this->getAppSecret(); + + return $secret['clientSecret'] ?? ''; + } + + /** + * Extracts the well known endpoint from the JSON stored in appSecret. + * + * @return string + */ + protected function getWellKnownEndpoint(): string + { + $secret = $this->getAppSecret(); + return $secret['wellKnownEndpoint'] ?? ''; + } + + /** + * Extracts the authorization endpoint from the JSON stored in appSecret. + * + * If one is not provided, it will be retrieved from the well-known configuration. + * + * @return string + */ + protected function getAuthorizationEndpoint(): string + { + $secret = $this->getAppSecret(); + + $endpoint = $secret['authorizationEndpoint'] ?? ''; + if (!empty($endpoint)) { + return $endpoint; + } + + $wellKnownConfiguration = $this->getWellKnownConfiguration(); + return $wellKnownConfiguration['authorization_endpoint'] ?? ''; + } + + /** + * Extracts the token endpoint from the JSON stored in appSecret. + * + * If one is not provided, it will be retrieved from the well-known configuration. + * + * @return string + */ + protected function getTokenEndpoint(): string + { + $secret = $this->getAppSecret(); + + $endpoint = $secret['tokenEndpoint'] ?? ''; + if (!empty($endpoint)) { + return $endpoint; + } + + $wellKnownConfiguration = $this->getWellKnownConfiguration(); + return $wellKnownConfiguration['token_endpoint'] ?? ''; + } + + /** + * Extracts the userinfo endpoint from the JSON stored in appSecret. + * + * If one is not provided, it will be retrieved from the well-known configuration. + * + * @return string + */ + protected function getUserinfoEndpoint(): string + { + $secret = $this->getAppSecret(); + $endpoint = $secret['userinfoEndpoint'] ?? ''; + if (!empty($endpoint)) { + return $endpoint; + } + + $wellKnownConfiguration = $this->getWellKnownConfiguration(); + return $wellKnownConfiguration['userinfo_endpoint'] ?? ''; + } + + /** + * Get the well-known configuration using the well known endpoint + */ + protected function getWellKnownConfiguration(): array + { + if (empty($this->wellKnownConfiguration)) { + $response = $this->request('GET', $this->getWellKnownEndpoint()); + $this->wellKnownConfiguration = \json_decode($response, true); + } + + return $this->wellKnownConfiguration; + } + + /** + * Decode the JSON stored in appSecret + * + * @return array + */ + protected function getAppSecret(): array + { + try { + $secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR); + } catch (\Throwable $th) { + throw new \Exception('Invalid secret'); + } + return $secret; + } +} From 803d653e44ea0a9118a34cbc0df6c53852828bfe Mon Sep 17 00:00:00 2001 From: Steven <1477010+stnguyen90@users.noreply.github.com> Date: Wed, 19 Jul 2023 18:29:01 +0000 Subject: [PATCH 02/11] Upgrade utopia-php/domains Version 1.1.0 was an incorrectly named git tag; it was equivalent to 0.2.0. This bump to 0.3.* is safe because it adds Registrar, but it won't be used in Appwrite yet. Bumping to 0.3.* is necessary to fix some bugs where valid domains were considered invalid. --- composer.json | 2 +- composer.lock | 36 +++++++++++++++++++++--------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/composer.json b/composer.json index 0ba26540bd..8560f450bc 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,7 @@ "utopia-php/cli": "0.13.*", "utopia-php/config": "0.2.*", "utopia-php/database": "0.36.*", - "utopia-php/domains": "1.1.*", + "utopia-php/domains": "0.3.*", "utopia-php/framework": "0.28.*", "utopia-php/image": "0.5.*", "utopia-php/locale": "0.4.*", diff --git a/composer.lock b/composer.lock index 0c6b961ea2..303130bea5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3eadbfe5543aafdf8682ea0465159e3c", + "content-hash": "21b8661eef2ca7648831651b99a11544", "packages": [ { "name": "adhocore/jwt", @@ -2164,23 +2164,25 @@ }, { "name": "utopia-php/domains", - "version": "v1.1.0", + "version": "0.3.2", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73" + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", - "reference": "1665e1d9932afa3be63b5c1e0dcfe01fe77d8e73", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", + "reference": "aaa8c9a96c69ccb397997b1f4f2299c66f77eefb", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.0", + "utopia-php/framework": "0.*.*" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" }, "type": "library", "autoload": { @@ -2196,6 +2198,10 @@ { "name": "Eldad Fux", "email": "eldad@appwrite.io" + }, + { + "name": "Wess Cope", + "email": "wess@appwrite.io" } ], "description": "Utopia Domains library is simple and lite library for parsing web domains. This library is aiming to be as simple and easy to learn and use.", @@ -2212,9 +2218,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/master" + "source": "https://github.com/utopia-php/domains/tree/0.3.2" }, - "time": "2020-02-23T07:40:02+00:00" + "time": "2023-07-19T16:39:24+00:00" }, { "name": "utopia-php/framework", @@ -3031,16 +3037,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.33.6", + "version": "0.33.7", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "237fe97b68090a244382c36f96482c352880a38c" + "reference": "9f5db4a637b23879ceacea9ed2d33b0486771ffc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/237fe97b68090a244382c36f96482c352880a38c", - "reference": "237fe97b68090a244382c36f96482c352880a38c", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9f5db4a637b23879ceacea9ed2d33b0486771ffc", + "reference": "9f5db4a637b23879ceacea9ed2d33b0486771ffc", "shasum": "" }, "require": { @@ -3076,9 +3082,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.33.6" + "source": "https://github.com/appwrite/sdk-generator/tree/0.33.7" }, - "time": "2023-07-10T16:27:53+00:00" + "time": "2023-07-12T12:15:43+00:00" }, { "name": "doctrine/deprecations", From cc42700299b307893ae429e43aaf5e5a036bb915 Mon Sep 17 00:00:00 2001 From: Steven <1477010+stnguyen90@users.noreply.github.com> Date: Wed, 19 Jul 2023 23:52:33 +0000 Subject: [PATCH 03/11] Fix test after session expire format was updated --- tests/e2e/Services/Account/AccountBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index b08686fa48..f6d10653d8 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -1296,7 +1296,7 @@ trait AccountBase $token = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256); - $expireTime = strpos($lastEmail['text'], 'expire=' . urlencode(DateTime::format(new \DateTime($response['body']['expire']))), 0); + $expireTime = strpos($lastEmail['text'], 'expire=' . urlencode($response['body']['expire']), 0); $this->assertNotFalse($expireTime); From 35a25cc2a74e1e33241c3fe85a9ca45705d1e133 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 12 Jul 2023 10:06:12 -0700 Subject: [PATCH 04/11] Bump audit version to get rid of userInternalId attribute --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 8560f450bc..b02a6111bc 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "appwrite/php-runtimes": "0.11.*", "utopia-php/abuse": "0.25.*", "utopia-php/analytics": "0.2.*", - "utopia-php/audit": "0.26.*", + "utopia-php/audit": "0.27.*", "utopia-php/cache": "0.8.*", "utopia-php/cli": "0.13.*", "utopia-php/config": "0.2.*", diff --git a/composer.lock b/composer.lock index 303130bea5..5e6640b9fb 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "21b8661eef2ca7648831651b99a11544", + "content-hash": "a15f62920dedaf99e321ffd94e89d9f4", "packages": [ { "name": "adhocore/jwt", @@ -1906,16 +1906,16 @@ }, { "name": "utopia-php/audit", - "version": "0.26.0", + "version": "0.27.0", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "e7228080f14df28737fbb050c180c26d86ac0403" + "reference": "bdf89d7fe381bd4c891ad217612580a35e8c7642" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/e7228080f14df28737fbb050c180c26d86ac0403", - "reference": "e7228080f14df28737fbb050c180c26d86ac0403", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/bdf89d7fe381bd4c891ad217612580a35e8c7642", + "reference": "bdf89d7fe381bd4c891ad217612580a35e8c7642", "shasum": "" }, "require": { @@ -1947,9 +1947,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.26.0" + "source": "https://github.com/utopia-php/audit/tree/0.27.0" }, - "time": "2023-04-27T15:43:50+00:00" + "time": "2023-05-15T07:04:48+00:00" }, { "name": "utopia-php/cache", From 58fa7546c15c878ffea2a0e8bd87a302056c5bf3 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 12 Jul 2023 10:18:34 -0700 Subject: [PATCH 05/11] Update Appwrite to save internal id as audit user id It's important to use userInternalId so that if a user is recreated with the same ID, lookups for the user will not return the data of the old deleted user. We will still store userId in data so that it can be pulled out and returned for the log API calls. --- app/workers/audits.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/workers/audits.php b/app/workers/audits.php index b25430ec41..57614e60f6 100644 --- a/app/workers/audits.php +++ b/app/workers/audits.php @@ -40,8 +40,7 @@ class AuditsV1 extends Worker $dbForProject = $this->getProjectDB($project->getId()); $audit = new Audit($dbForProject); $audit->log( - userInternalId: $user->getInternalId(), - userId: $user->getId(), + userId: $user->getInternalId(), // Pass first, most verbose event pattern event: $event, resource: $resource, @@ -49,6 +48,7 @@ class AuditsV1 extends Worker ip: $ip, location: '', data: [ + 'userId' => $user->getId(), 'userName' => $userName, 'userEmail' => $userEmail, 'mode' => $mode, From 187f3dc6ff85a4c4a2239da9fe5e6e4c2f58ab53 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 12 Jul 2023 10:27:57 -0700 Subject: [PATCH 06/11] Update get logs APIs to return userId from data The audit userId is actually the internal id and the user id is in the data attribute. --- app/controllers/api/databases.php | 6 +++--- app/controllers/api/teams.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 4459322baf..29f9c6d0be 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -566,7 +566,7 @@ App::get('/v1/databases/:databaseId/logs') $output[$i] = new Document([ 'event' => $log['event'], - 'userId' => ID::custom($log['userId']), + 'userId' => ID::custom($log['data']['userId']), 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, @@ -917,7 +917,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs') $output[$i] = new Document([ 'event' => $log['event'], - 'userId' => $log['userId'], + 'userId' => $log['data']['userId'], 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, @@ -3149,7 +3149,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $output[$i] = new Document([ 'event' => $log['event'], - 'userId' => $log['userId'], + 'userId' => $log['data']['userId'], 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index cdac9ba2fb..a06ab6b2a0 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -1043,7 +1043,7 @@ App::get('/v1/teams/:teamId/logs') $output[$i] = new Document([ 'event' => $log['event'], - 'userId' => $log['userId'], + 'userId' => $log['data']['userId'], 'userEmail' => $log['data']['userEmail'] ?? null, 'userName' => $log['data']['userName'] ?? null, 'mode' => $log['data']['mode'] ?? null, From 9908a9021f6504d6d74ee5a8ea0379270ac6ac8e Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 12 Jul 2023 10:56:24 -0700 Subject: [PATCH 07/11] Update get logs by user to pass user internal id The userId in audit is actually the userInternalId. --- app/controllers/api/account.php | 2 +- app/controllers/api/users.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 3cd2c7bd70..37485b92d8 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1427,7 +1427,7 @@ App::get('/v1/account/logs') $audit = new EventAudit($dbForProject); - $logs = $audit->getLogsByUser($user->getId(), $limit, $offset); + $logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset); $output = []; diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index cd217afe31..d84d83ff77 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -577,7 +577,7 @@ App::get('/v1/users/:userId/logs') $audit = new Audit($dbForProject); - $logs = $audit->getLogsByUser($user->getId(), $limit, $offset); + $logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset); $output = []; From cd78706944aff2372d5db684b84241a6a1a84d52 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 12 Jul 2023 10:57:55 -0700 Subject: [PATCH 08/11] Update migration for audit collection 1. Remove the userInternalId attribute 2. Replace userId in audit documents with userInternalId and put userId in data --- src/Appwrite/Migration/Version/V18.php | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/Appwrite/Migration/Version/V18.php b/src/Appwrite/Migration/Version/V18.php index be628e0fbb..839269f940 100644 --- a/src/Appwrite/Migration/Version/V18.php +++ b/src/Appwrite/Migration/Version/V18.php @@ -133,6 +133,16 @@ class V18 extends Migration Console::warning("'options' from {$id}: {$th->getMessage()}"); } break; + case 'audit': + try { + /** + * Delete 'userInternalId' attribute + */ + $this->projectDB->deleteAttribute($id, 'userInternalId'); + } catch (\Throwable $th) { + Console::warning("'userInternalId' from {$id}: {$th->getMessage()}"); + } + break; default: break; } @@ -195,6 +205,34 @@ class V18 extends Migration Console::warning($th->getMessage()); } break; + case 'audit': + /** + * Set the userId to the userInternalId and add userId to data + */ + try { + $userId = $document->getAttribute('userId'); + $data = $document->getAttribute('data', []); + $mode = $data['mode'] ?? 'default'; + $user = match ($mode) { + 'admin' => $this->consoleDB->getDocument('users', $userId), + default => $this->projectDB->getDocument('users', $userId), + }; + + if ($user->isEmpty()) { + // The audit userId could already be an internal Id. + // Otherwise, the user could have been deleted. + // Nonetheless, there's nothing else we can do here. + break; + } + $internalId = $user->getInternalId(); + $document->setAttribute('userId', $internalId); + $data = $document->getAttribute('data', []); + $data['userId'] = $user->getId(); + $document->setAttribute('data', $data); + } catch (\Throwable $th) { + Console::warning($th->getMessage()); + } + break; } return $document; From 3f9cedcc44e766f25c0a1f91f4c70d56bb81d0bf Mon Sep 17 00:00:00 2001 From: Steven <1477010+stnguyen90@users.noreply.github.com> Date: Tue, 18 Jul 2023 22:21:58 +0000 Subject: [PATCH 09/11] Prepare 1.3.8 release --- CHANGES.md | 5 +++++ README-CN.md | 6 +++--- README.md | 6 +++--- app/init.php | 2 +- src/Appwrite/Migration/Migration.php | 1 + 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d19c8490b7..fb46b2b7d1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +# Version 1.3.8 + +## Bugs +- Fix audit user internal [#5809](https://github.com/appwrite/appwrite/pull/5809) + # Version 1.3.7 ## Bugs diff --git a/README-CN.md b/README-CN.md index d3f52058b5..b634458547 100644 --- a/README-CN.md +++ b/README-CN.md @@ -66,7 +66,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.3.7 + appwrite/appwrite:1.3.8 ``` ### Windows @@ -78,7 +78,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.3.7 + appwrite/appwrite:1.3.8 ``` #### PowerShell @@ -88,7 +88,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.3.7 + appwrite/appwrite:1.3.8 ``` 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 diff --git a/README.md b/README.md index b8fffd110a..5099cd6238 100644 --- a/README.md +++ b/README.md @@ -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:1.3.7 + appwrite/appwrite:1.3.8 ``` ### Windows @@ -87,7 +87,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.3.7 + appwrite/appwrite:1.3.8 ``` #### PowerShell @@ -97,7 +97,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.3.7 + appwrite/appwrite:1.3.8 ``` Once the Docker installation is complete, 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 completing the installation. diff --git a/app/init.php b/app/init.php index fc7541513d..132acb62ce 100644 --- a/app/init.php +++ b/app/init.php @@ -101,7 +101,7 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 506; -const APP_VERSION_STABLE = '1.3.7'; +const APP_VERSION_STABLE = '1.3.8'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 7a1f3cad24..ffb5ce6ac4 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -63,6 +63,7 @@ abstract class Migration '1.3.5' => 'V18', '1.3.6' => 'V18', '1.3.7' => 'V18', + '1.3.8' => 'V18', ]; /** From 1e0158cdef7658ad6f83de91df2623256d58e230 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 20 Jul 2023 11:35:59 +0545 Subject: [PATCH 10/11] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index b8fffd110a..5613d4f560 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,12 @@ Choose from one of the providers below:
Gitpod + + + Akamai Logo +
Akamai
+ + From 2c2c7d9028eafe333d47c94294bca75762292891 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Thu, 20 Jul 2023 11:36:40 +0545 Subject: [PATCH 11/11] Create akamai-logo.svg --- public/images/integrations/akamai-logo.svg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 public/images/integrations/akamai-logo.svg diff --git a/public/images/integrations/akamai-logo.svg b/public/images/integrations/akamai-logo.svg new file mode 100644 index 0000000000..cdcf31b4c2 --- /dev/null +++ b/public/images/integrations/akamai-logo.svg @@ -0,0 +1,3 @@ + + +