From 0dd617d4a752c9d916d0da7b1c42a7518baf10c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 21 Mar 2022 11:29:49 +0000 Subject: [PATCH 01/26] Change default to empty string --- app/executor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/executor.php b/app/executor.php index 1fa32599ab..4ef174ab9c 100644 --- a/app/executor.php +++ b/app/executor.php @@ -396,7 +396,7 @@ App::post('/v1/execution') ->desc('Create an execution') ->param('runtimeId', '', new Text(64), 'The runtimeID to execute') ->param('vars', [], new Assoc(), 'Environment variables required for the build') - ->param('data', '{}', new Text(8192), 'Data to be forwarded to the function, this is user specified.', true) + ->param('data', '', new Text(8192), 'Data to be forwarded to the function, this is user specified.', true) ->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.') ->inject('activeRuntimes') ->inject('response') From f6d452621723ede95aebdc25bd341a696e9f4e7e Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 17 Apr 2022 10:18:00 +0000 Subject: [PATCH 02/26] customId console, reserved project --- app/controllers/api/projects.php | 3 +++ src/Appwrite/Extend/Exception.php | 1 + 2 files changed, 4 insertions(+) diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 2bee190628..40ae6f094d 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -77,6 +77,9 @@ App::post('/v1/projects') } $projectId = ($projectId == 'unique()') ? $dbForConsole->getId() : $projectId; + if($projectId === 'console') { + throw new Exception("'console' is a reserved project.", 400, Exception::PROJECT_RESERVED_PROJECT); + } $project = $dbForConsole->createDocument('projects', new Document([ '$id' => $projectId == 'unique()' ? $dbForConsole->getId() : $projectId, '$read' => ['team:' . $teamId], diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 4f396990fd..1099190cb6 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -145,6 +145,7 @@ class Exception extends \Exception const PROJECT_INVALID_SUCCESS_URL = 'project_invalid_success_url'; const PROJECT_INVALID_FAILURE_URL = 'project_invalid_failure_url'; const PROJECT_MISSING_USER_ID = 'project_missing_user_id'; + const PROJECT_RESERVED_PROJECT = 'project_reserved_project'; /** Webhooks */ const WEBHOOK_NOT_FOUND = 'webhook_not_found'; From 224c34b6a64b7e1e640e4a6e6dae7c9c6d388ea1 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 19 Apr 2022 15:28:30 +0200 Subject: [PATCH 03/26] fix: remove usage queue --- app/controllers/api/health.php | 18 ------------------ app/realtime.php | 22 ---------------------- src/Appwrite/Event/Event.php | 3 --- 3 files changed, 43 deletions(-) diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index bc57aa8ccb..dcb506faeb 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -214,24 +214,6 @@ App::get('/v1/health/queue/logs') $response->dynamic(new Document([ 'size' => Resque::size(Event::AUDITS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/queue/usage') - ->desc('Get Usage Queue') - ->groups(['api', 'health']) - ->label('scope', 'health.read') - ->label('sdk.auth', [APP_AUTH_TYPE_KEY]) - ->label('sdk.namespace', 'health') - ->label('sdk.method', 'getQueueUsage') - ->label('sdk.description', '/docs/references/health/get-queue-usage.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) - ->inject('response') - ->action(function ($response) { - /** @var Appwrite\Utopia\Response $response */ - - $response->dynamic(new Document([ 'size' => Resque::size(Event::USAGE_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); - }, ['response']); - App::get('/v1/health/queue/certificates') ->desc('Get Certificates Queue') ->groups(['api', 'health']) diff --git a/app/realtime.php b/app/realtime.php index 8d36069edb..76b9af5a78 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -163,28 +163,6 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume * Save current connections to the Database every 5 seconds. */ Timer::tick(5000, function () use ($register, $stats, &$statsDocument, $logError) { - /** @var Document $statsDocument */ - foreach ($stats as $projectId => $value) { - $connections = $stats->get($projectId, 'connections') ?? 0; - $messages = $stats->get($projectId, 'messages' ?? 0); - - $usage = new Event('v1-usage', 'UsageV1'); - $usage - ->setParam('projectId', $projectId) - ->setParam('realtimeConnections', $connections) - ->setParam('realtimeMessages', $messages) - ->setParam('networkRequestSize', 0) - ->setParam('networkResponseSize', 0); - - $stats->set($projectId, [ - 'messages' => 0, - 'connections' => 0 - ]); - - if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') { - $usage->trigger(); - } - } $payload = []; foreach ($stats as $projectId => $value) { $payload[$projectId] = $stats->get($projectId, 'connectionsTotal'); diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 2bd73248c3..4b15f5d04c 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -16,9 +16,6 @@ class Event const AUDITS_QUEUE_NAME = 'v1-audits'; const AUDITS_CLASS_NAME = 'AuditsV1'; - const USAGE_QUEUE_NAME = 'v1-usage'; - const USAGE_CLASS_NAME = 'UsageV1'; - const MAILS_QUEUE_NAME = 'v1-mails'; const MAILS_CLASS_NAME = 'MailsV1'; From 9c7b79f5fc5a2bf7b2a657c8c69d223b91b7b364 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 19 Apr 2022 15:29:17 +0200 Subject: [PATCH 04/26] tests: remove usage queue health check --- .../Health/HealthCustomServerTest.php | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/tests/e2e/Services/Health/HealthCustomServerTest.php b/tests/e2e/Services/Health/HealthCustomServerTest.php index a15e456e5f..47a2268e21 100644 --- a/tests/e2e/Services/Health/HealthCustomServerTest.php +++ b/tests/e2e/Services/Health/HealthCustomServerTest.php @@ -146,27 +146,6 @@ class HealthCustomServerTest extends Scope return []; } - public function testUsageSuccess(): array - { - /** - * Test for SUCCESS - */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/usage', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), []); - - $this->assertEquals(200, $response['headers']['status-code']); - $this->assertIsInt($response['body']['size']); - $this->assertLessThan(200, $response['body']['size']); - - /** - * Test for FAILURE - */ - - return []; - } - public function testCertificatesSuccess(): array { /** From fcb0d40ec79b6c6aa722b8bc4499588460c6a89e Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 19 Apr 2022 15:31:20 +0200 Subject: [PATCH 05/26] docs: remove docs for queue usage health --- docs/references/health/get-queue-usage.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/references/health/get-queue-usage.md diff --git a/docs/references/health/get-queue-usage.md b/docs/references/health/get-queue-usage.md deleted file mode 100644 index a066a11e89..0000000000 --- a/docs/references/health/get-queue-usage.md +++ /dev/null @@ -1 +0,0 @@ -Get the number of usage stats that are waiting to be processed in the Appwrite internal queue server. \ No newline at end of file From b26ed868246f7bdccb0bc5a77d9472f464adb3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 20 Apr 2022 14:02:57 +0000 Subject: [PATCH 06/26] Fix array enums dropdown --- app/views/console/database/document.phtml | 4 +-- composer.lock | 34 +++++++++-------------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/app/views/console/database/document.phtml b/app/views/console/database/document.phtml index e048fad7b9..793def259a 100644 --- a/app/views/console/database/document.phtml +++ b/app/views/console/database/document.phtml @@ -267,9 +267,9 @@ $logs = $this->getParam('logs', null); :required="attr.required" :name="attr.key" data-cast-to="string"> - @@ -226,6 +235,7 @@ $logs = $this->getParam('logs', null); :placeholder="attr.default" :name="attr.key" :required="attr.required" + :maxlength="attr.size" x-model="doc[attr.key][index]" data-cast-to="string"> From 82d30ebc35ad19b03c5d431960e78112d642855e Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Fri, 22 Apr 2022 14:21:04 +0200 Subject: [PATCH 23/26] fix(ui): increase alert time --- public/dist/scripts/app-all.js | 2 +- public/dist/scripts/app.js | 2 +- public/scripts/views/service.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/dist/scripts/app-all.js b/public/dist/scripts/app-all.js index ba65cee32d..ea271fea4f 100644 --- a/public/dist/scripts/app-all.js +++ b/public/dist/scripts/app-all.js @@ -3746,7 +3746,7 @@ if(this.getFile(id)){this.updateFile(id,{name:file.name,completed:false,failed:f target.reset();try{const response=await sdk.storage.createFile(bucketId,fileId,file,read,write,(progress)=>{this.updateFile(id,{id:progress.$id,progress:Math.round(progress.progress),error:"",});id=progress.$id;const file=this.getFile(id)??{};if(file.cancelled===true){throw'USER_CANCELLED';}});const existingFile=this.getFile(id)??{};if(existingFile.cancelled){this.updateFile(id,{id:response.$id,name:response.name,failed:false,});id=response.$id;throw'USER_CANCELLED'}else{this.updateFile(id,{id:response.$id,name:response.name,progress:100,completed:true,failed:false,});id=response.$id;} document.dispatchEvent(new CustomEvent('storage.createFile'));}catch(error){if(error==='USER_CANCELLED'){await sdk.storage.deleteFile(bucketId,id);this.updateFile(id,{cancelled:false,failed:true,});this.removeFile(id);}else{this.updateFile(id,{id:id,failed:true,error:error.message??error});} document.dispatchEvent(new CustomEvent('storage.createFile'));}}});});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();} -throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},3000);};},redirect:function(url){return function(router){if(url==="/console"){window.location=url;return;} +throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},6000);};},redirect:function(url){return function(router){if(url==="/console"){window.location=url;return;} router.change(url||"/");};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];} return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];} return url;}else{return url;}}};keys=keys.split(",").map(element=>element.trim());return function(serviceForm,router,window){let url=window.location.href;keys.map(node=>{node=node.split("=");let key=node[0]||"";let name=node[1]||key;let value=getValue(key,"param",serviceForm);url=updateQueryString(name,value?value:null,url);});if(url!==window.location.href){window.history.pushState({},"",url);router.reset();}};},trigger:function(events){return function(document){events=events.trim().split(",");for(let i=0;i{this.updateFile(id,{id:progress.$id,progress:Math.round(progress.progress),error:"",});id=progress.$id;const file=this.getFile(id)??{};if(file.cancelled===true){throw'USER_CANCELLED';}});const existingFile=this.getFile(id)??{};if(existingFile.cancelled){this.updateFile(id,{id:response.$id,name:response.name,failed:false,});id=response.$id;throw'USER_CANCELLED'}else{this.updateFile(id,{id:response.$id,name:response.name,progress:100,completed:true,failed:false,});id=response.$id;} document.dispatchEvent(new CustomEvent('storage.createFile'));}catch(error){if(error==='USER_CANCELLED'){await sdk.storage.deleteFile(bucketId,id);this.updateFile(id,{cancelled:false,failed:true,});this.removeFile(id);}else{this.updateFile(id,{id:id,failed:true,error:error.message??error});} document.dispatchEvent(new CustomEvent('storage.createFile'));}}});});})(window);(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||null;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";let running=false;let callbacks={hide:function(){return function(){return element.style.opacity='0';};},reset:function(){return function(){if("FORM"===element.tagName){return element.reset();} -throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},3000);};},redirect:function(url){return function(router){if(url==="/console"){window.location=url;return;} +throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},6000);};},redirect:function(url){return function(router){if(url==="/console"){window.location=url;return;} router.change(url||"/");};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];} return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];} return url;}else{return url;}}};keys=keys.split(",").map(element=>element.trim());return function(serviceForm,router,window){let url=window.location.href;keys.map(node=>{node=node.split("=");let key=node[0]||"";let name=node[1]||key;let value=getValue(key,"param",serviceForm);url=updateQueryString(name,value?value:null,url);});if(url!==window.location.href){window.history.pushState({},"",url);router.reset();}};},trigger:function(events){return function(document){events=events.trim().split(",");for(let i=0;i Date: Mon, 25 Apr 2022 14:58:34 +0200 Subject: [PATCH 24/26] fix(account): magic link session verifies email --- app/controllers/api/account.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 200b845987..587d0da862 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -838,6 +838,7 @@ App::put('/v1/account/sessions/magic-url') } $user + ->setAttribute('emailVerification', true) ->setAttribute('sessions', $session, Document::SET_TYPE_APPEND) ->setAttribute('tokens', $tokens); From 5dedd629bd2a580fe4c3c36554f550d757d8049c Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Mon, 25 Apr 2022 14:59:08 +0200 Subject: [PATCH 25/26] fix(account): countryName from session locale --- app/controllers/api/account.php | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 587d0da862..4b7db25b7b 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -869,9 +869,7 @@ App::put('/v1/account/sessions/magic-url') ->setStatusCode(Response::STATUS_CODE_CREATED) ; - $countryName = (isset($countries[strtoupper($session->getAttribute('countryCode'))])) - ? $countries[strtoupper($session->getAttribute('countryCode'))] - : $locale->getText('locale.country.unknown'); + $countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); $session ->setAttribute('current', true) @@ -1014,9 +1012,7 @@ App::post('/v1/account/sessions/anonymous') ->setStatusCode(Response::STATUS_CODE_CREATED) ; - $countryName = (isset($countries[strtoupper($session->getAttribute('countryCode'))])) - ? $countries[strtoupper($session->getAttribute('countryCode'))] - : $locale->getText('locale.country.unknown'); + $countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); $session ->setAttribute('current', true) @@ -1281,15 +1277,13 @@ App::get('/v1/account/sessions/:sessionId') $sessions = $user->getAttribute('sessions', []); $sessionId = ($sessionId === 'current') - ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) - : $sessionId; + ? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret) + : $sessionId; foreach ($sessions as $session) {/** @var Document $session */ if ($sessionId == $session->getId()) { - $countryName = (isset($countries[strtoupper($session->getAttribute('countryCode'))])) - ? $countries[strtoupper($session->getAttribute('countryCode'))] - : $locale->getText('locale.country.unknown'); + $countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')); $session ->setAttribute('current', ($session->getAttribute('secret') == Auth::hash(Auth::$secret))) @@ -1623,7 +1617,7 @@ App::delete('/v1/account/sessions/:sessionId') if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too $session ->setAttribute('current', true) - ->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))])) ? $countries[strtoupper($session->getAttribute('countryCode'))] : $locale->getText('locale.country.unknown')) + ->setAttribute('countryName', $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'))) ; if (!Config::getParam('domainVerification')) { @@ -1807,7 +1801,7 @@ App::delete('/v1/account/sessions') $session ->setAttribute('current', false) - ->setAttribute('countryName', (isset($countries[strtoupper($session->getAttribute('countryCode'))])) ? $countries[strtoupper($session->getAttribute('countryCode'))] : $locale->getText('locale.country.unknown')) + ->setAttribute('countryName', $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'))) ; if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too From 501563f8c4da6e111cf4b7171c4299fe5c2b359a Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Mon, 25 Apr 2022 15:07:39 +0200 Subject: [PATCH 26/26] tests: adapt magic url tests --- tests/e2e/Services/Account/AccountBase.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index cca6c8ff72..2fbe65a0e5 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -1279,7 +1279,7 @@ trait AccountBase $expireTime = strpos($lastEmail['text'], 'expire='.$response['body']['expire'], 0); $this->assertNotFalse($expireTime); - + $secretTest = strpos($lastEmail['text'], 'secret='.$response['body']['secret'], 0); $this->assertNotFalse($secretTest); @@ -1339,6 +1339,7 @@ trait AccountBase { $id = $data['id'] ?? ''; $token = $data['token'] ?? ''; + $email = $data['email'] ?? ''; /** * Test for SUCCESS @@ -1361,6 +1362,20 @@ trait AccountBase $sessionId = $response['body']['$id']; $session = $this->client->parseCookie((string)$response['headers']['set-cookie'])['a_session_'.$this->getProject()['$id']]; + $response = $this->client->call(Client::METHOD_GET, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session, + ])); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertIsNumeric($response['body']['registration']); + $this->assertEquals($response['body']['email'], $email); + $this->assertTrue($response['body']['emailVerification']); + /** * Test for FAILURE */