diff --git a/app/config/collections.php b/app/config/collections.php index d48094849f..18df85468b 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -1003,6 +1003,28 @@ $collections = [ 'array' => false, 'filters' => ['datetime'], ], + [ + '$id' => ID::custom('accessedAt'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('sdks'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => true, + 'filters' => [], + ], ], 'indexes' => [ [ @@ -1012,6 +1034,13 @@ $collections = [ 'lengths' => [Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC], ], + [ + '$id' => '_key_accessedAt', + 'type' => Database::INDEX_KEY, + 'attributes' => ['accessedAt'], + 'lengths' => [], + 'orders' => [], + ], ], ], diff --git a/app/config/providers.php b/app/config/providers.php index 205d07123b..98ad93b30c 100644 --- a/app/config/providers.php +++ b/app/config/providers.php @@ -121,6 +121,16 @@ return [ // Ordered by ABC. 'beta' => false, 'mock' => false, ], + 'etsy' => [ + 'name' => 'Etsy', + 'developers' => 'https://developers.etsy.com/', + 'icon' => 'icon-etsy', + 'enabled' => true, + 'sandbox' => false, + 'form' => false, + 'beta' => false, + 'mock' => false, + ], 'facebook' => [ 'name' => 'Facebook', 'developers' => 'https://developers.facebook.com/', diff --git a/app/config/roles.php b/app/config/roles.php index 01d1a1858f..80f8bbd459 100644 --- a/app/config/roles.php +++ b/app/config/roles.php @@ -59,9 +59,12 @@ return [ 'home', 'console', 'documents.read', + 'documents.write', 'files.read', + 'files.write', 'locale.read', 'avatars.read', + 'execution.write', ], ], Auth::USER_ROLE_USERS => [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index a00d485bec..1309eade74 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1358,7 +1358,7 @@ App::get('/v1/account/logs') $queries = Query::parseQueries($queries); $grouped = Query::groupByType($queries); - $limit = $grouped['limit'] ?? 25; + $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; $audit = new EventAudit($dbForProject); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 6360a89363..c8ddeb12c5 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -36,17 +36,12 @@ use Appwrite\Network\Validator\Email; use Appwrite\Network\Validator\IP; use Appwrite\Network\Validator\URL; use Appwrite\Utopia\Database\Validator\CustomId; -use Appwrite\Utopia\Database\Validator\IndexedQueries; -use Appwrite\Utopia\Database\Validator\Query\Cursor as Cursor; -use Appwrite\Utopia\Database\Validator\Query\Filter as Filter; -use Appwrite\Utopia\Database\Validator\Query\Limit as Limit; -use Appwrite\Utopia\Database\Validator\Query\Offset as Offset; -use Appwrite\Utopia\Database\Validator\Query\Order as Order; +use Appwrite\Utopia\Database\Validator\Query\Limit; +use Appwrite\Utopia\Database\Validator\Query\Offset; use Appwrite\Utopia\Response; use Appwrite\Detector\Detector; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Stats\Stats; use Appwrite\Utopia\Database\Validator\Queries; use Appwrite\Utopia\Database\Validator\Queries\Collections; use Appwrite\Utopia\Database\Validator\Queries\Databases; @@ -250,12 +245,10 @@ App::get('/v1/databases') $queries[] = Query::search('search', $search); } - // Set default limit - $queries[] = Query::limit(25); - // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE)[0] ?? null; - if ($cursor !== null) { + $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE); + $cursor = reset($cursor); + if ($cursor) { /** @var Query $cursor */ $databaseId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('databases', $databaseId); @@ -328,7 +321,7 @@ App::get('/v1/databases/:databaseId/logs') $queries = Query::parseQueries($queries); $grouped = Query::groupByType($queries); - $limit = $grouped['limit'] ?? 25; + $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; $audit = new Audit($dbForProject); @@ -573,12 +566,10 @@ App::get('/v1/databases/:databaseId/collections') $queries[] = Query::search('search', $search); } - // Set default limit - $queries[] = Query::limit(25); - // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE)[0] ?? null; - if ($cursor !== null) { + $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE); + $cursor = reset($cursor); + if ($cursor) { /** @var Query $cursor */ $collectionId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); @@ -670,7 +661,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs') $queries = Query::parseQueries($queries); $grouped = Query::groupByType($queries); - $limit = $grouped['limit'] ?? 25; + $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; $audit = new Audit($dbForProject); @@ -1822,6 +1813,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}') ->label('usage.metric', 'documents.{scope}.requests.create') ->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}']) + ->label('abuse-limit', 120) + ->label('abuse-time', 60) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'createDocument') @@ -1987,12 +1980,10 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $queries = Query::parseQueries($queries); - // Set default limit - $queries[] = Query::limit(25); - // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE)[0] ?? null; - if ($cursor !== null) { + $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE); + $cursor = reset($cursor); + if ($cursor) { /** @var Query $cursor */ $documentId = $cursor->getValue(); @@ -2135,7 +2126,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $queries = Query::parseQueries($queries); $grouped = Query::groupByType($queries); - $limit = $grouped['limit'] ?? 25; + $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; $audit = new Audit($dbForProject); @@ -2201,6 +2192,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}') ->label('usage.metric', 'documents.{scope}.requests.update') ->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}']) + ->label('abuse-limit', 60) + ->label('abuse-time', 60) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'updateDocument') @@ -2330,6 +2323,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{request.documentId}') ->label('usage.metric', 'documents.{scope}.requests.delete') ->label('usage.params', ['databaseId:{request.databaseId}', 'collectionId:{request.collectionId}']) + ->label('abuse-limit', 60) + ->label('abuse-time', 60) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'databases') ->label('sdk.method', 'deleteDocument') diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 5663656e1e..23d25d42eb 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -117,12 +117,10 @@ App::get('/v1/functions') $queries[] = Query::search('search', $search); } - // Set default limit - $queries[] = Query::limit(25); - // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE)[0] ?? null; - if ($cursor !== null) { + $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE); + $cursor = reset($cursor); + if ($cursor) { /** @var Query $cursor */ $functionId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('functions', $functionId); @@ -788,16 +786,14 @@ App::get('/v1/functions/:functionId/deployments') $queries[] = Query::search('search', $search); } - // Set default limit - $queries[] = Query::limit(25); - // Set resource queries $queries[] = Query::equal('resourceId', [$function->getId()]); $queries[] = Query::equal('resourceType', ['functions']); // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE)[0] ?? null; - if ($cursor !== null) { + $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE); + $cursor = reset($cursor); + if ($cursor) { /** @var Query $cursor */ $deploymentId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('deployments', $deploymentId); @@ -1149,15 +1145,13 @@ App::get('/v1/functions/:functionId/executions') $queries[] = Query::search('search', $search); } - // Set default limit - $queries[] = Query::limit(25); - // Set internal queries $queries[] = Query::equal('functionId', [$function->getId()]); // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE)[0] ?? null; - if ($cursor !== null) { + $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE); + $cursor = reset($cursor); + if ($cursor) { /** @var Query $cursor */ $executionId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('executions', $executionId); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 86660bbdb4..3b20d073c6 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -865,6 +865,8 @@ App::post('/v1/projects/:projectId/keys') 'name' => $name, 'scopes' => $scopes, 'expire' => $expire, + 'sdks' => [], + 'accessedAt' => null, 'secret' => \bin2hex(\random_bytes(128)), ]); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 5ab2e8450b..b99fc6afc4 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -164,12 +164,10 @@ App::get('/v1/storage/buckets') $queries[] = Query::search('search', $search); } - // Set default limit - $queries[] = Query::limit(25); - // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE)[0] ?? null; - if ($cursor !== null) { + $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE); + $cursor = reset($cursor); + if ($cursor) { /** @var Query $cursor */ $bucketId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('buckets', $bucketId); @@ -329,6 +327,8 @@ App::post('/v1/storage/buckets/:bucketId/files') ->label('audits.resource', 'files/{response.$id}') ->label('usage.metric', 'files.{scope}.requests.create') ->label('usage.params', ['bucketId:{request.bucketId}']) + ->label('abuse-limit', 60) + ->label('abuse-time', 60) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'createFile') @@ -678,12 +678,10 @@ App::get('/v1/storage/buckets/:bucketId/files') $queries[] = Query::search('search', $search); } - // Set default limit - $queries[] = Query::limit(25); - // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE)[0] ?? null; - if ($cursor !== null) { + $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE); + $cursor = reset($cursor); + if ($cursor) { /** @var Query $cursor */ $fileId = $cursor->getValue(); @@ -1209,6 +1207,8 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->label('audits.resource', 'files/{response.$id}') ->label('usage.metric', 'files.{scope}.requests.update') ->label('usage.params', ['bucketId:{request.bucketId}']) + ->label('abuse-limit', 60) + ->label('abuse-time', 60) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'updateFile') @@ -1308,6 +1308,8 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->label('audits.resource', 'file/{request.fileId}') ->label('usage.metric', 'files.{scope}.requests.delete') ->label('usage.params', ['bucketId:{request.bucketId}']) + ->label('abuse-limit', 60) + ->label('abuse-time', 60) ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) ->label('sdk.namespace', 'storage') ->label('sdk.method', 'deleteFile') @@ -1364,9 +1366,12 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->setResource('file/' . $fileId) ; - // Don't need to check valid here because we already ensured validity - if ($fileSecurity) { - $deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId); + if ($fileSecurity && !$valid) { + try { + $deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId); + } catch (AuthorizationException) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } } else { $deleted = Authorization::skip(fn() => $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId)); } diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index d5f7b88ca8..40eab55630 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -79,6 +79,10 @@ App::post('/v1/teams') ]))); if (!$isPrivilegedUser && !$isAppUser) { // Don't add user on server mode + if (!\in_array('owner', $roles)) { + $roles[] = 'owner'; + } + $membershipId = ID::unique(); $membership = new Document([ '$id' => $membershipId, @@ -139,12 +143,10 @@ App::get('/v1/teams') $queries[] = Query::search('search', $search); } - // Set default limit - $queries[] = Query::limit(25); - // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE)[0] ?? null; - if ($cursor !== null) { + $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE); + $cursor = reset($cursor); + if ($cursor) { /** @var Query $cursor */ $teamId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('teams', $teamId); @@ -484,15 +486,13 @@ App::get('/v1/teams/:teamId/memberships') $queries[] = Query::search('search', $search); } - // Set default limit - $queries[] = Query::limit(25); - // Set internal queries $queries[] = Query::equal('teamId', [$teamId]); // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE)[0] ?? null; - if ($cursor !== null) { + $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE); + $cursor = reset($cursor); + if ($cursor) { /** @var Query $cursor */ $membershipId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('memberships', $membershipId); @@ -872,7 +872,7 @@ App::get('/v1/teams/:teamId/logs') $queries = Query::parseQueries($queries); $grouped = Query::groupByType($queries); - $limit = $grouped['limit'] ?? 25; + $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; $audit = new Audit($dbForProject); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index a6d4c57f73..9f4b1489e8 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -355,12 +355,10 @@ App::get('/v1/users') $queries[] = Query::search('search', $search); } - // Set default limit - $queries[] = Query::limit(25); - // Get cursor document if there was a cursor query - $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE)[0] ?? null; - if ($cursor !== null) { + $cursor = Query::getByType($queries, Query::TYPE_CURSORAFTER, Query::TYPE_CURSORBEFORE); + $cursor = reset($cursor); + if ($cursor) { /** @var Query $cursor */ $userId = $cursor->getValue(); $cursorDocument = $dbForProject->getDocument('users', $userId); @@ -544,7 +542,7 @@ App::get('/v1/users/:userId/logs') $queries = Query::parseQueries($queries); $grouped = Query::groupByType($queries); - $limit = $grouped['limit'] ?? 25; + $limit = $grouped['limit'] ?? APP_LIMIT_COUNT; $offset = $grouped['offset'] ?? 0; $audit = new Audit($dbForProject); diff --git a/app/controllers/general.php b/app/controllers/general.php index ee08cca726..221e69678b 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -32,6 +32,7 @@ use Appwrite\Utopia\Request\Filters\V12 as RequestV12; use Appwrite\Utopia\Request\Filters\V13 as RequestV13; use Appwrite\Utopia\Request\Filters\V14 as RequestV14; use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; Config::setParam('domainVerification', false); Config::setParam('cookieDomain', 'localhost'); @@ -47,7 +48,8 @@ App::init() ->inject('user') ->inject('locale') ->inject('clients') - ->action(function (App $utopia, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, Document $user, Locale $locale, array $clients) { + ->inject('servers') + ->action(function (App $utopia, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, Document $user, Locale $locale, array $clients, array $servers) { /* * Request format */ @@ -303,6 +305,28 @@ App::init() Authorization::setRole(Auth::USER_ROLE_APPS); Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. + + $accessedAt = $key->getAttribute('accessedAt', ''); + if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCCESS)) > $accessedAt) { + $key->setAttribute('accessedAt', DateTime::now()); + $dbForConsole->updateDocument('keys', $key->getId(), $key); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); + } + + $sdkValidator = new WhiteList($servers, true); + $sdk = $request->getHeader('x-sdk-name', 'UNKNOWN'); + if ($sdkValidator->isValid($sdk)) { + $sdks = $key->getAttribute('sdks', []); + if (!in_array($sdk, $sdks)) { + array_push($sdks, $sdk); + $key->setAttribute('sdks', $sdks); + + /** Update access time as well */ + $key->setAttribute('accessedAt', Datetime::now()); + $dbForConsole->updateDocument('keys', $key->getId(), $key); + $dbForConsole->deleteCachedDocument('projects', $project->getId()); + } + } } } diff --git a/app/controllers/mock.php b/app/controllers/mock.php index c90675873c..80b9a4f0ac 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -7,6 +7,7 @@ use Utopia\Database\Document; use Appwrite\Network\Validator\Host; use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; +use Appwrite\Utopia\Response\Model; use Utopia\App; use Utopia\Validator\ArrayList; use Utopia\Validator\Integer; @@ -395,6 +396,35 @@ App::get('/v1/mock/tests/general/empty') $response->noContent(); }); +/** Endpoint to test if required headers are sent from the SDK */ +App::get('/v1/mock/tests/general/headers') + ->desc('Get headers') + ->groups(['mock']) + ->label('scope', 'public') + ->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT]) + ->label('sdk.namespace', 'general') + ->label('sdk.method', 'headers') + ->label('sdk.description', 'Return headers from the request') + ->label('sdk.response.code', Response::STATUS_CODE_OK) + ->label('sdk.response.model', Response::MODEL_MOCK) + ->label('sdk.mock', true) + ->inject('request') + ->inject('response') + ->action(function (Request $request, Response $response) { + $res = [ + 'x-sdk-name' => $request->getHeader('x-sdk-name'), + 'x-sdk-platform' => $request->getHeader('x-sdk-platform'), + 'x-sdk-language' => $request->getHeader('x-sdk-language'), + 'x-sdk-version' => $request->getHeader('x-sdk-version'), + ]; + $res = array_map(function ($key, $value) { + return $key . ': ' . $value; + }, array_keys($res), $res); + $res = implode("; ", $res); + + $response->dynamic(new Document(['result' => $res]), Response::MODEL_MOCK); + }); + App::get('/v1/mock/tests/general/400-error') ->desc('400 Error') ->groups(['mock']) diff --git a/app/controllers/web/console.php b/app/controllers/web/console.php index 480522a254..864ed535f2 100644 --- a/app/controllers/web/console.php +++ b/app/controllers/web/console.php @@ -294,6 +294,7 @@ App::get('/console/databases/collection') $permissions ->setParam('method', 'databases.getCollection') ->setParam('events', 'load,databases.updateCollection') + ->setParam('form', 'collectionPermissions') ->setParam('data', 'project-collection') ->setParam('params', [ 'collection-id' => '{{router.params.id}}', @@ -329,7 +330,6 @@ App::get('/console/databases/document') ->action(function (string $databaseId, string $collection, View $layout) { $logs = new View(__DIR__ . '/../../views/console/comps/logs.phtml'); - $logs ->setParam('interval', App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 0)) ->setParam('method', 'databases.listDocumentLogs') @@ -341,15 +341,16 @@ App::get('/console/databases/document') ; $permissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml'); - $permissions ->setParam('method', 'databases.getDocument') ->setParam('events', 'load,databases.updateDocument') + ->setParam('form', 'documentPermissions') ->setParam('data', 'project-document') - ->setParam('permissions', \array_filter( - Database::PERMISSIONS, - fn ($perm) => $perm != Database::PERMISSION_CREATE - )) + ->setParam('permissions', [ + Database::PERMISSION_READ, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE, + ]) ->setParam('params', [ 'collection-id' => '{{router.params.collection}}', 'database-id' => '{{router.params.databaseId}}', @@ -384,10 +385,12 @@ App::get('/console/databases/document/new') $permissions ->setParam('data', 'project-document') - ->setParam('permissions', \array_filter( - Database::PERMISSIONS, - fn ($perm) => $perm != Database::PERMISSION_CREATE - )) + ->setParam('form', 'documentPermissions') + ->setParam('permissions', [ + Database::PERMISSION_READ, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE, + ]) ->setParam('params', [ 'collection-id' => '{{router.params.collection}}', 'database-id' => '{{router.params.databaseId}}', @@ -451,20 +454,22 @@ App::get('/console/storage/bucket') $fileCreatePermissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml'); $fileCreatePermissions ->setParam('form', 'fileCreatePermissions') - ->setParam('permissions', \array_filter( - Database::PERMISSIONS, - fn ($perm) => $perm != Database::PERMISSION_CREATE - )); + ->setParam('permissions', [ + Database::PERMISSION_READ, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE, + ]); $fileUpdatePermissions = new View(__DIR__ . '/../../views/console/comps/permissions-matrix.phtml'); $fileUpdatePermissions ->setParam('method', 'storage.getFile') ->setParam('data', 'file') ->setParam('form', 'fileUpdatePermissions') - ->setParam('permissions', \array_filter( - Database::PERMISSIONS, - fn ($perm) => $perm != Database::PERMISSION_CREATE - )) + ->setParam('permissions', [ + Database::PERMISSION_READ, + Database::PERMISSION_UPDATE, + Database::PERMISSION_DELETE, + ]) ->setParam('params', [ 'bucket-id' => '{{router.params.id}}', ]); diff --git a/app/init.php b/app/init.php index 4f26f2e3cd..513555261e 100644 --- a/app/init.php +++ b/app/init.php @@ -90,6 +90,7 @@ const APP_LIMIT_COMPRESSION = 20000000; //20MB const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length. const APP_LIMIT_SUBQUERY = 1000; +const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 402; const APP_VERSION_STABLE = '0.15.3'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; @@ -1028,3 +1029,14 @@ App::setResource('sms', function () { default => null }; }); + +App::setResource('servers', function () { + $platforms = Config::getParam('platforms'); + $server = $platforms[APP_PLATFORM_SERVER]; + + $languages = array_map(function ($language) { + return strtolower($language['name']); + }, $server['languages']); + + return $languages; +}); diff --git a/app/tasks/sdks.php b/app/tasks/sdks.php index d873167743..b000cc7482 100644 --- a/app/tasks/sdks.php +++ b/app/tasks/sdks.php @@ -178,6 +178,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ->setLicense($license) ->setLicenseContent($licenseContent) ->setVersion($language['version']) + ->setPlatform($key) ->setGitURL($language['url']) ->setGitRepo($language['gitUrl']) ->setGitRepoName($language['gitRepoName']) diff --git a/app/views/console/comps/permissions-matrix.phtml b/app/views/console/comps/permissions-matrix.phtml index 09005bbb28..d3d937da7f 100644 --- a/app/views/console/comps/permissions-matrix.phtml +++ b/app/views/console/comps/permissions-matrix.phtml @@ -7,7 +7,7 @@ $params = $this->getParam('params', []); $events = $this->getParam('events', ''); $permissions = $this->getParam('permissions', Database::PERMISSIONS); $data = $this->getParam('data', ''); -$form = $this->getParam('form', 'form'); +$form = $this->getParam('form'); $escapedPermissions = \array_map(function ($perm) { // Alpine won't bind to a parameter named delete @@ -34,7 +34,7 @@ $escapedPermissions = \array_map(function ($perm) { data-name="" - @reset.window="permissions = rawPermissions = []"> + @reset.window="permissions.length = rawPermissions.length = 0">