diff --git a/CHANGES.md b/CHANGES.md index 0dd92eeff6..0f24324eeb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,7 +8,10 @@ - Renamed *Devices* to *Sessions* - Add Provider Icon to each Session - Add Anonymous Account Placeholder -- Upgraded telegraf docker image version to v1.1.0 +- Upgraded telegraf docker image version to v1.2.0 +- Added new environment variables to the `telegraf` service: + - _APP_INFLUXDB_HOST + - _APP_INFLUXDB_PORT ## Bugs diff --git a/app/config/collections2.php b/app/config/collections2.php index 79aa3637ca..b130092886 100644 --- a/app/config/collections2.php +++ b/app/config/collections2.php @@ -629,7 +629,7 @@ $collections = [ 'indexes' => [ [ '$id' => '_key_bucket', - 'type' => Database::INDEX_UNIQUE, + 'type' => Database::INDEX_KEY, 'attributes' => ['bucketId'], 'lengths' => [Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC], @@ -852,7 +852,7 @@ $collections = [ 'indexes' => [ [ '$id' => '_key_function', - 'type' => Database::INDEX_UNIQUE, + 'type' => Database::INDEX_KEY, 'attributes' => ['functionId'], 'lengths' => [Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC], @@ -960,7 +960,7 @@ $collections = [ 'indexes' => [ [ '$id' => '_key_function', - 'type' => Database::INDEX_UNIQUE, + 'type' => Database::INDEX_KEY, 'attributes' => ['functionId'], 'lengths' => [Database::LENGTH_KEY], 'orders' => [Database::ORDER_ASC], diff --git a/app/config/platforms.php b/app/config/platforms.php index deec77d98e..9f83e63028 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -109,19 +109,20 @@ return [ 'gitUserName' => 'appwrite', ], [ - 'key' => 'kotlin', - 'name' => 'Kotlin', - 'url' => '', - 'package' => '', - 'enabled' => false, - 'beta' => false, + 'key' => 'android', + 'name' => 'Android', + 'version' => '0.0.0-SNAPSHOT', + 'url' => 'https://github.com/appwrite/sdk-for-android', + 'package' => 'https://repo1.maven.org/maven2/io/appwrite/sdk-for-android/', + 'enabled' => true, + 'beta' => true, 'dev' => false, 'hidden' => false, 'family' => APP_PLATFORM_CLIENT, 'prism' => 'kotlin', - 'source' => false, - 'gitUrl' => 'git@github.com:appwrite/sdk-for-kotlin.git', - 'gitRepoName' => 'sdk-for-kotlin', + 'source' => \realpath(__DIR__ . '/../sdks/client-android'), + 'gitUrl' => 'git@github.com:appwrite/sdk-for-android.git', + 'gitRepoName' => 'sdk-for-android', 'gitUserName' => 'appwrite', ], // [ diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index cfd65f13d2..5d9e121772 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1,5 +1,6 @@ $dbForInternal->getId(), + 'userId' => $profile->getId(), 'provider' => Auth::SESSION_PROVIDER_EMAIL, 'providerUid' => $email, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak @@ -499,6 +499,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG; $session = new Document(array_merge([ '$id' => $dbForInternal->getId(), + 'userId' => $user->getId(), 'provider' => $provider, 'providerUid' => $oauth2ID, 'providerToken' => $accessToken, @@ -648,6 +649,7 @@ App::post('/v1/account/sessions/anonymous') $session = new Document(array_merge( [ '$id' => $dbForInternal->getId(), + 'userId' => $user->getId(), 'provider' => Auth::SESSION_PROVIDER_ANONYMOUS, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => $expiry, @@ -833,22 +835,19 @@ App::get('/v1/account/logs') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_LOG_LIST) ->inject('response') - ->inject('register') - ->inject('project') ->inject('user') ->inject('locale') ->inject('geodb') - ->action(function ($response, $register, $project, $user, $locale, $geodb) { + ->inject('dbForInternal') + ->action(function ($response, $user, $locale, $geodb, $dbForInternal) { /** @var Appwrite\Utopia\Response $response */ /** @var Appwrite\Database\Document $project */ /** @var Utopia\Database\Document $user */ /** @var Utopia\Locale\Locale $locale */ /** @var MaxMind\Db\Reader $geodb */ + /** @var Utopia\Database\Database $dbForInternal */ - $adapter = new AuditAdapter($register->get('db')); - $adapter->setNamespace('app_'.$project->getId()); - - $audit = new Audit($adapter); + $audit = new Audit($dbForInternal); $countries = $locale->getText('countries'); $logs = $audit->getLogsByUserAndActions($user->getId(), [ @@ -879,7 +878,7 @@ App::get('/v1/account/logs') $output[$i] = new Document(array_merge([ 'event' => $log['event'], 'ip' => $log['ip'], - 'time' => \strtotime($log['time']), + 'time' => $log['time'], ], $detector->getOS(), $detector->getClient(), $detector->getDevice())); $record = $geodb->get($log['ip']); @@ -1139,13 +1138,15 @@ App::delete('/v1/account/sessions/:sessionId') ->inject('response') ->inject('user') ->inject('dbForInternal') + ->inject('locale') ->inject('audits') ->inject('events') - ->action(function ($sessionId, $request, $response, $user, $dbForInternal, $audits, $events) { + ->action(function ($sessionId, $request, $response, $user, $dbForInternal, $locale, $audits, $events) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Database $dbForInternal */ + /** @var Utopia\Locale\Locale $locale */ /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $events */ @@ -1171,7 +1172,10 @@ App::delete('/v1/account/sessions/:sessionId') $session->setAttribute('current', false); if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too - $session->setAttribute('current', true); + $session + ->setAttribute('current', true) + ->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))])) ? $countries[strtoupper($session->getAttribute('countryCode'))] : $locale->getText('locale.country.unknown')) + ; if (!Config::getParam('domainVerification')) { $response @@ -1214,13 +1218,15 @@ App::delete('/v1/account/sessions') ->inject('response') ->inject('user') ->inject('dbForInternal') + ->inject('locale') ->inject('audits') ->inject('events') - ->action(function ($request, $response, $user, $dbForInternal, $audits, $events) { + ->action(function ($request, $response, $user, $dbForInternal, $locale, $audits, $events) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Document $user */ /** @var Utopia\Database\Database $dbForInternal */ + /** @var Utopia\Locale\Locale $locale */ /** @var Appwrite\Event\Event $audits */ /** @var Appwrite\Event\Event $events */ @@ -1242,7 +1248,10 @@ App::delete('/v1/account/sessions') ; } - $session->setAttribute('current', false); + $session + ->setAttribute('current', false) + ->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))])) ? $countries[strtoupper($session->getAttribute('countryCode'))] : $locale->getText('locale.country.unknown')) + ; if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too $session->setAttribute('current', true); @@ -1316,6 +1325,7 @@ App::post('/v1/account/recovery') $secret = Auth::tokenGenerator(); $recovery = new Document([ '$id' => $dbForInternal->getId(), + 'userId' => $profile->getId(), 'type' => Auth::TOKEN_TYPE_RECOVERY, 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'expire' => \time() + Auth::TOKEN_EXPIRATION_RECOVERY, @@ -1499,6 +1509,7 @@ App::post('/v1/account/verification') $verification = new Document([ '$id' => $dbForInternal->getId(), + 'userId' => $user->getId(), 'type' => Auth::TOKEN_TYPE_VERIFICATION, 'secret' => Auth::hash($verificationSecret), // One way hash encryption to protect DB leak 'expire' => \time() + Auth::TOKEN_EXPIRATION_CONFIRM, @@ -1629,4 +1640,4 @@ App::put('/v1/account/verification') ; $response->dynamic2($verification, Response::MODEL_TOKEN); - }); \ No newline at end of file + }); diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 10ad3dece4..78c2391dd4 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -12,6 +12,7 @@ use Appwrite\Utopia\Response; use Appwrite\Task\Validator\Cron; use Utopia\App; use Utopia\Exception; +use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; @@ -499,13 +500,13 @@ App::get('/v1/functions/:functionId/tags') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_TAG_LIST) ->param('functionId', '', new UID(), 'Function unique ID.') - // ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) + ->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true) ->param('limit', 25, new Range(0, 100), 'Results limit value. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true) ->param('offset', 0, new Range(0, 2000), 'Results offset. The default value is 0. Use this param to manage pagination.', true) - // ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true) + ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true) ->inject('response') ->inject('dbForInternal') - ->action(function ($functionId, $limit, $offset, $response, $dbForInternal) { + ->action(function ($functionId, $search, $limit, $offset, $orderType, $response, $dbForInternal) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ @@ -517,7 +518,7 @@ App::get('/v1/functions/:functionId/tags') $queries[] = new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]); - $results = $dbForInternal->find('tags', $queries, $limit, $offset); + $results = $dbForInternal->find('tags', $queries, $limit, $offset, ['_id'], [$orderType]); $sum = $dbForInternal->count('tags', $queries, APP_LIMIT_COUNT); $response->dynamic2(new Document([ @@ -759,7 +760,7 @@ App::get('/v1/functions/:functionId/executions') $results = $dbForInternal->find('executions', [ new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]), - ], $limit, $offset); + ], $limit, $offset, ['_id'], [Database::ORDER_DESC]); $sum = $dbForInternal->count('executions', [ new Query('functionId', Query::TYPE_EQUAL, [$function->getId()]), diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index ea14607674..bdcb89bb3d 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1,25 +1,27 @@ desc('Create Project') @@ -62,8 +64,8 @@ App::post('/v1/projects') $project = $dbForConsole->createDocument('projects', new Document([ '$collection' => 'projects', - '$read' => ['team:'.$teamId], - '$write' => ['team:'.$teamId.'/owner', 'team:'.$teamId.'/developer'], + '$read' => ['team:' . $teamId], + '$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'], 'name' => $name, 'description' => $description, 'logo' => $logo, @@ -89,11 +91,17 @@ App::post('/v1/projects') $collections = Config::getParam('collections2', []); /** @var array $collections */ - $dbForInternal->setNamespace('project_'.$project->getId().'_internal'); + $dbForInternal->setNamespace('project_' . $project->getId() . '_internal'); $dbForInternal->create(); - $dbForExternal->setNamespace('project_'.$project->getId().'_external'); + $dbForExternal->setNamespace('project_' . $project->getId() . '_external'); $dbForExternal->create(); + $audit = new Audit($dbForInternal); + $audit->setup(); + + $adapter = new TimeLimit("", 0, 1, $dbForInternal); + $adapter->setup(); + foreach ($collections as $key => $collection) { $dbForInternal->createCollection($key); @@ -150,7 +158,7 @@ App::get('/v1/projects') $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : []; - $results = $dbForConsole->find('projects', $queries, $limit, $offset); + $results = $dbForConsole->find('projects', $queries, $limit, $offset, ['_id'], [$orderType]); $sum = $dbForConsole->count('projects', $queries, APP_LIMIT_COUNT); $response->dynamic2(new Document([ @@ -210,7 +218,7 @@ App::get('/v1/projects/:projectId/usage') throw new Exception('Project not found', 404); } - if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { + if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { $period = [ '24h' => [ @@ -234,44 +242,44 @@ App::get('/v1/projects/:projectId/usage') 'group' => '1d', ], ]; - + $client = $register->get('influxdb'); - + $requests = []; $network = []; $functions = []; - + if ($client) { $start = $period[$range]['start']->format(DateTime::RFC3339); $end = $period[$range]['end']->format(DateTime::RFC3339); $database = $client->selectDB('telegraf'); - + // Requests - $result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_requests_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)'); + $result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_requests_all" WHERE time > \'' . $start . '\' AND time < \'' . $end . '\' AND "metric_type"=\'counter\' AND "project"=\'' . $project->getId() . '\' GROUP BY time(' . $period[$range]['group'] . ') FILL(null)'); $points = $result->getPoints(); - + foreach ($points as $point) { $requests[] = [ 'value' => (!empty($point['value'])) ? $point['value'] : 0, 'date' => \strtotime($point['time']), ]; } - + // Network - $result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_network_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)'); + $result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_network_all" WHERE time > \'' . $start . '\' AND time < \'' . $end . '\' AND "metric_type"=\'counter\' AND "project"=\'' . $project->getId() . '\' GROUP BY time(' . $period[$range]['group'] . ') FILL(null)'); $points = $result->getPoints(); - + foreach ($points as $point) { $network[] = [ 'value' => (!empty($point['value'])) ? $point['value'] : 0, 'date' => \strtotime($point['time']), ]; } - + // Functions - $result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)'); + $result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \'' . $start . '\' AND time < \'' . $end . '\' AND "metric_type"=\'counter\' AND "project"=\'' . $project->getId() . '\' GROUP BY time(' . $period[$range]['group'] . ') FILL(null)'); $points = $result->getPoints(); - + foreach ($points as $point) { $functions[] = [ 'value' => (!empty($point['value'])) ? $point['value'] : 0, @@ -285,14 +293,13 @@ App::get('/v1/projects/:projectId/usage') $functions = []; } - // Users $projectDB->getCollection([ 'limit' => 0, 'offset' => 0, 'filters' => [ - '$collection=users' + '$collection=users', ], ]); @@ -317,7 +324,7 @@ App::get('/v1/projects/:projectId/usage') 'limit' => 0, 'offset' => 0, 'filters' => [ - '$collection='.$collection['$id'], + '$collection=' . $collection['$id'], ], ]); @@ -373,7 +380,7 @@ App::get('/v1/projects/:projectId/usage') '$collection=files', ], ] - ) + + ) + $projectDB->getCount( [ 'attribute' => 'size', @@ -420,16 +427,16 @@ App::patch('/v1/projects/:projectId') } $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('name', $name) - ->setAttribute('description', $description) - ->setAttribute('logo', $logo) - ->setAttribute('url', $url) - ->setAttribute('legalName', $legalName) - ->setAttribute('legalCountry', $legalCountry) - ->setAttribute('legalState', $legalState) - ->setAttribute('legalCity', $legalCity) - ->setAttribute('legalAddress', $legalAddress) - ->setAttribute('legalTaxId', $legalTaxId) + ->setAttribute('name', $name) + ->setAttribute('description', $description) + ->setAttribute('logo', $logo) + ->setAttribute('url', $url) + ->setAttribute('legalName', $legalName) + ->setAttribute('legalCountry', $legalCountry) + ->setAttribute('legalState', $legalState) + ->setAttribute('legalCity', $legalCity) + ->setAttribute('legalAddress', $legalAddress) + ->setAttribute('legalTaxId', $legalTaxId) ); $response->dynamic2($project, Response::MODEL_PROJECT); @@ -462,8 +469,8 @@ App::patch('/v1/projects/:projectId/oauth2') } $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('usersOauth2'.\ucfirst($provider).'Appid', $appId) - ->setAttribute('usersOauth2'.\ucfirst($provider).'Secret', $secret) + ->setAttribute('usersOauth2' . \ucfirst($provider) . 'Appid', $appId) + ->setAttribute('usersOauth2' . \ucfirst($provider) . 'Secret', $secret) ); $response->dynamic2($project, Response::MODEL_PROJECT); @@ -494,7 +501,7 @@ App::patch('/v1/projects/:projectId/auth/limit') } $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('usersAuthLimit', $limit) + ->setAttribute('usersAuthLimit', $limit) ); $response->dynamic2($project, Response::MODEL_PROJECT); @@ -511,7 +518,7 @@ App::patch('/v1/projects/:projectId/auth/:method') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PROJECT) ->param('projectId', '', new UID(), 'Project unique ID.') - ->param('method', '', new WhiteList(\array_keys(Config::getParam('auth')), true), 'Auth Method. Possible values: '.implode(',', \array_keys(Config::getParam('auth'))), false) + ->param('method', '', new WhiteList(\array_keys(Config::getParam('auth')), true), 'Auth Method. Possible values: ' . implode(',', \array_keys(Config::getParam('auth'))), false) ->param('status', false, new Boolean(true), 'Set the status of this auth method.') ->inject('response') ->inject('dbForConsole') @@ -529,7 +536,7 @@ App::patch('/v1/projects/:projectId/auth/:method') } $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute($authKey, $status) + ->setAttribute($authKey, $status) ); $response->dynamic2($project, Response::MODEL_PROJECT); @@ -570,7 +577,7 @@ App::delete('/v1/projects/:projectId') ->setParam('type', DELETE_TYPE_DOCUMENT) ->setParam('document', $project) ; - + if (!$dbForConsole->deleteDocument('teams', $project->getAttribute('teamId', null))) { throw new Exception('Failed to remove project team from DB', 500); } @@ -626,7 +633,7 @@ App::post('/v1/projects/:projectId/webhooks') ]); $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('webhooks', $webhook, Document::SET_TYPE_APPEND) + ->setAttribute('webhooks', $webhook, Document::SET_TYPE_APPEND) ); $response->setStatusCode(Response::STATUS_CODE_CREATED); @@ -736,16 +743,16 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') } $project->findAndReplace('$id', $webhook->getId(), $webhook - ->setAttribute('name', $name) - ->setAttribute('events', $events) - ->setAttribute('url', $url) - ->setAttribute('security', $security) - ->setAttribute('httpUser', $httpUser) - ->setAttribute('httpPass', $httpPass) - , 'webhooks'); + ->setAttribute('name', $name) + ->setAttribute('events', $events) + ->setAttribute('url', $url) + ->setAttribute('security', $security) + ->setAttribute('httpUser', $httpUser) + ->setAttribute('httpPass', $httpPass) + , 'webhooks'); $dbForConsole->updateDocument('projects', $project->getId(), $project); - + $response->dynamic2($webhook, Response::MODEL_WEBHOOK); }); @@ -816,7 +823,7 @@ App::post('/v1/projects/:projectId/keys') ]); $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('keys', $key, Document::SET_TYPE_APPEND) + ->setAttribute('keys', $key, Document::SET_TYPE_APPEND) ); $response->setStatusCode(Response::STATUS_CODE_CREATED); @@ -839,7 +846,7 @@ App::get('/v1/projects/:projectId/keys') ->action(function ($projectId, $response, $dbForConsole) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForConsole */ - + $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { @@ -917,12 +924,12 @@ App::put('/v1/projects/:projectId/keys/:keyId') } $project->findAndReplace('$id', $key->getId(), $key - ->setAttribute('name', $name) - ->setAttribute('scopes', $scopes) - , 'keys'); + ->setAttribute('name', $name) + ->setAttribute('scopes', $scopes) + , 'keys'); $dbForConsole->updateDocument('projects', $project->getId(), $project); - + $response->dynamic2($key, Response::MODEL_KEY); }); @@ -1015,7 +1022,7 @@ App::post('/v1/projects/:projectId/tasks') ]); $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('tasks', $task, Document::SET_TYPE_APPEND) + ->setAttribute('tasks', $task, Document::SET_TYPE_APPEND) ); if ($next) { @@ -1135,18 +1142,18 @@ App::put('/v1/projects/:projectId/tasks/:taskId') $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); $project->findAndReplace('$id', $task->getId(), $task - ->setAttribute('name', $name) - ->setAttribute('status', $status) - ->setAttribute('schedule', $schedule) - ->setAttribute('updated', \time()) - ->setAttribute('next', $next) - ->setAttribute('security', $security) - ->setAttribute('httpMethod', $httpMethod) - ->setAttribute('httpUrl', $httpUrl) - ->setAttribute('httpHeaders', $httpHeaders) - ->setAttribute('httpUser', $httpUser) - ->setAttribute('httpPass', $httpPass) - , 'tasks'); + ->setAttribute('name', $name) + ->setAttribute('status', $status) + ->setAttribute('schedule', $schedule) + ->setAttribute('updated', \time()) + ->setAttribute('next', $next) + ->setAttribute('security', $security) + ->setAttribute('httpMethod', $httpMethod) + ->setAttribute('httpUrl', $httpUrl) + ->setAttribute('httpHeaders', $httpHeaders) + ->setAttribute('httpUser', $httpUser) + ->setAttribute('httpPass', $httpPass) + , 'tasks'); $dbForConsole->updateDocument('projects', $project->getId(), $project); @@ -1231,13 +1238,13 @@ App::post('/v1/projects/:projectId/platforms') ]); $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('platforms', $platform, Document::SET_TYPE_APPEND) + ->setAttribute('platforms', $platform, Document::SET_TYPE_APPEND) ); $response->setStatusCode(Response::STATUS_CODE_CREATED); $response->dynamic2($platform, Response::MODEL_PLATFORM); }); - + App::get('/v1/projects/:projectId/platforms') ->desc('List Platforms') ->groups(['api', 'projects']) @@ -1345,12 +1352,12 @@ App::put('/v1/projects/:projectId/platforms/:platformId') ; $project->findAndReplace('$id', $platform->getId(), $platform - ->setAttribute('name', $name) - ->setAttribute('dateUpdated', \time()) - ->setAttribute('key', $key) - ->setAttribute('store', $store) - ->setAttribute('hostname', $hostname) - , 'platforms'); + ->setAttribute('name', $name) + ->setAttribute('dateUpdated', \time()) + ->setAttribute('key', $key) + ->setAttribute('store', $store) + ->setAttribute('hostname', $hostname) + , 'platforms'); $dbForConsole->updateDocument('projects', $project->getId(), $project); @@ -1424,7 +1431,7 @@ App::post('/v1/projects/:projectId/domains') $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.', 500); + throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500); } $domain = new Domain($domain); @@ -1440,7 +1447,7 @@ App::post('/v1/projects/:projectId/domains') ]); $project = $dbForConsole->updateDocument('projects', $project->getId(), $project - ->setAttribute('domains', $domain, Document::SET_TYPE_APPEND) + ->setAttribute('domains', $domain, Document::SET_TYPE_APPEND) ); $response->setStatusCode(Response::STATUS_CODE_CREATED); @@ -1471,7 +1478,7 @@ App::get('/v1/projects/:projectId/domains') } $domains = $project->getAttribute('domains', []); - + $response->dynamic2(new Document([ 'domains' => $domains, 'sum' => count($domains), @@ -1544,7 +1551,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') $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.', 500); + throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500); } if ($domain->getAttribute('verification') === true) { @@ -1558,8 +1565,8 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') } $project->findAndReplace('$id', $domain->getId(), $domain - ->setAttribute('verification', true) - , 'domains'); + ->setAttribute('verification', true) + , 'domains'); $dbForConsole->updateDocument('projects', $project->getId(), $project); @@ -1610,4 +1617,4 @@ App::delete('/v1/projects/:projectId/domains/:domainId') ; $response->noContent(); - }); \ No newline at end of file + }); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index d4d63ecf47..a093f86cad 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -225,6 +225,7 @@ App::get('/v1/storage/files/:fileId/preview') ->param('fileId', '', new UID(), 'File unique ID') ->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true) ->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true) + ->param('gravity', Image::GRAVITY_CENTER, new WhiteList([Image::GRAVITY_CENTER, Image::GRAVITY_NORTH, Image::GRAVITY_NORTHWEST, Image::GRAVITY_NORTHEAST, Image::GRAVITY_WEST, Image::GRAVITY_EAST, Image::GRAVITY_SOUTHWEST, Image::GRAVITY_SOUTH, Image::GRAVITY_SOUTHEAST]), 'Image crop gravity', true) ->param('quality', 100, new Range(0, 100), 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.', true) ->param('borderWidth', 0, new Range(0, 100), 'Preview image border in pixels. Pass an integer between 0 to 100. Defaults to 0.', true) ->param('borderColor', '', new HexColor(), 'Preview image border color. Use a valid HEX color, no # is needed for prefix.', true) @@ -237,7 +238,7 @@ App::get('/v1/storage/files/:fileId/preview') ->inject('response') ->inject('project') ->inject('dbForInternal') - ->action(function ($fileId, $width, $height, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $dbForInternal) { + ->action(function ($fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $dbForInternal) { /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Document $project */ @@ -325,7 +326,7 @@ App::get('/v1/storage/files/:fileId/preview') $image = new Image($source); - $image->crop((int) $width, (int) $height); + $image->crop((int) $width, (int) $height, $gravity); if (!empty($opacity) || $opacity==0) { $image->setOpacity($opacity); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index e18f2e7e14..f8c3d25822 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -436,7 +436,7 @@ App::get('/v1/teams/:teamId/memberships') throw new Exception('Team not found', 404); } - $memberships = $dbForInternal->find('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], $limit, $offset); + $memberships = $dbForInternal->find('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], $limit, $offset, ['_id'], [$orderType]); $sum = $dbForInternal->count('memberships', [new Query('teamId', Query::TYPE_EQUAL, [$teamId])], APP_LIMIT_COUNT); $users = []; diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index b1fb0aaf5a..ab90647658 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -13,7 +13,6 @@ use Utopia\Validator\Text; use Utopia\Validator\Range; use Utopia\Validator\Boolean; use Utopia\Audit\Audit; -use Utopia\Audit\Adapters\MySQL as AuditAdapter; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; use Utopia\Database\Validator\UID; @@ -90,7 +89,7 @@ App::get('/v1/users') /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Database\Database $dbForInternal */ - $results = $dbForInternal->find('users', [], $limit, $offset); + $results = $dbForInternal->find('users', [], $limit, $offset, ['_id'], [$orderType]); $sum = $dbForInternal->count('users', [], APP_LIMIT_COUNT); $response->dynamic2(new Document([ @@ -214,12 +213,10 @@ App::get('/v1/users/:userId/logs') ->label('sdk.response.model', Response::MODEL_LOG_LIST) ->param('userId', '', new UID(), 'User unique ID.') ->inject('response') - ->inject('register') - ->inject('project') ->inject('dbForInternal') ->inject('locale') ->inject('geodb') - ->action(function ($userId, $response, $register, $project, $dbForInternal, $locale, $geodb) { + ->action(function ($userId, $response, $dbForInternal, $locale, $geodb) { /** @var Appwrite\Utopia\Response $response */ /** @var Utopia\Registry\Registry $register */ /** @var Appwrite\Database\Document $project */ @@ -233,10 +230,7 @@ App::get('/v1/users/:userId/logs') throw new Exception('User not found', 404); } - $adapter = new AuditAdapter($register->get('db')); - $adapter->setNamespace('app_'.$project->getId()); - - $audit = new Audit($adapter); + $audit = new Audit($dbForInternal); $countries = $locale->getText('countries'); @@ -285,7 +279,7 @@ App::get('/v1/users/:userId/logs') $output[$i] = new Document([ 'event' => $log['event'], 'ip' => $log['ip'], - 'time' => \strtotime($log['time']), + 'time' => $log['time'], 'osCode' => $osCode, 'osName' => $osName, diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 18aaea5b78..14555b502f 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -9,7 +9,7 @@ use Utopia\Abuse\Adapters\TimeLimit; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; -App::init(function ($utopia, $request, $response, $project, $user, $register, $events, $audits, $usage, $deletes) { +App::init(function ($utopia, $request, $response, $project, $user, $register, $events, $audits, $usage, $deletes, $dbForInternal) { /** @var Utopia\App $utopia */ /** @var Utopia\Swoole\Request $request */ /** @var Appwrite\Utopia\Response $response */ @@ -21,6 +21,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e /** @var Appwrite\Event\Event $usage */ /** @var Appwrite\Event\Event $deletes */ /** @var Appwrite\Event\Event $functions */ + /** @var Utopia\Database\Database $dbForInternal */ Storage::setDevice('files', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId())); Storage::setDevice('functions', new Local(APP_STORAGE_FUNCTIONS.'/app-'.$project->getId())); @@ -31,47 +32,44 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e throw new Exception('Missing or unknown project ID', 400); } - // /* - // * Abuse Check - // */ - // $timeLimit = new TimeLimit($route->getLabel('abuse-key', 'url:{url},ip:{ip}'), $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), function () use ($register) { - // return $register->get('db'); - // }); - // $timeLimit->setNamespace('app_'.$project->getId()); - // $timeLimit - // ->setParam('{userId}', $user->getId()) - // ->setParam('{userAgent}', $request->getUserAgent('')) - // ->setParam('{ip}', $request->getIP()) - // ->setParam('{url}', $request->getHostname().$route->getURL()) - // ; + /* + * Abuse Check + */ + $timeLimit = new TimeLimit($route->getLabel('abuse-key', 'url:{url},ip:{ip}'), $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForInternal); + $timeLimit + ->setParam('{userId}', $user->getId()) + ->setParam('{userAgent}', $request->getUserAgent('')) + ->setParam('{ip}', $request->getIP()) + ->setParam('{url}', $request->getHostname().$route->getURL()) + ; - // //TODO make sure we get array here + //TODO make sure we get array here - // foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys - // if(!empty($value)) { - // $timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value); - // } - // } + foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys + if(!empty($value)) { + $timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value); + } + } - // $abuse = new Abuse($timeLimit); + $abuse = new Abuse($timeLimit); - // if ($timeLimit->limit()) { - // $response - // ->addHeader('X-RateLimit-Limit', $timeLimit->limit()) - // ->addHeader('X-RateLimit-Remaining', $timeLimit->remaining()) - // ->addHeader('X-RateLimit-Reset', $timeLimit->time() + $route->getLabel('abuse-time', 3600)) - // ; - // } + if ($timeLimit->limit()) { + $response + ->addHeader('X-RateLimit-Limit', $timeLimit->limit()) + ->addHeader('X-RateLimit-Remaining', $timeLimit->remaining()) + ->addHeader('X-RateLimit-Reset', $timeLimit->time() + $route->getLabel('abuse-time', 3600)) + ; + } - // $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles); - // $isAppUser = Auth::isAppUser(Authorization::$roles); + $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles); + $isAppUser = Auth::isAppUser(Authorization::$roles); - // if (($abuse->check() // Route is rate-limited - // && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') // Abuse is not diabled - // && (!$isAppUser && !$isPrivilegedUser)) // User is not an admin or API key - // { - // throw new Exception('Too many requests', 429); - // } + if (($abuse->check() // Route is rate-limited + && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') // Abuse is not disabled + && (!$isAppUser && !$isPrivilegedUser)) // User is not an admin or API key + { + throw new Exception('Too many requests', 429); + } /* * Background Jobs @@ -111,7 +109,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e ->setParam('projectId', $project->getId()) ; -}, ['utopia', 'request', 'response', 'project', 'user', 'register', 'events', 'audits', 'usage', 'deletes'], 'api'); +}, ['utopia', 'request', 'response', 'project', 'user', 'register', 'events', 'audits', 'usage', 'deletes', 'dbForInternal'], 'api'); App::init(function ($utopia, $request, $response, $project, $user) { diff --git a/app/controllers/web/home.php b/app/controllers/web/home.php index 8282539544..f4325f7b44 100644 --- a/app/controllers/web/home.php +++ b/app/controllers/web/home.php @@ -233,6 +233,7 @@ App::get('/specs/:format') ->groups(['web', 'home']) ->label('scope', 'public') ->label('docs', false) + ->label('origin', '*') ->param('format', 'swagger2', new WhiteList(['swagger2', 'open-api3'], true), 'Spec format.', true) ->param('platform', APP_PLATFORM_CLIENT, new WhiteList([APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER, APP_PLATFORM_CONSOLE], true), 'Choose target platform.', true) ->param('tests', 0, function () {return new Range(0, 1);}, 'Include only test services.', true) diff --git a/app/http.php b/app/http.php index 246896477b..0e67e07c31 100644 --- a/app/http.php +++ b/app/http.php @@ -3,8 +3,6 @@ require_once __DIR__.'/../vendor/autoload.php'; use Appwrite\Database\Validator\Authorization; -use Utopia\Swoole\Files; -use Utopia\Swoole\Request; use Appwrite\Utopia\Response; use Swoole\Process; use Swoole\Http\Server; @@ -14,6 +12,10 @@ use Utopia\App; use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Validator\Authorization as Authorization2; +use Utopia\Audit\Audit; +use Utopia\Abuse\Adapters\TimeLimit; +use Utopia\Swoole\Files; +use Utopia\Swoole\Request; // xdebug_start_trace('/tmp/trace'); @@ -67,6 +69,12 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) { $register->get('cache')->flushAll(); $dbForConsole->create(); + + $audit = new Audit($dbForConsole); + $audit->setup(); + + $adapter = new TimeLimit("", 0, 1, $dbForConsole); + $adapter->setup(); foreach ($collections as $key => $collection) { $dbForConsole->createCollection($key); diff --git a/app/tasks/sdks.php b/app/tasks/sdks.php index 96d9dbf563..d5f858a7be 100644 --- a/app/tasks/sdks.php +++ b/app/tasks/sdks.php @@ -15,7 +15,7 @@ use Appwrite\SDK\Language\Deno; use Appwrite\SDK\Language\DotNet; use Appwrite\SDK\Language\Flutter; use Appwrite\SDK\Language\Go; -use Appwrite\SDK\Language\Java; +use Appwrite\SDK\Language\Kotlin; use Appwrite\SDK\Language\Swift; $cli @@ -134,9 +134,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND case 'go': $config = new Go(); break; - case 'java': - $config = new Java(); - break; case 'swift': $config = new Swift(); break; @@ -144,6 +141,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND $cover = ''; $config = new DotNet(); break; + case 'android': + $config = new Kotlin(); + break; default: throw new Exception('Language "'.$language['key'].'" not supported'); break; @@ -155,9 +155,8 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND $sdk ->setName($language['name']) - ->setDescription("Appwrite is an open-source backend as a service server that abstract and simplify complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. - Use the {$language['name']} SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. - For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)") + ->setNamespace('io appwrite') + ->setDescription("Appwrite is an open-source backend as a service server that abstract and simplify complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the {$language['name']} SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)") ->setShortDescription('Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API') ->setLicense($license) ->setLicenseContent($licenseContent) diff --git a/app/views/console/home/index.phtml b/app/views/console/home/index.phtml index db1b9c32fc..021cf89928 100644 --- a/app/views/console/home/index.phtml +++ b/app/views/console/home/index.phtml @@ -240,11 +240,11 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true);
  • -
  • - +
  • +
  • - +
  • @@ -329,6 +329,42 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled',true); + +