From f1dd1d1e181c08db13b144c9ee23a2f88cdd6256 Mon Sep 17 00:00:00 2001
From: Akhil Anand
Date: Thu, 5 Oct 2023 16:22:40 +0530
Subject: [PATCH 01/28] feature-5232-Security-Scans-OSV-Scanner
---
.github/workflows/osv-scanner.yml | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 .github/workflows/osv-scanner.yml
diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml
new file mode 100644
index 0000000000..e4ee26c79b
--- /dev/null
+++ b/.github/workflows/osv-scanner.yml
@@ -0,0 +1,27 @@
+name: OSV Scanner
+
+on:
+ pull_request:
+ push:
+
+jobs:
+ OSV-Scanner:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Install Golang
+ uses: actions/setup-go@v4
+ with:
+ go-version: '1.19'
+
+ - name: Install OSV Scanner
+ run: |
+ go install github.com/google/osv-scanner/cmd/osv-scanner@latest
+
+ - name: Scan for Vulnerabilities
+ run: |
+ osv-scanner -r .
From d3bbcce302beb239e5dbccc572628fb11ea1e629 Mon Sep 17 00:00:00 2001
From: Damodar Lohani
Date: Mon, 8 Apr 2024 09:31:15 +0545
Subject: [PATCH 02/28] Prevent functions domain to be used as custom domain
---
app/controllers/api/proxy.php | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php
index 1dfbc0ba0f..329e392b42 100644
--- a/app/controllers/api/proxy.php
+++ b/app/controllers/api/proxy.php
@@ -49,6 +49,12 @@ App::post('/v1/proxy/rules')
if ($domain === $mainDomain) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.');
}
+
+ $functionsDomain = App::getEnv('_APP_DOMAIN_FUNCTIONS', '');
+ if (str_ends_with($domain, $functionsDomain)) {
+ throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or it\'s subdomain to specific resource. Please use different domain.');
+ }
+
if ($domain === 'localhost' || $domain === APP_HOSTNAME_INTERNAL) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please pick another one.');
}
From 99cb38c674da93965fa978d59c79b5b30bdf5b80 Mon Sep 17 00:00:00 2001
From: Damodar Lohani
Date: Mon, 8 Apr 2024 03:57:08 +0000
Subject: [PATCH 03/28] fix linter issues
---
app/controllers/api/proxy.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php
index 329e392b42..0cd4c50662 100644
--- a/app/controllers/api/proxy.php
+++ b/app/controllers/api/proxy.php
@@ -49,12 +49,12 @@ App::post('/v1/proxy/rules')
if ($domain === $mainDomain) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.');
}
-
+
$functionsDomain = App::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if (str_ends_with($domain, $functionsDomain)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or it\'s subdomain to specific resource. Please use different domain.');
}
-
+
if ($domain === 'localhost' || $domain === APP_HOSTNAME_INTERNAL) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please pick another one.');
}
From 2f5be1080f104383921c0654b9131716985352b9 Mon Sep 17 00:00:00 2001
From: fuyangpengqi <995764973@qq.com>
Date: Thu, 18 Apr 2024 21:40:03 +0800
Subject: [PATCH 04/28] chore: fix some typos in comments
Signed-off-by: fuyangpengqi <995764973@qq.com>
---
app/config/errors.php | 8 ++++----
app/init.php | 4 ++--
src/Appwrite/Platform/Tasks/CalcTierStats.php | 4 ++--
src/Appwrite/Platform/Tasks/CreateInfMetric.php | 4 ++--
.../Platform/Tasks/PatchRecreateRepositoriesDocuments.php | 4 ++--
5 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/app/config/errors.php b/app/config/errors.php
index c999ddba58..3cb16d4063 100644
--- a/app/config/errors.php
+++ b/app/config/errors.php
@@ -11,7 +11,7 @@ return [
/** General Errors */
Exception::GENERAL_UNKNOWN => [
'name' => Exception::GENERAL_UNKNOWN,
- 'description' => 'An unknown error has occured. Please check the logs for more information.',
+ 'description' => 'An unknown error has occurred. Please check the logs for more information.',
'code' => 500,
],
Exception::GENERAL_MOCK => [
@@ -279,7 +279,7 @@ return [
],
Exception::USER_CHALLENGE_REQUIRED => [
'name' => Exception::USER_CHALLENGE_REQUIRED,
- 'description' => 'A recently succeessful challenge is required to complete this action. A challenge is considered recent for 5 minutes.',
+ 'description' => 'A recently successful challenge is required to complete this action. A challenge is considered recent for 5 minutes.',
'code' => 401,
],
Exception::USER_OAUTH2_BAD_REQUEST => [
@@ -484,7 +484,7 @@ return [
],
Exception::REPOSITORY_NOT_FOUND => [
'name' => Exception::REPOSITORY_NOT_FOUND,
- 'description' => 'Repository with the requested ID could not be found. Check to see if the ID is correct, or create the respository.',
+ 'description' => 'Repository with the requested ID could not be found. Check to see if the ID is correct, or create the repository.',
'code' => 404,
],
Exception::PROVIDER_CONTRIBUTION_CONFLICT => [
@@ -494,7 +494,7 @@ return [
],
Exception::GENERAL_PROVIDER_FAILURE => [
'name' => Exception::GENERAL_PROVIDER_FAILURE,
- 'description' => 'VCS (Version Control System) provider failed to proccess the request. We believe this is an error with the VCS provider. Try again, or contact support for more information.',
+ 'description' => 'VCS (Version Control System) provider failed to process the request. We believe this is an error with the VCS provider. Try again, or contact support for more information.',
'code' => 400,
],
diff --git a/app/init.php b/app/init.php
index 5877327ab6..5d688fd1b5 100644
--- a/app/init.php
+++ b/app/init.php
@@ -837,7 +837,7 @@ $register->set('pools', function () {
/**
* Get Resource
*
- * Creation could be reused accross connection types like database, cache, queue, etc.
+ * Creation could be reused across connection types like database, cache, queue, etc.
*
* Resource assignment to an adapter will happen below.
*/
@@ -847,7 +847,7 @@ $register->set('pools', function () {
$resource = function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array(
- // No need to set PDO::ATTR_ERRMODE it is overwitten in PDOProxy
+ // No need to set PDO::ATTR_ERRMODE it is overwritten in PDOProxy
PDO::ATTR_TIMEOUT => 3, // Seconds
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
diff --git a/src/Appwrite/Platform/Tasks/CalcTierStats.php b/src/Appwrite/Platform/Tasks/CalcTierStats.php
index 2d7dd3403f..ae8bd27fe3 100644
--- a/src/Appwrite/Platform/Tasks/CalcTierStats.php
+++ b/src/Appwrite/Platform/Tasks/CalcTierStats.php
@@ -103,7 +103,7 @@ class CalcTierStats extends Action
return;
} catch (\Throwable $th) {
- Console::error("Unexpected error occured with Project ID {$projectId}");
+ Console::error("Unexpected error occurred with Project ID {$projectId}");
Console::error('[Error] Type: ' . get_class($th));
Console::error('[Error] Message: ' . $th->getMessage());
Console::error('[Error] File: ' . $th->getFile());
@@ -129,7 +129,7 @@ class CalcTierStats extends Action
$data = $this->getData($project, $dbForConsole, $dbForProject);
$csv->insertOne($data);
} catch (\Throwable $th) {
- Console::error("Unexpected error occured with Project ID {$projectId}");
+ Console::error("Unexpected error occurred with Project ID {$projectId}");
Console::error('[Error] Type: ' . get_class($th));
Console::error('[Error] Message: ' . $th->getMessage());
Console::error('[Error] File: ' . $th->getFile());
diff --git a/src/Appwrite/Platform/Tasks/CreateInfMetric.php b/src/Appwrite/Platform/Tasks/CreateInfMetric.php
index c50b6e09f9..ce3484edbf 100644
--- a/src/Appwrite/Platform/Tasks/CreateInfMetric.php
+++ b/src/Appwrite/Platform/Tasks/CreateInfMetric.php
@@ -50,7 +50,7 @@ class CreateInfMetric extends Action
$dbForProject = call_user_func($getProjectDB, $project);
$this->getUsageData($dbForProject, $project);
} catch (\Throwable $th) {
- Console::error("Unexpected error occured with Project ID {$projectId}");
+ Console::error("Unexpected error occurred with Project ID {$projectId}");
Console::error('[Error] Type: ' . get_class($th));
Console::error('[Error] Message: ' . $th->getMessage());
Console::error('[Error] File: ' . $th->getFile());
@@ -72,7 +72,7 @@ class CreateInfMetric extends Action
$dbForProject = call_user_func($getProjectDB, $project);
$this->getUsageData($dbForProject, $project);
} catch (\Throwable $th) {
- Console::error("Unexpected error occured with Project ID {$projectId}");
+ Console::error("Unexpected error occurred with Project ID {$projectId}");
Console::error('[Error] Type: ' . get_class($th));
Console::error('[Error] Message: ' . $th->getMessage());
Console::error('[Error] File: ' . $th->getFile());
diff --git a/src/Appwrite/Platform/Tasks/PatchRecreateRepositoriesDocuments.php b/src/Appwrite/Platform/Tasks/PatchRecreateRepositoriesDocuments.php
index 9cf65d05b6..a7e2367d59 100644
--- a/src/Appwrite/Platform/Tasks/PatchRecreateRepositoriesDocuments.php
+++ b/src/Appwrite/Platform/Tasks/PatchRecreateRepositoriesDocuments.php
@@ -42,7 +42,7 @@ class PatchRecreateRepositoriesDocuments extends Action
$dbForProject = call_user_func($getProjectDB, $project);
$this->recreateRepositories($dbForConsole, $dbForProject, $project);
} catch (\Throwable $th) {
- Console::error("Unexpected error occured with Project ID {$projectId}");
+ Console::error("Unexpected error occurred with Project ID {$projectId}");
Console::error('[Error] Type: ' . get_class($th));
Console::error('[Error] Message: ' . $th->getMessage());
Console::error('[Error] File: ' . $th->getFile());
@@ -64,7 +64,7 @@ class PatchRecreateRepositoriesDocuments extends Action
$dbForProject = call_user_func($getProjectDB, $project);
$this->recreateRepositories($dbForConsole, $dbForProject, $project);
} catch (\Throwable $th) {
- Console::error("Unexpected error occured with Project ID {$projectId}");
+ Console::error("Unexpected error occurred with Project ID {$projectId}");
Console::error('[Error] Type: ' . get_class($th));
Console::error('[Error] Message: ' . $th->getMessage());
Console::error('[Error] File: ' . $th->getFile());
From 8eb5b3467a5e47095214554bfb80d8280151012c Mon Sep 17 00:00:00 2001
From: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com>
Date: Wed, 24 Apr 2024 23:00:23 +0000
Subject: [PATCH 05/28] feat(security): add github workflow to check
dependencies
This workflow action uses OSV Scanner, an open source vulnerability
scanner by Google. We're using OSV Scanner because it has:
* good usability - JSON output and multiple options
* good accuracy - OSV database from google and support
for multiple languages including PHP
---
.github/workflows/check-dependencies.yml | 19 +++++++++++++++++
.github/workflows/osv-scanner.yml | 27 ------------------------
2 files changed, 19 insertions(+), 27 deletions(-)
create mode 100644 .github/workflows/check-dependencies.yml
delete mode 100644 .github/workflows/osv-scanner.yml
diff --git a/.github/workflows/check-dependencies.yml b/.github/workflows/check-dependencies.yml
new file mode 100644
index 0000000000..17caf3aa6b
--- /dev/null
+++ b/.github/workflows/check-dependencies.yml
@@ -0,0 +1,19 @@
+name: Check dependencies
+
+# Adapted from https://google.github.io/osv-scanner/github-action/#scan-on-pull-request
+
+on:
+ pull_request:
+ branches: [main, 1.*.x]
+ merge_group:
+ branches: [main, 1.*.x]
+
+permissions:
+ # Require writing security events to upload SARIF file to security tab
+ security-events: write
+ # Only need to read contents
+ contents: read
+
+jobs:
+ scan-pr:
+ uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v1.7.1"
\ No newline at end of file
diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml
deleted file mode 100644
index e4ee26c79b..0000000000
--- a/.github/workflows/osv-scanner.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-name: OSV Scanner
-
-on:
- pull_request:
- push:
-
-jobs:
- OSV-Scanner:
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v3
- with:
- fetch-depth: 0
-
- - name: Install Golang
- uses: actions/setup-go@v4
- with:
- go-version: '1.19'
-
- - name: Install OSV Scanner
- run: |
- go install github.com/google/osv-scanner/cmd/osv-scanner@latest
-
- - name: Scan for Vulnerabilities
- run: |
- osv-scanner -r .
From 02ece2637ff8a0a863011ba6d743556ee12a48f3 Mon Sep 17 00:00:00 2001
From: ItzNotABug
Date: Mon, 29 Apr 2024 15:54:22 +0530
Subject: [PATCH 06/28] fix: use `resourceInternalId`.
---
app/controllers/api/functions.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php
index ed5af1d38a..4ff2b40241 100644
--- a/app/controllers/api/functions.php
+++ b/app/controllers/api/functions.php
@@ -1282,7 +1282,7 @@ App::get('/v1/functions/:functionId/deployments')
}
// Set resource queries
- $queries[] = Query::equal('resourceId', [$function->getId()]);
+ $queries[] = Query::equal('resourceInternalId', [$function->getInternalId()]);
$queries[] = Query::equal('resourceType', ['functions']);
/**
From f7f00fcf10be528f9ba84a3b19428a751dd92b12 Mon Sep 17 00:00:00 2001
From: Evan
Date: Mon, 29 Apr 2024 13:42:13 -0700
Subject: [PATCH 07/28] Close reason update
Due to failures, changing to not_planned
---
.github/workflows/stale.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 053f0191a2..5987eeeb0c 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -18,6 +18,6 @@ jobs:
days-before-close: 14
remove-stale-when-updated: true
close-issue-message: "This issue has been closed due to inactivity. If you still require assistance, please provide the requested information."
- close-issue-reason: "not-planned"
+ close-issue-reason: "not_planned"
operations-per-run: 100
only-labels: "question"
From 574ee319814172f302b2591d298d200b286eb288 Mon Sep 17 00:00:00 2001
From: Brendan Beltz
Date: Mon, 29 Apr 2024 20:09:33 -0400
Subject: [PATCH 08/28] Replace missing domain paths
---
README-CN.md | 112 ++++++++++++++++++++++++++-------------------------
README.md | 47 +++++++++++----------
2 files changed, 80 insertions(+), 79 deletions(-)
diff --git a/README-CN.md b/README-CN.md
index e526b5613d..ba800a3bd6 100644
--- a/README-CN.md
+++ b/README-CN.md
@@ -11,6 +11,7 @@
+
[](https://appwrite.io/company/careers)
[](https://hacktoberfest.appwrite.io)
[](https://appwrite.io/discord?r=Github)
@@ -25,7 +26,7 @@
[**Appwrite 云公开测试版!立即注册!**](https://cloud.appwrite.io)
-Appwrite是一个基于Docker的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,以及后端。Appwrite 通过视觉化界面简化了从零开始编写 API 的繁琐过程,在保证软件安全的前提下为开发者创造了一个高效的开发环境。
+Appwrite 是一个基于 Docker 的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,以及后端。Appwrite 通过视觉化界面简化了从零开始编写 API 的繁琐过程,在保证软件安全的前提下为开发者创造了一个高效的开发环境。
Appwrite 可以提供给开发者用户验证,外部授权,用户数据读写检索,文件储存,图像处理,云函数计算,[等多种服务](https://appwrite.io/docs).
@@ -93,7 +94,6 @@ docker run -it --rm `
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
-
需要自定义容器构架,请查看我们的 Docker [环境变量](https://appwrite.io/docs/environment-variables) 文档。您还可以参考我们的 [docker-compose.yml](https://appwrite.io/install/compose) 和 [.env](https://appwrite.io/install/env) 文件手动设置环境。
### 从旧版本升级
@@ -104,71 +104,73 @@ docker run -it --rm `
开始使用 Appwrite 只需要在控制台创建一个新项目,选择开发平台,然后抓取我们的开发套件。您可以从以下的教程中找到你喜欢的平台开始使用 Appwrite。
-| 类别 | 技术 |
-|---------------------|------|
-| **Web 应用** | [Web 快速开始](/docs/quick-starts/web) |
-| | [Next.js 快速开始](/docs/quick-starts/nextjs) |
-| | [React 快速开始](/docs/quick-starts/react) |
-| | [Vue.js 快速开始](/docs/quick-starts/vue) |
-| | [Nuxt 快速开始](/docs/quick-starts/nuxt) |
-| | [SvelteKit 快速开始](/docs/quick-starts/sveltekit) |
-| | [Refine 快速开始](/docs/quick-starts/refine) |
-| | [Angular 快速开始](/docs/quick-starts/angular) |
-| **苹果于安卓应用** | [React Native 快速开始](/docs/quick-starts/react-native) |
-| | [Flutter 快速开始](/docs/quick-starts/flutter) |
-| | [Apple 快速开始](/docs/quick-starts/apple) |
-| | [Android 快速开始](/docs/quick-starts/android) |
-| **服务器** | [Node.js 快速开始](/docs/quick-starts/node) |
-| | [Python 快速开始](/docs/quick-starts/python) |
-| | [.NET 快速开始](/docs/quick-starts/dotnet) |
-| | [Dart 快速开始](/docs/quick-starts/dart) |
-| | [Ruby 快速开始](/docs/quick-starts/ruby) |
-| | [Deno 快速开始](/docs/quick-starts/deno) |
-| | [PHP 快速开始](/docs/quick-starts/php) |
-| | [Kotlin 快速开始](/docs/quick-starts/kotlin) |
-| | [Swift 快速开始](/docs/quick-starts/swift) |
+| 类别 | 技术 |
+| ------------------ | --------------------------------------------------------------------------- |
+| **Web 应用** | [Web 快速开始](https://appwrite.io/docs/quick-starts/web) |
+| | [Next.js 快速开始](https://appwrite.io/docs/quick-starts/nextjs) |
+| | [React 快速开始](https://appwrite.io/docs/quick-starts/react) |
+| | [Vue.js 快速开始](https://appwrite.io/docs/quick-starts/vue) |
+| | [Nuxt 快速开始](https://appwrite.io/docs/quick-starts/nuxt) |
+| | [SvelteKit 快速开始](https://appwrite.io/docs/quick-starts/sveltekit) |
+| | [Refine 快速开始](https://appwrite.io/docs/quick-starts/refine) |
+| | [Angular 快速开始](https://appwrite.io/docs/quick-starts/angular) |
+| **苹果于安卓应用** | [React Native 快速开始](https://appwrite.io/docs/quick-starts/react-native) |
+| | [Flutter 快速开始](https://appwrite.io/docs/quick-starts/flutter) |
+| | [Apple 快速开始](https://appwrite.io/docs/quick-starts/apple) |
+| | [Android 快速开始](https://appwrite.io/docs/quick-starts/android) |
+| **服务器** | [Node.js 快速开始](https://appwrite.io/docs/quick-starts/node) |
+| | [Python 快速开始](https://appwrite.io/docs/quick-starts/python) |
+| | [.NET 快速开始](https://appwrite.io/docs/quick-starts/dotnet) |
+| | [Dart 快速开始](https://appwrite.io/docs/quick-starts/dart) |
+| | [Ruby 快速开始](https://appwrite.io/docs/quick-starts/ruby) |
+| | [Deno 快速开始](https://appwrite.io/docs/quick-starts/deno) |
+| | [PHP 快速开始](https://appwrite.io/docs/quick-starts/php) |
+| | [Kotlin 快速开始](https://appwrite.io/docs/quick-starts/kotlin) |
+| | [Swift 快速开始](https://appwrite.io/docs/quick-starts/swift) |
### 软件服务
-* [**帐户**](https://appwrite.io/docs/references/cloud/client-web/account) -管理当前用户的帐户和登录方式。跟踪和管理用户 Session,登录设备,登录方法和查看相关记录。
-* [**用户**](https://appwrite.io/docs/server/users) - 在以管理员模式登录时管理和列出所有用户。
-* [**团队**](https://appwrite.io/docs/references/cloud/client-web/teams) - 管理用户分组。邀请成员,管理团队中的用户权限和用户角色。
-* [**数据库**](https://appwrite.io/docs/references/cloud/client-web/databases) - 管理数据库文档和文档集。用检索界面来对文档和文档集进行读取,创建,更新,和删除。
-* [**贮存**](https://appwrite.io/docs/references/cloud/client-web/storage) - 管理文件的阅读、创建、删除和预览。设置文件的预览来满足程序的个性化需求。所有文件都由 ClamAV 扫描并安全存储和加密。
-* [**云函数**](https://appwrite.io/docs/server/functions) - 在安全,隔离的环境中运行自定义代码。这些代码可以被事件,CRON,或者手动操作触发。
-* [**消息传递**](https://appwrite.io/docs/references/cloud/client-web/messaging) - 使用 Appwrite 消息传递功能通过推送通知、电子邮件和短信与用户进行通信。
-* [**语言适配**](https://appwrite.io/docs/references/cloud/client-web/locale) - 根据用户所在的的国家和地区做出合适的语言适配。
-* [**头像**](https://appwrite.io/docs/references/cloud/client-web/avatars) -管理用户头像、国家旗帜、浏览器图标、信用卡符号,和生成二维码。
-如需完整的 API 界面文档,请访问 [https://appwrite.io/docs](https://appwrite.io/docs)。如需更多教程、新闻和公告,请订阅我们的 [博客](https://medium.com/appwrite-io) 和 加入我们的[Discord 社区](https://discord.gg/GSeTUeA)。
+- [**帐户**](https://appwrite.io/docs/references/cloud/client-web/account) -管理当前用户的帐户和登录方式。跟踪和管理用户 Session,登录设备,登录方法和查看相关记录。
+- [**用户**](https://appwrite.io/docs/server/users) - 在以管理员模式登录时管理和列出所有用户。
+- [**团队**](https://appwrite.io/docs/references/cloud/client-web/teams) - 管理用户分组。邀请成员,管理团队中的用户权限和用户角色。
+- [**数据库**](https://appwrite.io/docs/references/cloud/client-web/databases) - 管理数据库文档和文档集。用检索界面来对文档和文档集进行读取,创建,更新,和删除。
+- [**贮存**](https://appwrite.io/docs/references/cloud/client-web/storage) - 管理文件的阅读、创建、删除和预览。设置文件的预览来满足程序的个性化需求。所有文件都由 ClamAV 扫描并安全存储和加密。
+- [**云函数**](https://appwrite.io/docs/server/functions) - 在安全,隔离的环境中运行自定义代码。这些代码可以被事件,CRON,或者手动操作触发。
+- [**消息传递**](https://appwrite.io/docs/references/cloud/client-web/messaging) - 使用 Appwrite 消息传递功能通过推送通知、电子邮件和短信与用户进行通信。
+- [**语言适配**](https://appwrite.io/docs/references/cloud/client-web/locale) - 根据用户所在的的国家和地区做出合适的语言适配。
+- [**头像**](https://appwrite.io/docs/references/cloud/client-web/avatars) -管理用户头像、国家旗帜、浏览器图标、信用卡符号,和生成二维码。
+ 如需完整的 API 界面文档,请访问 [https://appwrite.io/docs](https://appwrite.io/docs)。如需更多教程、新闻和公告,请订阅我们的 [博客](https://medium.com/appwrite-io) 和 加入我们的[Discord 社区](https://discord.gg/GSeTUeA)。
### 开发套件
以下是当前支持的平台和语言列表。如果您想帮助我们为您选择的平台添加支持,您可以访问我们的 [SDK 生成器](https://github.com/appwrite/sdk-generator) 项目并查看我们的 [贡献指南](https://github.com/appwrite/sdk-generator/blob/master/CONTRIBUTING.md)。
#### 客户端
-* ✅ [Web](https://github.com/appwrite/sdk-for-web) (由 Appwrite 团队维护)
-* ✅ [Flutter](https://github.com/appwrite/sdk-for-flutter) (由 Appwrite 团队维护)
-* ✅ [Apple](https://github.com/appwrite/sdk-for-apple) (由 Appwrite 团队维护)
-* ✅ [Android](https://github.com/appwrite/sdk-for-android) (由 Appwrite 团队维护)
-* ✅ [React Native](https://github.com/appwrite/sdk-for-react-native) - **公测** (由 Appwrite 团队维护)
+
+- ✅ [Web](https://github.com/appwrite/sdk-for-web) (由 Appwrite 团队维护)
+- ✅ [Flutter](https://github.com/appwrite/sdk-for-flutter) (由 Appwrite 团队维护)
+- ✅ [Apple](https://github.com/appwrite/sdk-for-apple) (由 Appwrite 团队维护)
+- ✅ [Android](https://github.com/appwrite/sdk-for-android) (由 Appwrite 团队维护)
+- ✅ [React Native](https://github.com/appwrite/sdk-for-react-native) - **公测** (由 Appwrite 团队维护)
#### 服务器
-* ✅ [NodeJS](https://github.com/appwrite/sdk-for-node) (由 Appwrite 团队维护)
-* ✅ [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwrite 团队维护)
-* ✅ [Dart](https://github.com/appwrite/sdk-for-dart) (由 Appwrite 团队维护)
-* ✅ [Deno](https://github.com/appwrite/sdk-for-deno) (由 Appwrite 团队维护)
-* ✅ [Ruby](https://github.com/appwrite/sdk-for-ruby) (由 Appwrite 团队维护)
-* ✅ [Python](https://github.com/appwrite/sdk-for-python) (由 Appwrite 团队维护)
-* ✅ [Kotlin](https://github.com/appwrite/sdk-for-kotlin) (由 Appwrite 团队维护)
-* ✅ [Swift](https://github.com/appwrite/sdk-for-swift) (由 Appwrite 团队维护)
-* ✅ [.NET](https://github.com/appwrite/sdk-for-dotnet) - **公测** (由 Appwrite 团队维护)
+
+- ✅ [NodeJS](https://github.com/appwrite/sdk-for-node) (由 Appwrite 团队维护)
+- ✅ [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwrite 团队维护)
+- ✅ [Dart](https://github.com/appwrite/sdk-for-dart) (由 Appwrite 团队维护)
+- ✅ [Deno](https://github.com/appwrite/sdk-for-deno) (由 Appwrite 团队维护)
+- ✅ [Ruby](https://github.com/appwrite/sdk-for-ruby) (由 Appwrite 团队维护)
+- ✅ [Python](https://github.com/appwrite/sdk-for-python) (由 Appwrite 团队维护)
+- ✅ [Kotlin](https://github.com/appwrite/sdk-for-kotlin) (由 Appwrite 团队维护)
+- ✅ [Swift](https://github.com/appwrite/sdk-for-swift) (由 Appwrite 团队维护)
+- ✅ [.NET](https://github.com/appwrite/sdk-for-dotnet) - **公测** (由 Appwrite 团队维护)
#### 开发者社区
-* ✅ [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (维护者 [Michael Gangolf](https://github.com/m1ga/))
-* ✅ [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (维护者 [fenix-hub @GodotNuts](https://github.com/fenix-hub))
-找不到需要的的 SDK? - 欢迎通过发起PR来帮助我们完善Appwrite的软件生态环境 [SDK 生成器](https://github.com/appwrite/sdk-generator)!
+- ✅ [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (维护者 [Michael Gangolf](https://github.com/m1ga/))
+- ✅ [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (维护者 [fenix-hub @GodotNuts](https://github.com/fenix-hub))
+找不到需要的的 SDK? - 欢迎通过发起 PR 来帮助我们完善 Appwrite 的软件生态环境 [SDK 生成器](https://github.com/appwrite/sdk-generator)!
## 软件架构
@@ -180,13 +182,13 @@ Appwrite API 界面层利用后台缓存和任务委派来提供极速的响应
## 贡献代码
-为了确保正确审查,所有代码贡献 - 包括来自具有直接提交更改权限的贡献者 - 都必须提交PR请求并在合并分支之前得到核心开发人员的批准。
+为了确保正确审查,所有代码贡献 - 包括来自具有直接提交更改权限的贡献者 - 都必须提交 PR 请求并在合并分支之前得到核心开发人员的批准。
-我们欢迎所有人提交PR!如果您愿意提供帮助,可以在 [贡献指南](CONTRIBUTING.md) 中了解有关如何为项目做出贡献的更多信息。
+我们欢迎所有人提交 PR!如果您愿意提供帮助,可以在 [贡献指南](CONTRIBUTING.md) 中了解有关如何为项目做出贡献的更多信息。
## 安全
-为了保护您的隐私,请避免在GitHub 上发布安全问题。发送问题至 security@appwrite.io,我们将为您做更细致的解答。
+为了保护您的隐私,请避免在 GitHub 上发布安全问题。发送问题至 security@appwrite.io,我们将为您做更细致的解答。
## 订阅我们
diff --git a/README.md b/README.md
index eb4e963585..865df745b4 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,6 @@
-
[](https://appwrite.io/company/careers)
@@ -142,29 +141,29 @@ Choose from one of the providers below:
Getting started with Appwrite is as easy as creating a new project, choosing your platform, and integrating its SDK into your code. You can easily get started with your platform of choice by reading one of our Getting Started tutorials.
-| Platform | Technology |
-|--------------------|------------|
-| **Web app** | [Quick start for Web](/docs/quick-starts/web) |
-| | [Quick start for Next.js](/docs/quick-starts/nextjs) |
-| | [Quick start for React](/docs/quick-starts/react) |
-| | [Quick start for Vue.js](/docs/quick-starts/vue) |
-| | [Quick start for Nuxt](/docs/quick-starts/nuxt) |
-| | [Quick start for SvelteKit](/docs/quick-starts/sveltekit) |
-| | [Quick start for Refine](/docs/quick-starts/refine) |
-| | [Quick start for Angular](/docs/quick-starts/angular) |
-| **Mobile and Native** | [Quick start for React Native](/docs/quick-starts/react-native) |
-| | [Quick start for Flutter](/docs/quick-starts/flutter) |
-| | [Quick start for Apple](/docs/quick-starts/apple) |
-| | [Quick start for Android](/docs/quick-starts/android) |
-| **Server** | [Quick start for Node.js](/docs/quick-starts/node) |
-| | [Quick start for Python](/docs/quick-starts/python) |
-| | [Quick start for .NET](/docs/quick-starts/dotnet) |
-| | [Quick start for Dart](/docs/quick-starts/dart) |
-| | [Quick start for Ruby](/docs/quick-starts/ruby) |
-| | [Quick start for Deno](/docs/quick-starts/deno) |
-| | [Quick start for PHP](/docs/quick-starts/php) |
-| | [Quick start for Kotlin](/docs/quick-starts/kotlin) |
-| | [Quick start for Swift](/docs/quick-starts/swift) |
+| Platform | Technology |
+| --------------------- | ---------------------------------------------------------------------------------- |
+| **Web app** | [Quick start for Web](https://appwrite.io/docs/quick-starts/web) |
+| | [Quick start for Next.js](https://appwrite.io/docs/quick-starts/nextjs) |
+| | [Quick start for React](https://appwrite.io/docs/quick-starts/react) |
+| | [Quick start for Vue.js](https://appwrite.io/docs/quick-starts/vue) |
+| | [Quick start for Nuxt](https://appwrite.io/docs/quick-starts/nuxt) |
+| | [Quick start for SvelteKit](https://appwrite.io/docs/quick-starts/sveltekit) |
+| | [Quick start for Refine](https://appwrite.io/docs/quick-starts/refine) |
+| | [Quick start for Angular](https://appwrite.io/docs/quick-starts/angular) |
+| **Mobile and Native** | [Quick start for React Native](https://appwrite.io/docs/quick-starts/react-native) |
+| | [Quick start for Flutter](https://appwrite.io/docs/quick-starts/flutter) |
+| | [Quick start for Apple](https://appwrite.io/docs/quick-starts/apple) |
+| | [Quick start for Android](https://appwrite.io/docs/quick-starts/android) |
+| **Server** | [Quick start for Node.js](https://appwrite.io/docs/quick-starts/node) |
+| | [Quick start for Python](https://appwrite.io/docs/quick-starts/python) |
+| | [Quick start for .NET](https://appwrite.io/docs/quick-starts/dotnet) |
+| | [Quick start for Dart](https://appwrite.io/docs/quick-starts/dart) |
+| | [Quick start for Ruby](https://appwrite.io/docs/quick-starts/ruby) |
+| | [Quick start for Deno](https://appwrite.io/docs/quick-starts/deno) |
+| | [Quick start for PHP](https://appwrite.io/docs/quick-starts/php) |
+| | [Quick start for Kotlin](https://appwrite.io/docs/quick-starts/kotlin) |
+| | [Quick start for Swift](https://appwrite.io/docs/quick-starts/swift) |
### Products
From fc38b9473274e23e59e5e84a380cc2a58e22643e Mon Sep 17 00:00:00 2001
From: Sumanta
Date: Sat, 4 May 2024 17:08:44 +0530
Subject: [PATCH 09/28] fix: updated typo in credit-cards.php credit card image
filename
---
app/config/avatars/credit-cards.php | 2 +-
.../credit-cards/{censosud.png => cencosud.png} | Bin
2 files changed, 1 insertion(+), 1 deletion(-)
rename app/config/avatars/credit-cards/{censosud.png => cencosud.png} (100%)
diff --git a/app/config/avatars/credit-cards.php b/app/config/avatars/credit-cards.php
index 1aa22c4174..2bceee0d89 100644
--- a/app/config/avatars/credit-cards.php
+++ b/app/config/avatars/credit-cards.php
@@ -4,7 +4,7 @@ return [
'amex' => ['name' => 'American Express', 'path' => __DIR__ . '/credit-cards/amex.png'],
'argencard' => ['name' => 'Argencard', 'path' => __DIR__ . '/credit-cards/argencard.png'],
'cabal' => ['name' => 'Cabal', 'path' => __DIR__ . '/credit-cards/cabal.png'],
- 'censosud' => ['name' => 'Consosud', 'path' => __DIR__ . '/credit-cards/consosud.png'],
+ 'cencosud' => ['name' => 'Consosud', 'path' => __DIR__ . '/credit-cards/cencosud.png'],
'diners' => ['name' => 'Diners Club', 'path' => __DIR__ . '/credit-cards/diners.png'],
'discover' => ['name' => 'Discover', 'path' => __DIR__ . '/credit-cards/discover.png'],
'elo' => ['name' => 'Elo', 'path' => __DIR__ . '/credit-cards/elo.png'],
diff --git a/app/config/avatars/credit-cards/censosud.png b/app/config/avatars/credit-cards/cencosud.png
similarity index 100%
rename from app/config/avatars/credit-cards/censosud.png
rename to app/config/avatars/credit-cards/cencosud.png
From 7ca9e6923f921cacb372e62b7a57df6a9dae6ff3 Mon Sep 17 00:00:00 2001
From: Damodar Lohani
Date: Sun, 5 May 2024 10:47:54 +0545
Subject: [PATCH 10/28] add test
---
.../Projects/ProjectsCustomServerTest.php | 30 +++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/tests/e2e/Services/Projects/ProjectsCustomServerTest.php b/tests/e2e/Services/Projects/ProjectsCustomServerTest.php
index 436d1df611..bae0d8bda9 100644
--- a/tests/e2e/Services/Projects/ProjectsCustomServerTest.php
+++ b/tests/e2e/Services/Projects/ProjectsCustomServerTest.php
@@ -2,17 +2,43 @@
namespace Tests\E2E\Services\Projects;
+use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
+use Utopia\System\System;
class ProjectsCustomServerTest extends Scope
{
use ProjectCustom;
use SideServer;
- public function testMock()
+ // Domains
+
+ public function testCreateProjectRule()
{
- $this->assertEquals(true, true);
+ $headers = array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-mode' => 'admin',
+ 'cookie' => 'a_session_console=' . $this->getRoot()['session'],
+ ]);
+
+ $response = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [
+ 'resourceType' => 'api',
+ 'domain' => 'api.appwrite.test',
+ ]);
+
+ $this->assertEquals(201, $response['headers']['status-code']);
+
+ // prevent functions domain
+ $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
+
+ $response = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [
+ 'resourceType' => 'api',
+ 'domain' => $functionsDomain,
+ ]);
+
+ $this->assertEquals(400, $response['headers']['status-code']);
}
}
From 74264582cc31bc2cf32d30dfed10370b5aaf73ce Mon Sep 17 00:00:00 2001
From: Damodar Lohani
Date: Sun, 5 May 2024 11:07:59 +0545
Subject: [PATCH 11/28] Update proxy.php
---
app/controllers/api/proxy.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php
index 0cd4c50662..29f39c55af 100644
--- a/app/controllers/api/proxy.php
+++ b/app/controllers/api/proxy.php
@@ -50,7 +50,7 @@ App::post('/v1/proxy/rules')
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.');
}
- $functionsDomain = App::getEnv('_APP_DOMAIN_FUNCTIONS', '');
+ $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if (str_ends_with($domain, $functionsDomain)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or it\'s subdomain to specific resource. Please use different domain.');
}
From df5b1287b8d6ac969e8849d9be954252f1f7d3ef Mon Sep 17 00:00:00 2001
From: shimon
Date: Mon, 6 May 2024 08:54:51 +0300
Subject: [PATCH 12/28] adding new usage metric
---
CONTRIBUTING.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 529f6103d1..f934048d7a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -301,6 +301,57 @@ This will allow the Appwrite community to sufficiently discuss the new feature v
This is also important for the Appwrite lead developers to be able to provide technical input and potentially a different emphasis regarding the feature design and architecture. Some bigger features might need to go through our [RFC process](https://github.com/appwrite/rfc).
+## Adding new usage metrics
+
+metrics are collected to 3 scopes :
+Daily, monthly, an infinity.
+Adding new usage metrics in order to aggregate usage stats is very simple but very much depends on where do you want to collect.
+the statistics( via API or via background worker)
+Here are the steps needs to be taken in both cases:
+
+For both cases you need to add a const variable in app/init.php under the usage metrics list.
+```php
+// Usage metrics
+const METRIC_FUNCTIONS = 'functions';
+const METRIC_DEPLOYMENTS = 'deployments';
+const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
+const METRIC_BUILDS = 'builds';
+const METRIC_BUILDS_STORAGE = 'builds.storage';
+const METRIC_BUILDS_COMPUTE = 'builds.compute';
+```
+
+**API**
+
+On database listener, Add to existing or create a new switch case.
+Add a call to the usage worker with your new metric const like so:
+
+```php
+ case $document->getCollection() === 'functions':
+ $queueForUsage
+ ->addMetric(METRIC_FUNCTIONS, $value);
+ if ($event === Database::EVENT_DOCUMENT_DELETE) {
+ $queueForUsage
+ ->addReduce($document);
+ }
+ break;
+```
+
+
+**Background worker**
+
+```php
+$queueForUsage
+ ->addMetric(METRIC_BUILDS, 1)
+ ->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0))
+ ->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000)
+ ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1)
+ ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0))
+ ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000)
+ ->setProject($project)
+ ->trigger();
+```
+
+
## Build
To build a new version of the Appwrite server, all you need to do is run the build.sh file like this:
From 2f54446aee4bbf4ff91b92eaa4c9df19d67aa489 Mon Sep 17 00:00:00 2001
From: shimon
Date: Mon, 6 May 2024 17:31:36 +0300
Subject: [PATCH 13/28] adding new usage metric
---
CONTRIBUTING.md | 89 ++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 81 insertions(+), 8 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f934048d7a..2cc4a7cd6a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -301,12 +301,53 @@ This will allow the Appwrite community to sufficiently discuss the new feature v
This is also important for the Appwrite lead developers to be able to provide technical input and potentially a different emphasis regarding the feature design and architecture. Some bigger features might need to go through our [RFC process](https://github.com/appwrite/rfc).
-## Adding new usage metrics
+## Adding New Usage Metrics
+
+These are the current metrics we collect usage stats for:
+
+| Metric | Description |
+|--------|---------------------------------------------------|
+| teams | Total number of teams per project |
+| users | Total number of users per project |
+| executions | Total number of executions per project |
+| databases | Total number of databases per project |
+| collections | Total number of collections per project |
+| {databaseInternalId}.collections | Total number of collections per database |
+| documents | Total number of documents per project |
+| {databaseInternalId}.{collectionInternalId}.documents | Total number of documents per collection |
+| buckets | Total number of buckets per project |
+| files | Total number of files per project |
+| files.storage | Sum of files storage per project (in bytes) |
+| {bucketInternalId}.files.storage | Sum of files.storage per bucket |
+| functions | Total number of functions per project |
+| deployments | Total number of deployments per project |
+| deployments.storage | Sum of deployments storage per project (in bytes) |
+| builds | Total number of builds per project |
+| builds.storage | Sum of builds storage per project (in bytes) |
+| builds.compute | Sum of compute duration per project (in seconds) |
+| {functionInternalId}.builds.storage | Sum of builds storage per function |
+| {functionInternalId}.builds.compute | Sum of compute duration per function (in seconds) |
+| {resourceType}.{resourceInternalId}.deployments | Total number of deployments per function |
+| {resourceType}.{resourceInternalId}.deployments.storage | Sum of deployments storage per function |
+| executions | Total number of executions per project |
+| executions.compute | Sum of compute duration per project (in seconds) |
+| {functionInternalId}.executions | Total number of executions per function |
+| network.requests | Total number of network requests per project |
+| network.inbound | Sum of network inbound traffic per project |
+| network.outbound | Sum of network outbound traffic per project |
+
+* The curly brackets in the metric name act as placeholders and will be replaced with a value.
+
+Metrics are collected into 3 scopes: Daily, monthly, and infinity. Adding new usage metrics to aggregate usage stats is simple but depends on whether you want to collect the statistics via API or background worker. Here are the steps needed for both cases:
+
+For both cases, add a const variable in `app/init.php` under the usage metrics list:
+
+* The curly brackets in the metric name acts as a placeholder and will be replaced with a value.
metrics are collected to 3 scopes :
Daily, monthly, an infinity.
-Adding new usage metrics in order to aggregate usage stats is very simple but very much depends on where do you want to collect.
-the statistics( via API or via background worker)
+Adding new usage metrics in order to aggregate usage stats is very simple but very much depends on where do you want to collect
+the statistics(API or via background worker).
Here are the steps needs to be taken in both cases:
For both cases you need to add a const variable in app/init.php under the usage metrics list.
@@ -315,16 +356,22 @@ For both cases you need to add a const variable in app/init.php under the usage
const METRIC_FUNCTIONS = 'functions';
const METRIC_DEPLOYMENTS = 'deployments';
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
-const METRIC_BUILDS = 'builds';
-const METRIC_BUILDS_STORAGE = 'builds.storage';
-const METRIC_BUILDS_COMPUTE = 'builds.compute';
```
**API**
-On database listener, Add to existing or create a new switch case.
-Add a call to the usage worker with your new metric const like so:
+In the database listener, add to an existing or create a new switch case. Add a call to the usage worker with your new metric const like so:
+```php
+ case $document->getCollection() === 'teams':
+ $queueForUsage
+ ->addMetric(METRIC_TEAMS, $value); // per project
+ break;
+```
+There are cases when you need to handle metric the is a parent entity, like buckets.
+Files are linked to a parent bucket, you should verify you remove the files stats when you delete a bucket.
+
+In that case you need also to handle children removal using addReduce() method call.
```php
case $document->getCollection() === 'functions':
$queueForUsage
@@ -336,9 +383,35 @@ Add a call to the usage worker with your new metric const like so:
break;
```
+On top of that also adding logic on the usage worker located in /src/Appwrite/Platform/Workers/Usage.php, on the reduce method.
+```php
+private function reduce(Document $project, Document $document, array &$metrics, callable $getProjectDB): void
+```
+
**Background worker**
+On that case you need to inject the usage queue to the desired worker
+```php
+/**
+* @throws Exception
+*/
+public function __construct()
+{
+$this
+->desc('Functions worker')
+->groups(['functions'])
+->inject('message')
+->inject('dbForProject')
+->inject('queueForFunctions')
+->inject('queueForEvents')
+->inject('queueForUsage')
+->inject('log')
+->callback(fn (Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage, $log));
+```
+
+and then trigger the queue with the new metric like so:
+
```php
$queueForUsage
->addMetric(METRIC_BUILDS, 1)
From 6f3aefdf166e10b785697194898c039ebbbba5b8 Mon Sep 17 00:00:00 2001
From: Sumanta
Date: Tue, 7 May 2024 00:51:58 +0530
Subject: [PATCH 14/28] fix: updated typo in credit-cards.php from Consosud to
Cencosud
---
app/config/avatars/credit-cards.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/config/avatars/credit-cards.php b/app/config/avatars/credit-cards.php
index 2bceee0d89..eb76c576cf 100644
--- a/app/config/avatars/credit-cards.php
+++ b/app/config/avatars/credit-cards.php
@@ -4,7 +4,7 @@ return [
'amex' => ['name' => 'American Express', 'path' => __DIR__ . '/credit-cards/amex.png'],
'argencard' => ['name' => 'Argencard', 'path' => __DIR__ . '/credit-cards/argencard.png'],
'cabal' => ['name' => 'Cabal', 'path' => __DIR__ . '/credit-cards/cabal.png'],
- 'cencosud' => ['name' => 'Consosud', 'path' => __DIR__ . '/credit-cards/cencosud.png'],
+ 'cencosud' => ['name' => 'Cencosud', 'path' => __DIR__ . '/credit-cards/cencosud.png'],
'diners' => ['name' => 'Diners Club', 'path' => __DIR__ . '/credit-cards/diners.png'],
'discover' => ['name' => 'Discover', 'path' => __DIR__ . '/credit-cards/discover.png'],
'elo' => ['name' => 'Elo', 'path' => __DIR__ . '/credit-cards/elo.png'],
From 08bf5aace4de6d3a9d2c2920b6e19b119ff52ac3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matej=20Ba=C4=8Do?=
Date: Tue, 7 May 2024 09:01:57 +0000
Subject: [PATCH 15/28] Mark email verified after email OTP
---
app/controllers/api/account.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php
index 90a35ede78..6190cec905 100644
--- a/app/controllers/api/account.php
+++ b/app/controllers/api/account.php
@@ -123,7 +123,8 @@ $createSession = function (string $userId, string $secret, Request $request, Res
Authorization::skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId()));
$dbForProject->purgeCachedDocument('users', $user->getId());
- if ($verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_MAGIC_URL) {
+ // Magic URL + Email OTP
+ if ($verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_MAGIC_URL || $verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_EMAIL) {
$user->setAttribute('emailVerification', true);
}
From 4c6f01ad6835e675236e5ef98502e5ba626506cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matej=20Ba=C4=8Do?=
Date: Tue, 7 May 2024 09:05:54 +0000
Subject: [PATCH 16/28] Update tests
---
tests/e2e/Services/Account/AccountBase.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php
index d61f44ca1b..2d72625121 100644
--- a/tests/e2e/Services/Account/AccountBase.php
+++ b/tests/e2e/Services/Account/AccountBase.php
@@ -202,6 +202,8 @@ trait AccountBase
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($userId, $response['body']['$id']);
+ $this->assertEquals($userId, $response['body']['$id']);
+ $this->assertTrue($response['body']['emailVerification']);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/token', array_merge([
'origin' => 'http://localhost',
From 2962fbf7d791f24fd4ac66b8f3a93ffc2bcfd4d9 Mon Sep 17 00:00:00 2001
From: shimon
Date: Tue, 7 May 2024 20:12:58 +0300
Subject: [PATCH 17/28] adding new usage metric
---
CONTRIBUTING.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2cc4a7cd6a..438d0da2c3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -360,7 +360,7 @@ const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
**API**
-In the database listener, add to an existing or create a new switch case. Add a call to the usage worker with your new metric const like so:
+On file app/controllers/shared/api.php in database listener, add to an existing or create a new switch case. Add a call to the usage worker with your new metric const like so:
```php
case $document->getCollection() === 'teams':
From b8a2de84daebb9fe2cf94856abc0658550238743 Mon Sep 17 00:00:00 2001
From: Shimon Newman
Date: Tue, 7 May 2024 20:57:42 +0300
Subject: [PATCH 18/28] Update CONTRIBUTING.md
Co-authored-by: Christy Jacob
---
CONTRIBUTING.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 438d0da2c3..b4b1f32e64 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -336,7 +336,7 @@ These are the current metrics we collect usage stats for:
| network.inbound | Sum of network inbound traffic per project |
| network.outbound | Sum of network outbound traffic per project |
-* The curly brackets in the metric name act as placeholders and will be replaced with a value.
+> Note: The curly brackets in the metric name represents a template and is replaced with a value when the metric is processed.
Metrics are collected into 3 scopes: Daily, monthly, and infinity. Adding new usage metrics to aggregate usage stats is simple but depends on whether you want to collect the statistics via API or background worker. Here are the steps needed for both cases:
From d87df007075e6774e710792d49c0211230f563fb Mon Sep 17 00:00:00 2001
From: Shimon Newman
Date: Tue, 7 May 2024 20:57:54 +0300
Subject: [PATCH 19/28] Update CONTRIBUTING.md
Co-authored-by: Christy Jacob
---
CONTRIBUTING.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b4b1f32e64..f2fb6b0683 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -342,7 +342,6 @@ Metrics are collected into 3 scopes: Daily, monthly, and infinity. Adding new us
For both cases, add a const variable in `app/init.php` under the usage metrics list:
-* The curly brackets in the metric name acts as a placeholder and will be replaced with a value.
metrics are collected to 3 scopes :
Daily, monthly, an infinity.
From 7430cb48d052070efe249f678b84a90d7fe12938 Mon Sep 17 00:00:00 2001
From: Shimon Newman
Date: Tue, 7 May 2024 20:58:04 +0300
Subject: [PATCH 20/28] Update CONTRIBUTING.md
Co-authored-by: Christy Jacob
---
CONTRIBUTING.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f2fb6b0683..84fb340463 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -356,6 +356,7 @@ const METRIC_FUNCTIONS = 'functions';
const METRIC_DEPLOYMENTS = 'deployments';
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
```
+Next follow the appropriate steps below depending on whether you're adding the metric to the API or the worker.
**API**
From ad5c526ca0853e9c872494cfe8556bf4b7a96364 Mon Sep 17 00:00:00 2001
From: Shimon Newman
Date: Tue, 7 May 2024 20:58:11 +0300
Subject: [PATCH 21/28] Update CONTRIBUTING.md
Co-authored-by: Christy Jacob
---
CONTRIBUTING.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 84fb340463..dee13c4049 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -360,7 +360,7 @@ Next follow the appropriate steps below depending on whether you're adding the m
**API**
-On file app/controllers/shared/api.php in database listener, add to an existing or create a new switch case. Add a call to the usage worker with your new metric const like so:
+On file `app/controllers/shared/api.php` in database listener, add to an existing or create a new switch case. Add a call to the usage worker with your new metric const like so:
```php
case $document->getCollection() === 'teams':
From ddda24e34d03620b86c389890fe64abc3d5106c4 Mon Sep 17 00:00:00 2001
From: Shimon Newman
Date: Tue, 7 May 2024 20:59:12 +0300
Subject: [PATCH 22/28] Update CONTRIBUTING.md
Co-authored-by: Christy Jacob
---
CONTRIBUTING.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index dee13c4049..c895a6c666 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -349,7 +349,6 @@ Adding new usage metrics in order to aggregate usage stats is very simple but v
the statistics(API or via background worker).
Here are the steps needs to be taken in both cases:
-For both cases you need to add a const variable in app/init.php under the usage metrics list.
```php
// Usage metrics
const METRIC_FUNCTIONS = 'functions';
From 243bdeb29c9c6f57e60840e5d48d06d73b13b972 Mon Sep 17 00:00:00 2001
From: Shimon Newman
Date: Tue, 7 May 2024 20:59:19 +0300
Subject: [PATCH 23/28] Update CONTRIBUTING.md
Co-authored-by: Christy Jacob
---
CONTRIBUTING.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c895a6c666..bb828b1583 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -367,7 +367,7 @@ On file `app/controllers/shared/api.php` in database listener, add to an exist
->addMetric(METRIC_TEAMS, $value); // per project
break;
```
-There are cases when you need to handle metric the is a parent entity, like buckets.
+There are cases when you need to handle metric that has a parent entity, like buckets.
Files are linked to a parent bucket, you should verify you remove the files stats when you delete a bucket.
In that case you need also to handle children removal using addReduce() method call.
From 508691a9a6ab1e3669266a190fd7d13325151e8d Mon Sep 17 00:00:00 2001
From: Shimon Newman
Date: Tue, 7 May 2024 20:59:31 +0300
Subject: [PATCH 24/28] Update CONTRIBUTING.md
Co-authored-by: Christy Jacob
---
CONTRIBUTING.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bb828b1583..f764cc49c5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -382,7 +382,7 @@ In that case you need also to handle children removal using addReduce() method c
break;
```
-On top of that also adding logic on the usage worker located in /src/Appwrite/Platform/Workers/Usage.php, on the reduce method.
+In addition, you also need to add some logic to the `reduce()` method of the Usage worker located in `/src/Appwrite/Platform/Workers/Usage.php`.
```php
private function reduce(Document $project, Document $document, array &$metrics, callable $getProjectDB): void
```
From 8559753978bdb720a1eb250bd8f7888961cf4c86 Mon Sep 17 00:00:00 2001
From: shimon
Date: Tue, 7 May 2024 21:16:18 +0300
Subject: [PATCH 25/28] adding new usage metric
---
CONTRIBUTING.md | 100 ++++++++++++++++++++++++++----------------------
1 file changed, 54 insertions(+), 46 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f764cc49c5..7a642bc2db 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -306,48 +306,42 @@ This is also important for the Appwrite lead developers to be able to provide te
These are the current metrics we collect usage stats for:
| Metric | Description |
-|--------|---------------------------------------------------|
-| teams | Total number of teams per project |
-| users | Total number of users per project |
+|--------|-------------------------------------------------|
+| teams | Total number of teams per project |
+| users | Total number of users per project|
| executions | Total number of executions per project |
| databases | Total number of databases per project |
-| collections | Total number of collections per project |
-| {databaseInternalId}.collections | Total number of collections per database |
+| collections | Total number of collections per project |
+| {databaseInternalId}.collections | Total number of collections per database|
| documents | Total number of documents per project |
-| {databaseInternalId}.{collectionInternalId}.documents | Total number of documents per collection |
+| {databaseInternalId}.{collectionInternalId}.documents | Total number of documents per collection |
| buckets | Total number of buckets per project |
-| files | Total number of files per project |
-| files.storage | Sum of files storage per project (in bytes) |
-| {bucketInternalId}.files.storage | Sum of files.storage per bucket |
+| files | Total number of files per project |
+| {bucketInternalId}.files.storage | Sum of files.storage per bucket (in bytes) |
| functions | Total number of functions per project |
| deployments | Total number of deployments per project |
-| deployments.storage | Sum of deployments storage per project (in bytes) |
| builds | Total number of builds per project |
+| {resourceType}.{resourceInternalId}.deployments | Total number of deployments per function |
+| executions | Total number of executions per project |
+| {functionInternalId}.executions | Total number of executions per function |
+| files.storage | Sum of files storage per project (in bytes) |
+| deployments.storage | Sum of deployments storage per project (in bytes) |
+| {resourceType}.{resourceInternalId}.deployments.storage | Sum of deployments storage per function (in bytes) |
| builds.storage | Sum of builds storage per project (in bytes) |
| builds.compute | Sum of compute duration per project (in seconds) |
-| {functionInternalId}.builds.storage | Sum of builds storage per function |
+| {functionInternalId}.builds.storage | Sum of builds storage per function (in bytes) |
| {functionInternalId}.builds.compute | Sum of compute duration per function (in seconds) |
-| {resourceType}.{resourceInternalId}.deployments | Total number of deployments per function |
-| {resourceType}.{resourceInternalId}.deployments.storage | Sum of deployments storage per function |
-| executions | Total number of executions per project |
-| executions.compute | Sum of compute duration per project (in seconds) |
-| {functionInternalId}.executions | Total number of executions per function |
| network.requests | Total number of network requests per project |
+| executions.compute | Sum of compute duration per project (in seconds) |
| network.inbound | Sum of network inbound traffic per project |
| network.outbound | Sum of network outbound traffic per project |
> Note: The curly brackets in the metric name represents a template and is replaced with a value when the metric is processed.
-Metrics are collected into 3 scopes: Daily, monthly, and infinity. Adding new usage metrics to aggregate usage stats is simple but depends on whether you want to collect the statistics via API or background worker. Here are the steps needed for both cases:
-
-For both cases, add a const variable in `app/init.php` under the usage metrics list:
-
-
-metrics are collected to 3 scopes :
-Daily, monthly, an infinity.
-Adding new usage metrics in order to aggregate usage stats is very simple but very much depends on where do you want to collect
-the statistics(API or via background worker).
-Here are the steps needs to be taken in both cases:
+Metrics are collected within 3 scopes Daily, monthly, an infinity.
+Adding new usage metric in order to aggregate usage stats is very simple, but very much dependent on where do you want to collect
+statistics ,via API or via background worker.
+For both cases you will need to add a const variable in `app/init.php` under the usage metrics list.
```php
// Usage metrics
@@ -355,6 +349,7 @@ const METRIC_FUNCTIONS = 'functions';
const METRIC_DEPLOYMENTS = 'deployments';
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
```
+
Next follow the appropriate steps below depending on whether you're adding the metric to the API or the worker.
**API**
@@ -372,21 +367,33 @@ Files are linked to a parent bucket, you should verify you remove the files stat
In that case you need also to handle children removal using addReduce() method call.
```php
- case $document->getCollection() === 'functions':
- $queueForUsage
- ->addMetric(METRIC_FUNCTIONS, $value);
- if ($event === Database::EVENT_DOCUMENT_DELETE) {
- $queueForUsage
- ->addReduce($document);
- }
- break;
+ case $document->getCollection() === 'buckets':
+ $files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES)));
+ $storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE)));
+
+ if (!empty($files['value'])) {
+ $metrics[] = [
+ 'key' => METRIC_FILES,
+ 'value' => ($files['value'] * -1),
+ ];
+ }
+
+ if (!empty($storage['value'])) {
+ $metrics[] = [
+ 'key' => METRIC_FILES_STORAGE,
+ 'value' => ($storage['value'] * -1),
+ ];
+ }
+ break;
```
In addition, you also need to add some logic to the `reduce()` method of the Usage worker located in `/src/Appwrite/Platform/Workers/Usage.php`.
```php
-private function reduce(Document $project, Document $document, array &$metrics, callable $getProjectDB): void
-```
+private function reduce(Document $project, Document $document, array &$metrics, callable $getProjectDB): void
+{
+}
+```
**Background worker**
@@ -397,16 +404,17 @@ On that case you need to inject the usage queue to the desired worker
*/
public function __construct()
{
-$this
-->desc('Functions worker')
-->groups(['functions'])
-->inject('message')
-->inject('dbForProject')
-->inject('queueForFunctions')
-->inject('queueForEvents')
-->inject('queueForUsage')
-->inject('log')
-->callback(fn (Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage, $log));
+ $this
+ ->desc('Functions worker')
+ ->groups(['functions'])
+ ->inject('message')
+ ->inject('dbForProject')
+ ->inject('queueForFunctions')
+ ->inject('queueForEvents')
+ ->inject('queueForUsage')
+ ->inject('log')
+ ->callback(fn (Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage, $log));
+}
```
and then trigger the queue with the new metric like so:
From b0912889c88870114c557011e62642d97005881b Mon Sep 17 00:00:00 2001
From: shimon
Date: Tue, 7 May 2024 21:22:17 +0300
Subject: [PATCH 26/28] adding new usage metric
---
CONTRIBUTING.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7a642bc2db..118f5a83d5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -333,8 +333,8 @@ These are the current metrics we collect usage stats for:
| {functionInternalId}.builds.compute | Sum of compute duration per function (in seconds) |
| network.requests | Total number of network requests per project |
| executions.compute | Sum of compute duration per project (in seconds) |
-| network.inbound | Sum of network inbound traffic per project |
-| network.outbound | Sum of network outbound traffic per project |
+| network.inbound | Sum of network inbound traffic per project (in bytes)|
+| network.outbound | Sum of network outbound traffic per project (in bytes)|
> Note: The curly brackets in the metric name represents a template and is replaced with a value when the metric is processed.
@@ -354,7 +354,7 @@ Next follow the appropriate steps below depending on whether you're adding the m
**API**
-On file `app/controllers/shared/api.php` in database listener, add to an existing or create a new switch case. Add a call to the usage worker with your new metric const like so:
+In file `app/controllers/shared/api.php` On the database listener, add to an existing or create a new switch case. Add a call to the usage worker with your new metric const like so:
```php
case $document->getCollection() === 'teams':
@@ -397,7 +397,7 @@ private function reduce(Document $project, Document $document, array &$metrics,
**Background worker**
-On that case you need to inject the usage queue to the desired worker
+You need to inject the usage queue in the desired worker on the constructor method
```php
/**
* @throws Exception
From 415b70585a3a458dc935306b0613da3c34ff18e3 Mon Sep 17 00:00:00 2001
From: shimon
Date: Wed, 8 May 2024 10:01:24 +0300
Subject: [PATCH 27/28] adding new usage metric
---
CONTRIBUTING.md | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 118f5a83d5..709d01ac52 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -366,8 +366,24 @@ There are cases when you need to handle metric that has a parent entity, like bu
Files are linked to a parent bucket, you should verify you remove the files stats when you delete a bucket.
In that case you need also to handle children removal using addReduce() method call.
+
```php
- case $document->getCollection() === 'buckets':
+
+ case $document->getCollection() === 'buckets': //buckets
+ $queueForUsage
+ ->addMetric(METRIC_BUCKETS, $value); // per project
+ if ($event === Database::EVENT_DOCUMENT_DELETE) {
+ $queueForUsage
+ ->addReduce($document);
+ }
+ break;
+
+```
+
+In addition, you will also need to add some logic to the `reduce()` method of the Usage worker located in `/src/Appwrite/Platform/Workers/Usage.php`, like so:
+
+```php
+case $document->getCollection() === 'buckets':
$files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES)));
$storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE)));
@@ -387,14 +403,6 @@ In that case you need also to handle children removal using addReduce() method c
break;
```
-In addition, you also need to add some logic to the `reduce()` method of the Usage worker located in `/src/Appwrite/Platform/Workers/Usage.php`.
-```php
-private function reduce(Document $project, Document $document, array &$metrics, callable $getProjectDB): void
-{
-
-}
-```
-
**Background worker**
You need to inject the usage queue in the desired worker on the constructor method
From 499236833da5d5ec03a7a120ea2ace09fd14aae6 Mon Sep 17 00:00:00 2001
From: shimon
Date: Wed, 8 May 2024 19:47:55 +0300
Subject: [PATCH 28/28] getCountryCode
---
CONTRIBUTING.md | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 709d01ac52..8326164c48 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -338,10 +338,8 @@ These are the current metrics we collect usage stats for:
> Note: The curly brackets in the metric name represents a template and is replaced with a value when the metric is processed.
-Metrics are collected within 3 scopes Daily, monthly, an infinity.
-Adding new usage metric in order to aggregate usage stats is very simple, but very much dependent on where do you want to collect
-statistics ,via API or via background worker.
-For both cases you will need to add a const variable in `app/init.php` under the usage metrics list.
+Metrics are collected within 3 scopes Daily, monthly, an infinity. Adding new usage metric in order to aggregate usage stats is very simple, but very much dependent on where do you want to collect
+statistics ,via API or via background worker. For both cases you will need to add a `const` variable in `app/init.php` under the usage metrics list using the naming convention `METRIC_` as shown below.
```php
// Usage metrics