diff --git a/CHANGES.md b/CHANGES.md index 94b56bc7a..ca647e748 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,11 @@ +# Version 0.9.2 + +## Bugs + +- Fixed JWT session validation (#1408) +- Fixed passing valid JWT session to Cloud Functions (#1421) +- Fixed race condition when uploading and extracting bigger Cloud Functions (#1419) + # Version 0.9.1 ## Bugs diff --git a/app/config/collections2.php b/app/config/collections2.php index d13d2e938..6b7365cb9 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -609,13 +609,13 @@ $collections = [ ], ], 'indexes' => [ - // [ - // '$id' => '_key_provider_providerUid', - // 'type' => Database::INDEX_KEY, - // 'attributes' => ['provider', 'providerUid'], - // 'lengths' => [100, 100], - // 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], - // ] + [ + '$id' => '_key_provider_providerUid', + 'type' => Database::INDEX_KEY, + 'attributes' => ['provider', 'providerUid'], + 'lengths' => [100, 100], + 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC], + ] ], ], diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 044664d65..27d9f8200 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -53,7 +53,7 @@ App::post('/v1/account') ->action(function ($email, $password, $name, $request, $response, $project, $dbForInternal, $audits) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Database $dbForInternal */ /** @var Appwrite\Event\Event $audits */ @@ -119,7 +119,7 @@ App::post('/v1/account') ; $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($user, Response::MODEL_USER); + $response->dynamic($user, Response::MODEL_USER); }); App::post('/v1/account/sessions') @@ -229,7 +229,7 @@ App::post('/v1/account/sessions') ->setAttribute('countryName', $countryName) ; - $response->dynamic2($session, Response::MODEL_SESSION); + $response->dynamic($session, Response::MODEL_SESSION); }); App::get('/v1/account/sessions/oauth2/:provider') @@ -256,7 +256,7 @@ App::get('/v1/account/sessions/oauth2/:provider') ->action(function ($provider, $success, $failure, $scopes, $request, $response, $project) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ $protocol = $request->getProtocol(); $callback = $protocol.'://'.$request->getHostname().'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getId(); @@ -362,7 +362,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->action(function ($provider, $code, $state, $request, $response, $project, $user, $dbForInternal, $geodb, $audits, $events) use ($oauthDefaultSuccess) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Database $dbForInternal */ /** @var MaxMind\Db\Reader $geodb */ @@ -546,7 +546,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->setParam('data', ['provider' => $provider]) ; - $events->setParam('eventData', $response->output2($session, Response::MODEL_SESSION)); + $events->setParam('eventData', $response->output($session, Response::MODEL_SESSION)); if (!Config::getParam('domainVerification')) { $response @@ -603,7 +603,7 @@ App::post('/v1/account/sessions/anonymous') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Database\Document $user */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Database $dbForInternal */ /** @var MaxMind\Db\Reader $geodb */ /** @var Appwrite\Event\Event $audits */ @@ -710,7 +710,7 @@ App::post('/v1/account/sessions/anonymous') ->setAttribute('countryName', $countryName) ; - $response->dynamic2($session, Response::MODEL_SESSION); + $response->dynamic($session, Response::MODEL_SESSION); }); App::post('/v1/account/jwt') @@ -737,7 +737,7 @@ App::post('/v1/account/jwt') $current = new Document(); foreach ($sessions as $session) { - /** @var Appwrite\Database\Document $session */ + /** @var Utopia\Database\Document $session */ if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too $current = $session; @@ -751,7 +751,7 @@ App::post('/v1/account/jwt') $jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2(new Document(['jwt' => $jwt->encode([ + $response->dynamic(new Document(['jwt' => $jwt->encode([ // 'uid' => 1, // 'aud' => 'http://site.com', // 'scopes' => ['user'], @@ -778,7 +778,7 @@ App::get('/v1/account') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Document $user */ - $response->dynamic2($user, Response::MODEL_USER); + $response->dynamic($user, Response::MODEL_USER); }); App::get('/v1/account/prefs') @@ -800,7 +800,7 @@ App::get('/v1/account/prefs') $prefs = $user->getAttribute('prefs', new \stdClass()); - $response->dynamic2(new Document($prefs), Response::MODEL_PREFERENCES); + $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); App::get('/v1/account/sessions') @@ -837,7 +837,7 @@ App::get('/v1/account/sessions') $sessions[$key] = $session; } - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'sessions' => $sessions, 'sum' => count($sessions), ]), Response::MODEL_SESSION_LIST); @@ -861,7 +861,7 @@ App::get('/v1/account/logs') ->inject('dbForInternal') ->action(function ($response, $user, $locale, $geodb, $dbForInternal) { /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $user */ /** @var Utopia\Locale\Locale $locale */ /** @var MaxMind\Db\Reader $geodb */ @@ -914,7 +914,7 @@ App::get('/v1/account/logs') } - $response->dynamic2(new Document(['logs' => $output]), Response::MODEL_LOG_LIST); + $response->dynamic(new Document(['logs' => $output]), Response::MODEL_LOG_LIST); }); App::get('/v1/account/sessions/:sessionId') @@ -935,7 +935,7 @@ App::get('/v1/account/sessions/:sessionId') ->inject('dbForInternal') ->action(function ($sessionId, $response, $user, $locale, $dbForInternal) { /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $user */ + /** @var Utopia\Database\Document $user */ /** @var Utopia\Locale\Locale $locale */ /** @var Utopia\Database\Database $dbForInternal */ @@ -956,7 +956,7 @@ App::get('/v1/account/sessions/:sessionId') ->setAttribute('countryName', $countryName) ; - return $response->dynamic2($session, Response::MODEL_SESSION); + return $response->dynamic($session, Response::MODEL_SESSION); } } @@ -994,7 +994,7 @@ App::patch('/v1/account/name') ->setParam('resource', 'users/'.$user->getId()) ; - $response->dynamic2($user, Response::MODEL_USER); + $response->dynamic($user, Response::MODEL_USER); }); App::patch('/v1/account/password') @@ -1037,7 +1037,7 @@ App::patch('/v1/account/password') ->setParam('resource', 'users/'.$user->getId()) ; - $response->dynamic2($user, Response::MODEL_USER); + $response->dynamic($user, Response::MODEL_USER); }); App::patch('/v1/account/email') @@ -1092,7 +1092,7 @@ App::patch('/v1/account/email') ->setParam('resource', 'users/'.$user->getId()) ; - $response->dynamic2($user, Response::MODEL_USER); + $response->dynamic($user, Response::MODEL_USER); }); App::patch('/v1/account/prefs') @@ -1125,7 +1125,7 @@ App::patch('/v1/account/prefs') ->setParam('resource', 'users/'.$user->getId()) ; - $response->dynamic2($user, Response::MODEL_USER); + $response->dynamic($user, Response::MODEL_USER); }); App::delete('/v1/account') @@ -1172,7 +1172,7 @@ App::delete('/v1/account') ; $events - ->setParam('eventData', $response->output2($user, Response::MODEL_USER)) + ->setParam('eventData', $response->output($user, Response::MODEL_USER)) ; if (!Config::getParam('domainVerification')) { @@ -1259,7 +1259,7 @@ App::delete('/v1/account/sessions/:sessionId') $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $sessions)); $events - ->setParam('eventData', $response->output2($session, Response::MODEL_SESSION)) + ->setParam('eventData', $response->output($session, Response::MODEL_SESSION)) ; return $response->noContent(); @@ -1332,7 +1332,7 @@ App::delete('/v1/account/sessions') $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('sessions', [])); $events - ->setParam('eventData', $response->output2(new Document([ + ->setParam('eventData', $response->output(new Document([ 'sessions' => $sessions, 'sum' => count($sessions), ]), Response::MODEL_SESSION_LIST)) @@ -1369,7 +1369,7 @@ App::post('/v1/account/recovery') /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ /** @var Utopia\Locale\Locale $locale */ /** @var Appwrite\Event\Event $mails */ /** @var Appwrite\Event\Event $audits */ @@ -1443,7 +1443,7 @@ App::post('/v1/account/recovery') $events ->setParam('eventData', - $response->output2($recovery->setAttribute('secret', $secret), + $response->output($recovery->setAttribute('secret', $secret), Response::MODEL_TOKEN )) ; @@ -1459,7 +1459,7 @@ App::post('/v1/account/recovery') ; $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($recovery, Response::MODEL_TOKEN); + $response->dynamic($recovery, Response::MODEL_TOKEN); }); App::put('/v1/account/recovery') @@ -1533,7 +1533,7 @@ App::put('/v1/account/recovery') ->setParam('resource', 'users/'.$profile->getId()) ; - $response->dynamic2($recovery, Response::MODEL_TOKEN); + $response->dynamic($recovery, Response::MODEL_TOKEN); }); App::post('/v1/account/verification') @@ -1563,7 +1563,7 @@ App::post('/v1/account/verification') ->action(function ($url, $request, $response, $project, $user, $dbForInternal, $locale, $audits, $events, $mails) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Locale\Locale $locale */ @@ -1629,7 +1629,7 @@ App::post('/v1/account/verification') $events ->setParam('eventData', - $response->output2($verification->setAttribute('secret', $verificationSecret), + $response->output($verification->setAttribute('secret', $verificationSecret), Response::MODEL_TOKEN )) ; @@ -1645,7 +1645,7 @@ App::post('/v1/account/verification') ; $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($verification, Response::MODEL_TOKEN); + $response->dynamic($verification, Response::MODEL_TOKEN); }); App::put('/v1/account/verification') @@ -1710,5 +1710,5 @@ App::put('/v1/account/verification') ->setParam('resource', 'users/'.$user->getId()) ; - $response->dynamic2($verification, Response::MODEL_TOKEN); + $response->dynamic($verification, Response::MODEL_TOKEN); }); diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 2b6951295..13f335f4f 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -424,7 +424,7 @@ App::get('/v1/avatars/initials') ->inject('user') ->action(function ($name, $width, $height, $color, $background, $response, $user) { /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $user */ + /** @var Utopia\Database\Document $user */ $themes = [ ['color' => '#27005e', 'background' => '#e1d2f6'], // VIOLET diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 92a4f802f..4fcf64ee1 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -67,7 +67,7 @@ App::post('/v1/database/collections') ; $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($collection, Response::MODEL_COLLECTION); + $response->dynamic($collection, Response::MODEL_COLLECTION); }); App::get('/v1/database/collections') @@ -93,7 +93,7 @@ App::get('/v1/database/collections') $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : []; - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'collections' => $dbForExternal->find(Database::COLLECTIONS, $queries, $limit, $offset, ['_id'], [$orderType]), 'sum' => $dbForExternal->count(Database::COLLECTIONS, $queries, APP_LIMIT_COUNT), ]), Response::MODEL_COLLECTION_LIST); @@ -123,7 +123,7 @@ App::get('/v1/database/collections/:collectionId') throw new Exception('Collection not found', 404); } - $response->dynamic2($collection, Response::MODEL_COLLECTION); + $response->dynamic($collection, Response::MODEL_COLLECTION); }); App::put('/v1/database/collections/:collectionId') @@ -177,7 +177,7 @@ App::put('/v1/database/collections/:collectionId') ->setParam('data', $collection->getArrayCopy()) ; - $response->dynamic2($collection, Response::MODEL_COLLECTION); + $response->dynamic($collection, Response::MODEL_COLLECTION); }); App::delete('/v1/database/collections/:collectionId') @@ -212,7 +212,7 @@ App::delete('/v1/database/collections/:collectionId') $dbForExternal->deleteCollection($collectionId); $events - ->setParam('eventData', $response->output2($collection, Response::MODEL_COLLECTION)) + ->setParam('eventData', $response->output($collection, Response::MODEL_COLLECTION)) ; $audits @@ -296,7 +296,7 @@ App::post('/v1/database/collections/:collectionId/attributes') ; $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($attribute, Response::MODEL_ATTRIBUTE); + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE); }); App::get('/v1/database/collections/:collectionId/attributes') @@ -331,7 +331,7 @@ App::get('/v1/database/collections/:collectionId/attributes') ])]); }, $attributes); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'sum' => \count($attributes), 'attributes' => $attributes ]), Response::MODEL_ATTRIBUTE_LIST); @@ -375,7 +375,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId') 'collectionId' => $collectionId, ])]); - $response->dynamic2($attribute, Response::MODEL_ATTRIBUTE); + $response->dynamic($attribute, Response::MODEL_ATTRIBUTE); }); App::delete('/v1/database/collections/:collectionId/attributes/:attributeId') @@ -428,7 +428,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId') ; $events - ->setParam('payload', $response->output2($attribute, Response::MODEL_ATTRIBUTE)) + ->setParam('payload', $response->output($attribute, Response::MODEL_ATTRIBUTE)) ; $audits @@ -526,7 +526,7 @@ App::post('/v1/database/collections/:collectionId/indexes') ; $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($index, Response::MODEL_INDEX); + $response->dynamic($index, Response::MODEL_INDEX); }); @@ -562,7 +562,7 @@ App::get('/v1/database/collections/:collectionId/indexes') ])]); }, $indexes); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'sum' => \count($indexes), 'attributes' => $indexes, ]), Response::MODEL_INDEX_LIST); @@ -606,7 +606,7 @@ App::get('/v1/database/collections/:collectionId/indexes/:indexId') 'collectionId' => $collectionId, ])]); - $response->dynamic2($index, Response::MODEL_INDEX); + $response->dynamic($index, Response::MODEL_INDEX); }); App::delete('/v1/database/collections/:collectionId/indexes/:indexId') @@ -659,7 +659,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId') ; $events - ->setParam('payload', $response->output2($index, Response::MODEL_INDEX)) + ->setParam('payload', $response->output($index, Response::MODEL_INDEX)) ; $audits @@ -731,7 +731,7 @@ App::post('/v1/database/collections/:collectionId/documents') ; $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($document, Response::MODEL_DOCUMENT); + $response->dynamic($document, Response::MODEL_DOCUMENT); }); App::get('/v1/database/collections/:collectionId/documents') @@ -782,7 +782,7 @@ App::get('/v1/database/collections/:collectionId/documents') $documents = $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'sum' => \count($documents), 'documents' => $documents, ]), Response::MODEL_DOCUMENT_LIST); @@ -819,7 +819,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId') throw new Exception('No document found', 404); } - $response->dynamic2($document, Response::MODEL_DOCUMENT); + $response->dynamic($document, Response::MODEL_DOCUMENT); }); App::patch('/v1/database/collections/:collectionId/documents/:documentId') @@ -890,7 +890,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') ->setParam('data', $document->getArrayCopy()) ; - $response->dynamic2($document, Response::MODEL_DOCUMENT); + $response->dynamic($document, Response::MODEL_DOCUMENT); }); App::delete('/v1/database/collections/:collectionId/documents/:documentId') @@ -931,7 +931,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId') $success = $dbForExternal->deleteDocument($collectionId, $documentId); $events - ->setParam('eventData', $response->output2($document, Response::MODEL_DOCUMENT)) + ->setParam('eventData', $response->output($document, Response::MODEL_DOCUMENT)) ; $audits diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index fe2fc3912..8d2f28a87 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -2,7 +2,7 @@ use Ahc\Jwt\JWT; use Appwrite\Auth\Auth; -use Appwrite\Database\Validator\UID; +use Utopia\Database\Validator\UID; use Utopia\Storage\Storage; use Utopia\Storage\Validator\File; use Utopia\Storage\Validator\FileExt; @@ -68,7 +68,7 @@ App::post('/v1/functions') ])); $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($function, Response::MODEL_FUNCTION); + $response->dynamic($function, Response::MODEL_FUNCTION); }); App::get('/v1/functions') @@ -94,7 +94,7 @@ App::get('/v1/functions') $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : []; - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'functions' => $dbForInternal->find('functions', $queries, $limit, $offset, ['_id'], [$orderType]), 'sum' => $dbForInternal->count('functions', $queries, APP_LIMIT_COUNT), ]), Response::MODEL_FUNCTION_LIST); @@ -124,7 +124,7 @@ App::get('/v1/functions/:functionId') throw new Exception('Function not found', 404); } - $response->dynamic2($function, Response::MODEL_FUNCTION); + $response->dynamic($function, Response::MODEL_FUNCTION); }); App::get('/v1/functions/:functionId/usage') @@ -142,7 +142,7 @@ App::get('/v1/functions/:functionId/usage') ->inject('register') ->action(function ($functionId, $range, $response, $project, $dbForInternal, $register) { /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Registry\Registry $register */ @@ -272,7 +272,7 @@ App::put('/v1/functions/:functionId') ->action(function ($functionId, $name, $execute, $vars, $events, $schedule, $timeout, $response, $dbForInternal, $project) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ $function = $dbForInternal->getDocument('functions', $functionId); @@ -305,7 +305,7 @@ App::put('/v1/functions/:functionId') ]); // Async task rescheduale } - $response->dynamic2($function, Response::MODEL_FUNCTION); + $response->dynamic($function, Response::MODEL_FUNCTION); }); App::patch('/v1/functions/:functionId/tag') @@ -328,7 +328,7 @@ App::patch('/v1/functions/:functionId/tag') ->action(function ($functionId, $tag, $response, $dbForInternal, $project) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ $function = $dbForInternal->getDocument('functions', $functionId); $tag = $dbForInternal->getDocument('tags', $tag); @@ -360,7 +360,7 @@ App::patch('/v1/functions/:functionId/tag') ]); // Async task rescheduale } - $response->dynamic2($function, Response::MODEL_FUNCTION); + $response->dynamic($function, Response::MODEL_FUNCTION); }); App::delete('/v1/functions/:functionId') @@ -484,7 +484,7 @@ App::post('/v1/functions/:functionId/tags') ; $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($tag, Response::MODEL_TAG); + $response->dynamic($tag, Response::MODEL_TAG); }); App::get('/v1/functions/:functionId/tags') @@ -520,7 +520,7 @@ App::get('/v1/functions/:functionId/tags') $results = $dbForInternal->find('tags', $queries, $limit, $offset, ['_id'], [$orderType]); $sum = $dbForInternal->count('tags', $queries, APP_LIMIT_COUNT); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'tags' => $results, 'sum' => $sum, ]), Response::MODEL_TAG_LIST); @@ -561,7 +561,7 @@ App::get('/v1/functions/:functionId/tags/:tagId') throw new Exception('Tag not found', 404); } - $response->dynamic2($tag, Response::MODEL_TAG); + $response->dynamic($tag, Response::MODEL_TAG); }); App::delete('/v1/functions/:functionId/tags/:tagId') @@ -645,9 +645,9 @@ App::post('/v1/functions/:functionId/executions') ->inject('user') ->action(function ($functionId, $data, /*$async,*/ $response, $project, $dbForInternal, $user) { /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Database $dbForInternal */ - /** @var Appwrite\Database\Document $user */ + /** @var Utopia\Database\Document $user */ Authorization::disable(); @@ -696,20 +696,20 @@ App::post('/v1/functions/:functionId/executions') $jwt = ''; // initialize if (!$user->isEmpty()) { // If userId exists, generate a JWT for function - $tokens = $user->getAttribute('tokens', []); - $session = new Document(); + $sessions = $user->getAttribute('sessions', []); + $current = new Document(); - foreach ($tokens as $token) { /** @var Appwrite\Database\Document $token */ - if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too - $session = $token; + foreach ($sessions as $session) { /** @var Utopia\Database\Document $session */ + if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too + $current = $session; } } - if(!$session->isEmpty()) { + if(!$current->isEmpty()) { $jwtObj = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. $jwt = $jwtObj->encode([ 'userId' => $user->getId(), - 'sessionId' => $session->getId(), + 'sessionId' => $current->getId(), ]); } } @@ -726,7 +726,7 @@ App::post('/v1/functions/:functionId/executions') ]); $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($execution, Response::MODEL_EXECUTION); + $response->dynamic($execution, Response::MODEL_EXECUTION); }); App::get('/v1/functions/:functionId/executions') @@ -765,7 +765,7 @@ App::get('/v1/functions/:functionId/executions') new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]), ], APP_LIMIT_COUNT); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'executions' => $results, 'sum' => $sum, ]), Response::MODEL_EXECUTION_LIST); @@ -808,5 +808,5 @@ App::get('/v1/functions/:functionId/executions/:executionId') throw new Exception('Execution not found', 404); } - $response->dynamic2($execution, Response::MODEL_EXECUTION); + $response->dynamic($execution, Response::MODEL_EXECUTION); }); diff --git a/app/controllers/api/locale.php b/app/controllers/api/locale.php index 049478918..a1f9c1035 100644 --- a/app/controllers/api/locale.php +++ b/app/controllers/api/locale.php @@ -1,6 +1,6 @@ createNamespace($project->getId()); $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($project, Response::MODEL_PROJECT); + $response->dynamic($project, Response::MODEL_PROJECT); }); App::get('/v1/projects') @@ -162,7 +162,7 @@ App::get('/v1/projects') $results = $dbForConsole->find('projects', $queries, $limit, $offset, ['_id'], [$orderType]); $sum = $dbForConsole->count('projects', $queries, APP_LIMIT_COUNT); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'projects' => $results, 'sum' => $sum, ]), Response::MODEL_PROJECT_LIST); @@ -191,7 +191,7 @@ App::get('/v1/projects/:projectId') throw new Exception('Project not found', 404); } - $response->dynamic2($project, Response::MODEL_PROJECT); + $response->dynamic($project, Response::MODEL_PROJECT); }); App::get('/v1/projects/:projectId/usage') @@ -440,7 +440,7 @@ App::patch('/v1/projects/:projectId') ->setAttribute('legalTaxId', $legalTaxId) ); - $response->dynamic2($project, Response::MODEL_PROJECT); + $response->dynamic($project, Response::MODEL_PROJECT); }); App::patch('/v1/projects/:projectId/oauth2') @@ -474,7 +474,7 @@ App::patch('/v1/projects/:projectId/oauth2') ->setAttribute('usersOauth2' . \ucfirst($provider) . 'Secret', $secret) ); - $response->dynamic2($project, Response::MODEL_PROJECT); + $response->dynamic($project, Response::MODEL_PROJECT); }); App::patch('/v1/projects/:projectId/auth/limit') @@ -505,7 +505,7 @@ App::patch('/v1/projects/:projectId/auth/limit') ->setAttribute('usersAuthLimit', $limit) ); - $response->dynamic2($project, Response::MODEL_PROJECT); + $response->dynamic($project, Response::MODEL_PROJECT); }); App::patch('/v1/projects/:projectId/auth/:method') @@ -540,7 +540,7 @@ App::patch('/v1/projects/:projectId/auth/:method') ->setAttribute($authKey, $status) ); - $response->dynamic2($project, Response::MODEL_PROJECT); + $response->dynamic($project, Response::MODEL_PROJECT); }); App::delete('/v1/projects/:projectId') @@ -638,7 +638,7 @@ App::post('/v1/projects/:projectId/webhooks') ); $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($webhook, Response::MODEL_WEBHOOK); + $response->dynamic($webhook, Response::MODEL_WEBHOOK); }); App::get('/v1/projects/:projectId/webhooks') @@ -666,7 +666,7 @@ App::get('/v1/projects/:projectId/webhooks') $webhooks = $project->getAttribute('webhooks', []); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'webhooks' => $webhooks, 'sum' => count($webhooks), ]), Response::MODEL_WEBHOOK_LIST); @@ -702,7 +702,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') throw new Exception('Webhook not found', 404); } - $response->dynamic2($webhook, Response::MODEL_WEBHOOK); + $response->dynamic($webhook, Response::MODEL_WEBHOOK); }); App::put('/v1/projects/:projectId/webhooks/:webhookId') @@ -754,7 +754,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') $dbForConsole->updateDocument('projects', $project->getId(), $project); - $response->dynamic2($webhook, Response::MODEL_WEBHOOK); + $response->dynamic($webhook, Response::MODEL_WEBHOOK); }); App::delete('/v1/projects/:projectId/webhooks/:webhookId') @@ -828,7 +828,7 @@ App::post('/v1/projects/:projectId/keys') ); $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($key, Response::MODEL_KEY); + $response->dynamic($key, Response::MODEL_KEY); }); App::get('/v1/projects/:projectId/keys') @@ -856,7 +856,7 @@ App::get('/v1/projects/:projectId/keys') $keys = $project->getAttribute('keys', []); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'keys' => $keys, 'sum' => count($keys), ]), Response::MODEL_KEY_LIST); @@ -889,7 +889,7 @@ App::get('/v1/projects/:projectId/keys/:keyId') throw new Exception('Key not found', 404); } - $response->dynamic2($key, Response::MODEL_KEY); + $response->dynamic($key, Response::MODEL_KEY); }); App::put('/v1/projects/:projectId/keys/:keyId') @@ -931,7 +931,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') $dbForConsole->updateDocument('projects', $project->getId(), $project); - $response->dynamic2($key, Response::MODEL_KEY); + $response->dynamic($key, Response::MODEL_KEY); }); App::delete('/v1/projects/:projectId/keys/:keyId') @@ -1032,7 +1032,7 @@ App::post('/v1/projects/:projectId/tasks') } $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($task, Response::MODEL_TASK); + $response->dynamic($task, Response::MODEL_TASK); }); App::get('/v1/projects/:projectId/tasks') @@ -1060,7 +1060,7 @@ App::get('/v1/projects/:projectId/tasks') $tasks = $project->getAttribute('tasks', []); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'tasks' => $tasks, 'sum' => count($tasks), ]), Response::MODEL_TASK_LIST); @@ -1097,7 +1097,7 @@ App::get('/v1/projects/:projectId/tasks/:taskId') throw new Exception('Task not found', 404); } - $response->dynamic2($task, Response::MODEL_TASK); + $response->dynamic($task, Response::MODEL_TASK); }); App::put('/v1/projects/:projectId/tasks/:taskId') @@ -1163,7 +1163,7 @@ App::put('/v1/projects/:projectId/tasks/:taskId') ResqueScheduler::enqueueAt($next, 'v1-tasks', 'TasksV1', $task->getArrayCopy()); } - $response->dynamic2($task, Response::MODEL_TASK); + $response->dynamic($task, Response::MODEL_TASK); }); App::delete('/v1/projects/:projectId/tasks/:taskId') @@ -1244,7 +1244,7 @@ App::post('/v1/projects/:projectId/platforms') ); $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($platform, Response::MODEL_PLATFORM); + $response->dynamic($platform, Response::MODEL_PLATFORM); }); App::get('/v1/projects/:projectId/platforms') @@ -1272,7 +1272,7 @@ App::get('/v1/projects/:projectId/platforms') $platforms = $project->getAttribute('platforms', []); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'platforms' => $platforms, 'sum' => count($platforms), ]), Response::MODEL_PLATFORM_LIST); @@ -1308,7 +1308,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') throw new Exception('Platform not found', 404); } - $response->dynamic2($platform, Response::MODEL_PLATFORM); + $response->dynamic($platform, Response::MODEL_PLATFORM); }); App::put('/v1/projects/:projectId/platforms/:platformId') @@ -1363,7 +1363,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') $dbForConsole->updateDocument('projects', $project->getId(), $project); - $response->dynamic2($platform, Response::MODEL_PLATFORM); + $response->dynamic($platform, Response::MODEL_PLATFORM); }); App::delete('/v1/projects/:projectId/platforms/:platformId') @@ -1453,7 +1453,7 @@ App::post('/v1/projects/:projectId/domains') ); $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($domain, Response::MODEL_DOMAIN); + $response->dynamic($domain, Response::MODEL_DOMAIN); }); App::get('/v1/projects/:projectId/domains') @@ -1481,7 +1481,7 @@ App::get('/v1/projects/:projectId/domains') $domains = $project->getAttribute('domains', []); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'domains' => $domains, 'sum' => count($domains), ]), Response::MODEL_DOMAIN_LIST); @@ -1517,7 +1517,7 @@ App::get('/v1/projects/:projectId/domains/:domainId') throw new Exception('Domain not found', 404); } - $response->dynamic2($domain, Response::MODEL_DOMAIN); + $response->dynamic($domain, Response::MODEL_DOMAIN); }); App::patch('/v1/projects/:projectId/domains/:domainId/verification') @@ -1557,7 +1557,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') } if ($domain->getAttribute('verification') === true) { - return $response->dynamic2($domain, Response::MODEL_DOMAIN); + return $response->dynamic($domain, Response::MODEL_DOMAIN); } $validator = new CNAME($target->get()); // Verify Domain with DNS records @@ -1578,7 +1578,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') 'domain' => $domain->getAttribute('domain'), ]); - $response->dynamic2($domain, Response::MODEL_DOMAIN); + $response->dynamic($domain, Response::MODEL_DOMAIN); }); App::delete('/v1/projects/:projectId/domains/:domainId') diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index eec8a6b76..b3b60d10f 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -12,7 +12,7 @@ use Utopia\Cache\Cache; use Utopia\Cache\Adapter\Filesystem; use Appwrite\ClamAV\Network; use Utopia\Database\Document; -use Appwrite\Database\Validator\UID; +use Utopia\Database\Validator\UID; use Utopia\Storage\Storage; use Utopia\Storage\Validator\File; use Utopia\Storage\Validator\FileSize; @@ -77,7 +77,7 @@ App::post('/v1/storage/buckets') ; $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($data, Response::MODEL_BUCKET); + $response->dynamic($data, Response::MODEL_BUCKET); }); App::get('/v1/storage/buckets') @@ -103,7 +103,7 @@ App::get('/v1/storage/buckets') $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, $search)] : []; - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'buckets' => $dbForInternal->find('buckets', $queries, $limit, $offset, ['_id'], [$orderType]), 'sum' => $dbForInternal->count('buckets', $queries, APP_LIMIT_COUNT), ]), Response::MODEL_BUCKET_LIST); @@ -133,7 +133,7 @@ App::get('/v1/storage/buckets/:bucketId') throw new Exception('Bucket not found', 404); } - $response->dynamic2($bucket, Response::MODEL_BUCKET); + $response->dynamic($bucket, Response::MODEL_BUCKET); }); App::put('/v1/storage/buckets/:bucketId') @@ -173,6 +173,13 @@ App::put('/v1/storage/buckets/:bucketId') $read ??= $bucket->getAttribute('$read', []); // By default inherit read permissions $write ??= $bucket->getAttribute('$write',[]); // By default inherit write permissions + $read??=$bucket->getAttribute('$read', []); // By default inherit read permissions + $write??=$bucket->getAttribute('$write', []); // By default inherit write permissions + $maximumFileSize??=$bucket->getAttribute('maximumFileSize', (int)App::getEnv('_APP_STORAGE_LIMIT', 0)); + $allowedFileExtensions??=$bucket->getAttribute('allowedFileExtensions', []); + $enabled??=$bucket->getAttribute('enabled', true); + $encryption??=$bucket->getAttribute('encryption', true); + $antiVirus??=$bucket->getAttribute('antiVirus', true); $bucket = $dbForInternal->updateDocument('buckets', $bucket->getId(), $bucket ->setAttribute('name',$name) @@ -191,7 +198,7 @@ App::put('/v1/storage/buckets/:bucketId') ->setParam('data', $bucket->getArrayCopy()) ; - $response->dynamic2($bucket, Response::MODEL_BUCKET); + $response->dynamic($bucket, Response::MODEL_BUCKET); }); App::delete('/v1/storage/buckets/:bucketId') @@ -234,7 +241,7 @@ App::delete('/v1/storage/buckets/:bucketId') } $events - ->setParam('eventData', $response->output2($bucket, Response::MODEL_BUCKET)) + ->setParam('eventData', $response->output($bucket, Response::MODEL_BUCKET)) ; $audits @@ -275,7 +282,7 @@ App::post('/v1/storage/buckets/:bucketId/files') /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - /** @var Appwrite\Database\Document $user */ + /** @var Utopia\Database\Document $user */ /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $usage */ @@ -399,7 +406,8 @@ App::post('/v1/storage/buckets/:bucketId/files') ; $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($file, Response::MODEL_FILE); + $response->dynamic($file, Response::MODEL_FILE); + ; }); App::get('/v1/storage/buckets/:bucketId/files') @@ -437,7 +445,7 @@ App::get('/v1/storage/buckets/:bucketId/files') $queries[] = [new Query('name', Query::TYPE_SEARCH, [$search])]; } - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'files' => $dbForInternal->find('files', $queries, $limit, $offset, ['_id'], [$orderType]), 'sum' => $dbForInternal->count('files', $queries, APP_LIMIT_COUNT), ]), Response::MODEL_FILE_LIST); @@ -475,7 +483,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') throw new Exception('File not found', 404); } - $response->dynamic2($file, Response::MODEL_FILE); + $response->dynamic($file, Response::MODEL_FILE); }); App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') @@ -827,7 +835,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->setParam('resource', 'storage/files/'.$file->getId()) ; - $response->dynamic2($file, Response::MODEL_FILE); + $response->dynamic($file, Response::MODEL_FILE); }); App::delete('/v1/storage/buckets/:bucketId/files/:fileId') @@ -886,7 +894,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ; $events - ->setParam('eventData', $response->output2($file, Response::MODEL_FILE)) + ->setParam('eventData', $response->output($file, Response::MODEL_FILE)) ; $response->noContent(); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 5ad8ecd6c..4e8226034 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -1,7 +1,6 @@ desc('Create Team') @@ -80,7 +80,7 @@ App::post('/v1/teams') } $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($team, Response::MODEL_TEAM); + $response->dynamic($team, Response::MODEL_TEAM); }); App::get('/v1/teams') @@ -109,7 +109,7 @@ App::get('/v1/teams') $results = $dbForInternal->find('teams', $queries, $limit, $offset, ['_id'], [$orderType]); $sum = $dbForInternal->count('teams', $queries, APP_LIMIT_COUNT); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'teams' => $results, 'sum' => $sum, ]), Response::MODEL_TEAM_LIST); @@ -139,7 +139,7 @@ App::get('/v1/teams/:teamId') throw new Exception('Team not found', 404); } - $response->dynamic2($team, Response::MODEL_TEAM); + $response->dynamic($team, Response::MODEL_TEAM); }); App::put('/v1/teams/:teamId') @@ -170,7 +170,7 @@ App::put('/v1/teams/:teamId') $team = $dbForInternal->updateDocument('teams', $team->getId(), $team->setAttribute('name', $name)); - $response->dynamic2($team, Response::MODEL_TEAM); + $response->dynamic($team, Response::MODEL_TEAM); }); App::delete('/v1/teams/:teamId') @@ -222,7 +222,7 @@ App::delete('/v1/teams/:teamId') ; $events - ->setParam('eventData', $response->output2($team, Response::MODEL_TEAM)) + ->setParam('eventData', $response->output($team, Response::MODEL_TEAM)) ; $response->noContent(); @@ -256,8 +256,8 @@ App::post('/v1/teams/:teamId/memberships') ->inject('mails') ->action(function ($teamId, $email, $name, $roles, $url, $response, $project, $user, $dbForInternal, $locale, $audits, $mails) { /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $project */ - /** @var Appwrite\Database\Document $user */ + /** @var Utopia\Database\Document $project */ + /** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Database $dbForInternal */ /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $mails */ @@ -409,7 +409,7 @@ App::post('/v1/teams/:teamId/memberships') ; $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($membership + $response->dynamic($membership ->setAttribute('email', $email) ->setAttribute('name', $name) , Response::MODEL_MEMBERSHIP); @@ -457,7 +457,7 @@ App::get('/v1/teams/:teamId/memberships') $users[] = new Document(\array_merge($temp, $membership->getArrayCopy())); } - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'memberships' => $users, 'sum' => $sum, ]), Response::MODEL_MEMBERSHIP_LIST); @@ -486,7 +486,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') ->action(function ($teamId, $membershipId, $roles, $request, $response, $user, $dbForInternal, $audits) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $user */ + /** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Database $dbForInternal */ /** @var Appwrite\Event\Event $audits */ @@ -525,7 +525,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') ->setParam('resource', 'teams/'.$teamId) ; - $response->dynamic2($membership, Response::MODEL_MEMBERSHIP); + $response->dynamic($membership, Response::MODEL_MEMBERSHIP); }); App::patch('/v1/teams/:teamId/memberships/:membershipId/status') @@ -553,7 +553,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->action(function ($teamId, $membershipId, $userId, $secret, $request, $response, $user, $dbForInternal, $geodb, $audits) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $user */ + /** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Database $dbForInternal */ /** @var MaxMind\Db\Reader $geodb */ /** @var Appwrite\Event\Event $audits */ @@ -661,7 +661,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite')) ; - $response->dynamic2($membership + $response->dynamic($membership ->setAttribute('email', $user->getAttribute('email')) ->setAttribute('name', $user->getAttribute('name')) , Response::MODEL_MEMBERSHIP); @@ -744,7 +744,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') ; $events - ->setParam('eventData', $response->output2($membership, Response::MODEL_MEMBERSHIP)) + ->setParam('eventData', $response->output($membership, Response::MODEL_MEMBERSHIP)) ; $response->noContent(); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 8a1be0374..2abbc5b50 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -65,7 +65,7 @@ App::post('/v1/users') } $response->setStatusCode(Response::STATUS_CODE_CREATED); - $response->dynamic2($user, Response::MODEL_USER); + $response->dynamic($user, Response::MODEL_USER); }); App::get('/v1/users') @@ -92,7 +92,7 @@ App::get('/v1/users') $results = $dbForInternal->find('users', [], $limit, $offset, ['_id'], [$orderType]); $sum = $dbForInternal->count('users', [], APP_LIMIT_COUNT); - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'users' => $results, 'sum' => $sum, ]), Response::MODEL_USER_LIST); @@ -122,7 +122,7 @@ App::get('/v1/users/:userId') throw new Exception('User not found', 404); } - $response->dynamic2($user, Response::MODEL_USER); + $response->dynamic($user, Response::MODEL_USER); }); App::get('/v1/users/:userId/prefs') @@ -151,7 +151,7 @@ App::get('/v1/users/:userId/prefs') $prefs = $user->getAttribute('prefs', new \stdClass()); - $response->dynamic2(new Document($prefs), Response::MODEL_PREFERENCES); + $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); App::get('/v1/users/:userId/sessions') @@ -194,7 +194,7 @@ App::get('/v1/users/:userId/sessions') $sessions[$key] = $session; } - $response->dynamic2(new Document([ + $response->dynamic(new Document([ 'sessions' => $sessions, 'sum' => count($sessions), ]), Response::MODEL_SESSION_LIST); @@ -218,7 +218,7 @@ App::get('/v1/users/:userId/logs') ->inject('geodb') ->action(function ($userId, $response, $dbForInternal, $locale, $geodb) { /** @var Appwrite\Utopia\Response $response */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ /** @var Utopia\Database\Database $dbForInternal */ /** @var Utopia\Locale\Locale $locale */ /** @var MaxMind\Db\Reader $geodb */ @@ -305,7 +305,7 @@ App::get('/v1/users/:userId/logs') } } - $response->dynamic2(new Document(['logs' => $output]), Response::MODEL_LOG_LIST); + $response->dynamic(new Document(['logs' => $output]), Response::MODEL_LOG_LIST); }); App::patch('/v1/users/:userId/status') @@ -336,7 +336,7 @@ App::patch('/v1/users/:userId/status') $user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('status', (bool) $status)); - $response->dynamic2($user, Response::MODEL_USER); + $response->dynamic($user, Response::MODEL_USER); }); App::patch('/v1/users/:userId/verification') @@ -367,7 +367,7 @@ App::patch('/v1/users/:userId/verification') $user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification)); - $response->dynamic2($user, Response::MODEL_USER); + $response->dynamic($user, Response::MODEL_USER); }); App::patch('/v1/users/:userId/prefs') @@ -398,7 +398,7 @@ App::patch('/v1/users/:userId/prefs') $user = $dbForInternal->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs)); - $response->dynamic2(new Document($prefs), Response::MODEL_PREFERENCES); + $response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES); }); App::delete('/v1/users/:userId/sessions/:sessionId') @@ -440,7 +440,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId') $user->setAttribute('sessions', $sessions); $events - ->setParam('eventData', $response->output2($user, Response::MODEL_USER)) + ->setParam('eventData', $response->output($user, Response::MODEL_USER)) ; $dbForInternal->updateDocument('users', $user->getId(), $user); @@ -486,7 +486,7 @@ App::delete('/v1/users/:userId/sessions') $dbForInternal->updateDocument('users', $user->getId(), $user->getAttribute('sessions', [])); $events - ->setParam('eventData', $response->output2($user, Response::MODEL_USER)) + ->setParam('eventData', $response->output($user, Response::MODEL_USER)) ; $response->noContent(); @@ -535,7 +535,7 @@ App::delete('/v1/users/:userId') ; $events - ->setParam('eventData', $response->output2($user, Response::MODEL_USER)) + ->setParam('eventData', $response->output($user, Response::MODEL_USER)) ; // TODO : Response filter implementation diff --git a/app/controllers/general.php b/app/controllers/general.php index d8f24019f..46bd81689 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -10,16 +10,14 @@ use Utopia\Exception; use Utopia\Config\Config; use Utopia\Domains\Domain; use Appwrite\Auth\Auth; -use Appwrite\Database\Validator\Authorization; use Appwrite\Network\Validator\Origin; use Appwrite\Utopia\Response\Filters\V06; use Appwrite\Utopia\Response\Filters\V07; use Appwrite\Utopia\Response\Filters\V08; use Utopia\CLI\Console; -use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization as Authorization2; +use Utopia\Database\Validator\Authorization; Config::setParam('domainVerification', false); Config::setParam('cookieDomain', 'localhost'); @@ -44,7 +42,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons $domains[$domain->get()] = false; Console::warning($domain->get() . ' is not a publicly accessible domain. Skipping SSL certificate generation.'); } else { - Authorization2::disable(); + Authorization::disable(); $certificate = $dbForConsole->findFirst('certificates', [ new Query('domain', QUERY::TYPE_EQUAL, [$domain->get()]) @@ -55,7 +53,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons 'domain' => $domain->get(), ]); $certificate = $dbForConsole->createDocument('certificates', $certificate); - Authorization2::enable(); + Authorization::enable(); Console::info('Issuing a TLS certificate for the master domain (' . $domain->get() . ') in a few seconds...'); @@ -66,7 +64,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons 'validateCNAME' => false, ]); } else { - Authorization2::enable(); // ensure authorization is reenabled + Authorization::enable(); // ensure authorization is reenabled } $domains[$domain->get()] = true; } @@ -239,26 +237,21 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons $scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', [])); Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. - Authorization2::setDefaultStatus(false); // Cancel security segmentation for API keys. } } if ($user->getId()) { Authorization::setRole('user:'.$user->getId()); - Authorization2::setRole('user:'.$user->getId()); } Authorization::setRole('role:'.$role); - Authorization2::setRole('role:'.$role); \array_map(function ($node) { if (isset($node['teamId']) && isset($node['roles'])) { Authorization::setRole('team:'.$node['teamId']); - Authorization2::setRole('team:'.$node['teamId']); foreach ($node['roles'] as $nodeRole) { // Set all team roles Authorization::setRole('team:'.$node['teamId'].'/'.$nodeRole); - Authorization2::setRole('team:'.$node['teamId'].'/'.$nodeRole); } } }, $user->getAttribute('memberships', [])); @@ -305,7 +298,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\View $layout */ - /** @var Appwrite\Database\Document $project */ + /** @var Utopia\Database\Document $project */ if ($error instanceof PDOException) { throw $error; @@ -393,7 +386,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project) { $response->html($layout->render()); } - $response->dynamic2(new Document($output), + $response->dynamic(new Document($output), $utopia->isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR); }, ['error', 'utopia', 'request', 'response', 'layout', 'project']); diff --git a/app/controllers/mock.php b/app/controllers/mock.php index 17bade579..9d4479c01 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -2,7 +2,7 @@ global $utopia, $request, $response; -use Appwrite\Database\Document; +use Utopia\Database\Document; use Appwrite\Network\Validator\Host; use Appwrite\Utopia\Response; use Utopia\App; diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 24fed9661..836ef8f71 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -1,7 +1,7 @@ on('start', function (Server $http) use ($payloadSize, $register) { $app = new App('UTC'); go(function() use ($register, $app) { - $db = $register->get('dbPool')->get(); - $redis = $register->get('redisPool')->get(); + // wait for database to be ready + $attempts = 0; + $max = 10; + $sleep = 1; + + do { + try { + $attempts++; + $db = $register->get('dbPool')->get(); + $redis = $register->get('redisPool')->get(); + break; // leave the do-while if successful + } catch(\Exception $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: '. $e->getMessage()); + } + sleep($sleep); + } + } while ($attempts < $max); App::setResource('db', function () use (&$db) { return $db; @@ -69,9 +85,6 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) { return $app; }); - // wait for database to be ready - sleep(5); - $dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */ if(!$dbForConsole->exists()) { @@ -170,9 +183,6 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo Authorization::cleanRoles(); Authorization::setRole('role:all'); - Authorization2::cleanRoles(); - Authorization2::setRole('role:all'); - $app->run($request, $response); } catch (\Throwable $th) { Console::error('[Error] Type: '.get_class($th)); diff --git a/app/init.php b/app/init.php index a37112c4b..d689bba9f 100644 --- a/app/init.php +++ b/app/init.php @@ -24,7 +24,6 @@ use Appwrite\Database\Database; use Appwrite\Database\Adapter\MySQL as MySQLAdapter; use Appwrite\Database\Adapter\Redis as RedisAdapter; use Appwrite\Database\Document; -use Appwrite\Database\Validator\Authorization; use Appwrite\Event\Event; use Appwrite\OpenSSL\OpenSSL; use Utopia\App; @@ -39,7 +38,7 @@ use Utopia\Cache\Cache; use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Document as Document2; use Utopia\Database\Database as Database2; -use Utopia\Database\Validator\Authorization as Authorization2; +use Utopia\Database\Validator\Authorization; use Swoole\Database\PDOConfig; use Swoole\Database\PDOPool; use Swoole\Database\RedisConfig; @@ -58,8 +57,8 @@ const APP_LIMIT_USERS = 10000; const APP_LIMIT_ANTIVIRUS = 20971520; //20MB const APP_LIMIT_ENCRYPTION = 20971520; //20MB const APP_LIMIT_COMPRESSION = 20971520; //20MB -const APP_CACHE_BUSTER = 149; -const APP_VERSION_STABLE = '0.9.1'; +const APP_CACHE_BUSTER = 150; +const APP_VERSION_STABLE = '0.9.2'; const APP_STORAGE_UPLOADS = '/storage/uploads'; const APP_STORAGE_FUNCTIONS = '/storage/functions'; const APP_STORAGE_CACHE = '/storage/cache'; @@ -443,7 +442,6 @@ App::setResource('user', function($mode, $project, $console, $request, $response /** @var string $mode */ Authorization::setDefaultStatus(true); - Authorization2::setDefaultStatus(true); Auth::setCookieName('a_session_'.$project->getId()); @@ -488,7 +486,6 @@ App::setResource('user', function($mode, $project, $console, $request, $response if (APP_MODE_ADMIN === $mode) { if ($user->find('teamId', $project->getAttribute('teamId'), 'memberships')) { Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. - Authorization2::setDefaultStatus(false); // Cancel security segmentation for admin users. } else { $user = new Document2(['$id' => '', '$collection' => 'users']); } @@ -533,12 +530,10 @@ App::setResource('project', function($dbForConsole, $request, $console) { } Authorization::disable(); - Authorization2::disable(); $project = $dbForConsole->getDocument('projects', $projectId); Authorization::reset(); - Authorization2::reset(); return $project; }, ['dbForConsole', 'request', 'console']); diff --git a/app/workers.php b/app/workers.php index ff5ba1dbd..7dd48eaff 100644 --- a/app/workers.php +++ b/app/workers.php @@ -23,10 +23,12 @@ $register->set('db', function () { return $pdo; }); + $register->set('cache', function () { // Register cache connection $redis = new Redis(); $redis->pconnect(App::getEnv('_APP_REDIS_HOST', ''), App::getEnv('_APP_REDIS_PORT', '')); $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); return $redis; -}); \ No newline at end of file +}); + diff --git a/app/workers/audits.php b/app/workers/audits.php index 495c93bc4..663de3ce4 100644 --- a/app/workers/audits.php +++ b/app/workers/audits.php @@ -2,11 +2,7 @@ use Appwrite\Resque\Worker; use Utopia\Audit\Audit; -use Utopia\Cache\Adapter\Redis; -use Utopia\Cache\Cache; use Utopia\CLI\Console; -use Utopia\Database\Adapter\MariaDB; -use Utopia\Database\Database; require_once __DIR__.'/../workers.php'; @@ -23,8 +19,6 @@ class AuditsV1 extends Worker public function run(): void { - global $register; - $projectId = $this->args['projectId']; $userId = $this->args['userId']; $event = $this->args['event']; @@ -32,12 +26,8 @@ class AuditsV1 extends Worker $userAgent = $this->args['userAgent']; $ip = $this->args['ip']; $data = $this->args['data']; - $db = $register->get('db', true); - $cache = new Cache(new Redis($register->get('cache'))); - $dbForInternal = new Database(new MariaDB($db), $cache); - $dbForInternal->setNamespace('project_'.$projectId.'_internal'); - + $dbForInternal = $this->getInternalDB($projectId); $audit = new Audit($dbForInternal); $audit->log($userId, $event, $resource, $userAgent, $ip, '', $data); diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 1db2a4636..1720d6adb 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -3,13 +3,9 @@ use Appwrite\Network\Validator\CNAME; use Appwrite\Resque\Worker; use Utopia\App; -use Utopia\Cache\Adapter\Redis; -use Utopia\Cache\Cache; use Utopia\CLI\Console; -use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; -use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Validator\Authorization; use Utopia\Domains\Domain; @@ -28,180 +24,167 @@ class CertificatesV1 extends Worker public function run(): void { - global $register; + $dbForConsole = $this->getConsoleDB(); - go(function() use ($register) { - $db = $register->get('dbPool')->get(); - $redis = $register->get('redisPool')->get(); + /** + * 1. Get new domain document - DONE + * 1.1. Validate domain is valid, public suffix is known and CNAME records are verified - DONE + * 2. Check if a certificate already exists - DONE + * 3. Check if certificate is about to expire, if not - skip it + * 3.1. Create / renew certificate + * 3.2. Update loadblancer + * 3.3. Update database (domains, change date, expiry) + * 3.4. Set retry on failure + * 3.5. Schedule to renew certificate in 60 days + */ - $cache = new Cache(new Redis($redis)); - $dbForConsole = new Database(new MariaDB($db), $cache); - $dbForConsole->setNamespace('project_console_internal'); + Authorization::disable(); - /** - * 1. Get new domain document - DONE - * 1.1. Validate domain is valid, public suffix is known and CNAME records are verified - DONE - * 2. Check if a certificate already exists - DONE - * 3. Check if certificate is about to expire, if not - skip it - * 3.1. Create / renew certificate - * 3.2. Update loadblancer - * 3.3. Update database (domains, change date, expiry) - * 3.4. Set retry on failure - * 3.5. Schedule to renew certificate in 60 days - */ + // Args + $document = $this->args['document']; + $domain = $this->args['domain']; - Authorization::disable(); - - // Args - $document = $this->args['document']; - $domain = $this->args['domain']; - - // Validation Args - $validateTarget = $this->args['validateTarget'] ?? true; - $validateCNAME = $this->args['validateCNAME'] ?? true; - - // Options - $domain = new Domain((!empty($domain)) ? $domain : ''); - $expiry = 60 * 60 * 24 * 30 * 2; // 60 days - $safety = 60 * 60; // 1 hour - $renew = (\time() + $expiry); - - if(empty($domain->get())) { - throw new Exception('Missing domain'); - } - - if(!$domain->isKnown() || $domain->isTest()) { - throw new Exception('Unknown public suffix for domain'); - } - - if($validateTarget) { - $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); + // Validation Args + $validateTarget = $this->args['validateTarget'] ?? true; + $validateCNAME = $this->args['validateCNAME'] ?? true; - if(!$target->isKnown() || $target->isTest()) { - throw new Exception('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.'); - } + // Options + $domain = new Domain((!empty($domain)) ? $domain : ''); + $expiry = 60 * 60 * 24 * 30 * 2; // 60 days + $safety = 60 * 60; // 1 hour + $renew = (\time() + $expiry); + + if(empty($domain->get())) { + throw new Exception('Missing domain'); + } + + if(!$domain->isKnown() || $domain->isTest()) { + throw new Exception('Unknown public suffix for domain'); + } + + if($validateTarget) { + $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); + + if(!$target->isKnown() || $target->isTest()) { + throw new Exception('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.'); } + } - if($validateCNAME) { - $validator = new CNAME($target->get()); // Verify Domain with DNS records - - if(!$validator->isValid($domain->get())) { - throw new Exception('Failed to verify domain DNS records'); - } + if($validateCNAME) { + $validator = new CNAME($target->get()); // Verify Domain with DNS records + + if(!$validator->isValid($domain->get())) { + throw new Exception('Failed to verify domain DNS records'); } + } - $certificate = $dbForConsole->findFirst('certificates', [ - new Query('domain', QUERY::TYPE_EQUAL, [$domain->get()]) - ], /*limit*/ 1); + $certificate = $dbForConsole->findFirst('certificates', [ + new Query('domain', QUERY::TYPE_EQUAL, [$domain->get()]) + ], /*limit*/ 1); - // $condition = ($certificate - // && $certificate instanceof Document - // && isset($certificate['issueDate']) - // && (($certificate['issueDate'] + ($expiry)) > time())) ? 'true' : 'false'; + // $condition = ($certificate + // && $certificate instanceof Document + // && isset($certificate['issueDate']) + // && (($certificate['issueDate'] + ($expiry)) > time())) ? 'true' : 'false'; - // throw new Exception('cert issued at'.date('d.m.Y H:i', $certificate['issueDate']).' | renew date is: '.date('d.m.Y H:i', ($certificate['issueDate'] + ($expiry))).' | condition is '.$condition); + // throw new Exception('cert issued at'.date('d.m.Y H:i', $certificate['issueDate']).' | renew date is: '.date('d.m.Y H:i', ($certificate['issueDate'] + ($expiry))).' | condition is '.$condition); - $certificate = (!empty($certificate) && $certificate instanceof $certificate) ? $certificate->getArrayCopy() : []; + $certificate = (!empty($certificate) && $certificate instanceof $certificate) ? $certificate->getArrayCopy() : []; - if(!empty($certificate) - && isset($certificate['issueDate']) - && (($certificate['issueDate'] + ($expiry)) > \time())) { // Check last issue time - throw new Exception('Renew isn\'t required'); + if(!empty($certificate) + && isset($certificate['issueDate']) + && (($certificate['issueDate'] + ($expiry)) > \time())) { // Check last issue time + throw new Exception('Renew isn\'t required'); + } + + $staging = (App::isProduction()) ? '' : ' --dry-run'; + $email = App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'); + + if(empty($email)) { + throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate'); + } + + $stdout = ''; + $stderr = ''; + + $exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}" + ." --email ".$email + ." -w ".APP_STORAGE_CERTIFICATES + ." -d {$domain->get()}", '', $stdout, $stderr); + + if($exit !== 0) { + throw new Exception('Failed to issue a certificate with message: '.$stderr); + } + + $path = APP_STORAGE_CERTIFICATES.'/'.$domain->get(); + + if(!\is_readable($path)) { + if (!\mkdir($path, 0755, true)) { + throw new Exception('Failed to create path...'); } + } - $staging = (App::isProduction()) ? '' : ' --dry-run'; - $email = App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'); + if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/cert.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/cert.pem')) { + throw new Exception('Failed to rename certificate cert.pem: '.\json_encode($stdout)); + } - if(empty($email)) { - throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate'); - } + if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/chain.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/chain.pem')) { + throw new Exception('Failed to rename certificate chain.pem: '.\json_encode($stdout)); + } - $stdout = ''; - $stderr = ''; + if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/fullchain.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/fullchain.pem')) { + throw new Exception('Failed to rename certificate fullchain.pem: '.\json_encode($stdout)); + } - $exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}" - ." --email ".$email - ." -w ".APP_STORAGE_CERTIFICATES - ." -d {$domain->get()}", '', $stdout, $stderr); + if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/privkey.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/privkey.pem')) { + throw new Exception('Failed to rename certificate privkey.pem: '.\json_encode($stdout)); + } - if($exit !== 0) { - throw new Exception('Failed to issue a certificate with message: '.$stderr); - } + $certificate = new Document(\array_merge($certificate, [ + 'domain' => $domain->get(), + 'issueDate' => \time(), + 'renewDate' => $renew, + 'attempts' => 0, + 'log' => \json_encode($stdout), + ])); - $path = APP_STORAGE_CERTIFICATES.'/'.$domain->get(); + $certificate = $dbForConsole->createDocument('certificates', $certificate); - if(!\is_readable($path)) { - if (!\mkdir($path, 0755, true)) { - throw new Exception('Failed to create path...'); - } - } - - if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/cert.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/cert.pem')) { - throw new Exception('Failed to rename certificate cert.pem: '.\json_encode($stdout)); - } + if(!$certificate) { + throw new Exception('Failed saving certificate to DB'); + } - if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/chain.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/chain.pem')) { - throw new Exception('Failed to rename certificate chain.pem: '.\json_encode($stdout)); - } - - if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/fullchain.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/fullchain.pem')) { - throw new Exception('Failed to rename certificate fullchain.pem: '.\json_encode($stdout)); - } - - if(!@\rename('/etc/letsencrypt/live/'.$domain->get().'/privkey.pem', APP_STORAGE_CERTIFICATES.'/'.$domain->get().'/privkey.pem')) { - throw new Exception('Failed to rename certificate privkey.pem: '.\json_encode($stdout)); - } - - $certificate = new Document(\array_merge($certificate, [ - 'domain' => $domain->get(), - 'issueDate' => \time(), - 'renewDate' => $renew, - 'attempts' => 0, - 'log' => \json_encode($stdout), + if(!empty($document)) { + $certificate = new Document(\array_merge($document, [ + 'updated' => \time(), + 'certificateId' => $certificate->getId(), ])); - $certificate = $dbForConsole->createDocument('certificates', $certificate); + $certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); if(!$certificate) { - throw new Exception('Failed saving certificate to DB'); + throw new Exception('Failed saving domain to DB'); } + } - if(!empty($document)) { - $certificate = new Document(\array_merge($document, [ - 'updated' => \time(), - 'certificateId' => $certificate->getId(), - ])); - - $certificate = $dbForConsole->updateDocument('certificates', $certificate->getId(), $certificate); - - if(!$certificate) { - throw new Exception('Failed saving domain to DB'); - } - } - - $config = - "tls: - certificates: - - certFile: /storage/certificates/{$domain->get()}/fullchain.pem - keyFile: /storage/certificates/{$domain->get()}/privkey.pem"; + $config = +"tls: + certificates: + - certFile: /storage/certificates/{$domain->get()}/fullchain.pem + keyFile: /storage/certificates/{$domain->get()}/privkey.pem"; - if(!\file_put_contents(APP_STORAGE_CONFIG.'/'.$domain->get().'.yml', $config)) { - throw new Exception('Failed to save SSL configuration'); - } + if(!\file_put_contents(APP_STORAGE_CONFIG.'/'.$domain->get().'.yml', $config)) { + throw new Exception('Failed to save SSL configuration'); + } - ResqueScheduler::enqueueAt($renew + $safety, 'v1-certificates', 'CertificatesV1', [ - 'document' => [], - 'domain' => $domain->get(), - 'validateTarget' => $validateTarget, - 'validateCNAME' => $validateCNAME, - ]); // Async task rescheduale + ResqueScheduler::enqueueAt($renew + $safety, 'v1-certificates', 'CertificatesV1', [ + 'document' => [], + 'domain' => $domain->get(), + 'validateTarget' => $validateTarget, + 'validateCNAME' => $validateCNAME, + ]); // Async task rescheduale - Authorization::reset(); - - // Return db connections to pool - $register->get('dbPool')->put($db); - $register->get('redisPool')->put($redis); - }); + Authorization::reset(); } public function shutdown(): void diff --git a/app/workers/database.php b/app/workers/database.php index b02487104..544ffe106 100644 --- a/app/workers/database.php +++ b/app/workers/database.php @@ -1,14 +1,10 @@ deleteIndex($collectionId, $id); } - - /** - * @param string $projectId - * - * @return Database - */ - protected function getInternalDB($projectId): Database - { - global $register; - - $dbForInternal = null; - - go(function() use ($register, $projectId, &$dbForInternal) { - $db = $register->get('dbPool')->get(); - $redis = $register->get('redisPool')->get(); - - $cache = new Cache(new RedisCache($redis)); - $dbForInternal = new Database(new MariaDB($db), $cache); - $dbForInternal->setNamespace('project_'.$projectId.'_internal'); // Main DB - - }); - - return $dbForInternal; - } - - /** - * @param string $projectId - * - * @return Database - */ - protected function getExternalDB($projectId): Database - { - global $register; - - /** @var Database $dbForExternal */ - $dbForExternal = null; - - go(function() use ($register, $projectId, &$dbForExternal) { - $db = $register->get('dbPool')->get(); - $redis = $register->get('redisPool')->get(); - - $cache = new Cache(new RedisCache($redis)); - $dbForExternal = new Database(new MariaDB($db), $cache); - $dbForExternal->setNamespace('project_'.$projectId.'_external'); // Main DB - - }); - - return $dbForExternal; - } } diff --git a/app/workers/deletes.php b/app/workers/deletes.php index b6fca6343..319281837 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -3,15 +3,14 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; -use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Database\Validator\Authorization; use Appwrite\Resque\Worker; use Utopia\Storage\Device\Local; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; use Utopia\CLI\Console; -use Utopia\Config\Config; use Utopia\Audit\Audit; +use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Cache\Cache; use Utopia\Database\Adapter\MariaDB; diff --git a/app/workers/functions.php b/app/workers/functions.php index 8db7d6475..510346fb4 100644 --- a/app/workers/functions.php +++ b/app/workers/functions.php @@ -6,11 +6,8 @@ use Appwrite\Utopia\Response\Model\Execution; use Cron\CronExpression; use Swoole\Runtime; use Utopia\App; -use Utopia\Cache\Adapter\Redis; -use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; @@ -139,9 +136,6 @@ class FunctionsV1 extends Worker { global $register; - $db = $register->get('db'); - $cache = $register->get('cache'); - $projectId = $this->args['projectId'] ?? ''; $functionId = $this->args['functionId'] ?? ''; $webhooks = $this->args['webhooks'] ?? []; @@ -154,9 +148,7 @@ class FunctionsV1 extends Worker $userId = $this->args['userId'] ?? ''; $jwt = $this->args['jwt'] ?? ''; - $cache = new Cache(new Redis($cache)); - $database = new Database(new MariaDB($db), $cache); - $database->setNamespace('project_'.$projectId.'_internal'); + $database = $this->getInternalDB($projectId); switch ($trigger) { case 'event': @@ -413,15 +405,24 @@ class FunctionsV1 extends Worker " --workdir /usr/local/src". " ".\implode(" ", $vars). " {$runtime['image']}". - " sh -c 'mv /tmp/code.tar.gz /usr/local/src/code.tar.gz && tar -zxf /usr/local/src/code.tar.gz --strip 1 && rm /usr/local/src/code.tar.gz && tail -f /dev/null'" + " tail -f /dev/null" , '', $stdout, $stderr, 30); - $executionEnd = \microtime(true); - if($exitCode !== 0) { throw new Exception('Failed to create function environment: '.$stderr); } + $exitCodeUntar = Console::execute("docker exec ". + $container. + " sh -c 'mv /tmp/code.tar.gz /usr/local/src/code.tar.gz && tar -zxf /usr/local/src/code.tar.gz --strip 1 && rm /usr/local/src/code.tar.gz'" + , '', $stdout, $stderr, 60); + + if($exitCodeUntar !== 0) { + throw new Exception('Failed to extract tar: '.$stderr); + } + + $executionEnd = \microtime(true); + $list[$container] = [ 'name' => $container, 'online' => true, diff --git a/app/workers/tasks.php b/app/workers/tasks.php index cb24c65a9..b3469393e 100644 --- a/app/workers/tasks.php +++ b/app/workers/tasks.php @@ -28,11 +28,6 @@ class TasksV1 extends Worker public function run(): void { - global $register; - - $db = $register->get('db'); - $cache = $register->get('cache'); - $projectId = $this->args['projectId'] ?? null; $taskId = $this->args['$id'] ?? null; $updated = $this->args['updated'] ?? null; @@ -44,9 +39,7 @@ class TasksV1 extends Worker $logLimit = 5; $alert = ''; - $cache = new Cache(new Redis($cache)); - $dbForConsole = new Database(new MariaDB($db), $cache); - $dbForConsole->setNamespace('project_console_internal'); + $dbForConsole = $this->getConsoleDB(); /* * 1. Get Original Task diff --git a/composer.lock b/composer.lock index 13e7d8680..f8acf9d0d 100644 --- a/composer.lock +++ b/composer.lock @@ -2079,16 +2079,16 @@ }, { "name": "utopia-php/framework", - "version": "0.16.1", + "version": "0.16.2", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "e7440b91077ccf2f4f3908fde4f31d177f8ea692" + "reference": "df02354a670df366b92e2e927fbf128be9a8e64e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/e7440b91077ccf2f4f3908fde4f31d177f8ea692", - "reference": "e7440b91077ccf2f4f3908fde4f31d177f8ea692", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/df02354a670df366b92e2e927fbf128be9a8e64e", + "reference": "df02354a670df366b92e2e927fbf128be9a8e64e", "shasum": "" }, "require": { @@ -2122,9 +2122,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.16.1" + "source": "https://github.com/utopia-php/framework/tree/0.16.2" }, - "time": "2021-07-18T10:59:00+00:00" + "time": "2021-07-20T10:24:56+00:00" }, { "name": "utopia-php/image", @@ -3367,16 +3367,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.11.0", + "version": "v4.12.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "fe14cf3672a149364fb66dfe11bf6549af899f94" + "reference": "6608f01670c3cc5079e18c1dab1104e002579143" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/fe14cf3672a149364fb66dfe11bf6549af899f94", - "reference": "fe14cf3672a149364fb66dfe11bf6549af899f94", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6608f01670c3cc5079e18c1dab1104e002579143", + "reference": "6608f01670c3cc5079e18c1dab1104e002579143", "shasum": "" }, "require": { @@ -3417,9 +3417,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.11.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.12.0" }, - "time": "2021-07-03T13:36:55+00:00" + "time": "2021-07-21T10:44:31+00:00" }, { "name": "openlss/lib-array2xml", @@ -3476,16 +3476,16 @@ }, { "name": "phar-io/manifest", - "version": "2.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", - "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { @@ -3530,9 +3530,9 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/master" + "source": "https://github.com/phar-io/manifest/tree/2.0.3" }, - "time": "2020-06-27T14:33:11+00:00" + "time": "2021-07-20T11:28:43+00:00" }, { "name": "phar-io/version", @@ -5132,6 +5132,7 @@ "type": "github" } ], + "abandoned": true, "time": "2020-09-28T06:45:17+00:00" }, { @@ -5297,16 +5298,16 @@ }, { "name": "symfony/console", - "version": "v5.3.2", + "version": "v5.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "649730483885ff2ca99ca0560ef0e5f6b03f2ac1" + "reference": "ebd610dacd40d75b6a12bf64b5ccd494fc7d6ab1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/649730483885ff2ca99ca0560ef0e5f6b03f2ac1", - "reference": "649730483885ff2ca99ca0560ef0e5f6b03f2ac1", + "url": "https://api.github.com/repos/symfony/console/zipball/ebd610dacd40d75b6a12bf64b5ccd494fc7d6ab1", + "reference": "ebd610dacd40d75b6a12bf64b5ccd494fc7d6ab1", "shasum": "" }, "require": { @@ -5314,11 +5315,12 @@ "symfony/deprecation-contracts": "^2.1", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php73": "^1.8", - "symfony/polyfill-php80": "^1.15", + "symfony/polyfill-php80": "^1.16", "symfony/service-contracts": "^1.1|^2", "symfony/string": "^5.1" }, "conflict": { + "psr/log": ">=3", "symfony/dependency-injection": "<4.4", "symfony/dotenv": "<5.1", "symfony/event-dispatcher": "<4.4", @@ -5326,10 +5328,10 @@ "symfony/process": "<4.4" }, "provide": { - "psr/log-implementation": "1.0" + "psr/log-implementation": "1.0|2.0" }, "require-dev": { - "psr/log": "~1.0", + "psr/log": "^1|^2", "symfony/config": "^4.4|^5.0", "symfony/dependency-injection": "^4.4|^5.0", "symfony/event-dispatcher": "^4.4|^5.0", @@ -5375,7 +5377,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.3.2" + "source": "https://github.com/symfony/console/tree/v5.3.4" }, "funding": [ { @@ -5391,7 +5393,7 @@ "type": "tidelift" } ], - "time": "2021-06-12T09:42:48+00:00" + "time": "2021-07-26T16:33:26+00:00" }, { "name": "symfony/deprecation-contracts", diff --git a/docker-compose.yml b/docker-compose.yml index 32ec33231..ed50016ed 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -508,17 +508,6 @@ services: networks: - appwrite - swagger-validator: - image: 'swaggerapi/swagger-validator-v2:v2.0.5' - container_name: appwrite-swagger-validator - ports: - - '9506:8080' - environment: - - REJECT_LOCAL=false - - REJECT_REDIRECT=false - networks: - - appwrite - # redis-commander: # image: rediscommander/redis-commander:latest # restart: unless-stopped diff --git a/docs/references/teams/create-team-membership.md b/docs/references/teams/create-team-membership.md index a920c260b..c6d81de48 100644 --- a/docs/references/teams/create-team-membership.md +++ b/docs/references/teams/create-team-membership.md @@ -1,5 +1,5 @@ -Use this endpoint to invite a new member to join your team. An email with a link to join the team will be sent to the new member email address if the member doesn't exist in the project it will be created automatically. +Use this endpoint to invite a new member to join your team. If initiated from Client SDK, an email with a link to join the team will be sent to the new member's email address if the member doesn't exist in the project it will be created automatically. If initiated from server side SDKs, new member will automatically be added to the team. -Use the 'URL' parameter to redirect the user from the invitation email back to your app. When the user is redirected, use the [Update Team Membership Status](/docs/client/teams#teamsUpdateMembershipStatus) endpoint to allow the user to accept the invitation to the team. +Use the 'URL' parameter to redirect the user from the invitation email back to your app. When the user is redirected, use the [Update Team Membership Status](/docs/client/teams#teamsUpdateMembershipStatus) endpoint to allow the user to accept the invitation to the team. While calling from side SDKs the redirect url can be empty string. Please note that in order to avoid a [Redirect Attacks](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URL's are the once from domains you have set when added your platforms in the console interface. \ No newline at end of file diff --git a/src/Appwrite/Auth/Auth.php b/src/Appwrite/Auth/Auth.php index 3ae28997b..56f708493 100644 --- a/src/Appwrite/Auth/Auth.php +++ b/src/Appwrite/Auth/Auth.php @@ -2,7 +2,7 @@ namespace Appwrite\Auth; -use Appwrite\Database\Document; +use Utopia\Database\Document; class Auth { diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index bd03e7d33..8eae515b9 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -40,6 +40,7 @@ abstract class Migration '0.8.0' => 'V07', '0.9.0' => 'V08', '0.9.1' => 'V08', + '0.9.2' => 'V08', ]; /** diff --git a/src/Appwrite/Resque/Worker.php b/src/Appwrite/Resque/Worker.php index db8dc91ce..84241173f 100644 --- a/src/Appwrite/Resque/Worker.php +++ b/src/Appwrite/Resque/Worker.php @@ -2,6 +2,12 @@ namespace Appwrite\Resque; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Redis as RedisCache; +use Utopia\CLI\Console; +use Utopia\Database\Database; +use Utopia\Database\Adapter\MariaDB; + abstract class Worker { public $args = []; @@ -12,6 +18,13 @@ abstract class Worker abstract public function shutdown(): void; + const MAX_ATTEMPTS = 10; + const SLEEP_TIME = 2; + + const DATABASE_INTERNAL = 'internal'; + const DATABASE_EXTERNAL = 'external'; + const DATABASE_CONSOLE = 'console'; + public function setUp(): void { $this->init(); @@ -26,4 +39,91 @@ abstract class Worker { $this->shutdown(); } + /** + * Get internal project database + * @param string $projectId + * @return Database + */ + protected function getInternalDB(string $projectId): Database + { + return $this->getDB(self::DATABASE_INTERNAL, $projectId); + } + + /** + * Get external project database + * @param string $projectId + * @return Database + */ + protected function getExternalDB(string $projectId): Database + { + return $this->getDB(self::DATABASE_EXTERNAL, $projectId); + } + + /** + * Get console database + * @return Database + */ + protected function getConsoleDB(): Database + { + return $this->getDB(self::DATABASE_CONSOLE); + } + + /** + * Get console database + * @param string $type One of (internal, external, console) + * @param string $projectId of internal or external DB + * @return Database + */ + private function getDB($type, $projectId = ''): Database + { + global $register; + + $namespace = ''; + $sleep = self::SLEEP_TIME; // overwritten when necessary + + switch ($type) { + case self::DATABASE_INTERNAL: + if (!$projectId) { + throw new \Exception('ProjectID not provided - cannot get database'); + } + $namespace = "project_{$projectId}_internal"; + break; + case self::DATABASE_EXTERNAL: + if (!$projectId) { + throw new \Exception('ProjectID not provided - cannot get database'); + } + $namespace = "project_{$projectId}_external"; + break; + case self::DATABASE_CONSOLE: + $namespace = "project_console_internal"; + $sleep = 5; // ConsoleDB needs extra sleep time to ensure tables are created + break; + default: + throw new \Exception('Unknown database type: ' . $type); + break; + } + + $attempts = 0; + + do { + try { + $attempts++; + $cache = new Cache(new RedisCache($register->get('cache'))); + $database = new Database(new MariaDB($register->get('db')), $cache); + $database->setNamespace($namespace); // Main DB + if (!$database->exists()) { + throw new \Exception("Table does not exist: {$database->getNamespace()}"); + } + break; // leave loop if successful + } catch(\Exception $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= self::MAX_ATTEMPTS) { + throw new \Exception('Failed to connect to database: '. $e->getMessage()); + } + sleep($sleep); + } + } while ($attempts < self::MAX_ATTEMPTS); + + return $database; + } } \ No newline at end of file diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index b14134cdf..02b3c315d 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -238,7 +238,7 @@ class OpenAPI3 extends Format $node['schema']['type'] = $validator->getType(); $node['schema']['x-example'] = false; break; - case 'Appwrite\Database\Validator\UID': + case 'Utopia\Database\Validator\UID': $node['schema']['type'] = $validator->getType(); $node['schema']['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']'; break; diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 9809220f8..d8a84e3cc 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -234,7 +234,7 @@ class Swagger2 extends Format $node['type'] = $validator->getType(); $node['x-example'] = false; break; - case 'Appwrite\Database\Validator\UID': + case 'Utopia\Database\Validator\UID': $node['type'] = $validator->getType(); $node['x-example'] = '['.\strtoupper(Template::fromCamelCaseToSnake($node['name'])).']'; break; diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index ddfc9f781..bd71089a1 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -5,7 +5,7 @@ namespace Appwrite\Utopia; use Exception; use Utopia\Swoole\Response as SwooleResponse; use Swoole\Http\Response as SwooleHTTPResponse; -use Appwrite\Database\Document; +use Utopia\Database\Document; use Appwrite\Utopia\Response\Filter; use Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response\Model\None; @@ -46,7 +46,6 @@ use Appwrite\Utopia\Response\Model\Webhook; use Appwrite\Utopia\Response\Model\Preferences; use Appwrite\Utopia\Response\Model\Mock; // Keep last use stdClass; -use Utopia\Database\Document as DatabaseDocument; /** * @method Response public function setStatusCode(int $code = 200) @@ -290,27 +289,6 @@ class Response extends SwooleResponse $this->json(!empty($output) ? $output : new stdClass()); } - /** - * Validate response objects and outputs - * the response according to given format type - * - * @param DatabaseDocument $document - * @param string $model - * - * return void - */ - public function dynamic2(DatabaseDocument $document, string $model): void - { - $output = $this->output2($document, $model); - - // If filter is set, parse the output - if(self::isFilter()){ - $output = self::getFilter()->parse($output, $model); - } - - $this->json(!empty($output) ? $output : new stdClass()); - } - /** * Generate valid response object from document data * @@ -363,58 +341,6 @@ class Response extends SwooleResponse return $this->payload; } - /** - * Generate valid response object from document data - * - * @param DatabaseDocument $document - * @param string $model - * - * return array - */ - public function output2(DatabaseDocument $document, string $model): array - { - $data = $document; - $model = $this->getModel($model); - $output = []; - - if ($model->isAny()) { - $this->payload = $document->getArrayCopy(); - return $this->payload; - } - - foreach ($model->getRules() as $key => $rule) { - if (!$document->isSet($key)) { - if (!is_null($rule['default'])) { - $document->setAttribute($key, $rule['default']); - } else { - throw new Exception('Model '.$model->getName().' is missing response key: '.$key); - } - } - - if ($rule['array']) { - if (!is_array($data[$key])) { - throw new Exception($key.' must be an array of type '.$rule['type']); - } - - foreach ($data[$key] as &$item) { - if ($item instanceof Document) { - if (!array_key_exists($rule['type'], $this->models)) { - throw new Exception('Missing model for rule: '. $rule['type']); - } - - $item = $this->output($item, $rule['type']); - } - } - } - - $output[$key] = $data[$key]; - } - - $this->payload = $output; - - return $this->payload; - } - /** * YAML * diff --git a/tests/e2e/General/HTTPTest.php b/tests/e2e/General/HTTPTest.php index 78f77f4d1..4f9a8dc95 100644 --- a/tests/e2e/General/HTTPTest.php +++ b/tests/e2e/General/HTTPTest.php @@ -133,7 +133,7 @@ class HTTPTest extends Scope } $client = new Client(); - $client->setEndpoint('http://appwrite-swagger-validator:8080'); + $client->setEndpoint('https://validator.swagger.io'); /** * Test for SUCCESS diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index f958de520..4def7dede 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -185,7 +185,32 @@ class FunctionsCustomClientTest extends Scope $this->assertEquals(201, $execution['headers']['status-code']); + $executionId = $execution['body']['$id'] ?? ''; + sleep(10); + + $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions/'.$executionId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $projectId, + 'x-appwrite-key' => $apikey, + ]); + + $output = json_decode($executions['body']['stdout'], true); + + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertEquals('completed', $executions['body']['status']); + $this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']); + $this->assertEquals('Test', $output['APPWRITE_FUNCTION_NAME']); + $this->assertEquals($tagId, $output['APPWRITE_FUNCTION_TAG']); + $this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']); + $this->assertEquals('PHP', $output['APPWRITE_FUNCTION_RUNTIME_NAME']); + $this->assertEquals('8.0', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT_DATA']); + $this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']); + $this->assertEquals($this->getUser()['$id'], $output['APPWRITE_FUNCTION_USER_ID']); + $this->assertNotEmpty($output['APPWRITE_FUNCTION_JWT']); + $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', [ 'content-type' => 'application/json', 'x-appwrite-project' => $projectId, diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index cf0a1cafc..775eca06e 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -582,8 +582,31 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(201, $execution['headers']['status-code']); + $executionId = $execution['body']['$id'] ?? ''; + sleep(10); + $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions/'.$executionId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $output = json_decode($executions['body']['stdout'], true); + + $this->assertEquals(200, $executions['headers']['status-code']); + $this->assertEquals('completed', $executions['body']['status']); + $this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']); + $this->assertEquals('Test '.$name, $output['APPWRITE_FUNCTION_NAME']); + $this->assertEquals($tagId, $output['APPWRITE_FUNCTION_TAG']); + $this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']); + $this->assertEquals('PHP', $output['APPWRITE_FUNCTION_RUNTIME_NAME']); + $this->assertEquals('8.0', $output['APPWRITE_FUNCTION_RUNTIME_VERSION']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_EVENT_DATA']); + $this->assertEquals('foobar', $output['APPWRITE_FUNCTION_DATA']); + $this->assertEquals('', $output['APPWRITE_FUNCTION_USER_ID']); + $this->assertEmpty($output['APPWRITE_FUNCTION_JWT']); + $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], diff --git a/tests/e2e/Services/Webhooks/WebhooksBase.php b/tests/e2e/Services/Webhooks/WebhooksBase.php index c471ce37b..e25a718b7 100644 --- a/tests/e2e/Services/Webhooks/WebhooksBase.php +++ b/tests/e2e/Services/Webhooks/WebhooksBase.php @@ -233,12 +233,92 @@ trait WebhooksBase return $data; } - public function testCreateFile(): array + + public function testCreateStorageBucket(): array { /** * Test for SUCCESS */ - $file = $this->client->call(Client::METHOD_POST, '/storage/files', array_merge([ + $bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'Test Bucket', + 'read' => ['role:all'], + 'write' => ['role:all'] + ]); + + $this->assertEquals($bucket['headers']['status-code'], 201); + $this->assertNotEmpty($bucket['body']['$id']); + + $webhook = $this->getLastRequest(); + + $this->assertEquals($webhook['method'], 'POST'); + $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); + $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Event'], 'storage.buckets.create'); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], 'not-yet-implemented'); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); + $this->assertNotEmpty($webhook['data']['$id']); + $this->assertEquals('Test Bucket', $webhook['data']['name']); + $this->assertEquals(true, $webhook['data']['enabled']); + $this->assertIsArray($webhook['data']['$read']); + $this->assertIsArray($webhook['data']['$write']); + + return array_merge(['bucketId' => $bucket['body']['$id']]); + } + + /** + * @depends testCreateStorageBucket + */ + public function testUpdateStorageBucket(array $data): array + { + /** + * Test for SUCCESS + */ + $bucket = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $data['bucketId'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'name' => 'Test Bucket Updated', + 'enabled' => false, + ]); + + $this->assertEquals($bucket['headers']['status-code'], 200); + $this->assertNotEmpty($bucket['body']['$id']); + + $webhook = $this->getLastRequest(); + + $this->assertEquals($webhook['method'], 'POST'); + $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); + $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Event'], 'storage.buckets.update'); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], 'not-yet-implemented'); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); + $this->assertNotEmpty($webhook['data']['$id']); + $this->assertEquals('Test Bucket Updated', $webhook['data']['name']); + $this->assertEquals(false, $webhook['data']['enabled']); + $this->assertIsArray($webhook['data']['$read']); + $this->assertIsArray($webhook['data']['$write']); + + return array_merge(['bucketId' => $bucket['body']['$id']]); + } + + /** + * @depends testCreateStorageBucket + */ + public function testCreateBucketFile(array $data): array + { + /** + * Test for SUCCESS + */ + $file = $this->client->call(Client::METHOD_POST, '/storage/buckets/'. $data['bucketId'] . '/files', array_merge([ 'content-type' => 'multipart/form-data', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -273,18 +353,19 @@ trait WebhooksBase /** * Test for FAILURE */ - return ['fileId' => $file['body']['$id']]; + $data ['fileId'] = $file['body']['$id']; + return $data; } /** - * @depends testCreateFile + * @depends testCreateBucketFile */ - public function testUpdateFile(array $data): array + public function testUpdateBucketFile(array $data): array { /** * Test for SUCCESS */ - $file = $this->client->call(Client::METHOD_PUT, '/storage/files/' . $data['fileId'], array_merge([ + $file = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ @@ -318,14 +399,14 @@ trait WebhooksBase } /** - * @depends testUpdateFile + * @depends testUpdateBucketFile */ - public function testDeleteFile(array $data): array + public function testDeleteBucketFile(array $data): array { /** * Test for SUCCESS */ - $file = $this->client->call(Client::METHOD_DELETE, '/storage/files/' . $data['fileId'], array_merge([ + $file = $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $data['bucketId'] . '/files/' . $data['fileId'], array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); @@ -355,6 +436,40 @@ trait WebhooksBase return $data; } + /** + * @depends testDeleteBucketFile + */ + public function testDeleteStorageBucket(array $data) + { + /** + * Test for SUCCESS + */ + $bucket = $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $data['bucketId'] , array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals($bucket['headers']['status-code'], 204); + $this->assertEmpty($bucket['body']); + + $webhook = $this->getLastRequest(); + + $this->assertEquals($webhook['method'], 'POST'); + $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); + $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Event'], 'storage.buckets.delete'); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], 'not-yet-implemented'); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); + $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); + $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); + $this->assertNotEmpty($webhook['data']['$id']); + $this->assertEquals('Test Bucket Updated', $webhook['data']['name']); + $this->assertEquals(false, $webhook['data']['enabled']); + $this->assertIsArray($webhook['data']['$read']); + $this->assertIsArray($webhook['data']['$write']); + } + public function testCreateTeam(): array { /** @@ -586,114 +701,4 @@ trait WebhooksBase */ return []; } - - public function testCreateStorageBucket(): array - { - /** - * Test for SUCCESS - */ - $bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'name' => 'Test Bucket', - ]); - - $this->assertEquals($bucket['headers']['status-code'], 201); - $this->assertNotEmpty($bucket['body']['$id']); - - $webhook = $this->getLastRequest(); - - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Event'], 'storage.buckets.create'); - $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], 'not-yet-implemented'); - $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); - $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); - $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); - $this->assertNotEmpty($webhook['data']['$id']); - $this->assertEquals('Test Bucket', $webhook['data']['name']); - $this->assertEquals(true, $webhook['data']['enabled']); - $this->assertIsArray($webhook['data']['$read']); - $this->assertIsArray($webhook['data']['$write']); - - return array_merge(['bucketId' => $bucket['body']['$id']]); - } - - /** - * @depends testCreateStorageBucket - */ - public function testUpdateStorageBucket(array $data): array - { - $id = $data['bucketId']; - /** - * Test for SUCCESS - */ - $bucket = $this->client->call(Client::METHOD_PUT, '/storage/buckets/' . $id, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]), [ - 'name' => 'Test Bucket Updated', - 'enabled' => false, - ]); - - $this->assertEquals($bucket['headers']['status-code'], 200); - $this->assertNotEmpty($bucket['body']['$id']); - - $webhook = $this->getLastRequest(); - - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Event'], 'storage.buckets.update'); - $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], 'not-yet-implemented'); - $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); - $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); - $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); - $this->assertNotEmpty($webhook['data']['$id']); - $this->assertEquals('Test Bucket Updated', $webhook['data']['name']); - $this->assertEquals(false, $webhook['data']['enabled']); - $this->assertIsArray($webhook['data']['$read']); - $this->assertIsArray($webhook['data']['$write']); - - return array_merge(['bucketId' => $bucket['body']['$id']]); - } - - /** - * @depends testCreateStorageBucket - */ - public function testDeleteStorageBucket(array $data) - { - $id = $data['bucketId']; - /** - * Test for SUCCESS - */ - $bucket = $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $id, array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ])); - - $this->assertEquals($bucket['headers']['status-code'], 204); - $this->assertEmpty($bucket['body']); - - $webhook = $this->getLastRequest(); - - $this->assertEquals($webhook['method'], 'POST'); - $this->assertEquals($webhook['headers']['Content-Type'], 'application/json'); - $this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io'); - $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Event'], 'storage.buckets.delete'); - $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], 'not-yet-implemented'); - $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']); - $this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']); - $this->assertEquals(empty($webhook['headers']['X-Appwrite-Webhook-User-Id'] ?? ''), true); - $this->assertNotEmpty($webhook['data']['$id']); - $this->assertEquals('Test Bucket Updated', $webhook['data']['name']); - $this->assertEquals(false, $webhook['data']['enabled']); - $this->assertIsArray($webhook['data']['$read']); - $this->assertIsArray($webhook['data']['$write']); - } } \ No newline at end of file diff --git a/tests/resources/functions/php-fn.tar.gz b/tests/resources/functions/php-fn.tar.gz index a97a2e235..5e359122f 100644 Binary files a/tests/resources/functions/php-fn.tar.gz and b/tests/resources/functions/php-fn.tar.gz differ diff --git a/tests/resources/functions/php-fn/index.php b/tests/resources/functions/php-fn/index.php index 32af3ccb1..449658e1e 100644 --- a/tests/resources/functions/php-fn/index.php +++ b/tests/resources/functions/php-fn/index.php @@ -17,15 +17,18 @@ use Appwrite\Services\Storage; // $result = $storage->getFile($_ENV['APPWRITE_FILEID']); -echo $_ENV['APPWRITE_FUNCTION_ID']."\n"; -echo $_ENV['APPWRITE_FUNCTION_NAME']."\n"; -echo $_ENV['APPWRITE_FUNCTION_TAG']."\n"; -echo $_ENV['APPWRITE_FUNCTION_TRIGGER']."\n"; -echo $_ENV['APPWRITE_FUNCTION_RUNTIME_NAME']."\n"; -echo $_ENV['APPWRITE_FUNCTION_RUNTIME_VERSION']."\n"; -// echo $result['$id']; -echo $_ENV['APPWRITE_FUNCTION_EVENT']."\n"; -echo $_ENV['APPWRITE_FUNCTION_EVENT_DATA']."\n"; -echo 'data:'.$_ENV['APPWRITE_FUNCTION_DATA']."\n"; -echo 'userId:'.$_ENV['APPWRITE_FUNCTION_USER_ID']."\n"; -echo 'jwt:'.$_ENV['APPWRITE_FUNCTION_JWT']."\n"; +$output = [ + 'APPWRITE_FUNCTION_ID' => $_ENV['APPWRITE_FUNCTION_ID'], + 'APPWRITE_FUNCTION_NAME' => $_ENV['APPWRITE_FUNCTION_NAME'], + 'APPWRITE_FUNCTION_TAG' => $_ENV['APPWRITE_FUNCTION_TAG'], + 'APPWRITE_FUNCTION_TRIGGER' => $_ENV['APPWRITE_FUNCTION_TRIGGER'], + 'APPWRITE_FUNCTION_RUNTIME_NAME' => $_ENV['APPWRITE_FUNCTION_RUNTIME_NAME'], + 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $_ENV['APPWRITE_FUNCTION_RUNTIME_VERSION'], + 'APPWRITE_FUNCTION_EVENT' => $_ENV['APPWRITE_FUNCTION_EVENT'], + 'APPWRITE_FUNCTION_EVENT_DATA' => $_ENV['APPWRITE_FUNCTION_EVENT_DATA'], + 'APPWRITE_FUNCTION_DATA' => $_ENV['APPWRITE_FUNCTION_DATA'], + 'APPWRITE_FUNCTION_USER_ID' => $_ENV['APPWRITE_FUNCTION_USER_ID'], + 'APPWRITE_FUNCTION_JWT' => $_ENV['APPWRITE_FUNCTION_JWT'], +]; + +echo json_encode($output);