diff --git a/.env b/.env
index 66899e15cb..6fb438ee63 100644
--- a/.env
+++ b/.env
@@ -9,7 +9,8 @@ _APP_CONSOLE_COUNTRIES_DENYLIST=AQ
_APP_CONSOLE_HOSTNAMES=localhost,appwrite.io,*.appwrite.io
_APP_SYSTEM_EMAIL_NAME=Appwrite
_APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io
-_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io
+_APP_EMAIL_SECURITY=security@appwrite.io
+_APP_EMAIL_CERTIFICATES=certificates@appwrite.io
_APP_SYSTEM_RESPONSE_FORMAT=
_APP_OPTIONS_ABUSE=disabled
_APP_OPTIONS_ROUTER_PROTECTION=disabled
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 2cc4c700f7..7bc39392ef 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -145,3 +145,6 @@ jobs:
- name: Run ${{matrix.service}} Tests
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
+
+ - name: Run ${{matrix.service}} Shared Tables Tests
+ run: _APP_DATABASE_SHARED_TABLES=database_db_main docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
diff --git a/.gitmodules b/.gitmodules
index ffb7623bd7..653e6acbc0 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,4 +1,4 @@
[submodule "app/console"]
path = app/console
url = https://github.com/appwrite/console
- branch = 4.3.5
+ branch = 4.3.14
diff --git a/app/cli.php b/app/cli.php
index da7d23c18d..87bb711daa 100644
--- a/app/cli.php
+++ b/app/cli.php
@@ -109,7 +109,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
if (isset($databases[$dsn->getHost()])) {
$database = $databases[$dsn->getHost()];
- if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
+ if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
@@ -133,7 +133,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
$databases[$dsn->getHost()] = $database;
- if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
+ if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
@@ -202,7 +202,7 @@ CLI::setResource('logError', function (Registry $register) {
}, ['register']);
$platform = new Appwrite();
-$platform->init(Service::TYPE_CLI);
+$platform->init(Service::TYPE_TASK);
$cli = $platform->getCli();
diff --git a/app/config/locale/templates/email-certificate-failed.tpl b/app/config/locale/templates/email-certificate-failed.tpl
index 18751ff412..0363fc6538 100644
--- a/app/config/locale/templates/email-certificate-failed.tpl
+++ b/app/config/locale/templates/email-certificate-failed.tpl
@@ -4,7 +4,9 @@
|
- {{error}}
+
+ {{error}}
+
|
diff --git a/app/config/locale/translations/af.json b/app/config/locale/translations/af.json
index f8c8123979..238c6777bd 100644
--- a/app/config/locale/translations/af.json
+++ b/app/config/locale/translations/af.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroasië",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/ar.json b/app/config/locale/translations/ar.json
index b2659a97be..cd45b32e02 100644
--- a/app/config/locale/translations/ar.json
+++ b/app/config/locale/translations/ar.json
@@ -100,6 +100,7 @@
"countries.gd": "غرينادا",
"countries.gt": "غواتيمالا",
"countries.gy": "غيانا",
+ "countries.hk": "هونغ كونغ",
"countries.hn": "هندوراس",
"countries.hr": "كرواتيا",
"countries.ht": "هايتي",
diff --git a/app/config/locale/translations/as.json b/app/config/locale/translations/as.json
index 81a3c9aaa0..aa42483dde 100644
--- a/app/config/locale/translations/as.json
+++ b/app/config/locale/translations/as.json
@@ -100,6 +100,7 @@
"countries.gd": "গ্ৰেনাডা",
"countries.gt": "গুয়াতেমালা",
"countries.gy": "গায়ানা",
+ "countries.hk": "হং কং",
"countries.hn": "হণ্ডুৰাছ",
"countries.hr": "ক্ৰোৱেছিয়া",
"countries.ht": "হাইতি",
diff --git a/app/config/locale/translations/az.json b/app/config/locale/translations/az.json
index c6a5c1301c..df1264fe8d 100644
--- a/app/config/locale/translations/az.json
+++ b/app/config/locale/translations/az.json
@@ -100,6 +100,7 @@
"countries.gd": "Qrenada",
"countries.gt": "Qvatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Honq Konq",
"countries.hn": "Honduras",
"countries.hr": "Xorvatiya",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/be.json b/app/config/locale/translations/be.json
index a5c03d0e29..a33916f623 100644
--- a/app/config/locale/translations/be.json
+++ b/app/config/locale/translations/be.json
@@ -100,6 +100,7 @@
"countries.gd": "Грэнада",
"countries.gt": "Гватэмала",
"countries.gy": "Гаяна",
+ "countries.hk": "Гонконг",
"countries.hn": "Гандурас",
"countries.hr": "Харватыя",
"countries.ht": "Гаіці",
diff --git a/app/config/locale/translations/bg.json b/app/config/locale/translations/bg.json
index a3ed69f4c6..2dad3aecbe 100644
--- a/app/config/locale/translations/bg.json
+++ b/app/config/locale/translations/bg.json
@@ -100,6 +100,7 @@
"countries.gd": "Гренада",
"countries.gt": "Гватемала",
"countries.gy": "Гвиана",
+ "countries.hk": "Хонг Конг",
"countries.hn": "Хондурас",
"countries.hr": "Хърватия",
"countries.ht": "Хаити",
diff --git a/app/config/locale/translations/bh.json b/app/config/locale/translations/bh.json
index eb6de0f12d..347f6f5d31 100644
--- a/app/config/locale/translations/bh.json
+++ b/app/config/locale/translations/bh.json
@@ -100,6 +100,7 @@
"countries.gd": "ग्रेनाडा",
"countries.gt": "ग्वाटेमाला",
"countries.gy": "गयाना",
+ "countries.hk": "हांगकांग",
"countries.hn": "होंडुरस",
"countries.hr": "क्रोएशिया",
"countries.ht": "हैती",
diff --git a/app/config/locale/translations/bn.json b/app/config/locale/translations/bn.json
index 3e67c466d0..897faea7c1 100644
--- a/app/config/locale/translations/bn.json
+++ b/app/config/locale/translations/bn.json
@@ -100,6 +100,7 @@
"countries.gd": "গ্রেনাডা",
"countries.gt": "গুয়াতেমালা",
"countries.gy": "গায়ানা",
+ "countries.hk": "হংকং",
"countries.hn": "হন্ডুরাস",
"countries.hr": "ক্রোয়েশিয়া",
"countries.ht": "হাইতি",
diff --git a/app/config/locale/translations/bs.json b/app/config/locale/translations/bs.json
index bdf6fe5be4..1ce2d57a3e 100644
--- a/app/config/locale/translations/bs.json
+++ b/app/config/locale/translations/bs.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Gvajana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Hrvatska",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/ca.json b/app/config/locale/translations/ca.json
index 34c4b949cb..94e3ae24c5 100644
--- a/app/config/locale/translations/ca.json
+++ b/app/config/locale/translations/ca.json
@@ -100,6 +100,7 @@
"countries.gd": "Granada",
"countries.gt": "Guatemala",
"countries.gy": "Guaiana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Hondures",
"countries.hr": "Croàcia",
"countries.ht": "Haití",
diff --git a/app/config/locale/translations/cs.json b/app/config/locale/translations/cs.json
index 4156059080..4e3c533c77 100644
--- a/app/config/locale/translations/cs.json
+++ b/app/config/locale/translations/cs.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Chorvatsko",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/da.json b/app/config/locale/translations/da.json
index b5a2bcaa9d..d50d9c46c6 100644
--- a/app/config/locale/translations/da.json
+++ b/app/config/locale/translations/da.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatien",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/de.json b/app/config/locale/translations/de.json
index 61f4ad4d42..2279fcf4c0 100644
--- a/app/config/locale/translations/de.json
+++ b/app/config/locale/translations/de.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatien",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/el.json b/app/config/locale/translations/el.json
index de64b5738a..16c165ea39 100644
--- a/app/config/locale/translations/el.json
+++ b/app/config/locale/translations/el.json
@@ -100,6 +100,7 @@
"countries.gd": "Γρενάδα",
"countries.gt": "Γουατεμάλα",
"countries.gy": "Γουιάνα",
+ "countries.hk": "Χονγκ Κονγκ",
"countries.hn": "Ονδούρα",
"countries.hr": "Κροατία",
"countries.ht": "Αϊτή",
diff --git a/app/config/locale/translations/en.json b/app/config/locale/translations/en.json
index f93dfcc0fd..8c908ae5ab 100644
--- a/app/config/locale/translations/en.json
+++ b/app/config/locale/translations/en.json
@@ -43,6 +43,12 @@
"emails.invitation.footer": "If you are not interested, you can ignore this message.",
"emails.invitation.thanks": "Thanks",
"emails.invitation.signature": "{{project}} team",
+ "emails.certificate.subject": "Certificate failure for %s",
+ "emails.certificate.hello": "Hello",
+ "emails.certificate.body": "Certificate for your domain '{{domain}}' could not be generated. This is attempt no. {{attempt}}, and the failure was caused by: {{error}}",
+ "emails.certificate.footer": "Your previous certificate will be valid for 30 days since the first failure. We highly recommend investigating this case, otherwise your domain will end up without a valid SSL communication.",
+ "emails.certificate.thanks": "Thanks",
+ "emails.certificate.signature": "{{project}} team",
"sms.verification.body": "{{secret}}",
"locale.country.unknown": "Unknown",
"countries.af": "Afghanistan",
@@ -117,6 +123,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/eo.json b/app/config/locale/translations/eo.json
index ac54562128..406bd4f52c 100644
--- a/app/config/locale/translations/eo.json
+++ b/app/config/locale/translations/eo.json
@@ -99,6 +99,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/es.json b/app/config/locale/translations/es.json
index 69abecfc52..0e02f816fe 100644
--- a/app/config/locale/translations/es.json
+++ b/app/config/locale/translations/es.json
@@ -100,6 +100,7 @@
"countries.gd": "Granada",
"countries.gt": "Guatemala",
"countries.gy": "Guayana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croacia",
"countries.ht": "Haití",
diff --git a/app/config/locale/translations/fa.json b/app/config/locale/translations/fa.json
index 8a82b51e8c..9620b2c3f0 100644
--- a/app/config/locale/translations/fa.json
+++ b/app/config/locale/translations/fa.json
@@ -100,6 +100,7 @@
"countries.gd": "گرنادا",
"countries.gt": "گواتمالا",
"countries.gy": "گویان",
+ "countries.hk": "هنگ کنگ",
"countries.hn": "هندوراس",
"countries.hr": "کرواسی",
"countries.ht": "هائیتی",
diff --git a/app/config/locale/translations/fi.json b/app/config/locale/translations/fi.json
index c7bea9a077..d86810d925 100644
--- a/app/config/locale/translations/fi.json
+++ b/app/config/locale/translations/fi.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/fo.json b/app/config/locale/translations/fo.json
index 2c6110f7f0..3853dbab9f 100644
--- a/app/config/locale/translations/fo.json
+++ b/app/config/locale/translations/fo.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Gujana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/fr.json b/app/config/locale/translations/fr.json
index 8ee459c9a9..d73f4e7c81 100644
--- a/app/config/locale/translations/fr.json
+++ b/app/config/locale/translations/fr.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenade",
"countries.gt": "Guatemala",
"countries.gy": "Guyane",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatie",
"countries.ht": "Haïti",
diff --git a/app/config/locale/translations/ga.json b/app/config/locale/translations/ga.json
index 8217dbe042..b7a641ac01 100644
--- a/app/config/locale/translations/ga.json
+++ b/app/config/locale/translations/ga.json
@@ -100,6 +100,7 @@
"countries.gd": "Greanáda",
"countries.gt": "Guatamala",
"countries.gy": "An Ghuáin",
+ "countries.hk": "Hong Kong",
"countries.hn": "Hondúras",
"countries.hr": "An Chróit",
"countries.ht": "Háítí",
diff --git a/app/config/locale/translations/gu.json b/app/config/locale/translations/gu.json
index dc526cda61..8c41c76c54 100644
--- a/app/config/locale/translations/gu.json
+++ b/app/config/locale/translations/gu.json
@@ -100,6 +100,7 @@
"countries.gd": "ગ્રેનાડા",
"countries.gt": "ગ્વાટેમાલા",
"countries.gy": "ગુયાના",
+ "countries.hk": "હોંગ કોંગ",
"countries.hn": "હોન્ડુરાસ",
"countries.hr": "ક્રોએશિયા",
"countries.ht": "હૈતી",
diff --git a/app/config/locale/translations/he.json b/app/config/locale/translations/he.json
index 96e8c32cc7..81705d3492 100644
--- a/app/config/locale/translations/he.json
+++ b/app/config/locale/translations/he.json
@@ -100,6 +100,7 @@
"countries.gd": "גרנדה",
"countries.gt": "גואטמלה",
"countries.gy": "גיאנה",
+ "countries.hk": "הונג קונג",
"countries.hn": "הונדורס",
"countries.hr": "קרואטיה",
"countries.ht": "האיטי",
diff --git a/app/config/locale/translations/hi.json b/app/config/locale/translations/hi.json
index f3f1adf579..d764205ad6 100644
--- a/app/config/locale/translations/hi.json
+++ b/app/config/locale/translations/hi.json
@@ -100,6 +100,7 @@
"countries.gd": "ग्रेनाडा",
"countries.gt": "ग्वाटेमाला",
"countries.gy": "गयाना",
+ "countries.hk": "हांग कांग",
"countries.hn": "होंडुरस",
"countries.hr": "क्रोएशिया",
"countries.ht": "हैती",
diff --git a/app/config/locale/translations/hr.json b/app/config/locale/translations/hr.json
index 12d7fe846c..8e537f12ec 100644
--- a/app/config/locale/translations/hr.json
+++ b/app/config/locale/translations/hr.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Gvatemala",
"countries.gy": "Gvajana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Hrvatska",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/hu.json b/app/config/locale/translations/hu.json
index d266c3ae1f..fded03aee1 100644
--- a/app/config/locale/translations/hu.json
+++ b/app/config/locale/translations/hu.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Horvátország",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/hy.json b/app/config/locale/translations/hy.json
index f951686961..2c4e72ebe3 100644
--- a/app/config/locale/translations/hy.json
+++ b/app/config/locale/translations/hy.json
@@ -100,6 +100,7 @@
"countries.gd": "Գրենադա",
"countries.gt": "Գվատեմալա",
"countries.gy": "Գայանա",
+ "countries.hk": "Հոնգ Կոնգ",
"countries.hn": "Գոնդուրաս",
"countries.hr": "Խորվաթիա",
"countries.ht": "Հաիթի",
diff --git a/app/config/locale/translations/id.json b/app/config/locale/translations/id.json
index fae0246ca0..90b096e39e 100644
--- a/app/config/locale/translations/id.json
+++ b/app/config/locale/translations/id.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroasia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/is.json b/app/config/locale/translations/is.json
index 5b01dec76b..039d6849b7 100644
--- a/app/config/locale/translations/is.json
+++ b/app/config/locale/translations/is.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Gvatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Hondúras",
"countries.hr": "Króatía",
"countries.ht": "Haítí",
diff --git a/app/config/locale/translations/it.json b/app/config/locale/translations/it.json
index 5adf410708..d0716dd1e2 100644
--- a/app/config/locale/translations/it.json
+++ b/app/config/locale/translations/it.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croazia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/ja.json b/app/config/locale/translations/ja.json
index 124f014bd6..6482488cf5 100644
--- a/app/config/locale/translations/ja.json
+++ b/app/config/locale/translations/ja.json
@@ -100,6 +100,7 @@
"countries.gd": "グレナダ",
"countries.gt": "グアテマラ",
"countries.gy": "ガイアナ",
+ "countries.hk": "香港",
"countries.hn": "ホンジュラス",
"countries.hr": "クロアチア",
"countries.ht": "ハイチ",
diff --git a/app/config/locale/translations/jv.json b/app/config/locale/translations/jv.json
index 9e21d766f6..7f91a8dcef 100644
--- a/app/config/locale/translations/jv.json
+++ b/app/config/locale/translations/jv.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/km.json b/app/config/locale/translations/km.json
index 9088c7328a..72c362a77c 100644
--- a/app/config/locale/translations/km.json
+++ b/app/config/locale/translations/km.json
@@ -100,6 +100,7 @@
"countries.gd": "ហ្គ្រេណាដា",
"countries.gt": "ហ្គាតេម៉ាឡា",
"countries.gy": "ហ្គីយ៉ាណា",
+ "countries.hk": "ហុងកុង",
"countries.hn": "ហុងឌូរ៉ាស",
"countries.hr": "ក្រូអាត",
"countries.ht": "ហៃទី",
diff --git a/app/config/locale/translations/kn.json b/app/config/locale/translations/kn.json
index 2b1424aba7..429958fc1a 100644
--- a/app/config/locale/translations/kn.json
+++ b/app/config/locale/translations/kn.json
@@ -100,6 +100,7 @@
"countries.gd": "ಗ್ರೆನಡಾ",
"countries.gt": "ಗ್ವಾಟೆಮಾಲಾ",
"countries.gy": "ಗಯಾನಾ",
+ "countries.hk": "ಹಾಂಗ್ ಕಾಂಗ್",
"countries.hn": "ಹೊಂಡುರಾಸ್",
"countries.hr": "ಕ್ರೊಯೇಷಿಯಾ",
"countries.ht": "ಹೈಟಿ",
diff --git a/app/config/locale/translations/ko.json b/app/config/locale/translations/ko.json
index efca39eb72..372b7f1664 100644
--- a/app/config/locale/translations/ko.json
+++ b/app/config/locale/translations/ko.json
@@ -100,6 +100,7 @@
"countries.gd": "그레나다",
"countries.gt": "과테말라",
"countries.gy": "기아나",
+ "countries.hk": "홍콩",
"countries.hn": "온두라스",
"countries.hr": "크로아티아",
"countries.ht": "아이티",
diff --git a/app/config/locale/translations/la.json b/app/config/locale/translations/la.json
index 2fcfb92b49..9ccbaba86e 100644
--- a/app/config/locale/translations/la.json
+++ b/app/config/locale/translations/la.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",
@@ -250,4 +251,4 @@
"emails.certificate.thanks": "Gratias",
"emails.certificate.signature": "team {{project}}",
"sms.verification.body": "{{secret}}"
-}
\ No newline at end of file
+}
diff --git a/app/config/locale/translations/lt.json b/app/config/locale/translations/lt.json
index d026ee50e1..f72250c98e 100644
--- a/app/config/locale/translations/lt.json
+++ b/app/config/locale/translations/lt.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Gvatemala",
"countries.gy": "Gajana",
+ "countries.hk": "Honkongas",
"countries.hn": "Hondūras",
"countries.hr": "Kroatija",
"countries.ht": "Haitis",
diff --git a/app/config/locale/translations/lv.json b/app/config/locale/translations/lv.json
index d36e41f5b7..66604a0e3e 100644
--- a/app/config/locale/translations/lv.json
+++ b/app/config/locale/translations/lv.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Gvatemala",
"countries.gy": "Gajāna",
+ "countries.hk": "Honkonga",
"countries.hn": "Hondurasa",
"countries.hr": "Horvātija",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/ml.json b/app/config/locale/translations/ml.json
index 8535aa9bb3..60e84e2a92 100644
--- a/app/config/locale/translations/ml.json
+++ b/app/config/locale/translations/ml.json
@@ -100,6 +100,7 @@
"countries.gd": "ഗ്രെനാഡ",
"countries.gt": "ഗ്വാട്ടിമാല",
"countries.gy": "ഗയാന",
+ "countries.hk": "ഹോങ്കോങ്",
"countries.hn": "ഹോണ്ടുറാസ്",
"countries.hr": "ക്രോയേഷ്യ",
"countries.ht": "ഹെയ്തി",
diff --git a/app/config/locale/translations/mr.json b/app/config/locale/translations/mr.json
index d504755c70..4edd6bf617 100644
--- a/app/config/locale/translations/mr.json
+++ b/app/config/locale/translations/mr.json
@@ -100,6 +100,7 @@
"countries.gd": "ग्रेनाडा",
"countries.gt": "ग्वाटेमाला",
"countries.gy": "गुयाना",
+ "countries.hk": "हाँगकांग",
"countries.hn": "होंडुरास",
"countries.hr": "क्रोएशिया",
"countries.ht": "हैती",
diff --git a/app/config/locale/translations/ms.json b/app/config/locale/translations/ms.json
index 1453a261a3..ded7587d34 100644
--- a/app/config/locale/translations/ms.json
+++ b/app/config/locale/translations/ms.json
@@ -100,6 +100,7 @@
"countries.gd": "Granada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/nb.json b/app/config/locale/translations/nb.json
index 3419d7842a..f8680e8993 100644
--- a/app/config/locale/translations/nb.json
+++ b/app/config/locale/translations/nb.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/ne.json b/app/config/locale/translations/ne.json
index 99888c64d4..f31609eafc 100644
--- a/app/config/locale/translations/ne.json
+++ b/app/config/locale/translations/ne.json
@@ -100,6 +100,7 @@
"countries.gd": "ग्रेनेडा",
"countries.gt": "ग्वाटेमाला",
"countries.gy": "गुयाना",
+ "countries.hk": "हाँगकाँग",
"countries.hn": "होन्डुरस",
"countries.hr": "क्रोएशिया",
"countries.ht": "हैती",
diff --git a/app/config/locale/translations/nl.json b/app/config/locale/translations/nl.json
index 7ef9e776fd..55e48fa753 100644
--- a/app/config/locale/translations/nl.json
+++ b/app/config/locale/translations/nl.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hongkong",
"countries.hn": "Honduras",
"countries.hr": "Croatië",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/nn.json b/app/config/locale/translations/nn.json
index a77ac1b173..6ab037466f 100644
--- a/app/config/locale/translations/nn.json
+++ b/app/config/locale/translations/nn.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/or.json b/app/config/locale/translations/or.json
index 72c9888c4a..08b150fbb7 100644
--- a/app/config/locale/translations/or.json
+++ b/app/config/locale/translations/or.json
@@ -100,6 +100,7 @@
"countries.gd": "ଗ୍ରେନାଡା",
"countries.gt": "ଗୁଆଟେମାଲା",
"countries.gy": "ଗୁଇନ୍ଦା",
+ "countries.hk": "ହଂ କଙ୍ଗ",
"countries.hn": "ହୋଣ୍ଡୁରାସ୍",
"countries.hr": "କ୍ରୋଏସିଆ",
"countries.ht": "ହାଇତି",
diff --git a/app/config/locale/translations/pa.json b/app/config/locale/translations/pa.json
index aaa260cbaa..76efde346b 100644
--- a/app/config/locale/translations/pa.json
+++ b/app/config/locale/translations/pa.json
@@ -100,6 +100,7 @@
"countries.gd": "ਗ੍ਰੇਨਾਡਾ",
"countries.gt": "ਗੁਆਟੇਮਾਲਾ",
"countries.gy": "ਗੇਆਨਾ",
+ "countries.hk": "ਹਾਂਗ ਕਾਂਗ",
"countries.hn": "ਹੌਂਡੂਰਸ",
"countries.hr": "ਕਰੋਸ਼ੀਆ",
"countries.ht": "ਹੈਤੀ",
diff --git a/app/config/locale/translations/pl.json b/app/config/locale/translations/pl.json
index 05cad39be8..e596d2c04b 100644
--- a/app/config/locale/translations/pl.json
+++ b/app/config/locale/translations/pl.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Gwatemala",
"countries.gy": "Gujana",
+ "countries.hk": "Hongkong",
"countries.hn": "Honduras",
"countries.hr": "Chorwacja",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/pt-br.json b/app/config/locale/translations/pt-br.json
index 2094b053f0..b2c4931011 100644
--- a/app/config/locale/translations/pt-br.json
+++ b/app/config/locale/translations/pt-br.json
@@ -100,6 +100,7 @@
"countries.gd": "Granada",
"countries.gt": "Guatemala",
"countries.gy": "Guiana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croácia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/pt-pt.json b/app/config/locale/translations/pt-pt.json
index 173a7b3a3a..2dab03c9dd 100644
--- a/app/config/locale/translations/pt-pt.json
+++ b/app/config/locale/translations/pt-pt.json
@@ -100,6 +100,7 @@
"countries.gd": "Granada",
"countries.gt": "Guatemala",
"countries.gy": "Guiana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croácia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/ro.json b/app/config/locale/translations/ro.json
index e8cb94b2d6..45fb71d190 100644
--- a/app/config/locale/translations/ro.json
+++ b/app/config/locale/translations/ro.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croația",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/ru.json b/app/config/locale/translations/ru.json
index fa60ff83c6..a1d740bea2 100644
--- a/app/config/locale/translations/ru.json
+++ b/app/config/locale/translations/ru.json
@@ -100,6 +100,7 @@
"countries.gd": "Гренада",
"countries.gt": "Гватемала",
"countries.gy": "Гайана",
+ "countries.hk": "Гонконг",
"countries.hn": "Гондурас",
"countries.hr": "Хорватия",
"countries.ht": "Гаити",
diff --git a/app/config/locale/translations/sa.json b/app/config/locale/translations/sa.json
index b05ff33e6a..b82a3ed9ba 100644
--- a/app/config/locale/translations/sa.json
+++ b/app/config/locale/translations/sa.json
@@ -100,6 +100,7 @@
"countries.gd": "ग्रेनेडा",
"countries.gt": "ग्वाटेमाला",
"countries.gy": "गुयाना",
+ "countries.hk": "हांगकांग",
"countries.hn": "होंडुरस्",
"countries.hr": "क्रोएशिया",
"countries.ht": "हैती",
diff --git a/app/config/locale/translations/sd.json b/app/config/locale/translations/sd.json
index c6ba578f15..69cad52549 100644
--- a/app/config/locale/translations/sd.json
+++ b/app/config/locale/translations/sd.json
@@ -100,6 +100,7 @@
"countries.gd": "گرينڊا",
"countries.gt": "گٽيمالا",
"countries.gy": "گيانا",
+ "countries.hk": "هانگ کانگ",
"countries.hn": "هونڊرس",
"countries.hr": "ڪوريٽيا",
"countries.ht": "هيٽي",
diff --git a/app/config/locale/translations/si.json b/app/config/locale/translations/si.json
index e95f993438..f1c4b7c86b 100644
--- a/app/config/locale/translations/si.json
+++ b/app/config/locale/translations/si.json
@@ -100,6 +100,7 @@
"countries.gd": "ග්රෙනාඩා",
"countries.gt": "ග්වාතමාලාව",
"countries.gy": "ගයනා",
+ "countries.hk": "හොංකොං",
"countries.hn": "හොන්ඩුරාස්",
"countries.hr": "ක්රොඒෂියාව",
"countries.ht": "හයිටි",
diff --git a/app/config/locale/translations/sk.json b/app/config/locale/translations/sk.json
index 61405df84d..457e756c9a 100644
--- a/app/config/locale/translations/sk.json
+++ b/app/config/locale/translations/sk.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hongkong",
"countries.hn": "Honduras",
"countries.hr": "Chorvátsko",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/sl.json b/app/config/locale/translations/sl.json
index ffd7ee8902..ec7b4c1ebf 100644
--- a/app/config/locale/translations/sl.json
+++ b/app/config/locale/translations/sl.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Gvatemala",
"countries.gy": "Gvajana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Hrvaška",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/sn.json b/app/config/locale/translations/sn.json
index 62b97c4237..d8b2f2c682 100644
--- a/app/config/locale/translations/sn.json
+++ b/app/config/locale/translations/sn.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/sq.json b/app/config/locale/translations/sq.json
index 9a54e45d25..0fb066a8ea 100644
--- a/app/config/locale/translations/sq.json
+++ b/app/config/locale/translations/sq.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guajana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroacia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/sv.json b/app/config/locale/translations/sv.json
index 3ee4c1d0e3..b838c05084 100644
--- a/app/config/locale/translations/sv.json
+++ b/app/config/locale/translations/sv.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroatien",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/ta.json b/app/config/locale/translations/ta.json
index 252f91a579..659306c977 100644
--- a/app/config/locale/translations/ta.json
+++ b/app/config/locale/translations/ta.json
@@ -100,6 +100,7 @@
"countries.gd": "கிரெனடா",
"countries.gt": "குவாத்தமாலா",
"countries.gy": "கயானா",
+ "countries.hk": "ஹாங்காங்",
"countries.hn": "ஹொண்டூராஸ்",
"countries.hr": "குரோவாசியா",
"countries.ht": "ஹைத்தி",
diff --git a/app/config/locale/translations/te.json b/app/config/locale/translations/te.json
index 86ffbeca43..019b4581ca 100644
--- a/app/config/locale/translations/te.json
+++ b/app/config/locale/translations/te.json
@@ -100,6 +100,7 @@
"countries.gd": "గ్రెనడా",
"countries.gt": "గ్వాటెమాల",
"countries.gy": "గయానా",
+ "countries.hk": "హాంగ్ కొంగ",
"countries.hn": "హోండురాస్",
"countries.hr": "క్రొయేషియా",
"countries.ht": "హైతీ",
diff --git a/app/config/locale/translations/th.json b/app/config/locale/translations/th.json
index 95552f624b..97d224de1f 100644
--- a/app/config/locale/translations/th.json
+++ b/app/config/locale/translations/th.json
@@ -100,6 +100,7 @@
"countries.gd": "เกรเนดา",
"countries.gt": "กัวเตมาลา",
"countries.gy": "กายอานา",
+ "countries.hk": "ฮ่องกง",
"countries.hn": "ฮอนดูรัส",
"countries.hr": "โครเอเชีย",
"countries.ht": "ไฮติ",
diff --git a/app/config/locale/translations/tl.json b/app/config/locale/translations/tl.json
index 1da537230d..e6df9f8aef 100644
--- a/app/config/locale/translations/tl.json
+++ b/app/config/locale/translations/tl.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guwatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Kroasya",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/tr.json b/app/config/locale/translations/tr.json
index 54007fc723..808a20576c 100644
--- a/app/config/locale/translations/tr.json
+++ b/app/config/locale/translations/tr.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Hırvatistan",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/uk.json b/app/config/locale/translations/uk.json
index a7a0d7a78d..78e3a6c556 100644
--- a/app/config/locale/translations/uk.json
+++ b/app/config/locale/translations/uk.json
@@ -100,6 +100,7 @@
"countries.gd": "Гренада",
"countries.gt": "Гватемала",
"countries.gy": "Гайана",
+ "countries.hk": "Гонконг",
"countries.hn": "Гондурас",
"countries.hr": "Хорватія",
"countries.ht": "Гаїті",
diff --git a/app/config/locale/translations/ur.json b/app/config/locale/translations/ur.json
index de9a2768c3..060cea0736 100644
--- a/app/config/locale/translations/ur.json
+++ b/app/config/locale/translations/ur.json
@@ -100,6 +100,7 @@
"countries.gd": "گریناڈا",
"countries.gt": "گوئٹے مالا",
"countries.gy": "گیانا",
+ "countries.hk": "ہانگ کانگ",
"countries.hn": "ہونڈوراس",
"countries.hr": "کروشیا",
"countries.ht": "ہیٹی",
diff --git a/app/config/locale/translations/vi.json b/app/config/locale/translations/vi.json
index 400c8c09d1..cf04a5b737 100644
--- a/app/config/locale/translations/vi.json
+++ b/app/config/locale/translations/vi.json
@@ -100,6 +100,7 @@
"countries.gd": "Grenada",
"countries.gt": "Guatemala",
"countries.gy": "Guyana",
+ "countries.hk": "Hong Kong",
"countries.hn": "Honduras",
"countries.hr": "Croatia",
"countries.ht": "Haiti",
diff --git a/app/config/locale/translations/zh-cn.json b/app/config/locale/translations/zh-cn.json
index 5b634d990f..0bfbc54e0e 100644
--- a/app/config/locale/translations/zh-cn.json
+++ b/app/config/locale/translations/zh-cn.json
@@ -100,6 +100,7 @@
"countries.gd": "格林纳达",
"countries.gt": "危地马拉",
"countries.gy": "圭亚那",
+ "countries.hk": "香港",
"countries.hn": "洪都拉斯",
"countries.hr": "克罗地亚",
"countries.ht": "海地",
diff --git a/app/config/locale/translations/zh-tw.json b/app/config/locale/translations/zh-tw.json
index 61600da08e..24729de6b3 100644
--- a/app/config/locale/translations/zh-tw.json
+++ b/app/config/locale/translations/zh-tw.json
@@ -100,6 +100,7 @@
"countries.gd": "格瑞那達",
"countries.gt": "瓜地馬拉",
"countries.gy": "蓋亞那",
+ "countries.hk": "香港",
"countries.hn": "宏都拉斯",
"countries.hr": "克羅埃西亞",
"countries.ht": "海地",
diff --git a/app/config/platforms.php b/app/config/platforms.php
index d06c25a796..f0ce427c79 100644
--- a/app/config/platforms.php
+++ b/app/config/platforms.php
@@ -15,7 +15,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
- 'version' => '14.0.2',
+ 'version' => '15.0.0',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -138,7 +138,7 @@ return [
[
'key' => 'react-native',
'name' => 'React Native',
- 'version' => '0.3.2',
+ 'version' => '0.4.0',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
@@ -267,7 +267,7 @@ return [
[
'key' => 'deno',
'name' => 'Deno',
- 'version' => '10.0.2',
+ 'version' => '11.0.0',
'url' => 'https://github.com/appwrite/sdk-for-deno',
'package' => 'https://deno.land/x/appwrite',
'enabled' => true,
diff --git a/app/config/variables.php b/app/config/variables.php
index b61a267785..ff19b94a91 100644
--- a/app/config/variables.php
+++ b/app/config/variables.php
@@ -162,13 +162,31 @@ return [
],
[
'name' => '_APP_SYSTEM_SECURITY_EMAIL_ADDRESS',
- 'description' => 'This is the email address used to issue SSL certificates for custom domains or the user agent in your webhooks payload.',
+ 'description' => 'Deprecated since 1.5.1 use _APP_EMAIL_SECURITY and _APP_EMAIL_CERTIFICATES instead',
'introduction' => '0.7.0',
'default' => 'certs@appwrite.io',
'required' => false,
'question' => '',
'filter' => ''
],
+ [
+ 'name' => '_APP_EMAIL_SECURITY',
+ 'description' => 'This is the email address used as the user agent in your webhooks payload.',
+ 'introduction' => '1.5.1',
+ 'default' => '',
+ 'required' => false,
+ 'question' => '',
+ 'filter' => ''
+ ],
+ [
+ 'name' => '_APP_EMAIL_CERTIFICATES',
+ 'description' => 'This is the email address used to issue SSL certificates for custom domains',
+ 'introduction' => '1.5.1',
+ 'default' => '',
+ 'required' => true,
+ 'question' => '',
+ 'filter' => ''
+ ],
[
'name' => '_APP_USAGE_STATS',
'description' => 'This variable allows you to disable the collection and displaying of usage stats. This value is set to \'enabled\' by default, to disable the usage stats set the value to \'disabled\'. When disabled, it\'s recommended to turn off the Worker Usage container to reduce resource usage.',
@@ -450,7 +468,7 @@ return [
],
[
'name' => '_APP_SMS_FROM',
- 'description' => 'Phone number used for sending out messages. Must start with a leading \'+\' and maximum of 15 digits without spaces (+123456789).',
+ 'description' => 'Phone number used for sending out messages. If using Twilio, this may be a Messaging Service SID, starting with MG. Otherwise, the number must start with a leading \'+\' and maximum of 15 digits without spaces (+123456789). ',
'introduction' => '0.15.0',
'default' => '',
'required' => false,
diff --git a/app/console b/app/console
index 5169fe16d6..412bc33318 160000
--- a/app/console
+++ b/app/console
@@ -1 +1 @@
-Subproject commit 5169fe16d63066f64ab5013c78953aea04e24b53
+Subproject commit 412bc3331891c15ba7baf4f445e82ac3a678c6be
diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php
index e1ddeb6b68..a757d74ef5 100644
--- a/app/controllers/api/account.php
+++ b/app/controllers/api/account.php
@@ -86,8 +86,8 @@ $createSession = function (string $userId, string $secret, Request $request, Res
$factor = (match ($verifiedToken->getAttribute('type')) {
Auth::TOKEN_TYPE_MAGIC_URL,
Auth::TOKEN_TYPE_OAUTH2,
- Auth::TOKEN_TYPE_EMAIL => 'email',
- Auth::TOKEN_TYPE_PHONE => 'phone',
+ Auth::TOKEN_TYPE_EMAIL => Type::EMAIL,
+ Auth::TOKEN_TYPE_PHONE => Type::PHONE,
Auth::TOKEN_TYPE_GENERIC => 'token',
default => throw new Exception(Exception::USER_INVALID_TOKEN)
});
@@ -290,7 +290,9 @@ App::post('/v1/account')
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]);
- $user->setAttribute('targets', [...$user->getAttribute('targets', []), $existingTarget]);
+ if($existingTarget) {
+ $user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
+ }
}
$dbForProject->purgeCachedDocument('users', $user->getId());
@@ -1506,7 +1508,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
- 'factors' => ['email'],
+ 'factors' => [TYPE::EMAIL, 'oauth2'], // include a special oauth2 factor to bypass MFA checks
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
'expire' => DateTime::addSeconds(new \DateTime(), $duration)
], $detector->getOS(), $detector->getClient(), $detector->getDevice()));
@@ -2524,6 +2526,7 @@ App::patch('/v1/account/password')
->label('sdk.response.model', Response::MODEL_USER)
->label('sdk.offline.model', '/account')
->label('sdk.offline.key', 'current')
+ ->label('abuse-limit', 10)
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary'])
->param('oldPassword', '', new Password(), 'Current user password. Must be at least 8 chars.', true)
->inject('requestTimestamp')
diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php
index 337ba8fd9e..a3bd47595d 100644
--- a/app/controllers/api/avatars.php
+++ b/app/controllers/api/avatars.php
@@ -321,7 +321,7 @@ App::get('/v1/avatars/favicon')
->setUserAgent(\sprintf(
APP_USERAGENT,
System::getEnv('_APP_VERSION', 'UNKNOWN'),
- System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
+ System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY))
))
->fetch($url);
} catch (\Throwable) {
diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php
index 4b4ce8f307..f4581df8e4 100644
--- a/app/controllers/api/health.php
+++ b/app/controllers/api/health.php
@@ -852,7 +852,7 @@ App::get('/v1/health/queue/failed/:name')
Event::FUNCTIONS_QUEUE_NAME,
Event::USAGE_QUEUE_NAME,
Event::USAGE_DUMP_QUEUE_NAME,
- Event::WEBHOOK_CLASS_NAME,
+ Event::WEBHOOK_QUEUE_NAME,
Event::CERTIFICATES_QUEUE_NAME,
Event::BUILDS_QUEUE_NAME,
Event::MESSAGING_QUEUE_NAME,
diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php
index f872ef311b..91f19a2358 100644
--- a/app/controllers/api/projects.php
+++ b/app/controllers/api/projects.php
@@ -111,35 +111,8 @@ App::post('/v1/projects')
$projectId = ($projectId == 'unique()') ? ID::unique() : $projectId;
- $backups['database_db_fra1_v14x_02'] = ['from' => '03:00', 'to' => '05:00'];
- $backups['database_db_fra1_v14x_03'] = ['from' => '00:00', 'to' => '02:00'];
- $backups['database_db_fra1_v14x_04'] = ['from' => '00:00', 'to' => '02:00'];
- $backups['database_db_fra1_v14x_05'] = ['from' => '00:00', 'to' => '02:00'];
- $backups['database_db_fra1_v14x_06'] = ['from' => '00:00', 'to' => '02:00'];
- $backups['database_db_fra1_v14x_07'] = ['from' => '00:00', 'to' => '02:00'];
-
$databases = Config::getParam('pools-database', []);
- /**
- * Remove databases from the list that are currently undergoing an backup
- */
- if (count($databases) > 1) {
- $now = new \DateTime();
-
- foreach ($databases as $index => $database) {
- if (empty($backups[$database])) {
- continue;
- }
- $backup = $backups[$database];
- $from = \DateTime::createFromFormat('H:i', $backup['from']);
- $to = \DateTime::createFromFormat('H:i', $backup['to']);
- if ($now >= $from && $now <= $to) {
- unset($databases[$index]);
- break;
- }
- }
- }
-
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
$index = \array_search($databaseOverride, $databases);
if ($index !== false) {
@@ -152,37 +125,12 @@ App::post('/v1/projects')
throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project.");
}
- // TODO: 1 in 5 projects use shared tables. Temporary until all projects are using shared tables.
- if (
- (
- !\mt_rand(0, 4)
- && System::getEnv('_APP_DATABASE_SHARED_TABLES', 'enabled') === 'enabled'
- && System::getEnv('_APP_EDITION', 'self-hosted') !== 'self-hosted'
- ) ||
- (
- $dsn === DATABASE_SHARED_TABLES
- )
- ) {
+ // TODO: Temporary until all projects are using shared tables.
+ if ($dsn === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$schema = 'appwrite';
$database = 'appwrite';
$namespace = System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '');
- $dsn = $schema . '://' . DATABASE_SHARED_TABLES . '?database=' . $database;
-
- if (!empty($namespace)) {
- $dsn .= '&namespace=' . $namespace;
- }
- }
-
- // TODO: Allow overriding in development mode. Temporary until all projects are using shared tables.
- if (
- App::isDevelopment()
- && System::getEnv('_APP_EDITION', 'self-hosted') !== 'self-hosted'
- && $request->getHeader('x-appwrited-share-tables', false)
- ) {
- $schema = 'appwrite';
- $database = 'appwrite';
- $namespace = System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '');
- $dsn = $schema . '://' . DATABASE_SHARED_TABLES . '?database=' . $database;
+ $dsn = $schema . '://' . System::getEnv('_APP_DATABASE_SHARED_TABLES', '') . '?database=' . $database;
if (!empty($namespace)) {
$dsn .= '&namespace=' . $namespace;
@@ -236,7 +184,7 @@ App::post('/v1/projects')
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
$dbForProject = new Database($adapter, $cache);
- if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
+ if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$dbForProject
->setSharedTables(true)
->setTenant($project->getInternalId())
diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php
index 9215d99345..f7673ff221 100644
--- a/app/controllers/api/storage.php
+++ b/app/controllers/api/storage.php
@@ -63,7 +63,7 @@ App::post('/v1/storage/buckets')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true)
- ->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024 * 1024), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan'])
+ ->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan'])
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', Compression::NONE, new WhiteList([Compression::NONE, Compression::GZIP, Compression::ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . Compression::NONE . ', [' . Compression::GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . Compression::ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
@@ -240,7 +240,7 @@ App::put('/v1/storage/buckets/:bucketId')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true)
- ->param('maximumFileSize', null, new Range(1, (int) System::getEnv('_APP_STORAGE_LIMIT', 0)), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human((int)System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true)
+ ->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1024 * 1024), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan'])
->param('allowedFileExtensions', [], new ArrayList(new Text(64), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Allowed file extensions. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' extensions are allowed, each 64 characters long.', true)
->param('compression', Compression::NONE, new WhiteList([Compression::NONE, Compression::GZIP, Compression::ZSTD]), 'Compression algorithm choosen for compression. Can be one of ' . Compression::NONE . ', [' . Compression::GZIP . '](https://en.wikipedia.org/wiki/Gzip), or [' . Compression::ZSTD . '](https://en.wikipedia.org/wiki/Zstd), For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' compression is skipped even if it\'s enabled', true)
->param('encryption', true, new Boolean(true), 'Is encryption enabled? For file size above ' . Storage::human(APP_STORAGE_READ_BUFFER, 0) . ' encryption is skipped even if it\'s enabled', true)
@@ -351,7 +351,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->label('sdk.response.model', Response::MODEL_FILE)
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new CustomId(), 'File ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
- ->param('file', [], new File(), 'Binary file. Appwrite SDKs provide helpers to handle file input. [Learn about file input](https://appwrite.io/docs/storage#file-input).', skipValidation: true)
+ ->param('file', [], new File(), 'Binary file. Appwrite SDKs provide helpers to handle file input. [Learn about file input](https://appwrite.io/docs/products/storage/upload-download#input-file).', skipValidation: true)
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->inject('request')
->inject('response')
diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php
index d003fcfd37..5891fb67f0 100644
--- a/app/controllers/api/teams.php
+++ b/app/controllers/api/teams.php
@@ -387,7 +387,7 @@ App::post('/v1/teams/:teamId/memberships')
->param('userId', '', new UID(), 'ID of the user to be added to a team.', true)
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.')
- ->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) // TODO add our own built-in confirm page
+ ->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) // TODO add our own built-in confirm page
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
->inject('response')
->inject('project')
diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php
index abfcb34a5a..dcc214972b 100644
--- a/app/controllers/api/users.php
+++ b/app/controllers/api/users.php
@@ -138,7 +138,9 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]);
- $user->setAttribute('targets', [...$user->getAttribute('targets', []), $existingTarget]);
+ if($existingTarget) {
+ $user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
+ }
}
}
@@ -160,7 +162,9 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$phone]),
]);
- $user->setAttribute('targets', [...$user->getAttribute('targets', []), $existingTarget]);
+ if($existingTarget) {
+ $user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
+ }
}
}
@@ -1780,7 +1784,7 @@ App::post('/v1/users/:userId/sessions')
throw new Exception(Exception::USER_NOT_FOUND);
}
- $secret = Auth::codeGenerator();
+ $secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION);
$detector = new Detector($request->getUserAgent('UNKNOWN'));
$record = $geodb->get($request->getIP());
@@ -1797,6 +1801,7 @@ App::post('/v1/users/:userId/sessions')
'userAgent' => $request->getUserAgent('UNKNOWN'),
'ip' => $request->getIP(),
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
+ 'expire' => $expire,
],
$detector->getOS(),
$detector->getClient(),
@@ -1808,7 +1813,6 @@ App::post('/v1/users/:userId/sessions')
$session = $dbForProject->createDocument('sessions', $session);
$session
->setAttribute('secret', $secret)
- ->setAttribute('expire', $expire)
->setAttribute('countryName', $countryName);
$queueForEvents
diff --git a/app/controllers/general.php b/app/controllers/general.php
index e7dad32c70..ccc3c8083b 100644
--- a/app/controllers/general.php
+++ b/app/controllers/general.php
@@ -2,6 +2,7 @@
require_once __DIR__ . '/../init.php';
+use Appwrite\Auth\Auth;
use Appwrite\Event\Certificate;
use Appwrite\Event\Event;
use Appwrite\Event\Usage;
@@ -585,7 +586,7 @@ App::init()
->addHeader('Server', 'Appwrite')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
- ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-Appwrite-Shared-Tables, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent')
+ ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent')
->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies')
->addHeader('Access-Control-Allow-Origin', $refDomain)
->addHeader('Access-Control-Allow-Credentials', 'true');
@@ -636,7 +637,7 @@ App::options()
$response
->addHeader('Server', 'Appwrite')
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
- ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-Appwrite-Shared-Tables, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent')
+ ->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent')
->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies')
->addHeader('Access-Control-Allow-Origin', $origin)
->addHeader('Access-Control-Allow-Credentials', 'true')
@@ -651,7 +652,8 @@ App::error()
->inject('project')
->inject('logger')
->inject('log')
- ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log) {
+ ->inject('queueForUsage')
+ ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage) {
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$route = $utopia->getRoute();
$class = \get_class($error);
@@ -740,6 +742,26 @@ App::error()
}
}
+ if ($publish && $project->getId() !== 'console') {
+ if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
+ $fileSize = 0;
+ $file = $request->getFiles('file');
+ if (!empty($file)) {
+ $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
+ }
+
+ $queueForUsage
+ ->addMetric(METRIC_NETWORK_REQUESTS, 1)
+ ->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize)
+ ->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize());
+ }
+
+ $queueForUsage
+ ->setProject($project)
+ ->trigger();
+ }
+
+
if ($logger && $publish) {
try {
/** @var Utopia\Database\Document $user */
diff --git a/app/init.php b/app/init.php
index 6df2c02908..ea8b087faa 100644
--- a/app/init.php
+++ b/app/init.php
@@ -112,7 +112,7 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
-const APP_CACHE_BUSTER = 443;
+const APP_CACHE_BUSTER = 4314;
const APP_VERSION_STABLE = '1.5.7';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
@@ -143,9 +143,6 @@ const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
const APP_HOSTNAME_INTERNAL = 'appwrite';
-// Databases
-const DATABASE_SHARED_TABLES = 'database_db_fra1_self_hosted_16_0';
-
// Database Reconnect
const DATABASE_RECONNECT_SLEEP = 2;
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
@@ -610,9 +607,9 @@ Database::addFilter(
])
));
if (\count($targetIds) > 0) {
- return $database->find('targets', [
+ return $database->skipValidation(fn () => $database->find('targets', [
Query::equal('$internalId', $targetIds)
- ]);
+ ]));
}
return [];
}
@@ -1011,7 +1008,7 @@ foreach ($locales as $locale) {
'user_agent' => \sprintf(
APP_USERAGENT,
System::getEnv('_APP_VERSION', 'UNKNOWN'),
- System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
+ System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY))
),
'timeout' => 2,
],
@@ -1338,7 +1335,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForConsole,
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
- if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
+ if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
@@ -1391,7 +1388,7 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
- if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
+ if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
diff --git a/app/realtime.php b/app/realtime.php
index 2904b1db9c..cde4327417 100644
--- a/app/realtime.php
+++ b/app/realtime.php
@@ -92,7 +92,7 @@ if (!function_exists("getProjectDB")) {
$database = new Database($adapter, getCache());
- if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
+ if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml
index 9547cfda00..374e20eb39 100644
--- a/app/views/install/compose.phtml
+++ b/app/views/install/compose.phtml
@@ -79,7 +79,7 @@ $image = $this->getParam('image', '');
- _APP_CONSOLE_HOSTNAMES
- _APP_SYSTEM_EMAIL_NAME
- _APP_SYSTEM_EMAIL_ADDRESS
- - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
+ - _APP_EMAIL_SECURITY
- _APP_SYSTEM_RESPONSE_FORMAT
- _APP_OPTIONS_ABUSE
- _APP_OPTIONS_ROUTER_PROTECTION
@@ -249,6 +249,7 @@ $image = $this->getParam('image', '');
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
+ - _APP_EMAIL_SECURITY
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- _APP_DB_HOST
- _APP_DB_PORT
@@ -430,7 +431,7 @@ $image = $this->getParam('image', '');
- _APP_DOMAIN
- _APP_DOMAIN_TARGET
- _APP_DOMAIN_FUNCTIONS
- - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
+ - _APP_EMAIL_CERTIFICATES
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
@@ -580,7 +581,7 @@ $image = $this->getParam('image', '');
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_DOMAIN_TARGET
- - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
+ - _APP_EMAIL_SECURITY
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
diff --git a/app/worker.php b/app/worker.php
index 60358ad6b2..bca92c433f 100644
--- a/app/worker.php
+++ b/app/worker.php
@@ -93,7 +93,7 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
- if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
+ if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
@@ -126,7 +126,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForConso
if (isset($databases[$dsn->getHost()])) {
$database = $databases[$dsn->getHost()];
- if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
+ if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
@@ -150,7 +150,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForConso
$databases[$dsn->getHost()] = $database;
- if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
+ if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
@@ -284,11 +284,6 @@ if (!isset($args[1])) {
\array_shift($args);
$workerName = $args[0];
-$workerIndex = $args[1] ?? '';
-
-if (!empty($workerIndex)) {
- $workerName .= '_' . $workerIndex;
-}
if (\str_starts_with($workerName, 'databases')) {
$queueName = System::getEnv('_APP_QUEUE_NAME', 'database_db_main');
diff --git a/composer.json b/composer.json
index 205fe308f0..6ec50f2987 100644
--- a/composer.json
+++ b/composer.json
@@ -44,13 +44,13 @@
"ext-sockets": "*",
"appwrite/php-runtimes": "0.13.*",
"appwrite/php-clamav": "2.0.*",
- "utopia-php/abuse": "0.37.*",
+ "utopia-php/abuse": "0.38.*",
"utopia-php/analytics": "0.10.*",
- "utopia-php/audit": "0.39.*",
- "utopia-php/cache": "0.9.*",
+ "utopia-php/audit": "0.40.*",
+ "utopia-php/cache": "0.10.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
- "utopia-php/database": "0.49.*",
+ "utopia-php/database": "0.50.*",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
@@ -58,10 +58,10 @@
"utopia-php/image": "0.6.*",
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.5.*",
- "utopia-php/messaging": "0.11.*",
+ "utopia-php/messaging": "0.12.*",
"utopia-php/migration": "0.4.*",
"utopia-php/orchestration": "0.9.*",
- "utopia-php/platform": "0.5.*",
+ "utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.5.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/queue": "0.7.*",
diff --git a/composer.lock b/composer.lock
index 55d41222c7..acd0aafba7 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "53996479cd4ba0c73dbc72d46b240be0",
+ "content-hash": "89f2552eaa152516be5bf89628c0f86e",
"packages": [
{
"name": "adhocore/jwt",
@@ -1427,23 +1427,23 @@
},
{
"name": "utopia-php/abuse",
- "version": "0.37.1",
+ "version": "0.38.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
- "reference": "4dfcff4754c7804d1a70039792c0f2d59a5cc981"
+ "reference": "b7be9086c9d9b4561d810cbd42fdda798742f56c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/utopia-php/abuse/zipball/4dfcff4754c7804d1a70039792c0f2d59a5cc981",
- "reference": "4dfcff4754c7804d1a70039792c0f2d59a5cc981",
+ "url": "https://api.github.com/repos/utopia-php/abuse/zipball/b7be9086c9d9b4561d810cbd42fdda798742f56c",
+ "reference": "b7be9086c9d9b4561d810cbd42fdda798742f56c",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-pdo": "*",
"php": ">=8.0",
- "utopia-php/database": "0.49.*"
+ "utopia-php/database": "0.50.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
@@ -1470,9 +1470,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
- "source": "https://github.com/utopia-php/abuse/tree/0.37.1"
+ "source": "https://github.com/utopia-php/abuse/tree/0.38.0"
},
- "time": "2024-06-05T18:03:59+00:00"
+ "time": "2024-06-24T00:52:02+00:00"
},
{
"name": "utopia-php/analytics",
@@ -1522,21 +1522,21 @@
},
{
"name": "utopia-php/audit",
- "version": "0.39.1",
+ "version": "0.40.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
- "reference": "7ea91e0ceea7b94293612fea94022b73315677c2"
+ "reference": "735ae211ce5fee5b52b736731571b4030b1d7cdc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/utopia-php/audit/zipball/7ea91e0ceea7b94293612fea94022b73315677c2",
- "reference": "7ea91e0ceea7b94293612fea94022b73315677c2",
+ "url": "https://api.github.com/repos/utopia-php/audit/zipball/735ae211ce5fee5b52b736731571b4030b1d7cdc",
+ "reference": "735ae211ce5fee5b52b736731571b4030b1d7cdc",
"shasum": ""
},
"require": {
"php": ">=8.0",
- "utopia-php/database": "0.49.*"
+ "utopia-php/database": "0.50.*"
},
"require-dev": {
"laravel/pint": "1.5.*",
@@ -1563,22 +1563,22 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
- "source": "https://github.com/utopia-php/audit/tree/0.39.1"
+ "source": "https://github.com/utopia-php/audit/tree/0.40.0"
},
- "time": "2024-06-05T19:28:22+00:00"
+ "time": "2024-06-24T00:52:17+00:00"
},
{
"name": "utopia-php/cache",
- "version": "0.9.1",
+ "version": "0.10.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cache.git",
- "reference": "552b4c554bb14d0c529631ce304cdf4a2b9d06a6"
+ "reference": "b22c6eb6d308de246b023efd0fc9758aee8b8247"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/utopia-php/cache/zipball/552b4c554bb14d0c529631ce304cdf4a2b9d06a6",
- "reference": "552b4c554bb14d0c529631ce304cdf4a2b9d06a6",
+ "url": "https://api.github.com/repos/utopia-php/cache/zipball/b22c6eb6d308de246b023efd0fc9758aee8b8247",
+ "reference": "b22c6eb6d308de246b023efd0fc9758aee8b8247",
"shasum": ""
},
"require": {
@@ -1613,9 +1613,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/cache/issues",
- "source": "https://github.com/utopia-php/cache/tree/0.9.1"
+ "source": "https://github.com/utopia-php/cache/tree/0.10.2"
},
- "time": "2024-03-19T17:07:20+00:00"
+ "time": "2024-06-25T20:36:35+00:00"
},
{
"name": "utopia-php/cli",
@@ -1719,23 +1719,23 @@
},
{
"name": "utopia-php/database",
- "version": "0.49.11",
+ "version": "0.50.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
- "reference": "4f4b35d99ecdee971c3042279bb1ac8264825030"
+ "reference": "ce3eaccb2f3bbd34b2b97419836fec633b26b8f7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/utopia-php/database/zipball/4f4b35d99ecdee971c3042279bb1ac8264825030",
- "reference": "4f4b35d99ecdee971c3042279bb1ac8264825030",
+ "url": "https://api.github.com/repos/utopia-php/database/zipball/ce3eaccb2f3bbd34b2b97419836fec633b26b8f7",
+ "reference": "ce3eaccb2f3bbd34b2b97419836fec633b26b8f7",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"ext-pdo": "*",
"php": ">=8.0",
- "utopia-php/cache": "0.9.*",
+ "utopia-php/cache": "0.10.*",
"utopia-php/framework": "0.33.*",
"utopia-php/mongo": "0.3.*"
},
@@ -1769,9 +1769,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
- "source": "https://github.com/utopia-php/database/tree/0.49.11"
+ "source": "https://github.com/utopia-php/database/tree/0.50.0"
},
- "time": "2024-05-30T12:40:27+00:00"
+ "time": "2024-06-21T03:21:42+00:00"
},
{
"name": "utopia-php/domains",
@@ -2119,16 +2119,16 @@
},
{
"name": "utopia-php/messaging",
- "version": "0.11.0",
+ "version": "0.12.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/messaging.git",
- "reference": "b499c3ad11af711c28252c62d83f24e6106a2154"
+ "reference": "6e466d3511981291843c6ebf9ce3f44fc75e37b0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/utopia-php/messaging/zipball/b499c3ad11af711c28252c62d83f24e6106a2154",
- "reference": "b499c3ad11af711c28252c62d83f24e6106a2154",
+ "url": "https://api.github.com/repos/utopia-php/messaging/zipball/6e466d3511981291843c6ebf9ce3f44fc75e37b0",
+ "reference": "6e466d3511981291843c6ebf9ce3f44fc75e37b0",
"shasum": ""
},
"require": {
@@ -2164,9 +2164,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/messaging/issues",
- "source": "https://github.com/utopia-php/messaging/tree/0.11.0"
+ "source": "https://github.com/utopia-php/messaging/tree/0.12.0"
},
- "time": "2024-05-08T17:10:02+00:00"
+ "time": "2024-05-30T14:58:25+00:00"
},
{
"name": "utopia-php/migration",
@@ -2327,16 +2327,16 @@
},
{
"name": "utopia-php/platform",
- "version": "0.5.2",
+ "version": "0.7.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/platform.git",
- "reference": "b9feabc79b92dc2b05683a986ad43bce5c1583e3"
+ "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/utopia-php/platform/zipball/b9feabc79b92dc2b05683a986ad43bce5c1583e3",
- "reference": "b9feabc79b92dc2b05683a986ad43bce5c1583e3",
+ "url": "https://api.github.com/repos/utopia-php/platform/zipball/beeea0f2c9bce14a6869fc5c87a1047cdecb5c52",
+ "reference": "beeea0f2c9bce14a6869fc5c87a1047cdecb5c52",
"shasum": ""
},
"require": {
@@ -2344,7 +2344,8 @@
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/cli": "0.15.*",
- "utopia-php/framework": "0.33.*"
+ "utopia-php/framework": "0.33.*",
+ "utopia-php/queue": "0.7.*"
},
"require-dev": {
"laravel/pint": "1.2.*",
@@ -2370,9 +2371,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/platform/issues",
- "source": "https://github.com/utopia-php/platform/tree/0.5.2"
+ "source": "https://github.com/utopia-php/platform/tree/0.7.0"
},
- "time": "2024-05-22T12:50:35+00:00"
+ "time": "2024-05-08T17:00:55+00:00"
},
{
"name": "utopia-php/pools",
@@ -2755,22 +2756,22 @@
},
{
"name": "utopia-php/vcs",
- "version": "0.6.6",
+ "version": "0.6.7",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
- "reference": "e538264cfee5e3efdfe1771efba04750cf20b2c4"
+ "reference": "8d8ff1ac68e991b95adb6f91fcde8f9bb8f24974"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/utopia-php/vcs/zipball/e538264cfee5e3efdfe1771efba04750cf20b2c4",
- "reference": "e538264cfee5e3efdfe1771efba04750cf20b2c4",
+ "url": "https://api.github.com/repos/utopia-php/vcs/zipball/8d8ff1ac68e991b95adb6f91fcde8f9bb8f24974",
+ "reference": "8d8ff1ac68e991b95adb6f91fcde8f9bb8f24974",
"shasum": ""
},
"require": {
"adhocore/jwt": "^1.1",
"php": ">=8.0",
- "utopia-php/cache": "^0.9.0",
+ "utopia-php/cache": "^0.10.0",
"utopia-php/framework": "0.*.*"
},
"require-dev": {
@@ -2798,9 +2799,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/vcs/issues",
- "source": "https://github.com/utopia-php/vcs/tree/0.6.6"
+ "source": "https://github.com/utopia-php/vcs/tree/0.6.7"
},
- "time": "2024-05-17T09:36:30+00:00"
+ "time": "2024-06-05T17:38:29+00:00"
},
{
"name": "utopia-php/websocket",
diff --git a/docker-compose.yml b/docker-compose.yml
index b1e30a1e07..be46825992 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -101,7 +101,7 @@ services:
- _APP_CONSOLE_HOSTNAMES
- _APP_SYSTEM_EMAIL_NAME
- _APP_SYSTEM_EMAIL_ADDRESS
- - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
+ - _APP_EMAIL_SECURITY
- _APP_SYSTEM_RESPONSE_FORMAT
- _APP_OPTIONS_ABUSE
- _APP_OPTIONS_ROUTER_PROTECTION
@@ -189,6 +189,7 @@ services:
- _APP_CONSOLE_COUNTRIES_DENYLIST
- _APP_EXPERIMENT_LOGGING_PROVIDER
- _APP_EXPERIMENT_LOGGING_CONFIG
+ - _APP_DATABASE_SHARED_TABLES
appwrite-realtime:
entrypoint: realtime
@@ -238,6 +239,7 @@ services:
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
+ - _APP_DATABASE_SHARED_TABLES
appwrite-worker-audits:
entrypoint: worker-audits
@@ -267,6 +269,7 @@ services:
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
+ - _APP_DATABASE_SHARED_TABLES
appwrite-worker-webhooks:
entrypoint: worker-webhooks
@@ -286,7 +289,7 @@ services:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
+ - _APP_EMAIL_SECURITY
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -299,6 +302,7 @@ services:
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_WEBHOOK_MAX_FAILED_ATTEMPTS
+ - _APP_DATABASE_SHARED_TABLES
appwrite-worker-deletes:
entrypoint: worker-deletes
@@ -356,6 +360,7 @@ services:
- _APP_LOGGING_CONFIG
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
+ - _APP_DATABASE_SHARED_TABLES
appwrite-worker-databases:
entrypoint: worker-databases
@@ -387,6 +392,7 @@ services:
- _APP_LOGGING_CONFIG
- _APP_WORKERS_NUM
- _APP_QUEUE_NAME
+ - _APP_DATABASE_SHARED_TABLES
appwrite-worker-builds:
entrypoint: worker-builds
@@ -452,6 +458,7 @@ services:
- _APP_STORAGE_WASABI_SECRET
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
+ - _APP_DATABASE_SHARED_TABLES
appwrite-worker-certificates:
entrypoint: worker-certificates
@@ -475,7 +482,7 @@ services:
- _APP_DOMAIN
- _APP_DOMAIN_TARGET
- _APP_DOMAIN_FUNCTIONS
- - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
+ - _APP_EMAIL_CERTIFICATES
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
@@ -487,6 +494,7 @@ services:
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
+ - _APP_DATABASE_SHARED_TABLES
appwrite-worker-functions:
entrypoint: worker-functions
@@ -526,6 +534,7 @@ services:
- _APP_DOCKER_HUB_PASSWORD
- _APP_LOGGING_CONFIG
- _APP_LOGGING_PROVIDER
+ - _APP_DATABASE_SHARED_TABLES
appwrite-worker-mails:
entrypoint: worker-mails
@@ -560,6 +569,7 @@ services:
- _APP_LOGGING_CONFIG
- _APP_DOMAIN
- _APP_OPTIONS_FORCE_HTTPS
+ - _APP_DATABASE_SHARED_TABLES
appwrite-worker-messaging:
entrypoint: worker-messaging
@@ -614,6 +624,7 @@ services:
- _APP_STORAGE_WASABI_SECRET
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
+ - _APP_DATABASE_SHARED_TABLES
appwrite-worker-migrations:
entrypoint: worker-migrations
@@ -635,7 +646,7 @@ services:
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_DOMAIN_TARGET
- - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
+ - _APP_EMAIL_SECURITY
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
@@ -649,6 +660,7 @@ services:
- _APP_LOGGING_CONFIG
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
+ - _APP_DATABASE_SHARED_TABLES
appwrite-task-maintenance:
entrypoint: maintenance
@@ -686,6 +698,7 @@ services:
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
- _APP_MAINTENANCE_RETENTION_SCHEDULES
- _APP_MAINTENANCE_DELAY
+ - _APP_DATABASE_SHARED_TABLES
appwrite-worker-usage:
entrypoint: worker-usage
@@ -717,6 +730,7 @@ services:
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
+ - _APP_DATABASE_SHARED_TABLES
appwrite-worker-usage-dump:
entrypoint: worker-usage-dump
@@ -748,6 +762,7 @@ services:
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
+ - _APP_DATABASE_SHARED_TABLES
appwrite-task-scheduler-functions:
entrypoint: schedule-functions
@@ -775,6 +790,7 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
+ - _APP_DATABASE_SHARED_TABLES
appwrite-task-scheduler-messages:
entrypoint: schedule-messages
@@ -802,6 +818,7 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
+ - _APP_DATABASE_SHARED_TABLES
appwrite-assistant:
container_name: appwrite-assistant
@@ -900,20 +917,7 @@ services:
- MYSQL_USER=${_APP_DB_USER}
- MYSQL_PASSWORD=${_APP_DB_PASS}
- MARIADB_AUTO_UPGRADE=1
- command: "mysqld --innodb-flush-method=fsync" # add ' --query_cache_size=0' for DB tests
- # command: mv /var/lib/mysql/ib_logfile0 /var/lib/mysql/ib_logfile0.bu && mv /var/lib/mysql/ib_logfile1 /var/lib/mysql/ib_logfile1.bu
-
- # smtp:
- # image: appwrite/smtp:1.2.0
- # container_name: appwrite-smtp
- # restart: unless-stopped
- # networks:
- # - appwrite
- # environment:
- # - LOCAL_DOMAINS=@
- # - RELAY_FROM_HOSTS=192.168.0.0/16 ; *.yourdomain.com
- # - SMARTHOST_HOST=smtp
- # - SMARTHOST_PORT=587
+ command: "mysqld --innodb-flush-method=fsync"
redis:
image: redis:7.2.4-alpine
@@ -931,14 +935,6 @@ services:
volumes:
- appwrite-redis:/data:rw
- # clamav:
- # image: appwrite/clamav:1.2.0
- # container_name: appwrite-clamav
- # networks:
- # - appwrite
- # volumes:
- # - appwrite-uploads:/storage/uploads
-
# Dev Tools Start ------------------------------------------------------------------------------------------
#
# The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack
diff --git a/docs/references/account/update-mfa-authenticator.md b/docs/references/account/update-mfa-authenticator.md
index 253826a891..23a3251441 100644
--- a/docs/references/account/update-mfa-authenticator.md
+++ b/docs/references/account/update-mfa-authenticator.md
@@ -1 +1 @@
-Verify an authenticator app after adding it using the [add authenticator](/docs/references/cloud/client-web/account#createMfaAuthenticator) method. add
\ No newline at end of file
+Verify an authenticator app after adding it using the [add authenticator](/docs/references/cloud/client-web/account#createMfaAuthenticator) method.
\ No newline at end of file
diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php
index 15c14daf51..e3a2021c1a 100644
--- a/src/Appwrite/Migration/Migration.php
+++ b/src/Appwrite/Migration/Migration.php
@@ -239,7 +239,7 @@ abstract class Migration
}
/**
- * Creates colletion from the config collection.
+ * Creates collection from the config collection.
*
* @param string $id
* @param string|null $name
@@ -266,6 +266,7 @@ abstract class Migration
'type' => $attribute['type'],
'size' => $attribute['size'],
'required' => $attribute['required'],
+ 'default' => $attribute['default'] ?? null,
'signed' => $attribute['signed'],
'array' => $attribute['array'],
'filters' => $attribute['filters'],
diff --git a/src/Appwrite/Migration/Version/V20.php b/src/Appwrite/Migration/Version/V20.php
index 1a599d32f2..5a0807cedf 100644
--- a/src/Appwrite/Migration/Version/V20.php
+++ b/src/Appwrite/Migration/Version/V20.php
@@ -336,6 +336,32 @@ class V20 extends Migration
Console::warning("Purge cache from {$id}: {$th->getMessage()}");
}
+ break;
+ case 'topics':
+ try {
+ $this->projectDB->updateAttributeDefault($id, 'emailTotal', 0);
+ } catch (Throwable $th) {
+ Console::warning("'topics' from {$id}: {$th->getMessage()}");
+ }
+
+ try {
+ $this->projectDB->updateAttributeDefault($id, 'pushTotal', 0);
+ } catch (Throwable $th) {
+ Console::warning("'topics' from {$id}: {$th->getMessage()}");
+ }
+
+ try {
+ $this->projectDB->updateAttributeDefault($id, 'smsTotal', 0);
+ } catch (Throwable $th) {
+ Console::warning("'topics' from {$id}: {$th->getMessage()}");
+ }
+
+ try {
+ $this->projectDB->purgeCachedCollection($id);
+ } catch (Throwable $th) {
+ Console::warning("Purge cache from {$id}: {$th->getMessage()}");
+ }
+
break;
}
diff --git a/src/Appwrite/Platform/Appwrite.php b/src/Appwrite/Platform/Appwrite.php
index 05224799f3..6b3eb077fa 100644
--- a/src/Appwrite/Platform/Appwrite.php
+++ b/src/Appwrite/Platform/Appwrite.php
@@ -2,15 +2,13 @@
namespace Appwrite\Platform;
-use Appwrite\Platform\Services\Tasks;
-use Appwrite\Platform\Services\Workers;
+use Appwrite\Platform\Modules\Core;
use Utopia\Platform\Platform;
class Appwrite extends Platform
{
public function __construct()
{
- $this->addService('tasks', new Tasks());
- $this->addService('workers', new Workers());
+ parent::__construct(new Core());
}
}
diff --git a/src/Appwrite/Platform/Modules/Core.php b/src/Appwrite/Platform/Modules/Core.php
new file mode 100644
index 0000000000..859ca3b529
--- /dev/null
+++ b/src/Appwrite/Platform/Modules/Core.php
@@ -0,0 +1,17 @@
+addService('tasks', new Tasks());
+ $this->addService('workers', new Workers());
+ }
+
+}
diff --git a/src/Appwrite/Platform/Services/Tasks.php b/src/Appwrite/Platform/Services/Tasks.php
index ac1f99eec3..31465d2f26 100644
--- a/src/Appwrite/Platform/Services/Tasks.php
+++ b/src/Appwrite/Platform/Services/Tasks.php
@@ -22,7 +22,7 @@ class Tasks extends Service
{
public function __construct()
{
- $this->type = self::TYPE_CLI;
+ $this->type = Service::TYPE_TASK;
$this
->addAction(Doctor::getName(), new Doctor())
->addAction(Install::getName(), new Install())
diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php
index 62a7fcf3fb..0e79f4257c 100644
--- a/src/Appwrite/Platform/Services/Workers.php
+++ b/src/Appwrite/Platform/Services/Workers.php
@@ -20,7 +20,7 @@ class Workers extends Service
{
public function __construct()
{
- $this->type = self::TYPE_WORKER;
+ $this->type = Service::TYPE_WORKER;
$this
->addAction(Audits::getName(), new Audits())
->addAction(Builds::getName(), new Builds())
diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php
index cfbc4b200a..1da8a58ebe 100644
--- a/src/Appwrite/Platform/Tasks/Maintenance.php
+++ b/src/Appwrite/Platform/Tasks/Maintenance.php
@@ -49,6 +49,7 @@ class Maintenance extends Action
$this->foreachProject($dbForConsole, function (Document $project) use ($queueForDeletes, $usageStatsRetentionHourly) {
$queueForDeletes->setProject($project);
+ $this->notifyDeleteTargets($queueForDeletes);
$this->notifyDeleteExecutionLogs($queueForDeletes);
$this->notifyDeleteAbuseLogs($queueForDeletes);
$this->notifyDeleteAuditLogs($queueForDeletes);
@@ -60,7 +61,6 @@ class Maintenance extends Action
$this->renewCertificates($dbForConsole, $queueForCertificates);
$this->notifyDeleteCache($cacheRetention, $queueForDeletes);
$this->notifyDeleteSchedules($schedulesDeletionRetention, $queueForDeletes);
- $this->notifyDeleteTargets($queueForDeletes);
}, $interval, $delay);
}
diff --git a/src/Appwrite/Platform/Tasks/Migrate.php b/src/Appwrite/Platform/Tasks/Migrate.php
index 6e4fe38eb7..21f83109ff 100644
--- a/src/Appwrite/Platform/Tasks/Migrate.php
+++ b/src/Appwrite/Platform/Tasks/Migrate.php
@@ -93,6 +93,7 @@ class Migrate extends Action
// TODO: Iterate through all project DBs
/** @var Database $projectDB */
$projectDB = $getProjectDB($project);
+ $projectDB->disableValidation();
$migration
->setProject($project, $projectDB, $dbForConsole)
->setPDO($register->get('db', true))
diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php
index 1fbbd3b2ec..58dc1dd28a 100644
--- a/src/Appwrite/Platform/Workers/Certificates.php
+++ b/src/Appwrite/Platform/Workers/Certificates.php
@@ -135,9 +135,9 @@ class Certificates extends Action
try {
// Email for alerts is required by LetsEncrypt
- $email = System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS');
+ $email = System::getEnv('_APP_EMAIL_CERTIFICATES', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'));
if (empty($email)) {
- throw new Exception('You must set a valid security email address (_APP_SYSTEM_SECURITY_EMAIL_ADDRESS) to issue an SSL certificate.');
+ throw new Exception('You must set a valid security email address (_APP_EMAIL_CERTIFICATES) to issue an SSL certificate.');
}
// Validate domain and DNS records. Skip if job is forced
@@ -439,42 +439,26 @@ class Certificates extends Action
$locale = new Locale(System::getEnv('_APP_LOCALE', 'en'));
- // Send mail to administratore mail
+ // Send mail to administrator mail
$template = Template::fromFile(__DIR__ . '/../../../../app/config/locale/templates/email-certificate-failed.tpl');
$template->setParam('{{domain}}', $domain);
$template->setParam('{{error}}', \nl2br($errorMessage));
$template->setParam('{{attempts}}', $attempt);
-
- // TODO: Use setbodyTemplate once #7307 is merged
- $subject = 'Certificate failed to generate';
- $body = Template::fromFile(__DIR__ . '/../../../../app/config/locale/templates/email-base-styled.tpl');
-
- $subject = \sprintf($locale->getText("emails.certificate.subject"), $domain);
-
- $message = Template::fromFile(__DIR__ . '/../../../../app/config/locale/templates/email-inner-base.tpl');
- $message
- ->setParam('{{body}}', $locale->getText("emails.certificate.body"), escapeHtml: false)
- ->setParam('{{hello}}', $locale->getText("emails.certificate.hello"))
- ->setParam('{{footer}}', $locale->getText("emails.certificate.footer"))
- ->setParam('{{thanks}}', $locale->getText("emails.certificate.thanks"))
- ->setParam('{{signature}}', $locale->getText("emails.certificate.signature"));
- $body = $message->render();
+ $body = $template->render();
$emailVariables = [
'direction' => $locale->getText('settings.direction'),
- 'domain' => $domain,
- 'error' => '
' . $errorMessage . '
',
- 'attempt' => $attempt,
- 'project' => 'Console',
- 'redirect' => 'https://' . $domain,
];
+ $subject = \sprintf($locale->getText("emails.certificate.subject"), $domain);
+
$queueForMails
->setSubject($subject)
->setBody($body)
->setName('Appwrite Administrator')
+ ->setbodyTemplate(__DIR__ . '/../../../../app/config/locale/templates/email-base-styled.tpl')
->setVariables($emailVariables)
- ->setRecipient(System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS'))
+ ->setRecipient(System::getEnv('_APP_EMAIL_CERTIFICATES', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS')))
->trigger();
}
diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php
index 49b41da495..d54f3f5079 100644
--- a/src/Appwrite/Platform/Workers/Deletes.php
+++ b/src/Appwrite/Platform/Workers/Deletes.php
@@ -498,14 +498,14 @@ class Deletes extends Action
$collections = $dbForProject->listCollections($limit);
foreach ($collections as $collection) {
- if ($dsn->getHost() !== DATABASE_SHARED_TABLES || !\in_array($collection->getId(), $projectCollectionIds)) {
+ if ($dsn->getHost() !== System::getEnv('_APP_DATABASE_SHARED_TABLES', '') || !\in_array($collection->getId(), $projectCollectionIds)) {
$dbForProject->deleteCollection($collection->getId());
} else {
$this->deleteByGroup($collection->getId(), [], database: $dbForProject);
}
}
- if ($dsn->getHost() === DATABASE_SHARED_TABLES) {
+ if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$collectionsIds = \array_map(fn ($collection) => $collection->getId(), $collections);
if (empty(\array_diff($collectionsIds, $projectCollectionIds))) {
@@ -554,7 +554,7 @@ class Deletes extends Action
], $dbForConsole);
// Delete metadata table
- if ($dsn->getHost() !== DATABASE_SHARED_TABLES) {
+ if ($dsn->getHost() !== System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$dbForProject->deleteCollection('_metadata');
} else {
$this->deleteByGroup('_metadata', [], $dbForProject);
diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php
index 19d2c5a0fe..6f642fabb7 100644
--- a/src/Appwrite/Platform/Workers/Messaging.php
+++ b/src/Appwrite/Platform/Workers/Messaging.php
@@ -399,7 +399,10 @@ class Messaging extends Action
'credentials' => match ($host) {
'twilio' => [
'accountSid' => $user,
- 'authToken' => $password
+ 'authToken' => $password,
+ // Twilio Messaging Service SIDs always start with MG
+ // https://www.twilio.com/docs/messaging/services
+ 'messagingServiceSid' => \str_starts_with($from, 'MG') ? $from : null
],
'textmagic' => [
'username' => $user,
@@ -420,9 +423,14 @@ class Messaging extends Action
],
default => null
},
- 'options' => [
- 'from' => $from
- ]
+ 'options' => match ($host) {
+ 'twilio' => [
+ 'from' => \str_starts_with($from, 'MG') ? null : $from
+ ],
+ default => [
+ 'from' => $from
+ ]
+ }
]);
$adapter = $this->getSmsAdapter($provider);
@@ -465,7 +473,7 @@ class Messaging extends Action
return match ($provider->getAttribute('provider')) {
'mock' => new Mock('username', 'password'),
- 'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken']),
+ 'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken'], null, isset($credentials['messagingServiceSid']) ? $credentials['messagingServiceSid'] : null),
'textmagic' => new TextMagic($credentials['username'], $credentials['apiKey']),
'telesign' => new Telesign($credentials['customerId'], $credentials['apiKey']),
'msg91' => new Msg91($credentials['senderId'], $credentials['authKey'], $credentials['templateId']),
diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php
index d60946f709..88ca7871f2 100644
--- a/src/Appwrite/Platform/Workers/Webhooks.php
+++ b/src/Appwrite/Platform/Workers/Webhooks.php
@@ -104,7 +104,7 @@ class Webhooks extends Action
\curl_setopt($ch, CURLOPT_USERAGENT, \sprintf(
APP_USERAGENT,
System::getEnv('_APP_VERSION', 'UNKNOWN'),
- System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
+ System::getEnv('_APP_EMAIL_SECURITY', System::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY))
));
\curl_setopt(
$ch,
diff --git a/tests/e2e/General/HTTPTest.php b/tests/e2e/General/HTTPTest.php
index 0bb5ca4650..92bc52561c 100644
--- a/tests/e2e/General/HTTPTest.php
+++ b/tests/e2e/General/HTTPTest.php
@@ -31,7 +31,7 @@ class HTTPTest extends Scope
$this->assertEquals(204, $response['headers']['status-code']);
$this->assertEquals('Appwrite', $response['headers']['server']);
$this->assertEquals('GET, POST, PUT, PATCH, DELETE', $response['headers']['access-control-allow-methods']);
- $this->assertEquals('Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-Appwrite-Shared-Tables, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent', $response['headers']['access-control-allow-headers']);
+ $this->assertEquals('Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent', $response['headers']['access-control-allow-headers']);
$this->assertEquals('X-Appwrite-Session, X-Fallback-Cookies', $response['headers']['access-control-expose-headers']);
$this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin']);
$this->assertEquals('true', $response['headers']['access-control-allow-credentials']);
diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php
index 23771712e8..b1f7c85cd9 100644
--- a/tests/e2e/Services/Account/AccountCustomClientTest.php
+++ b/tests/e2e/Services/Account/AccountCustomClientTest.php
@@ -150,7 +150,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['registration']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['registration']));
$this->assertEquals($response['body']['email'], $email);
$this->assertEquals($response['body']['name'], $name);
$this->assertArrayHasKey('accessedAt', $response['body']);
@@ -294,7 +294,7 @@ class AccountCustomClientTest extends Scope
$this->assertIsNumeric($response['body']['total']);
$this->assertEquals("user.create", $response['body']['logs'][2]['event']);
$this->assertEquals(filter_var($response['body']['logs'][2]['ip'], FILTER_VALIDATE_IP), $response['body']['logs'][2]['ip']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['logs'][2]['time']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['logs'][2]['time']));
$this->assertEquals('Windows', $response['body']['logs'][1]['osName']);
$this->assertEquals('WIN', $response['body']['logs'][1]['osCode']);
@@ -327,7 +327,6 @@ class AccountCustomClientTest extends Scope
$this->assertEquals('desktop', $response['body']['logs'][2]['deviceName']);
$this->assertEquals('', $response['body']['logs'][2]['deviceBrand']);
$this->assertEquals('', $response['body']['logs'][2]['deviceModel']);
- $this->assertEquals($response['body']['logs'][2]['ip'], filter_var($response['body']['logs'][2]['ip'], FILTER_VALIDATE_IP));
$this->assertEquals('--', $response['body']['logs'][2]['countryCode']);
$this->assertEquals('Unknown', $response['body']['logs'][2]['countryName']);
@@ -343,7 +342,7 @@ class AccountCustomClientTest extends Scope
]
]);
- $this->assertEquals($responseLimit['headers']['status-code'], 200);
+ $this->assertEquals(200, $responseLimit['headers']['status-code']);
$this->assertIsArray($responseLimit['body']['logs']);
$this->assertNotEmpty($responseLimit['body']['logs']);
$this->assertCount(1, $responseLimit['body']['logs']);
@@ -382,7 +381,7 @@ class AccountCustomClientTest extends Scope
]
]);
- $this->assertEquals($responseLimitOffset['headers']['status-code'], 200);
+ $this->assertEquals(200, $responseLimitOffset['headers']['status-code']);
$this->assertIsArray($responseLimitOffset['body']['logs']);
$this->assertNotEmpty($responseLimitOffset['body']['logs']);
$this->assertCount(1, $responseLimitOffset['body']['logs']);
@@ -430,7 +429,7 @@ class AccountCustomClientTest extends Scope
$this->assertIsArray($response['body']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['registration']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['registration']));
$this->assertEquals($response['body']['email'], $email);
$this->assertEquals($response['body']['name'], $newName);
@@ -497,7 +496,7 @@ class AccountCustomClientTest extends Scope
$this->assertIsArray($response['body']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['registration']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['registration']));
$this->assertEquals($response['body']['email'], $email);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
@@ -587,7 +586,7 @@ class AccountCustomClientTest extends Scope
$this->assertIsArray($response['body']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['registration']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['registration']));
$this->assertEquals($response['body']['email'], $newEmail);
/**
@@ -629,7 +628,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['registration']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['registration']));
$this->assertEquals($response['body']['email'], $data['email']);
$this->assertEquals($response['body']['name'], $data['name']);
@@ -771,7 +770,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEmpty($response['body']['secret']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['expire']));
$lastEmail = $this->getLastEmail();
@@ -1073,7 +1072,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEmpty($response['body']['secret']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['expire']));
$lastEmail = $this->getLastEmail();
@@ -1668,7 +1667,7 @@ class AccountCustomClientTest extends Scope
$this->assertIsArray($response['body']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['registration']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['registration']));
$this->assertEquals($response['body']['email'], $email);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
@@ -1756,13 +1755,11 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals($response['body']['$id'], $userId);
- $this->assertEquals($response['body']['name'], 'User Name');
- $this->assertEquals($response['body']['email'], 'useroauth@localhost.test');
+ $this->assertEquals('User Name', $response['body']['name']);
+ $this->assertEquals('useroauth@localhost.test', $response['body']['email']);
// Since we only support one oauth user, let's also check updateSession here
- $this->assertEquals(200, $response['headers']['status-code']);
-
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/current', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
@@ -1808,7 +1805,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEmpty($response['body']['secret']);
- $this->assertEquals($response['body']['provider'], 'anonymous');
+ $this->assertEquals('anonymous', $response['body']['provider']);
$sessionID = $response['body']['$id'];
@@ -1821,7 +1818,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEmpty($response['body']['secret']);
- $this->assertEquals($response['body']['provider'], 'anonymous');
+ $this->assertEquals('anonymous', $response['body']['provider']);
$response = $this->client->call(Client::METHOD_GET, '/account/sessions/97823askjdkasd80921371980', array_merge([
'origin' => 'http://localhost',
@@ -1934,7 +1931,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEmpty($response['body']['secret']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['expire']));
$userId = $response['body']['userId'];
@@ -2085,7 +2082,7 @@ class AccountCustomClientTest extends Scope
$this->assertIsArray($response['body']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['registration']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['registration']));
$this->assertEquals($response['body']['email'], $email);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
@@ -2127,7 +2124,7 @@ class AccountCustomClientTest extends Scope
$this->assertIsArray($response['body']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['registration']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['registration']));
$this->assertEquals($response['body']['phone'], $newPhone);
/**
@@ -2240,7 +2237,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEmpty($response['body']['secret']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['expire']));
$smsRequest = $this->getLastRequest();
@@ -2327,7 +2324,7 @@ class AccountCustomClientTest extends Scope
$this->assertNotEmpty($response['body']['$id']);
$this->assertEmpty($response['body']['secret']);
$this->assertEmpty($response['body']['phrase']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['expire']));
$userId = $response['body']['userId'];
@@ -2452,7 +2449,7 @@ class AccountCustomClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['registration']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['registration']));
$this->assertEquals($response['body']['email'], $email);
$this->assertTrue($response['body']['emailVerification']);
@@ -2512,7 +2509,7 @@ class AccountCustomClientTest extends Scope
$this->assertIsArray($response['body']);
$this->assertNotEmpty($response['body']);
$this->assertNotEmpty($response['body']['$id']);
- $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['registration']));
+ $this->assertTrue((new DatetimeValidator())->isValid($response['body']['registration']));
$this->assertEquals($response['body']['email'], $email);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
index 119c1a2223..6274f5ddae 100644
--- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
+++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php
@@ -2,6 +2,7 @@
namespace Tests\E2E\Services\Functions;
+use Appwrite\Tests\Retry;
use CURLFile;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
@@ -42,6 +43,7 @@ class FunctionsCustomClientTest extends Scope
return [];
}
+ #[Retry(count: 2)]
public function testCreateExecution(): array
{
/**
@@ -164,7 +166,7 @@ class FunctionsCustomClientTest extends Scope
$this->assertEquals(202, $execution['headers']['status-code']);
// Wait for the first scheduled execution to be created
- sleep(65);
+ sleep(90);
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/executions', [
'content-type' => 'application/json',
@@ -716,4 +718,94 @@ class FunctionsCustomClientTest extends Scope
return [];
}
+
+ public function testNonOverrideOfHeaders(): array
+ {
+ /**
+ * Test for SUCCESS
+ */
+ $projectId = $this->getProject()['$id'];
+ $apikey = $this->getProject()['apiKey'];
+
+ $function = $this->client->call(Client::METHOD_POST, '/functions', [
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $projectId,
+ 'x-appwrite-key' => $apikey,
+ ], [
+ 'functionId' => ID::unique(),
+ 'name' => 'Test',
+ 'execute' => [Role::any()->toString()],
+ 'runtime' => 'node-18.0',
+ 'entrypoint' => 'index.js'
+ ]);
+
+ $functionId = $function['body']['$id'] ?? '';
+
+ $this->assertEquals(201, $function['headers']['status-code']);
+
+ $folder = 'node';
+ $code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
+ $this->packageCode($folder);
+
+ $deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', [
+ 'content-type' => 'multipart/form-data',
+ 'x-appwrite-project' => $projectId,
+ 'x-appwrite-key' => $apikey,
+ ], [
+ 'entrypoint' => 'index.js',
+ 'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), //different tarball names intentional
+ 'activate' => true
+ ]);
+
+ $deploymentId = $deployment['body']['$id'] ?? '';
+
+ $this->assertEquals(202, $deployment['headers']['status-code']);
+
+ // Poll until deployment is built
+ while (true) {
+ $deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $function['body']['$id'] . '/deployments/' . $deploymentId, [
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey'],
+ ]);
+
+ if (
+ $deployment['headers']['status-code'] >= 400
+ || \in_array($deployment['body']['status'], ['ready', 'failed'])
+ ) {
+ break;
+ }
+
+ \sleep(1);
+ }
+
+ $this->assertEquals('ready', $deployment['body']['status']);
+
+ $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'x-appwrite-event' => "OVERRIDDEN",
+ 'x-appwrite-trigger' => "OVERRIDDEN",
+ 'x-appwrite-user-id' => "OVERRIDDEN",
+ 'x-appwrite-user-jwt' => "OVERRIDDEN",
+ ]);
+
+ $output = json_decode($execution['body']['responseBody'], true);
+ $this->assertNotEquals('OVERRIDDEN', $output['APPWRITE_FUNCTION_JWT']);
+ $this->assertNotEquals('OVERRIDDEN', $output['APPWRITE_FUNCTION_EVENT']);
+ $this->assertNotEquals('OVERRIDDEN', $output['APPWRITE_FUNCTION_TRIGGER']);
+ $this->assertNotEquals('OVERRIDDEN', $output['APPWRITE_FUNCTION_USER_ID']);
+
+ // Cleanup : Delete function
+ $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey'],
+ ], []);
+
+ $this->assertEquals(204, $response['headers']['status-code']);
+
+ return [];
+ }
}
diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
index 5fa8915303..c11ceb1539 100644
--- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
+++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php
@@ -1075,7 +1075,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals($executions['body']['executions'][0]['logs'], '');
$this->assertStringContainsString('timed out', $executions['body']['executions'][0]['errors']);
- sleep(70); //wait for scheduled execution to be created and time out
+ sleep(75); // Wait for scheduled execution to be created and time out
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
@@ -1086,12 +1086,6 @@ class FunctionsCustomServerTest extends Scope
$this->assertCount(2, $executions['body']['executions']);
$this->assertIsArray($executions['body']['executions']);
$this->assertEquals($executions['body']['executions'][1]['trigger'], 'schedule');
- $this->assertEquals($executions['body']['executions'][1]['status'], 'failed');
- $this->assertEquals($executions['body']['executions'][1]['responseStatusCode'], 500);
- $this->assertLessThan(20, $executions['body']['executions'][1]['duration']);
- $this->assertEquals($executions['body']['executions'][1]['responseBody'], '');
- $this->assertEquals($executions['body']['executions'][1]['logs'], '');
- $this->assertStringContainsString('timed out', $executions['body']['executions'][1]['errors']);
// Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
diff --git a/tests/e2e/Services/Health/HealthCustomServerTest.php b/tests/e2e/Services/Health/HealthCustomServerTest.php
index 5fd7ab96da..8360af542e 100644
--- a/tests/e2e/Services/Health/HealthCustomServerTest.php
+++ b/tests/e2e/Services/Health/HealthCustomServerTest.php
@@ -455,7 +455,7 @@ class HealthCustomServerTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('/CN=www.google.com', $response['body']['name']);
$this->assertEquals('www.google.com', $response['body']['subjectSN']);
- $this->assertEquals('Google Trust Services LLC', $response['body']['issuerOrganisation']);
+ $this->assertStringContainsString('Google Trust Services', $response['body']['issuerOrganisation']);
$this->assertIsInt($response['body']['validFrom']);
$this->assertIsInt($response['body']['validTo']);
diff --git a/tests/e2e/Services/Messaging/MessagingBase.php b/tests/e2e/Services/Messaging/MessagingBase.php
index 0540479eb5..dc5ddb9d70 100644
--- a/tests/e2e/Services/Messaging/MessagingBase.php
+++ b/tests/e2e/Services/Messaging/MessagingBase.php
@@ -511,6 +511,55 @@ trait MessagingBase
];
}
+ public function testSubscriberTargetSubQuery()
+ {
+ $response = $this->client->call(Client::METHOD_POST, '/messaging/topics', [
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey'],
+ ], [
+ 'topicId' => 'sub-query-test',
+ 'name' => 'sub-query-test',
+ ]);
+
+ $this->assertEquals(201, $response['headers']['status-code']);
+
+ $topic = $response['body'];
+
+ $prefix = uniqid();
+
+ for ($i = 1; $i <= 101; $i++) {
+ $response = $this->client->call(Client::METHOD_POST, '/users', [
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ 'x-appwrite-key' => $this->getProject()['apiKey'],
+ ], [
+ 'userId' => "$prefix-$i",
+ 'email' => "$prefix-$i@example.com",
+ 'password' => 'password',
+ 'name' => "User $prefix $i",
+ ]);
+
+ $this->assertEquals(201, $response['headers']['status-code']);
+ $user = $response['body'];
+ $targets = $user['targets'] ?? [];
+
+ $this->assertGreaterThan(0, count($targets));
+
+ $target = $targets[0];
+
+ $response = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topic['$id'] . '/subscribers', \array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'subscriberId' => $user['$id'],
+ 'targetId' => $target['$id'],
+ ]);
+
+ $this->assertEquals(201, $response['headers']['status-code']);
+ }
+ }
+
/**
* @depends testCreateSubscriber
*/
diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php
index 8cdd325501..2af0c8f1cc 100644
--- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php
+++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php
@@ -9,7 +9,6 @@ use Tests\E2E\General\UsageTest;
use Tests\E2E\Scopes\ProjectConsole;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideClient;
-use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
@@ -3494,504 +3493,4 @@ class ProjectsConsoleClientTest extends Scope
return $data;
}
-
- public function testTenantIsolation(): void
- {
- // Create a team and a project
- $team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'teamId' => ID::unique(),
- 'name' => 'Amazing Team',
- ]);
-
- $teamId = $team['body']['$id'];
-
- // Project-level isolation
- $project1 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- 'x-appwrite-shared-tables' => false
- ], $this->getHeaders()), [
- 'projectId' => ID::unique(),
- 'name' => 'Amazing Project',
- 'teamId' => $teamId,
- 'region' => 'default'
- ]);
-
- // Application level isolation (shared tables)
- $project2 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- 'x-appwrite-shared-tables' => true
- ], $this->getHeaders()), [
- 'projectId' => ID::unique(),
- 'name' => 'Amazing Project',
- 'teamId' => $teamId,
- 'region' => 'default'
- ]);
-
- // Project-level isolation
- $project3 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- 'x-appwrite-shared-tables' => false
- ], $this->getHeaders()), [
- 'projectId' => ID::unique(),
- 'name' => 'Amazing Project',
- 'teamId' => $teamId,
- 'region' => 'default'
- ]);
-
- // Application level isolation (shared tables)
- $project4 = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- 'x-appwrite-shared-tables' => true
- ], $this->getHeaders()), [
- 'projectId' => ID::unique(),
- 'name' => 'Amazing Project',
- 'teamId' => $teamId,
- 'region' => 'default'
- ]);
-
- // Create and API key in each project
- $key1 = $this->client->call(Client::METHOD_POST, '/projects/' . $project1['body']['$id'] . '/keys', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'name' => 'Key Test',
- 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.read', 'attributes.write', 'indexes.read', 'indexes.write', 'documents.read', 'documents.write', 'users.read', 'users.write'],
- ]);
-
- $key2 = $this->client->call(Client::METHOD_POST, '/projects/' . $project2['body']['$id'] . '/keys', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'name' => 'Key Test',
- 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.read', 'attributes.write', 'indexes.read', 'indexes.write', 'documents.read', 'documents.write', 'users.read', 'users.write'],
- ]);
-
- $key3 = $this->client->call(Client::METHOD_POST, '/projects/' . $project3['body']['$id'] . '/keys', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'name' => 'Key Test',
- 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.read', 'attributes.write', 'indexes.read', 'indexes.write', 'documents.read', 'documents.write', 'users.read', 'users.write'],
- ]);
-
- $key4 = $this->client->call(Client::METHOD_POST, '/projects/' . $project4['body']['$id'] . '/keys', array_merge([
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $this->getProject()['$id'],
- ], $this->getHeaders()), [
- 'name' => 'Key Test',
- 'scopes' => ['databases.read', 'databases.write', 'collections.read', 'collections.write', 'attributes.read', 'attributes.write', 'indexes.read', 'indexes.write', 'documents.read', 'documents.write', 'users.read', 'users.write'],
- ]);
-
- // Create a database in each project
- $database1 = $this->client->call(Client::METHOD_POST, '/databases', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project1['body']['$id'],
- 'x-appwrite-key' => $key1['body']['secret']
- ], [
- 'databaseId' => ID::unique(),
- 'name' => 'Amazing Database',
- ]);
-
- $database2 = $this->client->call(Client::METHOD_POST, '/databases', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project2['body']['$id'],
- 'x-appwrite-key' => $key2['body']['secret']
- ], [
- 'databaseId' => ID::unique(),
- 'name' => 'Amazing Database',
- ]);
-
- $database3 = $this->client->call(Client::METHOD_POST, '/databases', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project3['body']['$id'],
- 'x-appwrite-key' => $key3['body']['secret']
- ], [
- 'databaseId' => ID::unique(),
- 'name' => 'Amazing Database',
- ]);
-
- $database4 = $this->client->call(Client::METHOD_POST, '/databases', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project4['body']['$id'],
- 'x-appwrite-key' => $key4['body']['secret']
- ], [
- 'databaseId' => ID::unique(),
- 'name' => 'Amazing Database',
- ]);
-
- // Create a collection in each project
- $collection1 = $this->client->call(Client::METHOD_POST, '/databases/' . $database1['body']['$id'] . '/collections', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project1['body']['$id'],
- 'x-appwrite-key' => $key1['body']['secret']
- ], [
- 'databaseId' => $database1['body']['$id'],
- 'collectionId' => ID::unique(),
- 'name' => 'Amazing Collection',
- ]);
-
- $collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $database2['body']['$id'] . '/collections', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project2['body']['$id'],
- 'x-appwrite-key' => $key2['body']['secret']
- ], [
- 'databaseId' => $database2['body']['$id'],
- 'collectionId' => ID::unique(),
- 'name' => 'Amazing Collection',
- ]);
-
- $collection3 = $this->client->call(Client::METHOD_POST, '/databases/' . $database3['body']['$id'] . '/collections', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project3['body']['$id'],
- 'x-appwrite-key' => $key3['body']['secret']
- ], [
- 'databaseId' => $database3['body']['$id'],
- 'collectionId' => ID::unique(),
- 'name' => 'Amazing Collection',
- ]);
-
- $collection4 = $this->client->call(Client::METHOD_POST, '/databases/' . $database4['body']['$id'] . '/collections', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project4['body']['$id'],
- 'x-appwrite-key' => $key4['body']['secret']
- ], [
- 'databaseId' => $database4['body']['$id'],
- 'collectionId' => ID::unique(),
- 'name' => 'Amazing Collection',
- ]);
-
- // Create an attribute in each project
- $attribute1 = $this->client->call(Client::METHOD_POST, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'] . '/attributes/string', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project1['body']['$id'],
- 'x-appwrite-key' => $key1['body']['secret']
- ], [
- 'databaseId' => $database1['body']['$id'],
- 'collectionId' => $collection1['body']['$id'],
- 'key' => ID::unique(),
- 'size' => 255,
- 'required' => true
- ]);
-
- $attribute2 = $this->client->call(Client::METHOD_POST, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'] . '/attributes/string', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project2['body']['$id'],
- 'x-appwrite-key' => $key2['body']['secret']
- ], [
- 'databaseId' => $database2['body']['$id'],
- 'collectionId' => $collection2['body']['$id'],
- 'key' => ID::unique(),
- 'size' => 255,
- 'required' => true
- ]);
-
- $attribute3 = $this->client->call(Client::METHOD_POST, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'] . '/attributes/string', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project3['body']['$id'],
- 'x-appwrite-key' => $key3['body']['secret']
- ], [
- 'databaseId' => $database3['body']['$id'],
- 'collectionId' => $collection3['body']['$id'],
- 'key' => ID::unique(),
- 'size' => 255,
- 'required' => true
- ]);
-
- $attribute4 = $this->client->call(Client::METHOD_POST, '/databases/' . $database4['body']['$id'] . '/collections/' . $collection4['body']['$id'] . '/attributes/string', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project4['body']['$id'],
- 'x-appwrite-key' => $key4['body']['secret']
- ], [
- 'databaseId' => $database4['body']['$id'],
- 'collectionId' => $collection4['body']['$id'],
- 'key' => ID::unique(),
- 'size' => 255,
- 'required' => true
- ]);
-
- // Wait for attributes
- \sleep(2);
-
- // Create an index in each project
- $index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'] . '/indexes', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project1['body']['$id'],
- 'x-appwrite-key' => $key1['body']['secret']
- ], [
- 'databaseId' => $database1['body']['$id'],
- 'collectionId' => $collection1['body']['$id'],
- 'key' => ID::unique(),
- 'type' => Database::INDEX_KEY,
- 'attributes' => [$attribute1['body']['key']],
- ]);
-
- $index2 = $this->client->call(Client::METHOD_POST, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'] . '/indexes', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project2['body']['$id'],
- 'x-appwrite-key' => $key2['body']['secret']
- ], [
- 'databaseId' => $database2['body']['$id'],
- 'collectionId' => $collection2['body']['$id'],
- 'key' => ID::unique(),
- 'type' => Database::INDEX_KEY,
- 'attributes' => [$attribute2['body']['key']],
- ]);
-
- $index3 = $this->client->call(Client::METHOD_POST, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'] . '/indexes', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project3['body']['$id'],
- 'x-appwrite-key' => $key3['body']['secret']
- ], [
- 'databaseId' => $database3['body']['$id'],
- 'collectionId' => $collection3['body']['$id'],
- 'key' => ID::unique(),
- 'type' => Database::INDEX_KEY,
- 'attributes' => [$attribute3['body']['key']],
- ]);
-
- $index4 = $this->client->call(Client::METHOD_POST, '/databases/' . $database4['body']['$id'] . '/collections/' . $collection4['body']['$id'] . '/indexes', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project4['body']['$id'],
- 'x-appwrite-key' => $key4['body']['secret']
- ], [
- 'databaseId' => $database4['body']['$id'],
- 'collectionId' => $collection4['body']['$id'],
- 'key' => ID::unique(),
- 'type' => Database::INDEX_KEY,
- 'attributes' => [$attribute4['body']['key']],
- ]);
-
- // Wait for indexes
- \sleep(2);
-
- // Assert that each project has only 1 database, 1 collection, 1 attribute and 1 index
- $databasesProject1 = $this->client->call(Client::METHOD_GET, '/databases', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project1['body']['$id'],
- 'x-appwrite-key' => $key1['body']['secret']
- ]);
-
- $this->assertEquals(1, $databasesProject1['body']['total']);
- $this->assertEquals(1, \count($databasesProject1['body']['databases']));
-
- $databasesProject2 = $this->client->call(Client::METHOD_GET, '/databases', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project2['body']['$id'],
- 'x-appwrite-key' => $key2['body']['secret']
- ]);
-
- $this->assertEquals(1, $databasesProject2['body']['total']);
- $this->assertEquals(1, \count($databasesProject2['body']['databases']));
-
- $databasesProject3 = $this->client->call(Client::METHOD_GET, '/databases', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project3['body']['$id'],
- 'x-appwrite-key' => $key3['body']['secret']
- ]);
-
- $this->assertEquals(1, $databasesProject3['body']['total']);
- $this->assertEquals(1, \count($databasesProject3['body']['databases']));
-
- $databasesProject4 = $this->client->call(Client::METHOD_GET, '/databases', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project4['body']['$id'],
- 'x-appwrite-key' => $key4['body']['secret']
- ]);
-
- $this->assertEquals(1, $databasesProject4['body']['total']);
- $this->assertEquals(1, \count($databasesProject4['body']['databases']));
-
- $collectionsProject1 = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project1['body']['$id'],
- 'x-appwrite-key' => $key1['body']['secret']
- ]);
-
- $this->assertEquals(1, $collectionsProject1['body']['total']);
- $this->assertEquals(1, \count($collectionsProject1['body']['collections']));
-
- $collectionsProject2 = $this->client->call(Client::METHOD_GET, '/databases/' . $database2['body']['$id'] . '/collections', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project2['body']['$id'],
- 'x-appwrite-key' => $key2['body']['secret']
- ]);
-
- $this->assertEquals(1, $collectionsProject2['body']['total']);
- $this->assertEquals(1, \count($collectionsProject2['body']['collections']));
-
- $collectionsProject3 = $this->client->call(Client::METHOD_GET, '/databases/' . $database3['body']['$id'] . '/collections', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project3['body']['$id'],
- 'x-appwrite-key' => $key3['body']['secret']
- ]);
-
- $this->assertEquals(1, $collectionsProject3['body']['total']);
- $this->assertEquals(1, \count($collectionsProject3['body']['collections']));
-
- $collectionsProject4 = $this->client->call(Client::METHOD_GET, '/databases/' . $database4['body']['$id'] . '/collections', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project4['body']['$id'],
- 'x-appwrite-key' => $key4['body']['secret']
- ]);
-
- $this->assertEquals(1, $collectionsProject4['body']['total']);
- $this->assertEquals(1, \count($collectionsProject4['body']['collections']));
-
- $attributesProject1 = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'] . '/attributes', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project1['body']['$id'],
- 'x-appwrite-key' => $key1['body']['secret']
- ]);
-
- $this->assertEquals(1, $attributesProject1['body']['total']);
- $this->assertEquals(1, \count($attributesProject1['body']['attributes']));
- $this->assertEquals('available', $attributesProject1['body']['attributes'][0]['status']);
-
- $attributesProject2 = $this->client->call(Client::METHOD_GET, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'] . '/attributes', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project2['body']['$id'],
- 'x-appwrite-key' => $key2['body']['secret']
- ]);
-
- $this->assertEquals(1, $attributesProject2['body']['total']);
- $this->assertEquals(1, \count($attributesProject2['body']['attributes']));
- $this->assertEquals('available', $attributesProject2['body']['attributes'][0]['status']);
-
- $attributesProject3 = $this->client->call(Client::METHOD_GET, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'] . '/attributes', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project3['body']['$id'],
- 'x-appwrite-key' => $key3['body']['secret']
- ]);
-
- $this->assertEquals(1, $attributesProject3['body']['total']);
- $this->assertEquals(1, \count($attributesProject3['body']['attributes']));
- $this->assertEquals('available', $attributesProject3['body']['attributes'][0]['status']);
-
- $attributesProject4 = $this->client->call(Client::METHOD_GET, '/databases/' . $database4['body']['$id'] . '/collections/' . $collection4['body']['$id'] . '/attributes', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project4['body']['$id'],
- 'x-appwrite-key' => $key4['body']['secret']
- ]);
-
- $this->assertEquals(1, $attributesProject4['body']['total']);
- $this->assertEquals(1, \count($attributesProject4['body']['attributes']));
- $this->assertEquals('available', $attributesProject4['body']['attributes'][0]['status']);
-
- $indexesProject1 = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'] . '/indexes', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project1['body']['$id'],
- 'x-appwrite-key' => $key1['body']['secret']
- ]);
-
- $this->assertEquals(1, $indexesProject1['body']['total']);
- $this->assertEquals(1, \count($indexesProject1['body']['indexes']));
-
- $indexesProject2 = $this->client->call(Client::METHOD_GET, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'] . '/indexes', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project2['body']['$id'],
- 'x-appwrite-key' => $key2['body']['secret']
- ]);
-
- $this->assertEquals(1, $indexesProject2['body']['total']);
- $this->assertEquals(1, \count($indexesProject2['body']['indexes']));
-
- $indexesProject3 = $this->client->call(Client::METHOD_GET, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'] . '/indexes', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project3['body']['$id'],
- 'x-appwrite-key' => $key3['body']['secret']
- ]);
-
- $this->assertEquals(1, $indexesProject3['body']['total']);
- $this->assertEquals(1, \count($indexesProject3['body']['indexes']));
-
- $indexesProject4 = $this->client->call(Client::METHOD_GET, '/databases/' . $database4['body']['$id'] . '/collections/' . $collection4['body']['$id'] . '/indexes', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project4['body']['$id'],
- 'x-appwrite-key' => $key4['body']['secret']
- ]);
-
- $this->assertEquals(1, $indexesProject4['body']['total']);
- $this->assertEquals(1, \count($indexesProject4['body']['indexes']));
-
- // Attempt to read cross-type resources
- $collectionProject2WithProject1Key = $this->client->call(Client::METHOD_GET, '/databases/' . $database2['body']['$id'] . '/collections/' . $collection2['body']['$id'], [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project1['body']['$id'],
- 'x-appwrite-key' => $key1['body']['secret']
- ]);
-
- $this->assertEquals(404, $collectionProject2WithProject1Key['headers']['status-code']);
-
- $collectionProject1WithProject2Key = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'], [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project2['body']['$id'],
- 'x-appwrite-key' => $key2['body']['secret']
- ]);
-
- $this->assertEquals(404, $collectionProject1WithProject2Key['headers']['status-code']);
-
- // Attempt to read cross-tenant resources
- $collectionProject3WithProject1Key = $this->client->call(Client::METHOD_GET, '/databases/' . $database3['body']['$id'] . '/collections/' . $collection3['body']['$id'], [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project1['body']['$id'],
- 'x-appwrite-key' => $key1['body']['secret']
- ]);
-
- $this->assertEquals(404, $collectionProject3WithProject1Key['headers']['status-code']);
-
- $collectionProject1WithProject3Key = $this->client->call(Client::METHOD_GET, '/databases/' . $database1['body']['$id'] . '/collections/' . $collection1['body']['$id'], [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project3['body']['$id'],
- 'x-appwrite-key' => $key3['body']['secret']
- ]);
-
- $this->assertEquals(404, $collectionProject1WithProject3Key['headers']['status-code']);
-
- // Assert that shared project resources can have the same ID as they're unique on tenant + ID not just ID
- $collection5 = $this->client->call(Client::METHOD_POST, '/databases/' . $database2['body']['$id'] . '/collections', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project2['body']['$id'],
- 'x-appwrite-key' => $key2['body']['secret']
- ], [
- 'databaseId' => $database2['body']['$id'],
- 'collectionId' => $collection4['body']['$id'],
- 'name' => 'Amazing Collection',
- ]);
-
- $this->assertEquals(201, $collection5['headers']['status-code']);
-
- // Assert that users across projects on shared tables can have the same email as they're unique on tenant + email not just email
- $user1 = $this->client->call(Client::METHOD_POST, '/users', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project2['body']['$id'],
- 'x-appwrite-key' => $key2['body']['secret']
- ], [
- 'userId' => 'user',
- 'email' => 'test@appwrite.io',
- 'password' => 'password',
- 'name' => 'Test User',
- ]);
-
- $this->assertEquals(201, $user1['headers']['status-code']);
-
- $user2 = $this->client->call(Client::METHOD_POST, '/users', [
- 'content-type' => 'application/json',
- 'x-appwrite-project' => $project4['body']['$id'],
- 'x-appwrite-key' => $key4['body']['secret']
- ], [
- 'userId' => 'user',
- 'email' => 'test@appwrite.io',
- 'password' => 'password',
- 'name' => 'Test User',
- ]);
-
- $this->assertEquals(201, $user2['headers']['status-code']);
- }
}
diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php
index a6334d8091..d2d75409b8 100644
--- a/tests/e2e/Services/Storage/StorageBase.php
+++ b/tests/e2e/Services/Storage/StorageBase.php
@@ -225,7 +225,7 @@ trait StorageBase
'bucketId' => ID::unique(),
'name' => 'Test Bucket 2',
'fileSecurity' => true,
- 'maximumFileSize' => 200000000, //200MB
+ 'maximumFileSize' => 6000000000, //6GB
'allowedFileExtensions' => ["jpg", "png"],
'permissions' => [
Permission::read(Role::any()),
diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php
index 1737e0483d..a24b6f8161 100644
--- a/tests/e2e/Services/Users/UsersBase.php
+++ b/tests/e2e/Services/Users/UsersBase.php
@@ -290,6 +290,28 @@ trait UsersBase
$this->assertArrayNotHasKey('secret', $token['body']);
}
+ /**
+ * @depends testCreateUser
+ */
+ public function testCreateSession(array $data): void
+ {
+ /**
+ * Test for SUCCESS
+ */
+ $response = $this->client->call(Client::METHOD_POST, '/users/' . $data['userId'] . '/sessions', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()));
+
+ $this->assertEquals(201, $response['headers']['status-code']);
+
+ $session = $response['body'];
+ $this->assertEquals($data['userId'], $session['userId']);
+ $this->assertNotEmpty($session['secret']);
+ $this->assertNotEmpty($session['expire']);
+ $this->assertEquals('server', $session['provider']);
+ }
+
/**
* Tests all optional parameters of createUser (email, phone, anonymous..)
@@ -986,7 +1008,7 @@ trait UsersBase
'password' => 'password'
]);
- $this->assertEquals($session['headers']['status-code'], 401);
+ $this->assertEquals(401, $session['headers']['status-code']);
$user = $this->client->call(Client::METHOD_PATCH, '/users/' . $data['userId'] . '/password', array_merge([
'content-type' => 'application/json',