diff --git a/CHANGES.md b/CHANGES.md
index e680482ce1..0fd4bc1bca 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -2,15 +2,23 @@
## Features
-- New route in Locale API to fetch a list of languages
-- Added option to force HTTPS connection to the Appwrite server (_APP_OPTIONS_FORCE_HTTPS)
+- New route in Locale API to fetch a list of languages (@TorstenDittmann)
- Added Google Fonts to Appwrite for offline availability
- Added a new route in the Avatars API to get user initials avatar
- Added option to delete team from the console
- Added option to view team members from the console
- Added option to join a user to any team from the console
-- Added support for Brotli compression
-- UI performance & accessibility improvments
+- Added support for Brotli compression (@PedroCisnerosSantana, @Rohitub222)
+- New UI micro-interactions and CSS fixes (@AnatoleLucet)
+- UI performance & accessibility improvments (#406)
+- Updated ClamAV conntainer to version 1.0.9
+- New Doctor CLI to debug the Appwrite server ([#415](https://github.com/appwrite/appwrite/issues/415))
+- All emails are now sent asynchronously for improved performance (@TorstenDittmann)
+- Updated grid for OAuth2 providers list in the console
+- Upgraded Redis Resque queue library to version 1.3.6
+- Added container names to docker-compose.yml (@drandell)
+- Upgraded ClamAV container image to version 1.0.9
+- Optimised function execution by using fully-qualified function calls
## Bug Fixes
@@ -20,10 +28,18 @@
- Fixed a UI bug preventing float values in numeric fields
- Fixed scroll positioning when moving rules order up & down
- Fixed missing validation for database documents key length (32 chars)
+- Grammer fix for pt-br email templates (@rubensdemelo)
+- Fixed update form labels and tooltips for Flutter Android apps
+- Fixed missing custom scopes param for OAuth2 session create API route
+- Fixed wrong JSON validation when creating and updating database documnets
+- Fixed bug where max file size was limited to max of 10MB
+- Fixed bug preventing the deletion of the project logo
## Security
- Access to Health API now requires authentication with an API Key with access to `health.read` scope allowed
+- Added option to force HTTPS connection to the Appwrite server (_APP_OPTIONS_FORCE_HTTPS)
+- Now using your `_APP_SYSTEM_EMAIL_ADDRESS` as the email address for issuing and renewing SSL certificates
# Version 0.6.2 (PRE-RELEASE)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 39d9a574c2..65e3446335 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -159,6 +159,26 @@ To run tests manually, run phpunit from your command line:
docker exec appwrite test
```
+## Code Maintenance
+
+We use some automation tools to help us keep a healthy code base.
+
+Improve PHP exeution time by using [fully-qualified function calls](https://veewee.github.io/blog/optimizing-php-performance-by-fq-function-calls/):
+
+```bash
+php-cs-fixer fix src/ --rules=native_function_invocation --allow-risky=yes
+```
+
+Coding Standards:
+
+```bash
+php-cs-fixer fix app/controllers --rules='{"braces": {"allow_single_line_closure": true}}'
+```
+
+```bash
+php-cs-fixer fix src --rules='{"braces": {"allow_single_line_closure": true}}'
+```
+
## Tutorials
From time to time, our team will add tutorials that will help contributors find their way in the Appwrite source code. Below is a list of currently available tutorials:
diff --git a/Dockerfile b/Dockerfile
index e4ffd73eb9..867812cbbd 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -139,13 +139,12 @@ RUN \
# Set Upload Limit (default to 100MB)
RUN echo "upload_max_filesize = ${_APP_STORAGE_LIMIT}" >> /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
RUN echo "post_max_size = ${_APP_STORAGE_LIMIT}" >> /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
-RUN echo "env[TESTME] = your-secret-key" >> /etc/php/$PHP_VERSION/fpm/conf.d/appwrite.ini
# Add logs file
RUN echo "" >> /var/log/appwrite.log
# Nginx Configuration (with self-signed ssl certificates)
-COPY ./docker/nginx.conf /etc/nginx/nginx.conf
+COPY ./docker/nginx.conf.template /etc/nginx/nginx.conf.template
COPY ./docker/ssl/cert.pem /etc/nginx/ssl/cert.pem
COPY ./docker/ssl/key.pem /etc/nginx/ssl/key.pem
@@ -175,6 +174,7 @@ COPY ./docker/supervisord.conf /etc/supervisord.conf
# Executables
RUN chmod +x /usr/local/bin/start
+RUN chmod +x /usr/local/bin/doctor
RUN chmod +x /usr/local/bin/migrate
RUN chmod +x /usr/local/bin/test
diff --git a/README.md b/README.md
index bc78047f0f..d0900ade4f 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
+
+
+
\ No newline at end of file
diff --git a/app/config/locales/templates/_cta.tpl b/app/config/locales/templates/_cta.tpl
new file mode 100644
index 0000000000..634609a09e
--- /dev/null
+++ b/app/config/locales/templates/_cta.tpl
@@ -0,0 +1,26 @@
+
\ No newline at end of file
diff --git a/app/config/locales/templates/af.email.auth.confirm.tpl b/app/config/locales/templates/af.email.auth.confirm.tpl
index e0a6c40356..a9dbb2af46 100644
--- a/app/config/locales/templates/af.email.auth.confirm.tpl
+++ b/app/config/locales/templates/af.email.auth.confirm.tpl
@@ -1,24 +1,15 @@
-
-
-
+
Hallo {{name}},
-
-
+
+
Om jou epos adres te verifieer, kliek op die web adres hier:
-
- {{redirect}}
-
-
+
+{{cta}}
+
As jy nie aangevra het om jou epos adres te verifieer nie, kan jy die boodskap ignoreer.
-
-
+
+
Baie dankie,
{{project}} span
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/af.email.auth.invitation.tpl b/app/config/locales/templates/af.email.auth.invitation.tpl
index eed6cf69d1..0c1c72c13a 100644
--- a/app/config/locales/templates/af.email.auth.invitation.tpl
+++ b/app/config/locales/templates/af.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Hallo,
-
-
+
+
Hierdie epos is vir jou gestuur omdat {{owner}} jou graag wou nooi om 'n spanlid te word van {{team}} by {{project}}.
-
-
+
+
Om by die {{team}} span aan te sluit:
-
- {{redirect}}
-
-
+
+{{cta}}
+
As jy nie belangstel nie, kan jy die boodskap ignoreer.
-
-
+
اتبع هذا الرابط للتحقق من عنوان بريدك الإلكتروني.
-
- {{redirect}}
-
-
+
+{{cta}}
+
إذا لم تطلب التحقق من هذا العنوان، فيمكنك تجاهل هذه الرسالة.
-
-
+
+
Thanks,
فريق {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/ar.email.auth.invitation.tpl b/app/config/locales/templates/ar.email.auth.invitation.tpl
index fba4a731e3..78a1321dab 100644
--- a/app/config/locales/templates/ar.email.auth.invitation.tpl
+++ b/app/config/locales/templates/ar.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
مرحبا،
-
-
+
+
تم إرسال هذه الرسالة إليك لأن {{owner}} أراد دعوتك لتصبح عضوًا في فريق {{team}} في {{project}}.
-
-
+
+
اتبع هذا الرابط للانضمام إلى فريق {{team}}:
-
- {{redirect}}
-
-
+
bitte folge diesem Link um deine E-Mail Adresse zu verifizieren.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Bitte ignoriere diese Nachricht, wenn du das Verifizieren deiner E-Mail Adresse nicht beantragt hast.
-
-
+
+
Vielen Dank,
{{project}} Team
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/de.email.auth.invitation.tpl b/app/config/locales/templates/de.email.auth.invitation.tpl
index c6d9162730..ae55630797 100644
--- a/app/config/locales/templates/de.email.auth.invitation.tpl
+++ b/app/config/locales/templates/de.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Hallo,
-
-
+
+
diese E-Mail wurde dir geschickt, weil {{owner}} dich eingeladen hat Teammitglied im Team {{team}} bei {{project}} zu werden.
-
-
+
+
Folge diesem Link um dem Team {{team}} beizutreten:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Wenn du daran nicht interessiert bist, kannst du diese Nachricht ignorieren.
-
-
+
Follow this link to verify your email address.
-
- {{redirect}}
-
-
+
+{{cta}}
+
If you didn’t ask to verify this address, you can ignore this message.
-
-
+
+
Thanks,
{{project}} team
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/en.email.auth.invitation.tpl b/app/config/locales/templates/en.email.auth.invitation.tpl
index fda2bbcce0..70ea922c0c 100644
--- a/app/config/locales/templates/en.email.auth.invitation.tpl
+++ b/app/config/locales/templates/en.email.auth.invitation.tpl
@@ -1,27 +1,14 @@
-
-
-
- Hello,
-
-
+
+ Hello,
+
+
This mail was sent to you because {{owner}} wanted to invite you to become a team member at the {{team}} team over at {{project}}.
-
-
- Follow this link to join the {{team}} team:
-
- {{redirect}}
-
-
- If you are not interested, you can ignore this message.
-
-
+
+{{cta}}
+
+ If you are not interested, you can ignore this message.
+
Thanks,
{{project}} team
-
+
\ No newline at end of file
diff --git a/app/config/locales/templates/en.email.auth.recovery.tpl b/app/config/locales/templates/en.email.auth.recovery.tpl
index 82b365546a..a4a982f46f 100644
--- a/app/config/locales/templates/en.email.auth.recovery.tpl
+++ b/app/config/locales/templates/en.email.auth.recovery.tpl
@@ -1,24 +1,15 @@
-
-
-
+
Hello {{name}},
-
-
+
+
Follow this link to reset your {{project}} password.
-
- {{redirect}}
-
-
- If you didn't ask to reset your password, you can ignore this message.
-
-
+
+{{cta}}
+
+ If you didn’t ask to verify this address, you can ignore this message.
+
+
Thanks,
{{project}} team
-
+
\ No newline at end of file
diff --git a/app/config/locales/templates/es.email.auth.confirm.tpl b/app/config/locales/templates/es.email.auth.confirm.tpl
index a5b9235be8..2c728ddce1 100644
--- a/app/config/locales/templates/es.email.auth.confirm.tpl
+++ b/app/config/locales/templates/es.email.auth.confirm.tpl
@@ -1,24 +1,15 @@
-
-
-
+
Hola {{name}},
-
-
+
+
Sigue este enlace para verificar tu dirección de correo.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Si no has solicitado verificar esta dirección, puedes ignorar este mensaje.
-
-
+
Varmista sähköpostiosoite tästä linkistä.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Jos et kysynyt tämän sähköpostiosoitteen varmistamista, voit sivuuttaa tämän viestin.
-
-
+
+
kiitos,
{{project}} tiimi
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/fi.email.auth.invitation.tpl b/app/config/locales/templates/fi.email.auth.invitation.tpl
index 7e955d0216..6680259aa7 100644
--- a/app/config/locales/templates/fi.email.auth.invitation.tpl
+++ b/app/config/locales/templates/fi.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Hei,
-
-
+
+
Sait tämän sähköpostin koska {{owner}} halusi kutsua sinut jäseneksi {{team}} tiimiin, täällä {{project}}.
-
-
+
Jos et pyytänyt salasanan nollaamista, voit sivuuttaa tämän viestin-
-
-
+
+
Kiitos,
{{project}} tiimi
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/fo.email.auth.confirm.tpl b/app/config/locales/templates/fo.email.auth.confirm.tpl
index 9af52651b8..5400c2ab01 100644
--- a/app/config/locales/templates/fo.email.auth.confirm.tpl
+++ b/app/config/locales/templates/fo.email.auth.confirm.tpl
@@ -1,24 +1,15 @@
-
-
-
Om tú ikke spurdi om at verifisera hesa teldupostur, kannst tú ignorera hesa boð.
-
-
+
+
Takk,
{{project}} lið
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/fo.email.auth.invitation.tpl b/app/config/locales/templates/fo.email.auth.invitation.tpl
index 586daac0e4..c264c7516f 100644
--- a/app/config/locales/templates/fo.email.auth.invitation.tpl
+++ b/app/config/locales/templates/fo.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Halló,
-
-
+
+
Hesa teldupost var send til tín tí {{owner}} vildi bjóða tær at vera eitt lið limur hjá {{team}} á {{project}}
-
-
+
+
Fylg hettar lenka fyrið at bliva vi {{team}} lið:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Om tú ikke er áhugaður, kannst tú ignorera hesa boð
-
-
+
Ακολούθησε αυτό τον σύνδεσμο για να επιβεβαιώσεις τη διεύθυνση email σου.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Αν δεν ζήτησες να επιβεβαιώσεις αυτή τη διεύθυνση, μπορείς να αγνοήσεις αυτό το μήνυμα.
-
-
+
+
Ευχαριστούμε,
Η ομάδα του {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/gr.email.auth.invitation.tpl b/app/config/locales/templates/gr.email.auth.invitation.tpl
index 101a1c673c..a7c0743ff6 100644
--- a/app/config/locales/templates/gr.email.auth.invitation.tpl
+++ b/app/config/locales/templates/gr.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Γεια,
-
-
+
+
Έλαβες αυτό το email επειδή ο {{owner}} σε προσκάλεσε να γίνεις μέλος της ομάδας {{team}} στο {{project}}.
-
-
+
+
Ακολούθησε αυτό τον σύνδεσμο για να γίνεις μέλος της ομάδας {{team}}:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Άν δεν ενδιαφέρεσαι, μπορείς να αγνοήσεις αυτό το μήνυμα.
-
-
+
נא ללחוץ על הקישור שלהלן כדי לאמת את החשבון שלך.
-
- {{redirect}}
-
-
+
+{{cta}}
+
אם לא ביקשת לאמת את כתובת הדוא״ל, ניתן להתעלם מההודעה זו.
-
-
+
+
בברכה,
צוות {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/he.email.auth.invitation.tpl b/app/config/locales/templates/he.email.auth.invitation.tpl
index a7514affda..9a9226a5ba 100644
--- a/app/config/locales/templates/he.email.auth.invitation.tpl
+++ b/app/config/locales/templates/he.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
שלום,
-
-
+
+
הודעת דוא״ל זו נשלחה אליך כי {{owner}} ביקש להזמינך להצטרף לצוות {{team}} ב־{{project}}.
-
-
+
+
כדי להצטרף לצוות {{team}}, נא ללחוץ על הקישור:
-
- {{redirect}}
-
-
+
+{{cta}}
+
אם איך לך עניין להצטרף לצוות, ניתן להתעלם מהודעת דוא״ל זו.
-
-
+
Անցեք հղումով, որպեսզի հաստատեք Ձեր էլեկտրոնային հասցեն։
-
- {{redirect}}
-
-
+
+{{cta}}
+
Եթե չեք պահանջել էլեկտրոնային հասցեի հաստատում, պարզապես արհամարհեք այս նամակը։
-
-
+
+
Շնորհակալություն,
{{project}} թիմ
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/hy.email.auth.invitation.tpl b/app/config/locales/templates/hy.email.auth.invitation.tpl
index e471c81b63..2d0f125403 100644
--- a/app/config/locales/templates/hy.email.auth.invitation.tpl
+++ b/app/config/locales/templates/hy.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Ողջույն,
-
-
+
+
Դուք ստացել եք այս նամակը, քանի որ {{owner}}-ը հրավիրում է Ձեզ {{team}} խումբ, {{project}} պրոեկտում։
-
-
+
Ikuti link ini untuk memverifikasi alamat email Anda.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Jika Anda tidak meminta untuk memverifikasi alamat ini, Anda dapat mengabaikan pesan ini.
-
-
+
+
Terima kasih,
Tim {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/id.email.auth.invitation.tpl b/app/config/locales/templates/id.email.auth.invitation.tpl
index 7ab49b2002..d81a4a5d83 100644
--- a/app/config/locales/templates/id.email.auth.invitation.tpl
+++ b/app/config/locales/templates/id.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Halo,
-
-
+
+
Email ini dikirimkan kepada Anda karena {{owner}} ingin mengundang Anda untuk menjadi anggota tim {{team}} di {{project}}.
-
-
+
+
Ikuti link ini untuk bergabung dengan tim {{team}}:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Jika Anda tidak tertarik, Anda dapat mengabaikan pesan ini.
-
-
+
Þessi póstur var sendur til þín vegna þess að {{owner}} vildi bjóða þér að gerast liðsmaður í {{team}} teymi í {{project}}.
-
-
+
+
Fylgdu þessum hlekk til að ganga í {{team}} liðið:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Ef þú hefur ekki áhuga geturðu hunsað þessi skilaboð.
-
-
+
+
Takk,
{{project}} Teymi
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/is.email.auth.recovery.tpl b/app/config/locales/templates/is.email.auth.recovery.tpl
index fc3bc57722..edddba1ffb 100644
--- a/app/config/locales/templates/is.email.auth.recovery.tpl
+++ b/app/config/locales/templates/is.email.auth.recovery.tpl
@@ -1,24 +1,15 @@
-
-
-
+
Halló {{name}},
-
-
+
+
Fylgdu þessum tengli til að núllstilla lykilorð {{project}}.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Ef þú baðst ekki um að endurstilla lykilorðið þitt geturðu hunsað þessi skilaboð.
-
-
+
+
Takk,
{{project}} Teymi
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/it.email.auth.confirm.tpl b/app/config/locales/templates/it.email.auth.confirm.tpl
index 93ee5e006f..ec7837ec3d 100644
--- a/app/config/locales/templates/it.email.auth.confirm.tpl
+++ b/app/config/locales/templates/it.email.auth.confirm.tpl
@@ -1,25 +1,16 @@
-
-
-
+
Ciao {{name}},
-
-
+
+
Segui questo link per verificare il tuo indirizzo email.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Se non hai chiesto di verificare questo indirizzo, puoi ignorare questo messaggio.
-
-
+
+
Grazie,
Il team di {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/it.email.auth.invitation.tpl b/app/config/locales/templates/it.email.auth.invitation.tpl
index e6eddb5d1b..f4b8e4f93b 100644
--- a/app/config/locales/templates/it.email.auth.invitation.tpl
+++ b/app/config/locales/templates/it.email.auth.invitation.tpl
@@ -1,28 +1,19 @@
-
-
-
+
Ciao,
-
-
+
+
Questa mail ti è stata inviata perchè {{owner}} vuole invitarti a diventare un membro del team {{team}} del progetto {{project}}.
-
-
+
+
Segui questo link per unirti al team {{team}}:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Se non sei interessato, puoi ignorare questo messaggio.
-
-
+
+
Grazie,
Il team di {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/it.email.auth.recovery.tpl b/app/config/locales/templates/it.email.auth.recovery.tpl
index 64e865fe76..870b3ab0f5 100644
--- a/app/config/locales/templates/it.email.auth.recovery.tpl
+++ b/app/config/locales/templates/it.email.auth.recovery.tpl
@@ -1,24 +1,15 @@
-
-
-
+
Ciao {{name}},
-
-
+
+
Segui questo link per reimpostare la tua password per {{project}}.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Se non hai chiesto di reimpostare la password, puoi ignorare questo messaggio.
-
-
+
+
Grazie,
Il team di {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/ja.email.auth.confirm.tpl b/app/config/locales/templates/ja.email.auth.confirm.tpl
index 6c7a89f355..a06f96cd83 100644
--- a/app/config/locales/templates/ja.email.auth.confirm.tpl
+++ b/app/config/locales/templates/ja.email.auth.confirm.tpl
@@ -1,24 +1,15 @@
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/ja.email.auth.invitation.tpl b/app/config/locales/templates/ja.email.auth.invitation.tpl
index ed59caff07..26d5de981b 100644
--- a/app/config/locales/templates/ja.email.auth.invitation.tpl
+++ b/app/config/locales/templates/ja.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
Email iki dikirim menyang sampeyan amarga {{owner}} pengin ngajak sampeyan dadi anggota tim ing {{team}} tim ing {{project}}.
-
-
+
+
Tindakake link iki kanggo gabung ing {{team}}:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Yen sampeyan ora kasengsem, sampeyan bisa nglirwakake pesen iki.
-
-
+
+
Matur suwun,
tim {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/jv.email.auth.recovery.tpl b/app/config/locales/templates/jv.email.auth.recovery.tpl
index ed6c879aeb..1efd86bc77 100644
--- a/app/config/locales/templates/jv.email.auth.recovery.tpl
+++ b/app/config/locales/templates/jv.email.auth.recovery.tpl
@@ -1,24 +1,15 @@
-
-
-
+
Halo {{name}},
-
-
+
+
Tindakake link iki kanggo ngreset {{project}} sandhi.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Yen sampeyan ora njaluk ngreset sandhi, sampeyan bisa nglalekake pesen iki.
-
-
+
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/km.email.auth.invitation.tpl b/app/config/locales/templates/km.email.auth.invitation.tpl
index 910a14451f..a366ed73e9 100644
--- a/app/config/locales/templates/km.email.auth.invitation.tpl
+++ b/app/config/locales/templates/km.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/ko.email.auth.invitation.tpl b/app/config/locales/templates/ko.email.auth.invitation.tpl
index 4fc62b8516..21ed74de68 100644
--- a/app/config/locales/templates/ko.email.auth.invitation.tpl
+++ b/app/config/locales/templates/ko.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
Ikuti pautan ini untuk mengesahkan alamat e-mel anda.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Jika anda tidak meminta untuk mengesahkan alamat ini, anda boleh mengabaikan mesej ini.
-
-
+
+
Terima kasih,
Kumpulan {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/my.email.auth.invitation.tpl b/app/config/locales/templates/my.email.auth.invitation.tpl
index 891e1eb4de..66e38bb608 100644
--- a/app/config/locales/templates/my.email.auth.invitation.tpl
+++ b/app/config/locales/templates/my.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Hello,
-
-
+
+
E-mel ini dihantar kepada anda kerana {{owner}}ingin mengundang Anda untuk menjadi anggota kumpulan {{team}} di dalam {{project}}.
-
-
+
+
Ikuti pautan ini untuk menyertai kumpulan {{team}}:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Sekiranya anda tidak berminat,anda boleh mengabaikan mesej ini.
-
-
+
Klink op deze link om uw emailadres te valideren.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Als u niet heeft gevraagd om dit adres te verifiëren, kunt u dit bericht negeren.
-
-
+
+
Bedankt,
{{project}} team
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/nl.email.auth.invitation.tpl b/app/config/locales/templates/nl.email.auth.invitation.tpl
index 252e104aff..75ac6f4510 100644
--- a/app/config/locales/templates/nl.email.auth.invitation.tpl
+++ b/app/config/locales/templates/nl.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Hallo,
-
-
+
+
Deze mail is naar je gestuurd omdat {{owner}} wilde dat u een lid zou worden in het {{team}} team voor {{project}}.
-
-
+
+
Volg deze link om het team te joinen {{team}}:
-
- {{redirect}}
-
-
+
+{{cta}}
+
ALs u niet geïnteresseerd bent, kunt u dit bericht negeren.
-
-
+
Følg denne lenken for å verifisere din e-postadresse.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Hvis du ikke har spurt om å verifisere din e-post, kan du ignorere denne meldingen.
-
-
+
+
Hilsen,
{{project}}-teamet
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/no.email.auth.invitation.tpl b/app/config/locales/templates/no.email.auth.invitation.tpl
index c56e913d3d..00cd9defe5 100644
--- a/app/config/locales/templates/no.email.auth.invitation.tpl
+++ b/app/config/locales/templates/no.email.auth.invitation.tpl
@@ -1,28 +1,19 @@
-
-
-
+
Hei,
-
-
+
+
Denne mailen ble sendt til deg fordi {{owner}} har invitert deg til å bli medlem av {{team}}-teamet på {{project}}.
-
-
+
+
Follow this link to join the {{team}} team:
Følg denne lenken for å bli med på {{team}}-teamet:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Hvis du ikke er interresert kan du ignorere denne meldingen.
-
-
+
Sundun ang link na ito upang ma-verify ang iyong email address.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Kung hindi mo hiniling na i-verify ang address na ito, maaari mong balewalain ang mensaheng ito.
-
-
+
+
Salamat,
Pangkat ng {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/ph.email.auth.invitation.tpl b/app/config/locales/templates/ph.email.auth.invitation.tpl
index f8a8668e50..23916efb0c 100644
--- a/app/config/locales/templates/ph.email.auth.invitation.tpl
+++ b/app/config/locales/templates/ph.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Kamusta,
-
-
+
+
Ipinadala ang email na ito sa iyo dahil nais kang anyayahan ni {{ owner }} upang maging isang kasapi ng pangkat ng {{ team }} sa {{ project }}.
-
-
+
+
Sundan ang link na ito upang sumali sa pangkat ng {{team}}:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Kung hindi ka interesado, maari mong balewalain ang mensahing ito.
-
-
+
Kliknij ten link, aby zweryfikować swój adres e-mail.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Jeśli nie prosiłeś o weryfikację tego adresu, możesz zignorować tę wiadomość.
-
-
+
+
Dziękujemy,
zespół {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/pl.email.auth.invitation.tpl b/app/config/locales/templates/pl.email.auth.invitation.tpl
index d11586de75..387b2e4ae8 100644
--- a/app/config/locales/templates/pl.email.auth.invitation.tpl
+++ b/app/config/locales/templates/pl.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Witaj,
-
-
+
+
Ta wiadomość została do Ciebie wysłana, ponieważ {{owner}} chciałby zaprosić Cię do dołączenia do zespołu {{team}} w {{project}}.
-
-
+
+
Kliknij ten link, aby dołączyć do zespołu {{team}}:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Jeśli nie jesteś zainteresowany, możesz zignorować tę wiadomość.
-
-
+
Acesse o link abaixo para redefinir sua senha do {{project}}.
-
- {{redirect}}
-
-
- Caso você não solicitou a redefinição de senha, por favor ignore esta mensagem.
-
-
+
+{{cta}}
+
+ Caso não tenha solicitado a redefinição de senha, por favor ignore esta mensagem.
+
Por favor, confirme o seu email através do link abaixo.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Se não solicitou a confirmação de email, por favor ignore esta mensagem.
-
-
+
+
Com os melhores cumprimentos,
Equipa {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/pt-pt.email.auth.invitation.tpl b/app/config/locales/templates/pt-pt.email.auth.invitation.tpl
index 429a30be9a..c0bdfa4136 100644
--- a/app/config/locales/templates/pt-pt.email.auth.invitation.tpl
+++ b/app/config/locales/templates/pt-pt.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Olá,
-
-
+
+
Recebeu este email porque {{owner}} deseja convidá-lo a tornar-se membro da equipa {{team}} no {{project}}.
-
-
+
+
Use este link para se juntar à equipa {{team}}:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Se não estiver interessado, por favor ignore esta mensagem.
-
-
+
+
Com os melhores cumprimentos,
Equipa {{project}}
-
Перейдите по ссылке, чтобы подтвердить свой адрес электронной почты.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Если вы не запрашивали подтверждение этого адреса, проигнорируйте это сообщение.
-
-
+
+
Спасибо,
команда {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/ru.email.auth.invitation.tpl b/app/config/locales/templates/ru.email.auth.invitation.tpl
index 41de21d09f..7dc637a3de 100644
--- a/app/config/locales/templates/ru.email.auth.invitation.tpl
+++ b/app/config/locales/templates/ru.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Здравствуйте,
-
-
+
+
Это письмо отправлено вам, потому что {{owner}} приглашает стать членом команды {{team}} в проекте {{project}}.
-
-
+
+
Перейдите по ссылке, чтобы присоединиться к команде {{team}} :
-
- {{redirect}}
-
-
+
+{{cta}}
+
Если вы не заинтересованы, проигнорируйте это сообщение.
-
-
+
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/si.email.auth.invitation.tpl b/app/config/locales/templates/si.email.auth.invitation.tpl
index 1a0842d2e8..2fa979751e 100644
--- a/app/config/locales/templates/si.email.auth.invitation.tpl
+++ b/app/config/locales/templates/si.email.auth.invitation.tpl
@@ -1,31 +1,22 @@
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/si.email.auth.recovery.tpl b/app/config/locales/templates/si.email.auth.recovery.tpl
index bcf32b61ba..4df6902070 100644
--- a/app/config/locales/templates/si.email.auth.recovery.tpl
+++ b/app/config/locales/templates/si.email.auth.recovery.tpl
@@ -1,25 +1,15 @@
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/sl.email.auth.confirm.tpl b/app/config/locales/templates/sl.email.auth.confirm.tpl
index e7f15272da..fb6c4a59d1 100644
--- a/app/config/locales/templates/sl.email.auth.confirm.tpl
+++ b/app/config/locales/templates/sl.email.auth.confirm.tpl
@@ -1,24 +1,15 @@
-
-
-
+
Pozdravljeni {{name}},
-
-
+
+
Sledite tej povezavi za potrditev vašega email naslova.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Če niste zahtevali potrditve tega naslova, lahko to sporočilo prezrete.
-
-
+
+
Hvala,
{{project}} ekipa
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/sl.email.auth.invitation.tpl b/app/config/locales/templates/sl.email.auth.invitation.tpl
index 13467789a3..0dc64594b0 100644
--- a/app/config/locales/templates/sl.email.auth.invitation.tpl
+++ b/app/config/locales/templates/sl.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Pozdravljeni,
-
-
+
+
To sporočilo vam je bilo posredovano, ker vas je {{owner}} povabil/a, da postanete član {{team}} ekipe za {{project}}.
-
-
+
+
Sledite tej povezavi, da se pridružite {{team}} ekipi:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Če vas ne zanima, lahko to sporočilo prezrete.
-
-
+
Vänligen följ länken nedan för att verifiera din epostadress.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Om du inte vill verifiera din epostadress så kan du ignorera detta meddelande.
-
-
+
+
Tack,
{{project}}-teamet
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/sv.email.auth.invitation.tpl b/app/config/locales/templates/sv.email.auth.invitation.tpl
index 604e94227d..12f4ddf858 100644
--- a/app/config/locales/templates/sv.email.auth.invitation.tpl
+++ b/app/config/locales/templates/sv.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Hej,
-
-
+
+
{{owner}} vill bjuda in dig att bli del av {{team}}-teamet inom {{project}}.
-
-
+
+
Följ denna länk för att bli del av {{team}}-teamet:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Om du inte är intresserad så kan du ignorera detta meddelande.
-
-
+
இந்த மின்னஞ்சல் உங்களுக்கு அனுப்பப்பட்டது, ஏனெனில் {{owner}} உங்களை {{team}} குழுவின் {{project}} திட்டத்தில் உறுப்பினராக அழைக்க விரும்பினார்கள்.
-
-
+
+
இந்த இணைப்பைப் பின்தொடர்ந்து {{team}} குழுவில் சேரவும்:
-
- {{redirect}}
-
-
+
+{{cta}}
+
இந்த முகவரியை சரிபார்க்க நீங்கள் கேட்கவில்லை என்றால், இந்த செய்தியை நீங்கள் புறக்கணிக்கலாம்.
-
-
+
Aşağıdaki bağlantıyı takip ederek email hesabınızı doğrulayın.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Bu adresi doğrulamayı istemediyseniz, bu mesajı yok sayabilirsiniz.
-
-
+
+
Teşekkürler,
{{project}} takımı
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/tr.email.auth.invitation.tpl b/app/config/locales/templates/tr.email.auth.invitation.tpl
index 07e8f06db4..c3c35b6bbd 100644
--- a/app/config/locales/templates/tr.email.auth.invitation.tpl
+++ b/app/config/locales/templates/tr.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Merhaba,
-
-
+
+
Bu posta size gönderildi, çünkü {{owner}} sizi {{project}} için {{team}} ekibinde takım üyesi olmaya davet etmek istedi.
-
-
+
+
Aşağıdaki bağlantıyı takip ederek {{team}} takımına takılın:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Eğer ilgilenmiyorsanız, bu mesajı yok sayabilirsiniz.
-
-
+
Перейдіть за цим посиланням, та підтвердіть свою електронну адресу
-
- {{redirect}}
-
-
+
+{{cta}}
+
Якщо ви не запитували підтвердження цієї адреси, проігноруйте це повідомлення.
-
-
+
+
Дякуємо,
команда {{project}}
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/ua.email.auth.invitation.tpl b/app/config/locales/templates/ua.email.auth.invitation.tpl
index 8daaecbaaf..dc4d3aed02 100644
--- a/app/config/locales/templates/ua.email.auth.invitation.tpl
+++ b/app/config/locales/templates/ua.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Вітаємо,
-
-
+
+
Цей лист був надісланий вам тому що {{owner}} хоче запросити вас стати членом команди {{team}} у {{project}}.
-
-
+
+
Перейдіть за цим посиланням щоб приєднатись до команди {{team}} :
-
- {{redirect}}
-
-
+
+{{cta}}
+
Якщо ви не зацікавлені, проігноруйте це повідомлення.
-
-
+
Hãy vào liên kết này để xác nhận địa chỉ email của bạn.
-
- {{redirect}}
-
-
+
+{{cta}}
+
Xin hãy bỏ qua email này nếu bạn không yêu cầu xác nhận địa chỉ này.
-
-
+
+
Xin cảm ơn,
{{project}} team
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/vi.email.auth.invitation.tpl b/app/config/locales/templates/vi.email.auth.invitation.tpl
index 387c537f55..8d222ea8c7 100644
--- a/app/config/locales/templates/vi.email.auth.invitation.tpl
+++ b/app/config/locales/templates/vi.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
+
Xin chào,
-
-
+
+
Bạn nhận được email này vì {{owner}} muốn mời bạn tham gia {{project}} cùng với {{team}} team.
-
-
+
+
Hãy theo liên kết này để tham gia vào {{team}} team:
-
- {{redirect}}
-
-
+
+{{cta}}
+
Nếu bạn không thích tham gia, hãy bỏ qua lời nhắn này.
-
-
+
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/zh-cn.email.auth.invitation.tpl b/app/config/locales/templates/zh-cn.email.auth.invitation.tpl
index 7ee56a401a..4ef8caba5c 100644
--- a/app/config/locales/templates/zh-cn.email.auth.invitation.tpl
+++ b/app/config/locales/templates/zh-cn.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/zh-tw.email.auth.invitation.tpl b/app/config/locales/templates/zh-tw.email.auth.invitation.tpl
index 08fb921fd7..b5e6cc8225 100644
--- a/app/config/locales/templates/zh-tw.email.auth.invitation.tpl
+++ b/app/config/locales/templates/zh-tw.email.auth.invitation.tpl
@@ -1,27 +1,18 @@
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/config/locales/templates/zh-tw.email.auth.recovery.tpl b/app/config/locales/templates/zh-tw.email.auth.recovery.tpl
index 8a473b5537..1144b36b3c 100644
--- a/app/config/locales/templates/zh-tw.email.auth.recovery.tpl
+++ b/app/config/locales/templates/zh-tw.email.auth.recovery.tpl
@@ -1,25 +1,16 @@
-
-
-
+
{{name}} 你好,
-
-
+
+
请点击下方的链接重新设置{{project}}的密码。
Follow this link to reset your {{project}} password.
-
- {{redirect}}
-
-
+
+{{cta}}
+
如果您未曾申请重设密码,请忽略本邮件。
-
-
+
+
谢谢。
来自 {{project}}
-
+
diff --git a/app/config/platforms.php b/app/config/platforms.php
index f05dcde39b..a0b4955a8e 100644
--- a/app/config/platforms.php
+++ b/app/config/platforms.php
@@ -21,7 +21,7 @@ return [
'beta' => false,
'family' => APP_PLATFORM_CLIENT,
'prism' => 'javascript',
- 'source' => realpath(__DIR__ . '/../sdks/client-web'),
+ 'source' => \realpath(__DIR__ . '/../sdks/client-web'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-js.git',
'gitRepoName' => 'sdk-for-js',
'gitUserName' => 'appwrite',
@@ -35,7 +35,7 @@ return [
'beta' => true,
'family' => APP_PLATFORM_CLIENT,
'prism' => 'dart',
- 'source' => realpath(__DIR__ . '/../sdks/client-flutter'),
+ 'source' => \realpath(__DIR__ . '/../sdks/client-flutter'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-flutter.git',
'gitRepoName' => 'sdk-for-flutter',
'gitUserName' => 'appwrite',
@@ -110,7 +110,7 @@ return [
'beta' => false,
'family' => APP_PLATFORM_CONSOLE,
'prism' => 'console',
- 'source' => realpath(__DIR__ . '/../sdks/console-web'),
+ 'source' => \realpath(__DIR__ . '/../sdks/console-web'),
'gitUrl' => null,
'gitRepoName' => 'sdk-for-console',
'gitUserName' => 'appwrite',
@@ -134,7 +134,7 @@ return [
'beta' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'javascript',
- 'source' => realpath(__DIR__ . '/../sdks/server-nodejs'),
+ 'source' => \realpath(__DIR__ . '/../sdks/server-nodejs'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-node.git',
'gitRepoName' => 'sdk-for-node',
'gitUserName' => 'appwrite',
@@ -148,7 +148,7 @@ return [
'beta' => true,
'family' => APP_PLATFORM_SERVER,
'prism' => 'typescript',
- 'source' => realpath(__DIR__ . '/../sdks/server-deno'),
+ 'source' => \realpath(__DIR__ . '/../sdks/server-deno'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-deno.git',
'gitRepoName' => 'sdk-for-deno',
'gitUserName' => 'appwrite',
@@ -162,7 +162,7 @@ return [
'beta' => false,
'family' => APP_PLATFORM_SERVER,
'prism' => 'php',
- 'source' => realpath(__DIR__ . '/../sdks/server-php'),
+ 'source' => \realpath(__DIR__ . '/../sdks/server-php'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-php.git',
'gitRepoName' => 'sdk-for-php',
'gitUserName' => 'appwrite',
@@ -176,7 +176,7 @@ return [
'beta' => true,
'family' => APP_PLATFORM_SERVER,
'prism' => 'python',
- 'source' => realpath(__DIR__ . '/../sdks/server-python'),
+ 'source' => \realpath(__DIR__ . '/../sdks/server-python'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-python.git',
'gitRepoName' => 'sdk-for-python',
'gitUserName' => 'appwrite',
@@ -190,7 +190,7 @@ return [
'beta' => true,
'family' => APP_PLATFORM_SERVER,
'prism' => 'ruby',
- 'source' => realpath(__DIR__ . '/../sdks/server-ruby'),
+ 'source' => \realpath(__DIR__ . '/../sdks/server-ruby'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-ruby.git',
'gitRepoName' => 'sdk-for-ruby',
'gitUserName' => 'appwrite',
@@ -204,7 +204,7 @@ return [
'beta' => true,
'family' => APP_PLATFORM_SERVER,
'prism' => 'go',
- 'source' => realpath(__DIR__ . '/../sdks/server-go'),
+ 'source' => \realpath(__DIR__ . '/../sdks/server-go'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-go.git',
'gitRepoName' => 'sdk-for-go',
'gitUserName' => 'appwrite',
@@ -218,7 +218,7 @@ return [
'beta' => true,
'family' => APP_PLATFORM_SERVER,
'prism' => 'java',
- 'source' => realpath(__DIR__ . '/../sdks/server-java'),
+ 'source' => \realpath(__DIR__ . '/../sdks/server-java'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-java.git',
'gitRepoName' => 'sdk-for-java',
'gitUserName' => 'appwrite',
@@ -232,7 +232,7 @@ return [
'beta' => true,
'family' => APP_PLATFORM_SERVER,
'prism' => 'java',
- 'source' => realpath(__DIR__ . '/../sdks/server-dart'),
+ 'source' => \realpath(__DIR__ . '/../sdks/server-dart'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-dart.git',
'gitRepoName' => 'sdk-for-dart',
'gitUserName' => 'appwrite',
diff --git a/app/config/providers.php b/app/config/providers.php
index b1b086a865..505c2898c4 100644
--- a/app/config/providers.php
+++ b/app/config/providers.php
@@ -74,7 +74,7 @@ return [ // Ordered by ABC.
'mock' => false,
],
'google' => [
- 'developers' => 'https://developers.google.com/',
+ 'developers' => 'https://support.google.com/googleapi/answer/6158849',
'icon' => 'icon-google',
'enabled' => true,
'form' => false,
diff --git a/app/config/roles.php b/app/config/roles.php
index 9bd12bdf22..0a4ec4369c 100644
--- a/app/config/roles.php
+++ b/app/config/roles.php
@@ -67,19 +67,19 @@ return [
],
ROLE_MEMBER => [
'label' => 'Member',
- 'scopes' => array_merge($logged, []),
+ 'scopes' => \array_merge($logged, []),
],
ROLE_ADMIN => [
'label' => 'Admin',
- 'scopes' => array_merge($admins, []),
+ 'scopes' => \array_merge($admins, []),
],
ROLE_DEVELOPER => [
'label' => 'Developer',
- 'scopes' => array_merge($admins, []),
+ 'scopes' => \array_merge($admins, []),
],
ROLE_OWNER => [
'label' => 'Owner',
- 'scopes' => array_merge($logged, $admins, []),
+ 'scopes' => \array_merge($logged, $admins, []),
],
ROLE_APP => [
'label' => 'Application',
diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php
index 282a5d9a22..3bce54eafa 100644
--- a/app/controllers/api/account.php
+++ b/app/controllers/api/account.php
@@ -1,7 +1,7 @@
getServer('_APP_HOME').'/auth/oauth2/success';
$oauthDefaultFailure = $request->getServer('_APP_HOME').'/auth/oauth2/failure';
@@ -41,14 +40,14 @@ $utopia->init(function() use (&$oauth2Keys) {
continue;
}
- $oauth2Keys[] = 'oauth2'.ucfirst($key);
- $oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
+ $oauth2Keys[] = 'oauth2'.\ucfirst($key);
+ $oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
-
-});
+}, 'account');
$utopia->post('/v1/account')
->desc('Create Account')
+ ->groups(['api', 'account'])
->label('webhook', 'account.create')
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
@@ -60,21 +59,21 @@ $utopia->post('/v1/account')
->param('password', '', function () { return new Password(); }, 'User password. Must be between 6 to 32 chars.')
->param('name', '', function () { return new Text(100); }, 'User name.', true)
->action(
- function ($email, $password, $name) use ($register, $request, $response, $audit, $projectDB, $project, $webhook, $oauth2Keys) {
+ function ($email, $password, $name) use ($request, $response, $audit, $projectDB, $project, $webhook, $oauth2Keys) {
if ('console' === $project->getId()) {
$whitlistEmails = $project->getAttribute('authWhitelistEmails');
$whitlistIPs = $project->getAttribute('authWhitelistIPs');
$whitlistDomains = $project->getAttribute('authWhitelistDomains');
- if (!empty($whitlistEmails) && !in_array($email, $whitlistEmails)) {
+ if (!empty($whitlistEmails) && !\in_array($email, $whitlistEmails)) {
throw new Exception('Console registration is restricted to specific emails. Contact your administrator for more information.', 401);
}
- if (!empty($whitlistIPs) && !in_array($request->getIP(), $whitlistIPs)) {
+ if (!empty($whitlistIPs) && !\in_array($request->getIP(), $whitlistIPs)) {
throw new Exception('Console registration is restricted to specific IPs. Contact your administrator for more information.', 401);
}
- if (!empty($whitlistDomains) && !in_array(substr(strrchr($email, '@'), 1), $whitlistDomains)) {
+ if (!empty($whitlistDomains) && !\in_array(\substr(\strrchr($email, '@'), 1), $whitlistDomains)) {
throw new Exception('Console registration is restricted to specific domains. Contact your administrator for more information.', 401);
}
}
@@ -105,8 +104,8 @@ $utopia->post('/v1/account')
'emailVerification' => false,
'status' => Auth::USER_STATUS_UNACTIVATED,
'password' => Auth::passwordHash($password),
- 'password-update' => time(),
- 'registration' => time(),
+ 'password-update' => \time(),
+ 'registration' => \time(),
'reset' => false,
'name' => $name,
], ['email' => $email]);
@@ -135,7 +134,7 @@ $utopia->post('/v1/account')
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
- ->json(array_merge($user->getArrayCopy(array_merge(
+ ->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
@@ -149,6 +148,7 @@ $utopia->post('/v1/account')
$utopia->post('/v1/account/sessions')
->desc('Create Account Session')
+ ->groups(['api', 'account'])
->label('webhook', 'account.sessions.create')
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
@@ -181,7 +181,7 @@ $utopia->post('/v1/account/sessions')
throw new Exception('Invalid credentials', 401); // Wrong password or username
}
- $expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
+ $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$secret = Auth::tokenGenerator();
$session = new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
@@ -222,9 +222,9 @@ $utopia->post('/v1/account/sessions')
->setParam('resource', 'users/'.$profile->getId())
;
- if(!Config::getParam('domainVerification')) {
+ if (!Config::getParam('domainVerification')) {
$response
- ->addHeader('X-Fallback-Cookies', json_encode([Auth::$cookieName => Auth::encodeSession($profile->getId(), $secret)]))
+ ->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($profile->getId(), $secret)]))
;
}
@@ -239,6 +239,7 @@ $utopia->post('/v1/account/sessions')
$utopia->get('/v1/account/sessions/oauth2/:provider')
->desc('Create Account Session with OAuth2')
+ ->groups(['api', 'account'])
->label('error', __DIR__.'/../../views/general/error.phtml')
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
@@ -250,34 +251,35 @@ $utopia->get('/v1/account/sessions/oauth2/:provider')
->label('sdk.methodType', 'webAuth')
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
- ->param('provider', '', function () { return new WhiteList(array_keys(Config::getParam('providers'))); }, 'OAuth2 Provider. Currently, supported providers are: ' . implode(', ', array_keys(array_filter(Config::getParam('providers'), function($node) {return (!$node['mock']);}))).'.')
+ ->param('provider', '', function () { return new WhiteList(\array_keys(Config::getParam('providers'))); }, 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('providers'), function($node) {return (!$node['mock']);}))).'.')
->param('success', $oauthDefaultSuccess, function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a successful login attempt. 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)
->param('failure', $oauthDefaultFailure, function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a failed login attempt. 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)
+ ->param('scopes', [], function () { return new ArrayList(new Text(128)); }, 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes.', true)
->action(
- function ($provider, $success, $failure) use ($response, $request, $project) {
+ function ($provider, $success, $failure, $scopes) use ($response, $request, $project) {
$protocol = Config::getParam('protocol');
$callback = $protocol.'://'.$request->getServer('HTTP_HOST').'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getId();
- $appId = $project->getAttribute('usersOauth2'.ucfirst($provider).'Appid', '');
- $appSecret = $project->getAttribute('usersOauth2'.ucfirst($provider).'Secret', '{}');
+ $appId = $project->getAttribute('usersOauth2'.\ucfirst($provider).'Appid', '');
+ $appSecret = $project->getAttribute('usersOauth2'.\ucfirst($provider).'Secret', '{}');
- $appSecret = json_decode($appSecret, true);
+ $appSecret = \json_decode($appSecret, true);
if (!empty($appSecret) && isset($appSecret['version'])) {
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$appSecret['version']);
- $appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, hex2bin($appSecret['iv']), hex2bin($appSecret['tag']));
+ $appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, \hex2bin($appSecret['iv']), \hex2bin($appSecret['tag']));
}
if (empty($appId) || empty($appSecret)) {
throw new Exception('This provider is disabled. Please configure the provider app ID and app secret key from your '.APP_NAME.' console to continue.', 412);
}
- $classname = 'Appwrite\\Auth\\OAuth2\\'.ucfirst($provider);
+ $classname = 'Appwrite\\Auth\\OAuth2\\'.\ucfirst($provider);
- if (!class_exists($classname)) {
+ if (!\class_exists($classname)) {
throw new Exception('Provider is not supported', 501);
}
- $oauth2 = new $classname($appId, $appSecret, $callback, ['success' => $success, 'failure' => $failure]);
+ $oauth2 = new $classname($appId, $appSecret, $callback, ['success' => $success, 'failure' => $failure], $scopes);
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
@@ -288,11 +290,12 @@ $utopia->get('/v1/account/sessions/oauth2/:provider')
$utopia->get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->desc('OAuth2 Callback')
+ ->groups(['api', 'account'])
->label('error', __DIR__.'/../../views/general/error.phtml')
->label('scope', 'public')
->label('docs', false)
->param('projectId', '', function () { return new Text(1024); }, 'Project unique ID.')
- ->param('provider', '', function () { return new WhiteList(array_keys(Config::getParam('providers'))); }, 'OAuth2 provider.')
+ ->param('provider', '', function () { return new WhiteList(\array_keys(Config::getParam('providers'))); }, 'OAuth2 provider.')
->param('code', '', function () { return new Text(1024); }, 'OAuth2 code.')
->param('state', '', function () { return new Text(2048); }, 'Login state params.', true)
->action(
@@ -304,18 +307,19 @@ $utopia->get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
->redirect($protocol.'://'.$domain.'/v1/account/sessions/oauth2/'.$provider.'/redirect?'
- .http_build_query(['project' => $projectId, 'code' => $code, 'state' => $state]));
+ .\http_build_query(['project' => $projectId, 'code' => $code, 'state' => $state]));
}
);
$utopia->post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->desc('OAuth2 Callback')
+ ->groups(['api', 'account'])
->label('error', __DIR__.'/../../views/general/error.phtml')
->label('scope', 'public')
->label('origin', '*')
->label('docs', false)
->param('projectId', '', function () { return new Text(1024); }, 'Project unique ID.')
- ->param('provider', '', function () { return new WhiteList(array_keys(Config::getParam('providers'))); }, 'OAuth2 provider.')
+ ->param('provider', '', function () { return new WhiteList(\array_keys(Config::getParam('providers'))); }, 'OAuth2 provider.')
->param('code', '', function () { return new Text(1024); }, 'OAuth2 code.')
->param('state', '', function () { return new Text(2048); }, 'Login state params.', true)
->action(
@@ -327,19 +331,20 @@ $utopia->post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
->redirect($protocol.'://'.$domain.'/v1/account/sessions/oauth2/'.$provider.'/redirect?'
- .http_build_query(['project' => $projectId, 'code' => $code, 'state' => $state]));
+ .\http_build_query(['project' => $projectId, 'code' => $code, 'state' => $state]));
}
);
$utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
->desc('OAuth2 Redirect')
+ ->groups(['api', 'account'])
->label('error', __DIR__.'/../../views/general/error.phtml')
->label('webhook', 'account.sessions.create')
->label('scope', 'public')
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
->label('docs', false)
- ->param('provider', '', function () { return new WhiteList(array_keys(Config::getParam('providers'))); }, 'OAuth2 provider.')
+ ->param('provider', '', function () { return new WhiteList(\array_keys(Config::getParam('providers'))); }, 'OAuth2 provider.')
->param('code', '', function () { return new Text(1024); }, 'OAuth2 code.')
->param('state', '', function () { return new Text(2048); }, 'OAuth2 state params.', true)
->action(
@@ -349,19 +354,19 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
$validateURL = new URL();
- $appId = $project->getAttribute('usersOauth2'.ucfirst($provider).'Appid', '');
- $appSecret = $project->getAttribute('usersOauth2'.ucfirst($provider).'Secret', '{}');
+ $appId = $project->getAttribute('usersOauth2'.\ucfirst($provider).'Appid', '');
+ $appSecret = $project->getAttribute('usersOauth2'.\ucfirst($provider).'Secret', '{}');
- $appSecret = json_decode($appSecret, true);
+ $appSecret = \json_decode($appSecret, true);
if (!empty($appSecret) && isset($appSecret['version'])) {
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$appSecret['version']);
- $appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, hex2bin($appSecret['iv']), hex2bin($appSecret['tag']));
+ $appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, \hex2bin($appSecret['iv']), \hex2bin($appSecret['tag']));
}
- $classname = 'Appwrite\\Auth\\OAuth2\\'.ucfirst($provider);
+ $classname = 'Appwrite\\Auth\\OAuth2\\'.\ucfirst($provider);
- if (!class_exists($classname)) {
+ if (!\class_exists($classname)) {
throw new Exception('Provider is not supported', 501);
}
@@ -369,7 +374,7 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
if (!empty($state)) {
try {
- $state = array_merge($defaultState, $oauth2->parseState($state));
+ $state = \array_merge($defaultState, $oauth2->parseState($state));
} catch (\Exception $exception) {
throw new Exception('Failed to parse login state params as passed from OAuth2 provider');
}
@@ -417,7 +422,7 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
'first' => true,
'filters' => [
'$collection='.Database::SYSTEM_COLLECTION_USERS,
- 'oauth2'.ucfirst($provider).'='.$oauth2ID,
+ 'oauth2'.\ucfirst($provider).'='.$oauth2ID,
],
]) : $user;
@@ -445,8 +450,8 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
'emailVerification' => true,
'status' => Auth::USER_STATUS_ACTIVATED, // Email should already be authenticated by OAuth2 provider
'password' => Auth::passwordHash(Auth::passwordGenerator()),
- 'password-update' => time(),
- 'registration' => time(),
+ 'password-update' => \time(),
+ 'registration' => \time(),
'reset' => false,
'name' => $name,
], ['email' => $email]);
@@ -465,7 +470,7 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
// Create session token, verify user account and update OAuth2 ID and Access Token
$secret = Auth::tokenGenerator();
- $expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
+ $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$session = new Document([
'$collection' => Database::SYSTEM_COLLECTION_TOKENS,
'$permissions' => ['read' => ['user:'.$user['$id']], 'write' => ['user:'.$user['$id']]],
@@ -477,8 +482,8 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
]);
$user
- ->setAttribute('oauth2'.ucfirst($provider), $oauth2ID)
- ->setAttribute('oauth2'.ucfirst($provider).'AccessToken', $accessToken)
+ ->setAttribute('oauth2'.\ucfirst($provider), $oauth2ID)
+ ->setAttribute('oauth2'.\ucfirst($provider).'AccessToken', $accessToken)
->setAttribute('status', Auth::USER_STATUS_ACTIVATED)
->setAttribute('tokens', $session, Document::SET_TYPE_APPEND)
;
@@ -498,13 +503,13 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
->setParam('data', ['provider' => $provider])
;
- if(!Config::getParam('domainVerification')) {
+ if (!Config::getParam('domainVerification')) {
$response
- ->addHeader('X-Fallback-Cookies', json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
+ ->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
;
}
- if($state['success'] === $oauthDefaultSuccess) { // Add keys for non-web platforms
+ if ($state['success'] === $oauthDefaultSuccess) { // Add keys for non-web platforms
$state['success'] = URLParser::parse($state['success']);
$query = URLParser::parseQuery($state['success']['query']);
$query['project'] = $project->getId();
@@ -527,6 +532,7 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
$utopia->get('/v1/account')
->desc('Get Account')
+ ->groups(['api', 'account'])
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
@@ -535,7 +541,7 @@ $utopia->get('/v1/account')
->label('sdk.response', ['200' => 'user'])
->action(
function () use ($response, &$user, $oauth2Keys) {
- $response->json(array_merge($user->getArrayCopy(array_merge(
+ $response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
@@ -550,6 +556,7 @@ $utopia->get('/v1/account')
$utopia->get('/v1/account/prefs')
->desc('Get Account Preferences')
+ ->groups(['api', 'account'])
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
@@ -560,7 +567,7 @@ $utopia->get('/v1/account/prefs')
$prefs = $user->getAttribute('prefs', '{}');
try {
- $prefs = json_decode($prefs, true);
+ $prefs = \json_decode($prefs, true);
$prefs = ($prefs) ? $prefs : [];
} catch (\Exception $error) {
throw new Exception('Failed to parse prefs', 500);
@@ -572,6 +579,7 @@ $utopia->get('/v1/account/prefs')
$utopia->get('/v1/account/sessions')
->desc('Get Account Sessions')
+ ->groups(['api', 'account'])
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
@@ -614,7 +622,7 @@ $utopia->get('/v1/account/sessions')
try {
$record = $reader->country($token->getAttribute('ip', ''));
- $sessions[$index]['geo']['isoCode'] = strtolower($record->country->isoCode);
+ $sessions[$index]['geo']['isoCode'] = \strtolower($record->country->isoCode);
$sessions[$index]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown');
} catch (\Exception $e) {
$sessions[$index]['geo']['isoCode'] = '--';
@@ -630,6 +638,7 @@ $utopia->get('/v1/account/sessions')
$utopia->get('/v1/account/logs')
->desc('Get Account Logs')
+ ->groups(['api', 'account'])
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
@@ -676,7 +685,7 @@ $utopia->get('/v1/account/logs')
$output[$i] = [
'event' => $log['event'],
'ip' => $log['ip'],
- 'time' => strtotime($log['time']),
+ 'time' => \strtotime($log['time']),
'OS' => $dd->getOs(),
'client' => $dd->getClient(),
'device' => $dd->getDevice(),
@@ -687,7 +696,7 @@ $utopia->get('/v1/account/logs')
try {
$record = $reader->country($log['ip']);
- $output[$i]['geo']['isoCode'] = strtolower($record->country->isoCode);
+ $output[$i]['geo']['isoCode'] = \strtolower($record->country->isoCode);
$output[$i]['geo']['country'] = $record->country->name;
$output[$i]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown');
} catch (\Exception $e) {
@@ -702,6 +711,7 @@ $utopia->get('/v1/account/logs')
$utopia->patch('/v1/account/name')
->desc('Update Account Name')
+ ->groups(['api', 'account'])
->label('webhook', 'account.update.name')
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
@@ -711,7 +721,7 @@ $utopia->patch('/v1/account/name')
->param('name', '', function () { return new Text(100); }, 'User name.')
->action(
function ($name) use ($response, $user, $projectDB, $audit, $oauth2Keys) {
- $user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
+ $user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'name' => $name,
]));
@@ -725,7 +735,7 @@ $utopia->patch('/v1/account/name')
->setParam('resource', 'users/'.$user->getId())
;
- $response->json(array_merge($user->getArrayCopy(array_merge(
+ $response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
@@ -739,6 +749,7 @@ $utopia->patch('/v1/account/name')
$utopia->patch('/v1/account/password')
->desc('Update Account Password')
+ ->groups(['api', 'account'])
->label('webhook', 'account.update.password')
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
@@ -753,7 +764,7 @@ $utopia->patch('/v1/account/password')
throw new Exception('Invalid credentials', 401);
}
- $user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
+ $user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'password' => Auth::passwordHash($password),
]));
@@ -767,7 +778,7 @@ $utopia->patch('/v1/account/password')
->setParam('resource', 'users/'.$user->getId())
;
- $response->json(array_merge($user->getArrayCopy(array_merge(
+ $response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
@@ -781,6 +792,7 @@ $utopia->patch('/v1/account/password')
$utopia->patch('/v1/account/email')
->desc('Update Account Email')
+ ->groups(['api', 'account'])
->label('webhook', 'account.update.email')
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
@@ -810,7 +822,7 @@ $utopia->patch('/v1/account/email')
// TODO after this user needs to confirm mail again
- $user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
+ $user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'email' => $email,
'emailVerification' => false,
]));
@@ -825,7 +837,7 @@ $utopia->patch('/v1/account/email')
->setParam('resource', 'users/'.$user->getId())
;
- $response->json(array_merge($user->getArrayCopy(array_merge(
+ $response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'email',
@@ -839,6 +851,7 @@ $utopia->patch('/v1/account/email')
$utopia->patch('/v1/account/prefs')
->desc('Update Account Preferences')
+ ->groups(['api', 'account'])
->label('webhook', 'account.update.prefs')
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
@@ -848,11 +861,11 @@ $utopia->patch('/v1/account/prefs')
->label('sdk.description', '/docs/references/account/update-prefs.md')
->action(
function ($prefs) use ($response, $user, $projectDB, $audit) {
- $old = json_decode($user->getAttribute('prefs', '{}'), true);
+ $old = \json_decode($user->getAttribute('prefs', '{}'), true);
$old = ($old) ? $old : [];
- $user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
- 'prefs' => json_encode(array_merge($old, $prefs)),
+ $user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
+ 'prefs' => \json_encode(\array_merge($old, $prefs)),
]));
if (false === $user) {
@@ -867,7 +880,7 @@ $utopia->patch('/v1/account/prefs')
$prefs = $user->getAttribute('prefs', '{}');
try {
- $prefs = json_decode($prefs, true);
+ $prefs = \json_decode($prefs, true);
$prefs = ($prefs) ? $prefs : [];
} catch (\Exception $error) {
throw new Exception('Failed to parse prefs', 500);
@@ -879,6 +892,7 @@ $utopia->patch('/v1/account/prefs')
$utopia->delete('/v1/account')
->desc('Delete Account')
+ ->groups(['api', 'account'])
->label('webhook', 'account.delete')
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
@@ -888,7 +902,7 @@ $utopia->delete('/v1/account')
->action(
function () use ($response, $user, $projectDB, $audit, $webhook) {
$protocol = Config::getParam('protocol');
- $user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
+ $user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'status' => Auth::USER_STATUS_BLOCKED,
]));
@@ -918,15 +932,15 @@ $utopia->delete('/v1/account')
])
;
- if(!Config::getParam('domainVerification')) {
+ if (!Config::getParam('domainVerification')) {
$response
- ->addHeader('X-Fallback-Cookies', json_encode([]))
+ ->addHeader('X-Fallback-Cookies', \json_encode([]))
;
}
$response
- ->addCookie(Auth::$cookieName.'_legacy', '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
- ->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
+ ->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
+ ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
->noContent()
;
}
@@ -934,6 +948,7 @@ $utopia->delete('/v1/account')
$utopia->delete('/v1/account/sessions/:sessionId')
->desc('Delete Account Session')
+ ->groups(['api', 'account'])
->label('scope', 'account')
->label('webhook', 'account.sessions.delete')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
@@ -970,16 +985,16 @@ $utopia->delete('/v1/account/sessions/:sessionId')
])
;
- if(!Config::getParam('domainVerification')) {
+ if (!Config::getParam('domainVerification')) {
$response
- ->addHeader('X-Fallback-Cookies', json_encode([]))
+ ->addHeader('X-Fallback-Cookies', \json_encode([]))
;
}
if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$response
- ->addCookie(Auth::$cookieName.'_legacy', '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
- ->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
+ ->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
+ ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
;
}
@@ -993,6 +1008,7 @@ $utopia->delete('/v1/account/sessions/:sessionId')
$utopia->delete('/v1/account/sessions')
->desc('Delete All Account Sessions')
+ ->groups(['api', 'account'])
->label('scope', 'account')
->label('webhook', 'account.sessions.delete')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
@@ -1023,16 +1039,16 @@ $utopia->delete('/v1/account/sessions')
])
;
- if(!Config::getParam('domainVerification')) {
+ if (!Config::getParam('domainVerification')) {
$response
- ->addHeader('X-Fallback-Cookies', json_encode([]))
+ ->addHeader('X-Fallback-Cookies', \json_encode([]))
;
}
if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$response
- ->addCookie(Auth::$cookieName.'_legacy', '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
- ->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
+ ->addCookie(Auth::$cookieName.'_legacy', '', \time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
+ ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
;
}
}
@@ -1043,6 +1059,7 @@ $utopia->delete('/v1/account/sessions')
$utopia->post('/v1/account/recovery')
->desc('Create Password Recovery')
+ ->groups(['api', 'account'])
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
@@ -1053,7 +1070,7 @@ $utopia->post('/v1/account/recovery')
->param('email', '', function () { return new Email(); }, 'User email.')
->param('url', '', function () use ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the recovery 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.')
->action(
- function ($email, $url) use ($request, $response, $projectDB, $register, $audit, $project) {
+ function ($email, $url) use ($request, $response, $projectDB, $mail, $audit, $project) {
$profile = $projectDB->getCollection([ // Get user by email address
'limit' => 1,
'first' => true,
@@ -1073,7 +1090,7 @@ $utopia->post('/v1/account/recovery')
'$permissions' => ['read' => ['user:'.$profile->getId()], 'write' => ['user:'.$profile->getId()]],
'type' => Auth::TOKEN_TYPE_RECOVERY,
'secret' => Auth::hash($secret), // On way hash encryption to protect DB leak
- 'expire' => time() + Auth::TOKEN_EXPIRATION_RECOVERY,
+ 'expire' => \time() + Auth::TOKEN_EXPIRATION_RECOVERY,
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
'ip' => $request->getIP(),
]);
@@ -1098,27 +1115,34 @@ $utopia->post('/v1/account/recovery')
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $profile->getId(), 'secret' => $secret]);
$url = Template::unParseURL($url);
- $body = new Template(__DIR__.'/../../config/locales/templates/'.Locale::getText('account.emails.recovery.body'));
+ $body = new Template(__DIR__.'/../../config/locales/templates/_base.tpl');
+ $content = new Template(__DIR__.'/../../config/locales/templates/'.Locale::getText('account.emails.recovery.body'));
+ $cta = new Template(__DIR__.'/../../config/locales/templates/_cta.tpl');
+
$body
+ ->setParam('{{content}}', $content->render())
+ ->setParam('{{cta}}', $cta->render())
+ ->setParam('{{title}}', Locale::getText('account.emails.recovery.title'))
->setParam('{{direction}}', Locale::getText('settings.direction'))
->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]']))
->setParam('{{name}}', $profile->getAttribute('name'))
->setParam('{{redirect}}', $url)
+ ->setParam('{{bg-body}}', '#f6f6f6')
+ ->setParam('{{bg-content}}', '#ffffff')
+ ->setParam('{{bg-cta}}', '#3498db')
+ ->setParam('{{bg-cta-hover}}', '#34495e')
+ ->setParam('{{text-content}}', '#000000')
+ ->setParam('{{text-cta}}', '#ffffff')
;
- $mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */
-
- $mail->addAddress($profile->getAttribute('email', ''), $profile->getAttribute('name', ''));
-
- $mail->Subject = Locale::getText('account.emails.recovery.title');
- $mail->Body = $body->render();
- $mail->AltBody = strip_tags($body->render());
-
- try {
- $mail->send();
- } catch (\Exception $error) {
- throw new Exception('Error sending mail: ' . $error->getMessage(), 500);
- }
+ $mail
+ ->setParam('event', 'account.recovery.create')
+ ->setParam('recipient', $profile->getAttribute('email', ''))
+ ->setParam('name', $profile->getAttribute('name', ''))
+ ->setParam('subject', Locale::getText('account.emails.recovery.title'))
+ ->setParam('body', $body->render())
+ ->trigger();
+ ;
$audit
->setParam('userId', $profile->getId())
@@ -1135,6 +1159,7 @@ $utopia->post('/v1/account/recovery')
$utopia->put('/v1/account/recovery')
->desc('Complete Password Recovery')
+ ->groups(['api', 'account'])
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
@@ -1173,9 +1198,9 @@ $utopia->put('/v1/account/recovery')
Authorization::setRole('user:'.$profile->getId());
- $profile = $projectDB->updateDocument(array_merge($profile->getArrayCopy(), [
+ $profile = $projectDB->updateDocument(\array_merge($profile->getArrayCopy(), [
'password' => Auth::passwordHash($password),
- 'password-update' => time(),
+ 'password-update' => \time(),
'emailVerification' => true,
]));
@@ -1205,6 +1230,7 @@ $utopia->put('/v1/account/recovery')
$utopia->post('/v1/account/verification')
->desc('Create Email Verification')
+ ->groups(['api', 'account'])
->label('scope', 'account')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
@@ -1214,7 +1240,7 @@ $utopia->post('/v1/account/verification')
->label('abuse-key', 'url:{url},email:{param-email}')
->param('url', '', function () use ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the verification 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.') // TODO add built-in confirm page
->action(
- function ($url) use ($request, $response, $register, $user, $project, $projectDB, $audit) {
+ function ($url) use ($request, $response, $mail, $user, $project, $projectDB, $audit) {
$verificationSecret = Auth::tokenGenerator();
$verification = new Document([
@@ -1222,7 +1248,7 @@ $utopia->post('/v1/account/verification')
'$permissions' => ['read' => ['user:'.$user->getId()], 'write' => ['user:'.$user->getId()]],
'type' => Auth::TOKEN_TYPE_VERIFICATION,
'secret' => Auth::hash($verificationSecret), // On way hash encryption to protect DB leak
- 'expire' => time() + Auth::TOKEN_EXPIRATION_CONFIRM,
+ 'expire' => \time() + Auth::TOKEN_EXPIRATION_CONFIRM,
'userAgent' => $request->getServer('HTTP_USER_AGENT', 'UNKNOWN'),
'ip' => $request->getIP(),
]);
@@ -1247,27 +1273,34 @@ $utopia->post('/v1/account/verification')
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $user->getId(), 'secret' => $verificationSecret]);
$url = Template::unParseURL($url);
- $body = new Template(__DIR__.'/../../config/locales/templates/'.Locale::getText('account.emails.verification.body'));
+ $body = new Template(__DIR__.'/../../config/locales/templates/_base.tpl');
+ $content = new Template(__DIR__.'/../../config/locales/templates/'.Locale::getText('account.emails.verification.body'));
+ $cta = new Template(__DIR__.'/../../config/locales/templates/_cta.tpl');
+
$body
+ ->setParam('{{content}}', $content->render())
+ ->setParam('{{cta}}', $cta->render())
+ ->setParam('{{title}}', Locale::getText('account.emails.verification.title'))
->setParam('{{direction}}', Locale::getText('settings.direction'))
->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]']))
->setParam('{{name}}', $user->getAttribute('name'))
->setParam('{{redirect}}', $url)
+ ->setParam('{{bg-body}}', '#f6f6f6')
+ ->setParam('{{bg-content}}', '#ffffff')
+ ->setParam('{{bg-cta}}', '#3498db')
+ ->setParam('{{bg-cta-hover}}', '#34495e')
+ ->setParam('{{text-content}}', '#000000')
+ ->setParam('{{text-cta}}', '#ffffff')
;
- $mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */
-
- $mail->addAddress($user->getAttribute('email'), $user->getAttribute('name'));
-
- $mail->Subject = Locale::getText('account.emails.verification.title');
- $mail->Body = $body->render();
- $mail->AltBody = strip_tags($body->render());
-
- try {
- $mail->send();
- } catch (\Exception $error) {
- throw new Exception('Problem sending mail: ' . $error->getMessage(), 500);
- }
+ $mail
+ ->setParam('event', 'account.verification.create')
+ ->setParam('recipient', $user->getAttribute('email'))
+ ->setParam('name', $user->getAttribute('name'))
+ ->setParam('subject', Locale::getText('account.emails.verification.title'))
+ ->setParam('body', $body->render())
+ ->trigger()
+ ;
$audit
->setParam('userId', $user->getId())
@@ -1284,6 +1317,7 @@ $utopia->post('/v1/account/verification')
$utopia->put('/v1/account/verification')
->desc('Complete Email Verification')
+ ->groups(['api', 'account'])
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'account')
@@ -1316,7 +1350,7 @@ $utopia->put('/v1/account/verification')
Authorization::setRole('user:'.$profile->getId());
- $profile = $projectDB->updateDocument(array_merge($profile->getArrayCopy(), [
+ $profile = $projectDB->updateDocument(\array_merge($profile->getArrayCopy(), [
'emailVerification' => true,
]));
diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php
index b1c8d352b3..90f8ac34c8 100644
--- a/app/controllers/api/avatars.php
+++ b/app/controllers/api/avatars.php
@@ -18,37 +18,35 @@ use BaconQrCode\Writer;
use Utopia\Config\Config;
use Utopia\Validator\HexColor;
-include_once __DIR__ . '/../shared/api.php';
-
$types = [
'browsers' => include __DIR__.'/../../config/avatars/browsers.php',
'credit-cards' => include __DIR__.'/../../config/avatars/credit-cards.php',
'flags' => include __DIR__.'/../../config/avatars/flags.php',
];
-$avatarCallback = function ($type, $code, $width, $height, $quality) use ($types, $response, $request) {
- $code = strtolower($code);
- $type = strtolower($type);
+$avatarCallback = function ($type, $code, $width, $height, $quality) use ($types, $response) {
+ $code = \strtolower($code);
+ $type = \strtolower($type);
- if (!array_key_exists($type, $types)) {
+ if (!\array_key_exists($type, $types)) {
throw new Exception('Avatar set not found', 404);
}
- if (!array_key_exists($code, $types[$type])) {
+ if (!\array_key_exists($code, $types[$type])) {
throw new Exception('Avatar not found', 404);
}
- if (!extension_loaded('imagick')) {
+ if (!\extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
$output = 'png';
- $date = date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
- $key = md5('/v1/avatars/:type/:code-'.$code.$width.$height.$quality.$output);
+ $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
+ $key = \md5('/v1/avatars/:type/:code-'.$code.$width.$height.$quality.$output);
$path = $types[$type][$code];
$type = 'png';
- if (!is_readable($path)) {
+ if (!\is_readable($path)) {
throw new Exception('File not readable in '.$path, 500);
}
@@ -66,7 +64,7 @@ $avatarCallback = function ($type, $code, $width, $height, $quality) use ($types
;
}
- $resize = new Resize(file_get_contents($path));
+ $resize = new Resize(\file_get_contents($path));
$resize->crop((int) $width, (int) $height);
@@ -86,13 +84,12 @@ $avatarCallback = function ($type, $code, $width, $height, $quality) use ($types
echo $data;
unset($resize);
-
- exit(0);
};
$utopia->get('/v1/avatars/credit-cards/:code')
->desc('Get Credit Card Icon')
- ->param('code', '', function () use ($types) { return new WhiteList(array_keys($types['credit-cards'])); }, 'Credit Card Code. Possible values: '.implode(', ', array_keys($types['credit-cards'])).'.')
+ ->groups(['api', 'avatars'])
+ ->param('code', '', function () use ($types) { return new WhiteList(\array_keys($types['credit-cards'])); }, 'Credit Card Code. Possible values: '.\implode(', ', \array_keys($types['credit-cards'])).'.')
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
@@ -102,12 +99,14 @@ $utopia->get('/v1/avatars/credit-cards/:code')
->label('sdk.method', 'getCreditCard')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-credit-card.md')
- ->action(function ($code, $width, $height, $quality) use ($avatarCallback) { return $avatarCallback('credit-cards', $code, $width, $height, $quality);
+ ->action(function ($code, $width, $height, $quality) use ($avatarCallback) {
+ return $avatarCallback('credit-cards', $code, $width, $height, $quality);
});
$utopia->get('/v1/avatars/browsers/:code')
->desc('Get Browser Icon')
- ->param('code', '', function () use ($types) { return new WhiteList(array_keys($types['browsers'])); }, 'Browser Code.')
+ ->groups(['api', 'avatars'])
+ ->param('code', '', function () use ($types) { return new WhiteList(\array_keys($types['browsers'])); }, 'Browser Code.')
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
@@ -117,12 +116,14 @@ $utopia->get('/v1/avatars/browsers/:code')
->label('sdk.method', 'getBrowser')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-browser.md')
- ->action(function ($code, $width, $height, $quality) use ($avatarCallback) { return $avatarCallback('browsers', $code, $width, $height, $quality);
+ ->action(function ($code, $width, $height, $quality) use ($avatarCallback) {
+ return $avatarCallback('browsers', $code, $width, $height, $quality);
});
$utopia->get('/v1/avatars/flags/:code')
->desc('Get Country Flag')
- ->param('code', '', function () use ($types) { return new WhiteList(array_keys($types['flags'])); }, 'Country Code. ISO Alpha-2 country code format.')
+ ->groups(['api', 'avatars'])
+ ->param('code', '', function () use ($types) { return new WhiteList(\array_keys($types['flags'])); }, 'Country Code. ISO Alpha-2 country code format.')
->param('width', 100, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
@@ -132,11 +133,13 @@ $utopia->get('/v1/avatars/flags/:code')
->label('sdk.method', 'getFlag')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-flag.md')
- ->action(function ($code, $width, $height, $quality) use ($avatarCallback) { return $avatarCallback('flags', $code, $width, $height, $quality);
+ ->action(function ($code, $width, $height, $quality) use ($avatarCallback) {
+ return $avatarCallback('flags', $code, $width, $height, $quality);
});
$utopia->get('/v1/avatars/image')
->desc('Get Image from URL')
+ ->groups(['api', 'avatars'])
->param('url', '', function () { return new URL(); }, 'Image URL which you want to crop.')
->param('width', 400, function () { return new Range(0, 2000); }, 'Resize preview image width, Pass an integer between 0 to 2000.', true)
->param('height', 400, function () { return new Range(0, 2000); }, 'Resize preview image height, Pass an integer between 0 to 2000.', true)
@@ -150,8 +153,8 @@ $utopia->get('/v1/avatars/image')
function ($url, $width, $height) use ($response) {
$quality = 80;
$output = 'png';
- $date = date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
- $key = md5('/v2/avatars/images-'.$url.'-'.$width.'/'.$height.'/'.$quality);
+ $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
+ $key = \md5('/v2/avatars/images-'.$url.'-'.$width.'/'.$height.'/'.$quality);
$type = 'png';
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-0')); // Limit file number or size
$data = $cache->load($key, 60 * 60 * 24 * 7 /* 1 week */);
@@ -165,11 +168,11 @@ $utopia->get('/v1/avatars/image')
;
}
- if (!extension_loaded('imagick')) {
+ if (!\extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
- $fetch = @file_get_contents($url, false);
+ $fetch = @\file_get_contents($url, false);
if (!$fetch) {
throw new Exception('Image not found', 404);
@@ -199,13 +202,12 @@ $utopia->get('/v1/avatars/image')
echo $data;
unset($resize);
-
- exit(0);
}
);
$utopia->get('/v1/avatars/favicon')
->desc('Get Favicon')
+ ->groups(['api', 'avatars'])
->param('url', '', function () { return new URL(); }, 'Website URL which you want to fetch the favicon from.')
->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
@@ -219,8 +221,8 @@ $utopia->get('/v1/avatars/favicon')
$height = 56;
$quality = 80;
$output = 'png';
- $date = date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
- $key = md5('/v2/avatars/favicon-'.$url);
+ $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
+ $key = \md5('/v2/avatars/favicon-'.$url);
$type = 'png';
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE.'/app-0')); // Limit file number or size
$data = $cache->load($key, 60 * 60 * 24 * 30 * 3 /* 3 months */);
@@ -234,26 +236,26 @@ $utopia->get('/v1/avatars/favicon')
;
}
- if (!extension_loaded('imagick')) {
+ if (!\extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
- $curl = curl_init();
+ $curl = \curl_init();
- curl_setopt_array($curl, [
+ \curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_URL => $url,
- CURLOPT_USERAGENT => sprintf(APP_USERAGENT,
+ CURLOPT_USERAGENT => \sprintf(APP_USERAGENT,
Config::getParam('version'),
$request->getServer('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
),
]);
- $html = curl_exec($curl);
+ $html = \curl_exec($curl);
- curl_close($curl);
+ \curl_close($curl);
if (!$html) {
throw new Exception('Failed to fetch remote URL', 404);
@@ -272,20 +274,20 @@ $utopia->get('/v1/avatars/favicon')
$href = $link->getAttribute('href');
$rel = $link->getAttribute('rel');
$sizes = $link->getAttribute('sizes');
- $absolute = URLParse::unparse(array_merge(parse_url($url), parse_url($href)));
+ $absolute = URLParse::unparse(\array_merge(\parse_url($url), \parse_url($href)));
- switch (strtolower($rel)) {
+ switch (\strtolower($rel)) {
case 'icon':
case 'shortcut icon':
//case 'apple-touch-icon':
- $ext = pathinfo(parse_url($absolute, PHP_URL_PATH), PATHINFO_EXTENSION);
+ $ext = \pathinfo(\parse_url($absolute, PHP_URL_PATH), PATHINFO_EXTENSION);
switch ($ext) {
case 'ico':
case 'png':
case 'jpg':
case 'jpeg':
- $size = explode('x', strtolower($sizes));
+ $size = \explode('x', \strtolower($sizes));
$sizeWidth = (isset($size[0])) ? (int) $size[0] : 0;
$sizeHeight = (isset($size[1])) ? (int) $size[1] : 0;
@@ -304,16 +306,16 @@ $utopia->get('/v1/avatars/favicon')
}
if (empty($outputHref) || empty($outputExt)) {
- $default = parse_url($url);
+ $default = \parse_url($url);
$outputHref = $default['scheme'].'://'.$default['host'].'/favicon.ico';
$outputExt = 'ico';
}
if ('ico' == $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
- $data = @file_get_contents($outputHref, false);
+ $data = @\file_get_contents($outputHref, false);
- if (empty($data) || (mb_substr($data, 0, 5) === 'get('/v1/avatars/favicon')
;
}
- $fetch = @file_get_contents($outputHref, false);
+ $fetch = @\file_get_contents($outputHref, false);
if (!$fetch) {
throw new Exception('Icon not found', 404);
@@ -353,13 +355,12 @@ $utopia->get('/v1/avatars/favicon')
echo $data;
unset($resize);
-
- exit(0);
}
);
$utopia->get('/v1/avatars/qr')
->desc('Get QR Code')
+ ->groups(['api', 'avatars'])
->param('text', '', function () { return new Text(512); }, 'Plain text to be converted to QR code image.')
->param('size', 400, function () { return new Range(0, 1000); }, 'QR code size. Pass an integer between 0 to 1000. Defaults to 400.', true)
->param('margin', 1, function () { return new Range(0, 10); }, 'Margin from edge. Pass an integer between 0 to 10. Defaults to 1.', true)
@@ -384,7 +385,7 @@ $utopia->get('/v1/avatars/qr')
}
$response
- ->addHeader('Expires', date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
+ ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->send($writer->writeString($text))
;
@@ -393,6 +394,7 @@ $utopia->get('/v1/avatars/qr')
$utopia->get('/v1/avatars/initials')
->desc('Get User Initials')
+ ->groups(['api', 'avatars'])
->param('name', '', function () { return new Text(512); }, 'Full Name. When empty, current user name or email will be used.', true)
->param('width', 500, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 500, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
@@ -419,30 +421,30 @@ $utopia->get('/v1/avatars/initials')
['color' => '#610008', 'background' => '#f6d2d5'] // RED
];
- $rand = rand(0, count($themes)-1);
+ $rand = \rand(0, \count($themes)-1);
$name = (!empty($name)) ? $name : $user->getAttribute('name', $user->getAttribute('email', ''));
- $words = explode(' ', strtoupper($name));
+ $words = \explode(' ', \strtoupper($name));
$initials = null;
$code = 0;
foreach ($words as $key => $w) {
$initials .= (isset($w[0])) ? $w[0] : '';
- $code += (isset($w[0])) ? ord($w[0]) : 0;
+ $code += (isset($w[0])) ? \ord($w[0]) : 0;
- if($key == 1) {
+ if ($key == 1) {
break;
}
}
- $length = count($words);
- $rand = substr($code,-1);
+ $length = \count($words);
+ $rand = \substr($code,-1);
$background = (!empty($background)) ? '#'.$background : $themes[$rand]['background'];
$color = (!empty($color)) ? '#'.$color : $themes[$rand]['color'];
$image = new \Imagick();
$draw = new \ImagickDraw();
- $fontSize = min($width, $height) / 2;
+ $fontSize = \min($width, $height) / 2;
$draw->setFont(__DIR__."/../../../public/fonts/poppins-v9-latin-500.ttf");
$image->setFont(__DIR__."/../../../public/fonts/poppins-v9-latin-500.ttf");
@@ -460,7 +462,7 @@ $utopia->get('/v1/avatars/initials')
//$image->setImageCompressionQuality(9 - round(($quality / 100) * 9));
$response
- ->addHeader('Expires', date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
+ ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->send($image->getImageBlob())
;
diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php
index 69d01e9dc7..fafed2624b 100644
--- a/app/controllers/api/database.php
+++ b/app/controllers/api/database.php
@@ -9,6 +9,7 @@ use Utopia\Validator\Range;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Text;
use Utopia\Validator\ArrayList;
+use Utopia\Validator\JSON;
use Utopia\Locale\Locale;
use Utopia\Audit\Audit;
use Utopia\Audit\Adapters\MySQL as AuditAdapter;
@@ -24,12 +25,9 @@ use Appwrite\Database\Exception\Structure as StructureException;
use DeviceDetector\DeviceDetector;
use GeoIp2\Database\Reader;
-include_once __DIR__ . '/../shared/api.php';
-
-$isDev = (App::ENV_TYPE_PRODUCTION !== $utopia->getEnv());
-
$utopia->post('/v1/database/collections')
->desc('Create Collection')
+ ->groups(['api', 'database'])
->label('webhook', 'database.collections.create')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
@@ -45,7 +43,7 @@ $utopia->post('/v1/database/collections')
$parsedRules = [];
foreach ($rules as &$rule) {
- $parsedRules[] = array_merge([
+ $parsedRules[] = \array_merge([
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'$permissions' => [
'read' => $read,
@@ -58,8 +56,8 @@ $utopia->post('/v1/database/collections')
$data = $projectDB->createDocument([
'$collection' => Database::SYSTEM_COLLECTION_COLLECTIONS,
'name' => $name,
- 'dateCreated' => time(),
- 'dateUpdated' => time(),
+ 'dateCreated' => \time(),
+ 'dateUpdated' => \time(),
'structure' => true,
'$permissions' => [
'read' => $read,
@@ -103,6 +101,7 @@ $utopia->post('/v1/database/collections')
$utopia->get('/v1/database/collections')
->desc('List Collections')
+ ->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
@@ -151,6 +150,7 @@ $utopia->get('/v1/database/collections')
$utopia->get('/v1/database/collections/:collectionId')
->desc('Get Collection')
+ ->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_SERVER])
@@ -169,72 +169,74 @@ $utopia->get('/v1/database/collections/:collectionId')
}
);
-$utopia->get('/v1/database/collections/:collectionId/logs')
- ->desc('Get Collection Logs')
- ->label('scope', 'collections.read')
- ->label('sdk.platform', [APP_PLATFORM_SERVER])
- ->label('sdk.namespace', 'database')
- ->label('sdk.method', 'getCollectionLogs')
- ->label('sdk.description', '/docs/references/database/get-collection-logs.md')
- ->param('collectionId', '', function () { return new UID(); }, 'Collection unique ID.')
- ->action(
- function ($collectionId) use ($response, $register, $projectDB, $project) {
- $collection = $projectDB->getDocument($collectionId);
+// $utopia->get('/v1/database/collections/:collectionId/logs')
+// ->desc('Get Collection Logs')
+// ->groups(['api', 'database'])
+// ->label('scope', 'collections.read')
+// ->label('sdk.platform', [APP_PLATFORM_SERVER])
+// ->label('sdk.namespace', 'database')
+// ->label('sdk.method', 'getCollectionLogs')
+// ->label('sdk.description', '/docs/references/database/get-collection-logs.md')
+// ->param('collectionId', '', function () { return new UID(); }, 'Collection unique ID.')
+// ->action(
+// function ($collectionId) use ($response, $register, $projectDB, $project) {
+// $collection = $projectDB->getDocument($collectionId, false);
- if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
- throw new Exception('Collection not found', 404);
- }
+// if (empty($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
+// throw new Exception('Collection not found', 404);
+// }
- $adapter = new AuditAdapter($register->get('db'));
- $adapter->setNamespace('app_'.$project->getId());
+// $adapter = new AuditAdapter($register->get('db'));
+// $adapter->setNamespace('app_'.$project->getId());
- $audit = new Audit($adapter);
+// $audit = new Audit($adapter);
- $countries = Locale::getText('countries');
+// $countries = Locale::getText('countries');
- $logs = $audit->getLogsByResource('database/collection/'.$collection->getId());
+// $logs = $audit->getLogsByResource('database/collection/'.$collection->getId());
- $reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
- $output = [];
+// $reader = new Reader(__DIR__.'/../../db/DBIP/dbip-country-lite-2020-01.mmdb');
+// $output = [];
- foreach ($logs as $i => &$log) {
- $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
+// foreach ($logs as $i => &$log) {
+// $log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
- $dd = new DeviceDetector($log['userAgent']);
+// $dd = new DeviceDetector($log['userAgent']);
- $dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
+// $dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
- $dd->parse();
+// $dd->parse();
- $output[$i] = [
- 'event' => $log['event'],
- 'ip' => $log['ip'],
- 'time' => strtotime($log['time']),
- 'OS' => $dd->getOs(),
- 'client' => $dd->getClient(),
- 'device' => $dd->getDevice(),
- 'brand' => $dd->getBrand(),
- 'model' => $dd->getModel(),
- 'geo' => [],
- ];
+// $output[$i] = [
+// 'event' => $log['event'],
+// 'ip' => $log['ip'],
+// 'time' => strtotime($log['time']),
+// 'OS' => $dd->getOs(),
+// 'client' => $dd->getClient(),
+// 'device' => $dd->getDevice(),
+// 'brand' => $dd->getBrand(),
+// 'model' => $dd->getModel(),
+// 'geo' => [],
+// ];
- try {
- $record = $reader->country($log['ip']);
- $output[$i]['geo']['isoCode'] = strtolower($record->country->isoCode);
- $output[$i]['geo']['country'] = $record->country->name;
- $output[$i]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown');
- } catch (\Exception $e) {
- $output[$i]['geo']['isoCode'] = '--';
- $output[$i]['geo']['country'] = Locale::getText('locale.country.unknown');
- }
- }
+// try {
+// $record = $reader->country($log['ip']);
+// $output[$i]['geo']['isoCode'] = strtolower($record->country->isoCode);
+// $output[$i]['geo']['country'] = $record->country->name;
+// $output[$i]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown');
+// } catch (\Exception $e) {
+// $output[$i]['geo']['isoCode'] = '--';
+// $output[$i]['geo']['country'] = Locale::getText('locale.country.unknown');
+// }
+// }
- $response->json($output);
- }
- );
+// $response->json($output);
+// }
+// );
$utopia->put('/v1/database/collections/:collectionId')
->desc('Update Collection')
+ ->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('webhook', 'database.collections.update')
->label('sdk.namespace', 'database')
@@ -257,7 +259,7 @@ $utopia->put('/v1/database/collections/:collectionId')
$parsedRules = [];
foreach ($rules as &$rule) {
- $parsedRules[] = array_merge([
+ $parsedRules[] = \array_merge([
'$collection' => Database::SYSTEM_COLLECTION_RULES,
'$permissions' => [
'read' => $read,
@@ -267,10 +269,10 @@ $utopia->put('/v1/database/collections/:collectionId')
}
try {
- $collection = $projectDB->updateDocument(array_merge($collection->getArrayCopy(), [
+ $collection = $projectDB->updateDocument(\array_merge($collection->getArrayCopy(), [
'name' => $name,
'structure' => true,
- 'dateUpdated' => time(),
+ 'dateUpdated' => \time(),
'$permissions' => [
'read' => $read,
'write' => $write,
@@ -307,6 +309,7 @@ $utopia->put('/v1/database/collections/:collectionId')
$utopia->delete('/v1/database/collections/:collectionId')
->desc('Delete Collection')
+ ->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('webhook', 'database.collections.delete')
->label('sdk.namespace', 'database')
@@ -344,14 +347,15 @@ $utopia->delete('/v1/database/collections/:collectionId')
$utopia->post('/v1/database/collections/:collectionId/documents')
->desc('Create Document')
+ ->groups(['api', 'database'])
->label('webhook', 'database.documents.create')
->label('scope', 'documents.write')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.method', 'createDocument')
->label('sdk.description', '/docs/references/database/create-document.md')
- ->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/database?platform=server#createCollection).')
- ->param('data', [], function () { return new \Utopia\Validator\Mock(); }, 'Document data as JSON object.')
+ ->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
+ ->param('data', [], function () { return new JSON(); }, 'Document data as JSON object.')
->param('read', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('parentDocument', '', function () { return new UID(); }, 'Parent document unique ID. Use when you want your new document to be a child of a parent document.', true)
@@ -359,8 +363,8 @@ $utopia->post('/v1/database/collections/:collectionId/documents')
->param('parentPropertyType', Document::SET_TYPE_ASSIGN, function () { return new WhiteList([Document::SET_TYPE_ASSIGN, Document::SET_TYPE_APPEND, Document::SET_TYPE_PREPEND]); }, 'Parent document property connection type. You can set this value to **assign**, **append** or **prepend**, default value is assign. Use when you want your new document to be a child of a parent document.', true)
->action(
function ($collectionId, $data, $read, $write, $parentDocument, $parentProperty, $parentPropertyType) use ($response, $projectDB, $webhook, $audit) {
- $data = (is_string($data) && $result = json_decode($data, true)) ? $result : $data; // Cast to JSON array
-
+ $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
+
if (empty($data)) {
throw new Exception('Missing payload', 400);
}
@@ -369,9 +373,9 @@ $utopia->post('/v1/database/collections/:collectionId/documents')
throw new Exception('$id is not allowed for creating new documents, try update instead', 400);
}
- $collection = $projectDB->getDocument($collectionId/*, $isDev*/);
+ $collection = $projectDB->getDocument($collectionId, false);
- if (is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
+ if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
@@ -384,7 +388,7 @@ $utopia->post('/v1/database/collections/:collectionId/documents')
// Read parent document + validate not 404 + validate read / write permission like patch method
// Add payload to parent document property
if ((!empty($parentDocument)) && (!empty($parentProperty))) {
- $parentDocument = $projectDB->getDocument($parentDocument);
+ $parentDocument = $projectDB->getDocument($parentDocument, false);
if (empty($parentDocument->getArrayCopy())) { // Check empty
throw new Exception('No parent document found', 404);
@@ -424,7 +428,7 @@ $utopia->post('/v1/database/collections/:collectionId/documents')
$key = (isset($rule['key'])) ? $rule['key'] : '';
$default = (isset($rule['default'])) ? $rule['default'] : null;
- if(!isset($data[$key])) {
+ if (!isset($data[$key])) {
$data[$key] = $default;
}
}
@@ -463,12 +467,13 @@ $utopia->post('/v1/database/collections/:collectionId/documents')
$utopia->get('/v1/database/collections/:collectionId/documents')
->desc('List Documents')
+ ->groups(['api', 'database'])
->label('scope', 'documents.read')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.method', 'listDocuments')
->label('sdk.description', '/docs/references/database/list-documents.md')
- ->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/database?platform=server#createCollection).')
+ ->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
->param('filters', [], function () { return new ArrayList(new Text(128)); }, 'Array of filter strings. Each filter is constructed from a key name, comparison operator (=, !=, >, <, <=, >=) and a value. You can also use a dot (.) separator in attribute names to filter by child document attributes. Examples: \'name=John Doe\' or \'category.$id>=5bed2d152c362\'.', true)
->param('offset', 0, function () { return new Range(0, 900000000); }, 'Offset value. Use this value to manage pagination.', true)
->param('limit', 50, function () { return new Range(0, 1000); }, 'Maximum number of documents to return in response. Use this value to manage pagination.', true)
@@ -479,10 +484,10 @@ $utopia->get('/v1/database/collections/:collectionId/documents')
->param('first', 0, function () { return new Range(0, 1); }, 'Return only the first document. Pass 1 for true or 0 for false. The default value is 0.', true)
->param('last', 0, function () { return new Range(0, 1); }, 'Return only the last document. Pass 1 for true or 0 for false. The default value is 0.', true)
->action(
- function ($collectionId, $filters, $offset, $limit, $orderField, $orderType, $orderCast, $search, $first, $last) use ($response, $projectDB, $isDev) {
- $collection = $projectDB->getDocument($collectionId, $isDev);
+ function ($collectionId, $filters, $offset, $limit, $orderField, $orderType, $orderCast, $search, $first, $last) use ($response, $projectDB, $utopia) {
+ $collection = $projectDB->getDocument($collectionId, false);
- if (is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
+ if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
@@ -495,7 +500,7 @@ $utopia->get('/v1/database/collections/:collectionId/documents')
'search' => $search,
'first' => (bool) $first,
'last' => (bool) $last,
- 'filters' => array_merge($filters, [
+ 'filters' => \array_merge($filters, [
'$collection='.$collectionId,
]),
]);
@@ -503,7 +508,7 @@ $utopia->get('/v1/database/collections/:collectionId/documents')
if ($first || $last) {
$response->json((!empty($list) ? $list->getArrayCopy() : []));
} else {
- if ($isDev) {
+ if ($utopia->isDevelopment()) {
$collection
->setAttribute('debug', $projectDB->getDebug())
->setAttribute('limit', $limit)
@@ -530,17 +535,18 @@ $utopia->get('/v1/database/collections/:collectionId/documents')
$utopia->get('/v1/database/collections/:collectionId/documents/:documentId')
->desc('Get Document')
+ ->groups(['api', 'database'])
->label('scope', 'documents.read')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.method', 'getDocument')
->label('sdk.description', '/docs/references/database/get-document.md')
- ->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/database?platform=server#createCollection).')
+ ->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
->param('documentId', null, function () { return new UID(); }, 'Document unique ID.')
->action(
- function ($collectionId, $documentId) use ($response, $request, $projectDB, $isDev) {
- $document = $projectDB->getDocument($documentId, $isDev);
- $collection = $projectDB->getDocument($collectionId, $isDev);
+ function ($collectionId, $documentId) use ($response, $request, $projectDB) {
+ $document = $projectDB->getDocument($documentId, false);
+ $collection = $projectDB->getDocument($collectionId, false);
if (empty($document->getArrayCopy()) || $document->getCollection() != $collection->getId()) { // Check empty
throw new Exception('No document found', 404);
@@ -548,20 +554,20 @@ $utopia->get('/v1/database/collections/:collectionId/documents/:documentId')
$output = $document->getArrayCopy();
- $paths = explode('/', $request->getParam('q', ''));
- $paths = array_slice($paths, 7, count($paths));
+ $paths = \explode('/', $request->getParam('q', ''));
+ $paths = \array_slice($paths, 7, \count($paths));
- if (count($paths) > 0) {
- if (count($paths) % 2 == 1) {
- $output = $document->getAttribute(implode('.', $paths));
+ if (\count($paths) > 0) {
+ if (\count($paths) % 2 == 1) {
+ $output = $document->getAttribute(\implode('.', $paths));
} else {
- $id = (int) array_pop($paths);
- $output = $document->search('$id', $id, $document->getAttribute(implode('.', $paths)));
+ $id = (int) \array_pop($paths);
+ $output = $document->search('$id', $id, $document->getAttribute(\implode('.', $paths)));
}
$output = ($output instanceof Document) ? $output->getArrayCopy() : $output;
- if (!is_array($output)) {
+ if (!\is_array($output)) {
throw new Exception('No document found', 404);
}
}
@@ -575,29 +581,30 @@ $utopia->get('/v1/database/collections/:collectionId/documents/:documentId')
$utopia->patch('/v1/database/collections/:collectionId/documents/:documentId')
->desc('Update Document')
+ ->groups(['api', 'database'])
->label('webhook', 'database.documents.update')
->label('scope', 'documents.write')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.method', 'updateDocument')
->label('sdk.description', '/docs/references/database/update-document.md')
- ->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/database?platform=server#createCollection).')
+ ->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
->param('documentId', null, function () { return new UID(); }, 'Document unique ID.')
- ->param('data', [], function () { return new \Utopia\Validator\Mock(); }, 'Document data as JSON object.')
+ ->param('data', [], function () { return new JSON(); }, 'Document data as JSON object.')
->param('read', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('write', [], function () { return new ArrayList(new Text(64)); }, 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->action(
- function ($collectionId, $documentId, $data, $read, $write) use ($response, $projectDB, &$output, $webhook, $audit, $isDev) {
- $collection = $projectDB->getDocument($collectionId/*, $isDev*/);
- $document = $projectDB->getDocument($documentId, $isDev);
+ function ($collectionId, $documentId, $data, $read, $write) use ($response, $projectDB, $webhook, $audit) {
+ $collection = $projectDB->getDocument($collectionId, false);
+ $document = $projectDB->getDocument($documentId, false);
- $data = (is_string($data) && $result = json_decode($data, true)) ? $result : $data; // Cast to JSON array
+ $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
- if (!is_array($data)) {
+ if (!\is_array($data)) {
throw new Exception('Data param should be a valid JSON', 400);
}
- if (is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
+ if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
@@ -615,7 +622,7 @@ $utopia->patch('/v1/database/collections/:collectionId/documents/:documentId')
$data['$permissions']['write'] = $read;
}
- $data = array_merge($document->getArrayCopy(), $data);
+ $data = \array_merge($document->getArrayCopy(), $data);
$data['$collection'] = $collection->getId(); // Make sure user don't switch collectionID
$data['$id'] = $document->getId(); // Make sure user don't switch document unique ID
@@ -654,24 +661,25 @@ $utopia->patch('/v1/database/collections/:collectionId/documents/:documentId')
$utopia->delete('/v1/database/collections/:collectionId/documents/:documentId')
->desc('Delete Document')
+ ->groups(['api', 'database'])
->label('scope', 'documents.write')
->label('webhook', 'database.documents.delete')
->label('sdk.namespace', 'database')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.method', 'deleteDocument')
->label('sdk.description', '/docs/references/database/delete-document.md')
- ->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/database?platform=server#createCollection).')
+ ->param('collectionId', null, function () { return new UID(); }, 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
->param('documentId', null, function () { return new UID(); }, 'Document unique ID.')
->action(
- function ($collectionId, $documentId) use ($response, $projectDB, $audit, $webhook, $isDev) {
- $collection = $projectDB->getDocument($collectionId, $isDev);
- $document = $projectDB->getDocument($documentId, $isDev);
+ function ($collectionId, $documentId) use ($response, $projectDB, $audit, $webhook) {
+ $collection = $projectDB->getDocument($collectionId, false);
+ $document = $projectDB->getDocument($documentId, false);
if (empty($document->getArrayCopy()) || $document->getCollection() != $collectionId) { // Check empty
throw new Exception('No document found', 404);
}
- if (is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
+ if (\is_null($collection->getId()) || Database::SYSTEM_COLLECTION_COLLECTIONS != $collection->getCollection()) {
throw new Exception('Collection not found', 404);
}
diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php
index ce39d8e799..0eecb23e15 100644
--- a/app/controllers/api/graphql.php
+++ b/app/controllers/api/graphql.php
@@ -14,6 +14,7 @@ global $utopia;
$utopia->post('/v1/graphql')
->desc('GraphQL Endpoint')
+ ->groups(['api', 'graphql'])
->label('scope', 'public')
->action(
function () {
diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php
index db95fe5db1..76727563ae 100644
--- a/app/controllers/api/health.php
+++ b/app/controllers/api/health.php
@@ -9,6 +9,7 @@ use Appwrite\ClamAV\Network;
$utopia->get('/v1/health')
->desc('Get HTTP')
+ ->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'health')
@@ -20,8 +21,19 @@ $utopia->get('/v1/health')
}
);
+$utopia->get('/v1/health/version')
+ ->desc('Get Version')
+ ->groups(['api', 'health'])
+ ->label('scope', 'public')
+ ->action(
+ function () use ($response) {
+ $response->json(['version' => APP_VERSION_STABLE]);
+ }
+ );
+
$utopia->get('/v1/health/db')
->desc('Get DB')
+ ->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'health')
@@ -37,6 +49,7 @@ $utopia->get('/v1/health/db')
$utopia->get('/v1/health/cache')
->desc('Get Cache')
+ ->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'health')
@@ -52,6 +65,7 @@ $utopia->get('/v1/health/cache')
$utopia->get('/v1/health/time')
->desc('Get Time')
+ ->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'health')
@@ -66,39 +80,40 @@ $utopia->get('/v1/health/time')
$gap = 60; // Allow [X] seconds gap
/* Create a socket and connect to NTP server */
- $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
+ $sock = \socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
- socket_connect($sock, $host, 123);
+ \socket_connect($sock, $host, 123);
/* Send request */
- $msg = "\010".str_repeat("\0", 47);
+ $msg = "\010".\str_repeat("\0", 47);
- socket_send($sock, $msg, strlen($msg), 0);
+ \socket_send($sock, $msg, \strlen($msg), 0);
/* Receive response and close socket */
- socket_recv($sock, $recv, 48, MSG_WAITALL);
- socket_close($sock);
+ \socket_recv($sock, $recv, 48, MSG_WAITALL);
+ \socket_close($sock);
/* Interpret response */
- $data = unpack('N12', $recv);
- $timestamp = sprintf('%u', $data[9]);
+ $data = \unpack('N12', $recv);
+ $timestamp = \sprintf('%u', $data[9]);
/* NTP is number of seconds since 0000 UT on 1 January 1900
Unix time is seconds since 0000 UT on 1 January 1970 */
$timestamp -= 2208988800;
- $diff = ($timestamp - time());
+ $diff = ($timestamp - \time());
if ($diff > $gap || $diff < ($gap * -1)) {
throw new Exception('Server time gaps detected');
}
- $response->json(['remote' => $timestamp, 'local' => time(), 'diff' => $diff]);
+ $response->json(['remote' => $timestamp, 'local' => \time(), 'diff' => $diff]);
}
);
$utopia->get('/v1/health/queue/webhooks')
->desc('Get Webhooks Queue')
+ ->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'health')
@@ -112,6 +127,7 @@ $utopia->get('/v1/health/queue/webhooks')
$utopia->get('/v1/health/queue/tasks')
->desc('Get Tasks Queue')
+ ->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'health')
@@ -124,7 +140,8 @@ $utopia->get('/v1/health/queue/tasks')
);
$utopia->get('/v1/health/queue/logs')
-->desc('Get Logs Queue')
+ ->desc('Get Logs Queue')
+ ->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'health')
@@ -138,6 +155,7 @@ $utopia->get('/v1/health/queue/logs')
$utopia->get('/v1/health/queue/usage')
->desc('Get Usage Queue')
+ ->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'health')
@@ -151,6 +169,7 @@ $utopia->get('/v1/health/queue/usage')
$utopia->get('/v1/health/queue/certificates')
->desc('Get Certificate Queue')
+ ->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'health')
@@ -164,6 +183,7 @@ $utopia->get('/v1/health/queue/certificates')
$utopia->get('/v1/health/queue/functions')
->desc('Get Functions Queue')
+ ->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'health')
@@ -177,6 +197,7 @@ $utopia->get('/v1/health/queue/functions')
$utopia->get('/v1/health/storage/local')
->desc('Get Local Storage')
+ ->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'health')
@@ -184,26 +205,21 @@ $utopia->get('/v1/health/storage/local')
->label('sdk.description', '/docs/references/health/get-storage-local.md')
->action(
function () use ($response) {
- $device = new Local(APP_STORAGE_UPLOADS.'/');
+ foreach ([
+ 'Uploads' => APP_STORAGE_UPLOADS,
+ 'Cache' => APP_STORAGE_CACHE,
+ 'Config' => APP_STORAGE_CONFIG,
+ 'Certs' => APP_STORAGE_CERTIFICATES
+ ] as $key => $volume) {
+ $device = new Local($volume);
- if (!is_readable($device->getRoot().'/..')) {
- throw new Exception('Device is not readable');
- }
-
- if (!is_writable($device->getRoot().'/../uploads')) {
- throw new Exception('Device uploads dir is not writable');
- }
-
- if (!is_writable($device->getRoot().'/../cache')) {
- throw new Exception('Device cache dir is not writable');
- }
+ if (!\is_readable($device->getRoot())) {
+ throw new Exception('Device '.$key.' dir is not readable');
+ }
- if (!is_writable($device->getRoot().'/../config')) {
- throw new Exception('Device config dir is not writable');
- }
-
- if (!is_writable($device->getRoot().'/../certificates')) {
- throw new Exception('Device certificates dir is not writable');
+ if (!\is_writable($device->getRoot())) {
+ throw new Exception('Device '.$key.' dir is not writable');
+ }
}
$response->json(['status' => 'OK']);
@@ -212,6 +228,7 @@ $utopia->get('/v1/health/storage/local')
$utopia->get('/v1/health/anti-virus')
->desc('Get Anti virus')
+ ->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'health')
@@ -219,7 +236,7 @@ $utopia->get('/v1/health/anti-virus')
->label('sdk.description', '/docs/references/health/get-storage-anti-virus.md')
->action(
function () use ($request, $response) {
- if($request->getServer('_APP_STORAGE_ANTIVIRUS') === 'disabled') { // Check if scans are enabled
+ if ($request->getServer('_APP_STORAGE_ANTIVIRUS') === 'disabled') { // Check if scans are enabled
throw new Exception('Anitvirus is disabled');
}
@@ -234,6 +251,7 @@ $utopia->get('/v1/health/anti-virus')
$utopia->get('/v1/health/stats') // Currently only used internally
->desc('Get System Stats')
+ ->groups(['api', 'health'])
->label('scope', 'god')
// ->label('sdk.platform', [APP_PLATFORM_SERVER])
// ->label('sdk.namespace', 'health')
@@ -250,7 +268,7 @@ $utopia->get('/v1/health/stats') // Currently only used internally
->json([
'server' => [
'name' => 'nginx',
- 'version' => shell_exec('nginx -v 2>&1'),
+ 'version' => \shell_exec('nginx -v 2>&1'),
],
'storage' => [
'used' => Storage::human($device->getDirectorySize($device->getRoot().'/')),
diff --git a/app/controllers/api/locale.php b/app/controllers/api/locale.php
index 64978ac355..148ee3e8e9 100644
--- a/app/controllers/api/locale.php
+++ b/app/controllers/api/locale.php
@@ -6,10 +6,9 @@ use Utopia\App;
use Utopia\Locale\Locale;
use GeoIp2\Database\Reader;
-include_once __DIR__ . '/../shared/api.php';
-
$utopia->get('/v1/locale')
->desc('Get User Locale')
+ ->groups(['api', 'locale'])
->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'locale')
@@ -26,7 +25,7 @@ $utopia->get('/v1/locale')
$countries = Locale::getText('countries');
$continents = Locale::getText('continents');
- if (App::ENV_TYPE_PRODUCTION !== $utopia->getEnv()) {
+ if (App::MODE_TYPE_PRODUCTION !== $utopia->getMode()) {
$ip = '79.177.241.94';
}
@@ -41,10 +40,10 @@ $utopia->get('/v1/locale')
//$output['countryTimeZone'] = DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $record->country->isoCode);
$output['continent'] = (isset($continents[$record->continent->code])) ? $continents[$record->continent->code] : Locale::getText('locale.country.unknown');
$output['continentCode'] = $record->continent->code;
- $output['eu'] = (in_array($record->country->isoCode, $eu)) ? true : false;
+ $output['eu'] = (\in_array($record->country->isoCode, $eu)) ? true : false;
foreach ($currencies as $code => $element) {
- if (isset($element['locations']) && isset($element['code']) && in_array($record->country->isoCode, $element['locations'])) {
+ if (isset($element['locations']) && isset($element['code']) && \in_array($record->country->isoCode, $element['locations'])) {
$currency = $element['code'];
}
}
@@ -61,13 +60,14 @@ $utopia->get('/v1/locale')
$response
->addHeader('Cache-Control', 'public, max-age='.$time)
- ->addHeader('Expires', date('D, d M Y H:i:s', time() + $time).' GMT') // 45 days cache
+ ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache
->json($output);
}
);
$utopia->get('/v1/locale/countries')
->desc('List Countries')
+ ->groups(['api', 'locale'])
->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'locale')
@@ -77,7 +77,7 @@ $utopia->get('/v1/locale/countries')
function () use ($response) {
$list = Locale::getText('countries'); /* @var $list array */
- asort($list);
+ \asort($list);
$response->json($list);
}
@@ -85,6 +85,7 @@ $utopia->get('/v1/locale/countries')
$utopia->get('/v1/locale/countries/eu')
->desc('List EU Countries')
+ ->groups(['api', 'locale'])
->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'locale')
@@ -97,12 +98,12 @@ $utopia->get('/v1/locale/countries/eu')
$list = [];
foreach ($eu as $code) {
- if (array_key_exists($code, $countries)) {
+ if (\array_key_exists($code, $countries)) {
$list[$code] = $countries[$code];
}
}
- asort($list);
+ \asort($list);
$response->json($list);
}
@@ -110,6 +111,7 @@ $utopia->get('/v1/locale/countries/eu')
$utopia->get('/v1/locale/countries/phones')
->desc('List Countries Phone Codes')
+ ->groups(['api', 'locale'])
->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'locale')
@@ -122,12 +124,12 @@ $utopia->get('/v1/locale/countries/phones')
$countries = Locale::getText('countries'); /* @var $countries array */
foreach ($list as $code => $name) {
- if (array_key_exists($code, $countries)) {
+ if (\array_key_exists($code, $countries)) {
$list[$code] = '+'.$list[$code];
}
}
- asort($list);
+ \asort($list);
$response->json($list);
}
@@ -135,6 +137,7 @@ $utopia->get('/v1/locale/countries/phones')
$utopia->get('/v1/locale/continents')
->desc('List Continents')
+ ->groups(['api', 'locale'])
->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'locale')
@@ -144,7 +147,7 @@ $utopia->get('/v1/locale/continents')
function () use ($response) {
$list = Locale::getText('continents'); /* @var $list array */
- asort($list);
+ \asort($list);
$response->json($list);
}
@@ -153,6 +156,7 @@ $utopia->get('/v1/locale/continents')
$utopia->get('/v1/locale/currencies')
->desc('List Currencies')
+ ->groups(['api', 'locale'])
->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'locale')
@@ -169,6 +173,7 @@ $utopia->get('/v1/locale/currencies')
$utopia->get('/v1/locale/languages')
->desc('List Languages')
+ ->groups(['api', 'locale'])
->label('scope', 'locale.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'locale')
diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php
index 766b2f48fb..89f26feb15 100644
--- a/app/controllers/api/projects.php
+++ b/app/controllers/api/projects.php
@@ -21,12 +21,11 @@ use Appwrite\OpenSSL\OpenSSL;
use Appwrite\Network\Validator\CNAME;
use Cron\CronExpression;
-include_once __DIR__ . '/../shared/api.php';
-
$scopes = include __DIR__.'/../../../app/config/scopes.php';
$utopia->post('/v1/projects')
->desc('Create Project')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'create')
@@ -89,6 +88,7 @@ $utopia->post('/v1/projects')
$utopia->get('/v1/projects')
->desc('List Projects')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'list')
@@ -107,11 +107,11 @@ $utopia->get('/v1/projects')
foreach ($results as $project) {
foreach (Config::getParam('providers') as $provider => $node) {
- $secret = json_decode($project->getAttribute('usersOauth2'.ucfirst($provider).'Secret', '{}'), true);
+ $secret = \json_decode($project->getAttribute('usersOauth2'.\ucfirst($provider).'Secret', '{}'), true);
if (!empty($secret) && isset($secret['version'])) {
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$secret['version']);
- $project->setAttribute('usersOauth2'.ucfirst($provider).'Secret', OpenSSL::decrypt($secret['data'], $secret['method'], $key, 0, hex2bin($secret['iv']), hex2bin($secret['tag'])));
+ $project->setAttribute('usersOauth2'.\ucfirst($provider).'Secret', OpenSSL::decrypt($secret['data'], $secret['method'], $key, 0, \hex2bin($secret['iv']), \hex2bin($secret['tag'])));
}
}
}
@@ -122,6 +122,7 @@ $utopia->get('/v1/projects')
$utopia->get('/v1/projects/:projectId')
->desc('Get Project')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'get')
@@ -135,11 +136,11 @@ $utopia->get('/v1/projects/:projectId')
}
foreach (Config::getParam('providers') as $provider => $node) {
- $secret = json_decode($project->getAttribute('usersOauth2'.ucfirst($provider).'Secret', '{}'), true);
+ $secret = \json_decode($project->getAttribute('usersOauth2'.\ucfirst($provider).'Secret', '{}'), true);
if (!empty($secret) && isset($secret['version'])) {
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$secret['version']);
- $project->setAttribute('usersOauth2'.ucfirst($provider).'Secret', OpenSSL::decrypt($secret['data'], $secret['method'], $key, 0, hex2bin($secret['iv']), hex2bin($secret['tag'])));
+ $project->setAttribute('usersOauth2'.\ucfirst($provider).'Secret', OpenSSL::decrypt($secret['data'], $secret['method'], $key, 0, \hex2bin($secret['iv']), \hex2bin($secret['tag'])));
}
}
@@ -149,6 +150,7 @@ $utopia->get('/v1/projects/:projectId')
$utopia->get('/v1/projects/:projectId/usage')
->desc('Get Project')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getUsage')
@@ -164,23 +166,23 @@ $utopia->get('/v1/projects/:projectId/usage')
$period = [
'daily' => [
- 'start' => DateTime::createFromFormat('U', strtotime('today')),
- 'end' => DateTime::createFromFormat('U', strtotime('tomorrow')),
+ 'start' => DateTime::createFromFormat('U', \strtotime('today')),
+ 'end' => DateTime::createFromFormat('U', \strtotime('tomorrow')),
'group' => '1m',
],
'monthly' => [
- 'start' => DateTime::createFromFormat('U', strtotime('midnight first day of this month')),
- 'end' => DateTime::createFromFormat('U', strtotime('midnight last day of this month')),
+ 'start' => DateTime::createFromFormat('U', \strtotime('midnight first day of this month')),
+ 'end' => DateTime::createFromFormat('U', \strtotime('midnight last day of this month')),
'group' => '1d',
],
'last30' => [
- 'start' => DateTime::createFromFormat('U', strtotime('-30 days')),
- 'end' => DateTime::createFromFormat('U', strtotime('tomorrow')),
+ 'start' => DateTime::createFromFormat('U', \strtotime('-30 days')),
+ 'end' => DateTime::createFromFormat('U', \strtotime('tomorrow')),
'group' => '1d',
],
'last90' => [
- 'start' => DateTime::createFromFormat('U', strtotime('-90 days')),
- 'end' => DateTime::createFromFormat('U', strtotime('today')),
+ 'start' => DateTime::createFromFormat('U', \strtotime('-90 days')),
+ 'end' => DateTime::createFromFormat('U', \strtotime('today')),
'group' => '1d',
],
// 'yearly' => [
@@ -207,7 +209,7 @@ $utopia->get('/v1/projects/:projectId/usage')
foreach ($points as $point) {
$requests[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
- 'date' => strtotime($point['time']),
+ 'date' => \strtotime($point['time']),
];
}
@@ -218,7 +220,7 @@ $utopia->get('/v1/projects/:projectId/usage')
foreach ($points as $point) {
$network[] = [
'value' => (!empty($point['value'])) ? $point['value'] : 0,
- 'date' => strtotime($point['time']),
+ 'date' => \strtotime($point['time']),
];
}
}
@@ -262,18 +264,18 @@ $utopia->get('/v1/projects/:projectId/usage')
}
// Tasks
- $tasksTotal = count($project->getAttribute('tasks', []));
+ $tasksTotal = \count($project->getAttribute('tasks', []));
$response->json([
'requests' => [
'data' => $requests,
- 'total' => array_sum(array_map(function ($item) {
+ 'total' => \array_sum(\array_map(function ($item) {
return $item['value'];
}, $requests)),
],
'network' => [
- 'data' => array_map(function ($value) {return ['value' => round($value['value'] / 1000000, 2), 'date' => $value['date']];}, $network), // convert bytes to mb
- 'total' => array_sum(array_map(function ($item) {
+ 'data' => \array_map(function ($value) {return ['value' => \round($value['value'] / 1000000, 2), 'date' => $value['date']];}, $network), // convert bytes to mb
+ 'total' => \array_sum(\array_map(function ($item) {
return $item['value'];
}, $network)),
],
@@ -283,7 +285,7 @@ $utopia->get('/v1/projects/:projectId/usage')
],
'documents' => [
'data' => $documents,
- 'total' => array_sum(array_map(function ($item) {
+ 'total' => \array_sum(\array_map(function ($item) {
return $item['total'];
}, $documents)),
],
@@ -310,6 +312,7 @@ $utopia->get('/v1/projects/:projectId/usage')
$utopia->patch('/v1/projects/:projectId')
->desc('Update Project')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'update')
@@ -332,7 +335,7 @@ $utopia->patch('/v1/projects/:projectId')
throw new Exception('Project not found', 404);
}
- $project = $consoleDB->updateDocument(array_merge($project->getArrayCopy(), [
+ $project = $consoleDB->updateDocument(\array_merge($project->getArrayCopy(), [
'name' => $name,
'description' => $description,
'logo' => $logo,
@@ -355,11 +358,12 @@ $utopia->patch('/v1/projects/:projectId')
$utopia->patch('/v1/projects/:projectId/oauth2')
->desc('Update Project OAuth2')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateOAuth2')
->param('projectId', '', function () { return new UID(); }, 'Project unique ID.')
- ->param('provider', '', function () { return new WhiteList(array_keys(Config::getParam('providers'))); }, 'Provider Name', false)
+ ->param('provider', '', function () { return new WhiteList(\array_keys(Config::getParam('providers'))); }, 'Provider Name', false)
->param('appId', '', function () { return new Text(256); }, 'Provider app ID.', true)
->param('secret', '', function () { return new text(512); }, 'Provider secret key.', true)
->action(
@@ -373,17 +377,17 @@ $utopia->patch('/v1/projects/:projectId/oauth2')
$key = $request->getServer('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$tag = null;
- $secret = json_encode([
+ $secret = \json_encode([
'data' => OpenSSL::encrypt($secret, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
'method' => OpenSSL::CIPHER_AES_128_GCM,
- 'iv' => bin2hex($iv),
- 'tag' => bin2hex($tag),
+ 'iv' => \bin2hex($iv),
+ 'tag' => \bin2hex($tag),
'version' => '1',
]);
- $project = $consoleDB->updateDocument(array_merge($project->getArrayCopy(), [
- 'usersOauth2'.ucfirst($provider).'Appid' => $appId,
- 'usersOauth2'.ucfirst($provider).'Secret' => $secret,
+ $project = $consoleDB->updateDocument(\array_merge($project->getArrayCopy(), [
+ 'usersOauth2'.\ucfirst($provider).'Appid' => $appId,
+ 'usersOauth2'.\ucfirst($provider).'Secret' => $secret,
]));
if (false === $project) {
@@ -396,6 +400,7 @@ $utopia->patch('/v1/projects/:projectId/oauth2')
$utopia->delete('/v1/projects/:projectId')
->desc('Delete Project')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'delete')
@@ -415,7 +420,7 @@ $utopia->delete('/v1/projects/:projectId')
$deletes->setParam('document', $project->getArrayCopy());
- foreach(['keys', 'webhooks', 'tasks', 'platforms', 'domains'] as $key) { // Delete all children (keys, webhooks, tasks [stop tasks?], platforms)
+ foreach (['keys', 'webhooks', 'tasks', 'platforms', 'domains'] as $key) { // Delete all children (keys, webhooks, tasks [stop tasks?], platforms)
$list = $project->getAttribute('webhooks', []);
foreach ($list as $document) { /* @var $document Document */
@@ -441,6 +446,7 @@ $utopia->delete('/v1/projects/:projectId')
$utopia->post('/v1/projects/:projectId/webhooks')
->desc('Create Webhook')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createWebhook')
@@ -462,11 +468,11 @@ $utopia->post('/v1/projects/:projectId/webhooks')
$key = $request->getServer('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$tag = null;
- $httpPass = json_encode([
+ $httpPass = \json_encode([
'data' => OpenSSL::encrypt($httpPass, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
'method' => OpenSSL::CIPHER_AES_128_GCM,
- 'iv' => bin2hex($iv),
- 'tag' => bin2hex($tag),
+ 'iv' => \bin2hex($iv),
+ 'tag' => \bin2hex($tag),
'version' => '1',
]);
@@ -505,6 +511,7 @@ $utopia->post('/v1/projects/:projectId/webhooks')
$utopia->get('/v1/projects/:projectId/webhooks')
->desc('List Webhooks')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listWebhooks')
@@ -520,7 +527,7 @@ $utopia->get('/v1/projects/:projectId/webhooks')
$webhooks = $project->getAttribute('webhooks', []);
foreach ($webhooks as $webhook) { /* @var $webhook Document */
- $httpPass = json_decode($webhook->getAttribute('httpPass', '{}'), true);
+ $httpPass = \json_decode($webhook->getAttribute('httpPass', '{}'), true);
if (empty($httpPass) || !isset($httpPass['version'])) {
continue;
@@ -528,7 +535,7 @@ $utopia->get('/v1/projects/:projectId/webhooks')
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$httpPass['version']);
- $webhook->setAttribute('httpPass', OpenSSL::decrypt($httpPass['data'], $httpPass['method'], $key, 0, hex2bin($httpPass['iv']), hex2bin($httpPass['tag'])));
+ $webhook->setAttribute('httpPass', OpenSSL::decrypt($httpPass['data'], $httpPass['method'], $key, 0, \hex2bin($httpPass['iv']), \hex2bin($httpPass['tag'])));
}
$response->json($webhooks);
@@ -537,6 +544,7 @@ $utopia->get('/v1/projects/:projectId/webhooks')
$utopia->get('/v1/projects/:projectId/webhooks/:webhookId')
->desc('Get Webhook')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getWebhook')
@@ -556,11 +564,11 @@ $utopia->get('/v1/projects/:projectId/webhooks/:webhookId')
throw new Exception('Webhook not found', 404);
}
- $httpPass = json_decode($webhook->getAttribute('httpPass', '{}'), true);
+ $httpPass = \json_decode($webhook->getAttribute('httpPass', '{}'), true);
if (!empty($httpPass) && isset($httpPass['version'])) {
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$httpPass['version']);
- $webhook->setAttribute('httpPass', OpenSSL::decrypt($httpPass['data'], $httpPass['method'], $key, 0, hex2bin($httpPass['iv']), hex2bin($httpPass['tag'])));
+ $webhook->setAttribute('httpPass', OpenSSL::decrypt($httpPass['data'], $httpPass['method'], $key, 0, \hex2bin($httpPass['iv']), \hex2bin($httpPass['tag'])));
}
$response->json($webhook->getArrayCopy());
@@ -570,6 +578,7 @@ $utopia->get('/v1/projects/:projectId/webhooks/:webhookId')
$utopia->put('/v1/projects/:projectId/webhooks/:webhookId')
->desc('Update Webhook')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateWebhook')
@@ -592,11 +601,11 @@ $utopia->put('/v1/projects/:projectId/webhooks/:webhookId')
$key = $request->getServer('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$tag = null;
- $httpPass = json_encode([
+ $httpPass = \json_encode([
'data' => OpenSSL::encrypt($httpPass, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
'method' => OpenSSL::CIPHER_AES_128_GCM,
- 'iv' => bin2hex($iv),
- 'tag' => bin2hex($tag),
+ 'iv' => \bin2hex($iv),
+ 'tag' => \bin2hex($tag),
'version' => '1',
]);
@@ -625,6 +634,7 @@ $utopia->put('/v1/projects/:projectId/webhooks/:webhookId')
$utopia->delete('/v1/projects/:projectId/webhooks/:webhookId')
->desc('Delete Webhook')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteWebhook')
@@ -656,6 +666,7 @@ $utopia->delete('/v1/projects/:projectId/webhooks/:webhookId')
$utopia->post('/v1/projects/:projectId/keys')
->desc('Create Key')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createKey')
@@ -678,7 +689,7 @@ $utopia->post('/v1/projects/:projectId/keys')
],
'name' => $name,
'scopes' => $scopes,
- 'secret' => bin2hex(random_bytes(128)),
+ 'secret' => \bin2hex(\random_bytes(128)),
]);
if (false === $key) {
@@ -702,6 +713,7 @@ $utopia->post('/v1/projects/:projectId/keys')
$utopia->get('/v1/projects/:projectId/keys')
->desc('List Keys')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listKeys')
@@ -720,6 +732,7 @@ $utopia->get('/v1/projects/:projectId/keys')
$utopia->get('/v1/projects/:projectId/keys/:keyId')
->desc('Get Key')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getKey')
@@ -745,6 +758,7 @@ $utopia->get('/v1/projects/:projectId/keys/:keyId')
$utopia->put('/v1/projects/:projectId/keys/:keyId')
->desc('Update Key')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateKey')
@@ -781,6 +795,7 @@ $utopia->put('/v1/projects/:projectId/keys/:keyId')
$utopia->delete('/v1/projects/:projectId/keys/:keyId')
->desc('Delete Key')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteKey')
@@ -812,6 +827,7 @@ $utopia->delete('/v1/projects/:projectId/keys/:keyId')
$utopia->post('/v1/projects/:projectId/tasks')
->desc('Create Task')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createTask')
@@ -839,11 +855,11 @@ $utopia->post('/v1/projects/:projectId/tasks')
$key = $request->getServer('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$tag = null;
- $httpPass = json_encode([
+ $httpPass = \json_encode([
'data' => OpenSSL::encrypt($httpPass, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
'method' => OpenSSL::CIPHER_AES_128_GCM,
- 'iv' => bin2hex($iv),
- 'tag' => bin2hex($tag),
+ 'iv' => \bin2hex($iv),
+ 'tag' => \bin2hex($tag),
'version' => '1',
]);
@@ -856,7 +872,7 @@ $utopia->post('/v1/projects/:projectId/tasks')
'name' => $name,
'status' => $status,
'schedule' => $schedule,
- 'updated' => time(),
+ 'updated' => \time(),
'previous' => null,
'next' => $next,
'security' => (int) $security,
@@ -894,6 +910,7 @@ $utopia->post('/v1/projects/:projectId/tasks')
$utopia->get('/v1/projects/:projectId/tasks')
->desc('List Tasks')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listTasks')
@@ -909,7 +926,7 @@ $utopia->get('/v1/projects/:projectId/tasks')
$tasks = $project->getAttribute('tasks', []);
foreach ($tasks as $task) { /* @var $task Document */
- $httpPass = json_decode($task->getAttribute('httpPass', '{}'), true);
+ $httpPass = \json_decode($task->getAttribute('httpPass', '{}'), true);
if (empty($httpPass) || !isset($httpPass['version'])) {
continue;
@@ -917,7 +934,7 @@ $utopia->get('/v1/projects/:projectId/tasks')
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$httpPass['version']);
- $task->setAttribute('httpPass', OpenSSL::decrypt($httpPass['data'], $httpPass['method'], $key, 0, hex2bin($httpPass['iv']), hex2bin($httpPass['tag'])));
+ $task->setAttribute('httpPass', OpenSSL::decrypt($httpPass['data'], $httpPass['method'], $key, 0, \hex2bin($httpPass['iv']), \hex2bin($httpPass['tag'])));
}
$response->json($tasks);
@@ -926,6 +943,7 @@ $utopia->get('/v1/projects/:projectId/tasks')
$utopia->get('/v1/projects/:projectId/tasks/:taskId')
->desc('Get Task')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getTask')
@@ -945,11 +963,11 @@ $utopia->get('/v1/projects/:projectId/tasks/:taskId')
throw new Exception('Task not found', 404);
}
- $httpPass = json_decode($task->getAttribute('httpPass', '{}'), true);
+ $httpPass = \json_decode($task->getAttribute('httpPass', '{}'), true);
if (!empty($httpPass) && isset($httpPass['version'])) {
$key = $request->getServer('_APP_OPENSSL_KEY_V'.$httpPass['version']);
- $task->setAttribute('httpPass', OpenSSL::decrypt($httpPass['data'], $httpPass['method'], $key, 0, hex2bin($httpPass['iv']), hex2bin($httpPass['tag'])));
+ $task->setAttribute('httpPass', OpenSSL::decrypt($httpPass['data'], $httpPass['method'], $key, 0, \hex2bin($httpPass['iv']), \hex2bin($httpPass['tag'])));
}
$response->json($task->getArrayCopy());
@@ -958,6 +976,7 @@ $utopia->get('/v1/projects/:projectId/tasks/:taskId')
$utopia->put('/v1/projects/:projectId/tasks/:taskId')
->desc('Update Task')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateTask')
@@ -992,11 +1011,11 @@ $utopia->put('/v1/projects/:projectId/tasks/:taskId')
$key = $request->getServer('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$tag = null;
- $httpPass = json_encode([
+ $httpPass = \json_encode([
'data' => OpenSSL::encrypt($httpPass, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
'method' => OpenSSL::CIPHER_AES_128_GCM,
- 'iv' => bin2hex($iv),
- 'tag' => bin2hex($tag),
+ 'iv' => \bin2hex($iv),
+ 'tag' => \bin2hex($tag),
'version' => '1',
]);
@@ -1004,7 +1023,7 @@ $utopia->put('/v1/projects/:projectId/tasks/:taskId')
->setAttribute('name', $name)
->setAttribute('status', $status)
->setAttribute('schedule', $schedule)
- ->setAttribute('updated', time())
+ ->setAttribute('updated', \time())
->setAttribute('next', $next)
->setAttribute('security', (int) $security)
->setAttribute('httpMethod', $httpMethod)
@@ -1028,6 +1047,7 @@ $utopia->put('/v1/projects/:projectId/tasks/:taskId')
$utopia->delete('/v1/projects/:projectId/tasks/:taskId')
->desc('Delete Task')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteTask')
@@ -1059,6 +1079,7 @@ $utopia->delete('/v1/projects/:projectId/tasks/:taskId')
$utopia->post('/v1/projects/:projectId/platforms')
->desc('Create Platform')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createPlatform')
@@ -1087,8 +1108,8 @@ $utopia->post('/v1/projects/:projectId/platforms')
'key' => $key,
'store' => $store,
'hostname' => $hostname,
- 'dateCreated' => time(),
- 'dateUpdated' => time(),
+ 'dateCreated' => \time(),
+ 'dateUpdated' => \time(),
]);
if (false === $platform) {
@@ -1112,6 +1133,7 @@ $utopia->post('/v1/projects/:projectId/platforms')
$utopia->get('/v1/projects/:projectId/platforms')
->desc('List Platforms')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listPlatforms')
@@ -1132,6 +1154,7 @@ $utopia->get('/v1/projects/:projectId/platforms')
$utopia->get('/v1/projects/:projectId/platforms/:platformId')
->desc('Get Platform')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getPlatform')
@@ -1157,6 +1180,7 @@ $utopia->get('/v1/projects/:projectId/platforms/:platformId')
$utopia->put('/v1/projects/:projectId/platforms/:platformId')
->desc('Update Platform')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updatePlatform')
@@ -1182,7 +1206,7 @@ $utopia->put('/v1/projects/:projectId/platforms/:platformId')
$platform
->setAttribute('name', $name)
- ->setAttribute('dateUpdated', time())
+ ->setAttribute('dateUpdated', \time())
->setAttribute('key', $key)
->setAttribute('store', $store)
->setAttribute('hostname', $hostname)
@@ -1198,6 +1222,7 @@ $utopia->put('/v1/projects/:projectId/platforms/:platformId')
$utopia->delete('/v1/projects/:projectId/platforms/:platformId')
->desc('Delete Platform')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deletePlatform')
@@ -1229,6 +1254,7 @@ $utopia->delete('/v1/projects/:projectId/platforms/:platformId')
$utopia->post('/v1/projects/:projectId/domains')
->desc('Create Domain')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createDomain')
@@ -1250,7 +1276,7 @@ $utopia->post('/v1/projects/:projectId/domains')
$target = new Domain($request->getServer('_APP_DOMAIN_TARGET', ''));
- if(!$target->isKnown() || $target->isTest()) {
+ if (!$target->isKnown() || $target->isTest()) {
throw new Exception('Unreachable CNAME target ('.$target->get().'), plesse use a domain with a public suffix.', 500);
}
@@ -1262,7 +1288,7 @@ $utopia->post('/v1/projects/:projectId/domains')
'read' => ['team:'.$project->getAttribute('teamId', null)],
'write' => ['team:'.$project->getAttribute('teamId', null).'/owner', 'team:'.$project->getAttribute('teamId', null).'/developer'],
],
- 'updated' => time(),
+ 'updated' => \time(),
'domain' => $domain->get(),
'tld' => $domain->getSuffix(),
'registerable' => $domain->getRegisterable(),
@@ -1291,6 +1317,7 @@ $utopia->post('/v1/projects/:projectId/domains')
$utopia->get('/v1/projects/:projectId/domains')
->desc('List Domains')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listDomains')
@@ -1311,6 +1338,7 @@ $utopia->get('/v1/projects/:projectId/domains')
$utopia->get('/v1/projects/:projectId/domains/:domainId')
->desc('Get Domain')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getDomain')
@@ -1336,6 +1364,7 @@ $utopia->get('/v1/projects/:projectId/domains/:domainId')
$utopia->patch('/v1/projects/:projectId/domains/:domainId/verification')
->desc('Update Domain Verification Status')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateDomainVerification')
@@ -1357,18 +1386,18 @@ $utopia->patch('/v1/projects/:projectId/domains/:domainId/verification')
$target = new Domain($request->getServer('_APP_DOMAIN_TARGET', ''));
- if(!$target->isKnown() || $target->isTest()) {
+ if (!$target->isKnown() || $target->isTest()) {
throw new Exception('Unreachable CNAME target ('.$target->get().'), plesse use a domain with a public suffix.', 500);
}
- if($domain->getAttribute('verification') === true) {
+ if ($domain->getAttribute('verification') === true) {
return $response->json($domain->getArrayCopy());
}
// Verify Domain with DNS records
$validator = new CNAME($target->get());
- if(!$validator->isValid($domain->getAttribute('domain', ''))) {
+ if (!$validator->isValid($domain->getAttribute('domain', ''))) {
throw new Exception('Failed to verify domain', 401);
}
@@ -1392,6 +1421,7 @@ $utopia->patch('/v1/projects/:projectId/domains/:domainId/verification')
$utopia->delete('/v1/projects/:projectId/domains/:domainId')
->desc('Delete Domain')
+ ->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteDomain')
diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php
index ebcb85f32d..9a12b56048 100644
--- a/app/controllers/api/storage.php
+++ b/app/controllers/api/storage.php
@@ -23,8 +23,6 @@ use Appwrite\Storage\Compression\Algorithms\GZIP;
use Appwrite\Resize\Resize;
use Appwrite\OpenSSL\OpenSSL;
-include_once __DIR__ . '/../shared/api.php';
-
Storage::addDevice('local', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId()));
$fileLogos = [ // Based on this list @see http://stackoverflow.com/a/4212908/2299554
@@ -135,6 +133,7 @@ $mimes = [
$utopia->post('/v1/storage/files')
->desc('Create File')
+ ->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('webhook', 'storage.files.create')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
@@ -165,9 +164,9 @@ $utopia->post('/v1/storage/files')
}
// Make sure we handle a single file and multiple files the same way
- $file['name'] = (is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
- $file['tmp_name'] = (is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
- $file['size'] = (is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
+ $file['name'] = (\is_array($file['name']) && isset($file['name'][0])) ? $file['name'][0] : $file['name'];
+ $file['tmp_name'] = (\is_array($file['tmp_name']) && isset($file['tmp_name'][0])) ? $file['tmp_name'][0] : $file['tmp_name'];
+ $file['size'] = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
// Check if file type is allowed (feature for project settings?)
//if (!$fileType->isValid($file['tmp_name'])) {
@@ -190,7 +189,7 @@ $utopia->post('/v1/storage/files')
// Save to storage
$size = $device->getFileSize($file['tmp_name']);
- $path = $device->getPath(uniqid().'.'.pathinfo($file['name'], PATHINFO_EXTENSION));
+ $path = $device->getPath(\uniqid().'.'.\pathinfo($file['name'], PATHINFO_EXTENSION));
if (!$device->upload($file['tmp_name'], $path)) { // TODO deprecate 'upload' and replace with 'move'
throw new Exception('Failed moving file', 500);
@@ -198,7 +197,7 @@ $utopia->post('/v1/storage/files')
$mimeType = $device->getFileMimeType($path); // Get mime-type before compression and encryption
- if($request->getServer('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled
+ if ($request->getServer('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled
$antiVirus = new Network('clamav', 3310);
// Check if file size is exceeding allowed limit
@@ -216,7 +215,7 @@ $utopia->post('/v1/storage/files')
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$data = OpenSSL::encrypt($data, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag);
- if(!$device->write($path, $data)) {
+ if (!$device->write($path, $data)) {
throw new Exception('Failed to save file', 500);
}
@@ -228,7 +227,7 @@ $utopia->post('/v1/storage/files')
'read' => $read,
'write' => $write,
],
- 'dateCreated' => time(),
+ 'dateCreated' => \time(),
'folderId' => $folderId,
'name' => $file['name'],
'path' => $path,
@@ -237,12 +236,12 @@ $utopia->post('/v1/storage/files')
'sizeOriginal' => $size,
'sizeActual' => $sizeActual,
'algorithm' => $compressor->getName(),
- 'token' => bin2hex(random_bytes(64)),
+ 'token' => \bin2hex(\random_bytes(64)),
'comment' => '',
'fileOpenSSLVersion' => '1',
'fileOpenSSLCipher' => OpenSSL::CIPHER_AES_128_GCM,
- 'fileOpenSSLTag' => bin2hex($tag),
- 'fileOpenSSLIV' => bin2hex($iv),
+ 'fileOpenSSLTag' => \bin2hex($tag),
+ 'fileOpenSSLIV' => \bin2hex($iv),
]);
if (false === $file) {
@@ -271,6 +270,7 @@ $utopia->post('/v1/storage/files')
$utopia->get('/v1/storage/files')
->desc('List Files')
+ ->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'storage')
@@ -294,7 +294,7 @@ $utopia->get('/v1/storage/files')
],
]);
- $results = array_map(function ($value) { /* @var $value \Database\Document */
+ $results = \array_map(function ($value) { /* @var $value \Database\Document */
return $value->getArrayCopy(['$id', '$permissions', 'name', 'dateCreated', 'signature', 'mimeType', 'sizeOriginal']);
}, $results);
@@ -304,6 +304,7 @@ $utopia->get('/v1/storage/files')
$utopia->get('/v1/storage/files/:fileId')
->desc('Get File')
+ ->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'storage')
@@ -324,6 +325,7 @@ $utopia->get('/v1/storage/files/:fileId')
$utopia->get('/v1/storage/files/:fileId/preview')
->desc('Get File Preview')
+ ->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'storage')
@@ -336,14 +338,12 @@ $utopia->get('/v1/storage/files/:fileId/preview')
->param('height', 0, function () { return new Range(0, 4000); }, 'Resize preview image height, Pass an integer between 0 to 4000.', true)
->param('quality', 100, function () { return new Range(0, 100); }, 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
->param('background', '', function () { return new HexColor(); }, 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true)
- ->param('output', null, function () use ($outputs) { return new WhiteList(array_merge(array_keys($outputs), [null])); }, 'Output format type (jpeg, jpg, png, gif and webp).', true)
- //->param('storage', 'local', function () {return new WhiteList(array('local'));}, 'Selected storage device. defaults to local')
- //->param('token', '', function () {return new Text(128);}, 'Preview token', true)
+ ->param('output', null, function () use ($outputs) { return new WhiteList(\array_merge(\array_keys($outputs), [null])); }, 'Output format type (jpeg, jpg, png, gif and webp).', true)
->action(
function ($fileId, $width, $height, $quality, $background, $output) use ($request, $response, $projectDB, $project, $inputs, $outputs, $fileLogos) {
$storage = 'local';
- if (!extension_loaded('imagick')) {
+ if (!\extension_loaded('imagick')) {
throw new Exception('Imagick extension is missing', 500);
}
@@ -351,12 +351,12 @@ $utopia->get('/v1/storage/files/:fileId/preview')
throw new Exception('No such storage device', 400);
}
- if ((strpos($request->getServer('HTTP_ACCEPT'), 'image/webp') === false) && ('webp' == $output)) { // Fallback webp to jpeg when no browser support
+ if ((\strpos($request->getServer('HTTP_ACCEPT'), 'image/webp') === false) && ('webp' == $output)) { // Fallback webp to jpeg when no browser support
$output = 'jpg';
}
- $date = date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
- $key = md5($fileId.$width.$height.$quality.$background.$storage.$output);
+ $date = \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT'; // 45 days cache
+ $key = \md5($fileId.$width.$height.$quality.$background.$storage.$output);
$file = $projectDB->getDocument($fileId);
@@ -365,24 +365,24 @@ $utopia->get('/v1/storage/files/:fileId/preview')
}
$path = $file->getAttribute('path');
- $type = strtolower(pathinfo($path, PATHINFO_EXTENSION));
+ $type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
$algorithm = $file->getAttribute('algorithm');
$cipher = $file->getAttribute('fileOpenSSLCipher');
$mime = $file->getAttribute('mimeType');
- if(!in_array($mime, $inputs)) {
- $path = (array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default'];
+ if (!\in_array($mime, $inputs)) {
+ $path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default'];
$algorithm = null;
$cipher = null;
$background = (empty($background)) ? 'eceff1' : $background;
- $type = strtolower(pathinfo($path, PATHINFO_EXTENSION));
- $key = md5($path.$width.$height.$quality.$background.$storage.$output);
+ $type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION));
+ $key = \md5($path.$width.$height.$quality.$background.$storage.$output);
}
$compressor = new GZIP();
$device = Storage::getDevice('local');
- if (!file_exists($path)) {
+ if (!\file_exists($path)) {
throw new Exception('File not found', 404);
}
@@ -393,7 +393,7 @@ $utopia->get('/v1/storage/files/:fileId/preview')
$output = (empty($output)) ? $type : $output;
$response
- ->setContentType((in_array($output, $outputs)) ? $outputs[$output] : $outputs['jpg'])
+ ->setContentType((\in_array($output, $outputs)) ? $outputs[$output] : $outputs['jpg'])
->addHeader('Expires', $date)
->addHeader('X-Appwrite-Cache', 'hit')
->send($data)
@@ -410,8 +410,8 @@ $utopia->get('/v1/storage/files/:fileId/preview')
$file->getAttribute('fileOpenSSLCipher'),
$request->getServer('_APP_OPENSSL_KEY_V'.$file->getAttribute('fileOpenSSLVersion')),
0,
- hex2bin($file->getAttribute('fileOpenSSLIV')),
- hex2bin($file->getAttribute('fileOpenSSLTag'))
+ \hex2bin($file->getAttribute('fileOpenSSLIV')),
+ \hex2bin($file->getAttribute('fileOpenSSLTag'))
);
}
@@ -448,6 +448,7 @@ $utopia->get('/v1/storage/files/:fileId/preview')
$utopia->get('/v1/storage/files/:fileId/download')
->desc('Get File for Download')
+ ->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'storage')
@@ -466,7 +467,7 @@ $utopia->get('/v1/storage/files/:fileId/download')
$path = $file->getAttribute('path', '');
- if (!file_exists($path)) {
+ if (!\file_exists($path)) {
throw new Exception('File not found in '.$path, 404);
}
@@ -481,8 +482,8 @@ $utopia->get('/v1/storage/files/:fileId/download')
$file->getAttribute('fileOpenSSLCipher'),
$request->getServer('_APP_OPENSSL_KEY_V'.$file->getAttribute('fileOpenSSLVersion')),
0,
- hex2bin($file->getAttribute('fileOpenSSLIV')),
- hex2bin($file->getAttribute('fileOpenSSLTag'))
+ \hex2bin($file->getAttribute('fileOpenSSLIV')),
+ \hex2bin($file->getAttribute('fileOpenSSLTag'))
);
}
@@ -492,8 +493,8 @@ $utopia->get('/v1/storage/files/:fileId/download')
$response
->setContentType($file->getAttribute('mimeType'))
->addHeader('Content-Disposition', 'attachment; filename="'.$file->getAttribute('name', '').'"')
- ->addHeader('Expires', date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
- ->addHeader('X-Peak', memory_get_peak_usage())
+ ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
+ ->addHeader('X-Peak', \memory_get_peak_usage())
->send($source)
;
}
@@ -501,6 +502,7 @@ $utopia->get('/v1/storage/files/:fileId/download')
$utopia->get('/v1/storage/files/:fileId/view')
->desc('Get File for View')
+ ->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'storage')
@@ -520,7 +522,7 @@ $utopia->get('/v1/storage/files/:fileId/view')
$path = $file->getAttribute('path', '');
- if (!file_exists($path)) {
+ if (!\file_exists($path)) {
throw new Exception('File not found in '.$path, 404);
}
@@ -529,7 +531,7 @@ $utopia->get('/v1/storage/files/:fileId/view')
$contentType = 'text/plain';
- if (in_array($file->getAttribute('mimeType'), $mimes)) {
+ if (\in_array($file->getAttribute('mimeType'), $mimes)) {
$contentType = $file->getAttribute('mimeType');
}
@@ -541,8 +543,8 @@ $utopia->get('/v1/storage/files/:fileId/view')
$file->getAttribute('fileOpenSSLCipher'),
$request->getServer('_APP_OPENSSL_KEY_V'.$file->getAttribute('fileOpenSSLVersion')),
0,
- hex2bin($file->getAttribute('fileOpenSSLIV')),
- hex2bin($file->getAttribute('fileOpenSSLTag'))
+ \hex2bin($file->getAttribute('fileOpenSSLIV')),
+ \hex2bin($file->getAttribute('fileOpenSSLTag'))
);
}
@@ -554,7 +556,7 @@ $utopia->get('/v1/storage/files/:fileId/view')
'text' => 'text/plain',
];
- $contentType = (array_key_exists($as, $contentTypes)) ? $contentTypes[$as] : $contentType;
+ $contentType = (\array_key_exists($as, $contentTypes)) ? $contentTypes[$as] : $contentType;
// Response
$response
@@ -562,8 +564,8 @@ $utopia->get('/v1/storage/files/:fileId/view')
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="'.$fileName.'"')
- ->addHeader('Expires', date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
- ->addHeader('X-Peak', memory_get_peak_usage())
+ ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
+ ->addHeader('X-Peak', \memory_get_peak_usage())
->send($output)
;
}
@@ -571,6 +573,7 @@ $utopia->get('/v1/storage/files/:fileId/view')
$utopia->put('/v1/storage/files/:fileId')
->desc('Update File')
+ ->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('webhook', 'storage.files.update')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
@@ -589,7 +592,7 @@ $utopia->put('/v1/storage/files/:fileId')
throw new Exception('File not found', 404);
}
- $file = $projectDB->updateDocument(array_merge($file->getArrayCopy(), [
+ $file = $projectDB->updateDocument(\array_merge($file->getArrayCopy(), [
'$permissions' => [
'read' => $read,
'write' => $write,
@@ -616,6 +619,7 @@ $utopia->put('/v1/storage/files/:fileId')
$utopia->delete('/v1/storage/files/:fileId')
->desc('Delete File')
+ ->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('webhook', 'storage.files.delete')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
@@ -658,6 +662,7 @@ $utopia->delete('/v1/storage/files/:fileId')
// $utopia->get('/v1/storage/files/:fileId/scan')
// ->desc('Scan Storage')
+// ->groups(['api', 'storage'])
// ->label('scope', 'god')
// ->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
// ->label('sdk.namespace', 'storage')
diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php
index ded2445faa..67b4599b2d 100644
--- a/app/controllers/api/teams.php
+++ b/app/controllers/api/teams.php
@@ -1,6 +1,6 @@
post('/v1/teams')
->desc('Create Team')
+ ->groups(['api', 'teams'])
->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
@@ -43,7 +42,7 @@ $utopia->post('/v1/teams')
],
'name' => $name,
'sum' => ($mode !== APP_MODE_ADMIN && $user->getId()) ? 1 : 0,
- 'dateCreated' => time(),
+ 'dateCreated' => \time(),
]);
Authorization::reset();
@@ -62,8 +61,8 @@ $utopia->post('/v1/teams')
'userId' => $user->getId(),
'teamId' => $team->getId(),
'roles' => $roles,
- 'invited' => time(),
- 'joined' => time(),
+ 'invited' => \time(),
+ 'joined' => \time(),
'confirm' => true,
'secret' => '',
]);
@@ -87,6 +86,7 @@ $utopia->post('/v1/teams')
$utopia->get('/v1/teams')
->desc('List Teams')
+ ->groups(['api', 'teams'])
->label('scope', 'teams.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
@@ -116,6 +116,7 @@ $utopia->get('/v1/teams')
$utopia->get('/v1/teams/:teamId')
->desc('Get Team')
+ ->groups(['api', 'teams'])
->label('scope', 'teams.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
@@ -136,6 +137,7 @@ $utopia->get('/v1/teams/:teamId')
$utopia->put('/v1/teams/:teamId')
->desc('Update Team')
+ ->groups(['api', 'teams'])
->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
@@ -151,7 +153,7 @@ $utopia->put('/v1/teams/:teamId')
throw new Exception('Team not found', 404);
}
- $team = $projectDB->updateDocument(array_merge($team->getArrayCopy(), [
+ $team = $projectDB->updateDocument(\array_merge($team->getArrayCopy(), [
'name' => $name,
]));
@@ -165,6 +167,7 @@ $utopia->put('/v1/teams/:teamId')
$utopia->delete('/v1/teams/:teamId')
->desc('Delete Team')
+ ->groups(['api', 'teams'])
->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
@@ -204,6 +207,7 @@ $utopia->delete('/v1/teams/:teamId')
$utopia->post('/v1/teams/:teamId/memberships')
->desc('Create Team Membership')
+ ->groups(['api', 'teams'])
->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
@@ -215,7 +219,7 @@ $utopia->post('/v1/teams/:teamId/memberships')
->param('roles', [], function () { return new ArrayList(new Text(128)); }, '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](/docs/permissions).')
->param('url', '', function () use ($clients) { return 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.') // TODO add our own built-in confirm page
->action(
- function ($teamId, $email, $name, $roles, $url) use ($response, $register, $project, $user, $audit, $projectDB, $mode) {
+ function ($teamId, $email, $name, $roles, $url) use ($response, $mail, $project, $user, $audit, $projectDB, $mode) {
$name = (empty($name)) ? $email : $name;
$team = $projectDB->getDocument($teamId);
@@ -256,8 +260,8 @@ $utopia->post('/v1/teams/:teamId/memberships')
'emailVerification' => false,
'status' => Auth::USER_STATUS_UNACTIVATED,
'password' => Auth::passwordHash(Auth::passwordGenerator()),
- 'password-update' => time(),
- 'registration' => time(),
+ 'password-update' => \time(),
+ 'registration' => \time(),
'reset' => false,
'name' => $name,
'tokens' => [],
@@ -280,7 +284,7 @@ $utopia->post('/v1/teams/:teamId/memberships')
throw new Exception('User has already been invited or is already a member of this team', 409);
}
- if ($member->getAttribute('userId') == $user->getId() && in_array('owner', $member->getAttribute('roles', []))) {
+ if ($member->getAttribute('userId') == $user->getId() && \in_array('owner', $member->getAttribute('roles', []))) {
$isOwner = true;
}
}
@@ -300,18 +304,17 @@ $utopia->post('/v1/teams/:teamId/memberships')
'userId' => $invitee->getId(),
'teamId' => $team->getId(),
'roles' => $roles,
- 'invited' => time(),
+ 'invited' => \time(),
'joined' => 0,
'confirm' => (APP_MODE_ADMIN === $mode),
'secret' => Auth::hash($secret),
]);
- if(APP_MODE_ADMIN === $mode) { // Allow admin to create membership
+ if (APP_MODE_ADMIN === $mode) { // Allow admin to create membership
Authorization::disable();
$membership = $projectDB->createDocument($membership->getArrayCopy());
Authorization::reset();
- }
- else {
+ } else {
$membership = $projectDB->createDocument($membership->getArrayCopy());
}
@@ -323,29 +326,36 @@ $utopia->post('/v1/teams/:teamId/memberships')
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['inviteId' => $membership->getId(), 'teamId' => $team->getId(), 'userId' => $invitee->getId(), 'secret' => $secret, 'teamId' => $teamId]);
$url = Template::unParseURL($url);
- $body = new Template(__DIR__.'/../../config/locales/templates/'.Locale::getText('account.emails.invitation.body'));
+ $body = new Template(__DIR__.'/../../config/locales/templates/_base.tpl');
+ $content = new Template(__DIR__.'/../../config/locales/templates/'.Locale::getText('account.emails.invitation.body'));
+ $cta = new Template(__DIR__.'/../../config/locales/templates/_cta.tpl');
+
$body
+ ->setParam('{{content}}', $content->render())
+ ->setParam('{{cta}}', $cta->render())
+ ->setParam('{{title}}', Locale::getText('account.emails.invitation.title'))
->setParam('{{direction}}', Locale::getText('settings.direction'))
->setParam('{{project}}', $project->getAttribute('name', ['[APP-NAME]']))
->setParam('{{team}}', $team->getAttribute('name', '[TEAM-NAME]'))
->setParam('{{owner}}', $user->getAttribute('name', ''))
->setParam('{{redirect}}', $url)
+ ->setParam('{{bg-body}}', '#f6f6f6')
+ ->setParam('{{bg-content}}', '#ffffff')
+ ->setParam('{{bg-cta}}', '#3498db')
+ ->setParam('{{bg-cta-hover}}', '#34495e')
+ ->setParam('{{text-content}}', '#000000')
+ ->setParam('{{text-cta}}', '#ffffff')
;
- $mail = $register->get('smtp'); /* @var $mail \PHPMailer\PHPMailer\PHPMailer */
-
- $mail->addAddress($email, $name);
-
- $mail->Subject = sprintf(Locale::getText('account.emails.invitation.title'), $team->getAttribute('name', '[TEAM-NAME]'), $project->getAttribute('name', ['[APP-NAME]']));
- $mail->Body = $body->render();
- $mail->AltBody = strip_tags($body->render());
-
- try {
- if(APP_MODE_ADMIN !== $mode) { // No need in comfirmation when in admin mode
- $mail->send();
- }
- } catch (\Exception $error) {
- throw new Exception('Error sending mail: ' . $error->getMessage(), 500);
+ if (APP_MODE_ADMIN !== $mode) { // No need in comfirmation when in admin mode
+ $mail
+ ->setParam('event', 'teams.membership.create')
+ ->setParam('recipient', $email)
+ ->setParam('name', $name)
+ ->setParam('subject', \sprintf(Locale::getText('account.emails.invitation.title'), $team->getAttribute('name', '[TEAM-NAME]'), $project->getAttribute('name', ['[APP-NAME]'])))
+ ->setParam('body', $body->render())
+ ->trigger();
+ ;
}
$audit
@@ -356,7 +366,7 @@ $utopia->post('/v1/teams/:teamId/memberships')
$response
->setStatusCode(Response::STATUS_CODE_CREATED) // TODO change response of this endpoint
- ->json(array_merge($membership->getArrayCopy([
+ ->json(\array_merge($membership->getArrayCopy([
'$id',
'userId',
'teamId',
@@ -374,6 +384,7 @@ $utopia->post('/v1/teams/:teamId/memberships')
$utopia->get('/v1/teams/:teamId/memberships')
->desc('Get Team Memberships')
+ ->groups(['api', 'teams'])
->label('scope', 'teams.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
@@ -414,7 +425,7 @@ $utopia->get('/v1/teams/:teamId/memberships')
$temp = $projectDB->getDocument($membership->getAttribute('userId', null))->getArrayCopy(['email', 'name']);
- $users[] = array_merge($temp, $membership->getArrayCopy([
+ $users[] = \array_merge($temp, $membership->getArrayCopy([
'$id',
'userId',
'teamId',
@@ -426,12 +437,12 @@ $utopia->get('/v1/teams/:teamId/memberships')
}
$response->json(['sum' => $projectDB->getSum(), 'memberships' => $users]);
-
}
);
$utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status')
->desc('Update Team Membership Status')
+ ->groups(['api', 'teams'])
->label('scope', 'public')
->label('sdk.platform', [APP_PLATFORM_CLIENT])
->label('sdk.namespace', 'teams')
@@ -488,7 +499,7 @@ $utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status')
}
$membership // Attach user to team
- ->setAttribute('joined', time())
+ ->setAttribute('joined', \time())
->setAttribute('confirm', true)
;
@@ -498,7 +509,7 @@ $utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status')
;
// Log user in
- $expiry = time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
+ $expiry = \time() + Auth::TOKEN_EXPIRATION_LOGIN_LONG;
$secret = Auth::tokenGenerator();
$user->setAttribute('tokens', new Document([
@@ -521,7 +532,7 @@ $utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status')
Authorization::disable();
- $team = $projectDB->updateDocument(array_merge($team->getArrayCopy(), [
+ $team = $projectDB->updateDocument(\array_merge($team->getArrayCopy(), [
'sum' => $team->getAttribute('sum', 0) + 1,
]));
@@ -537,16 +548,16 @@ $utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status')
->setParam('resource', 'teams/'.$teamId)
;
- if(!Config::getParam('domainVerification')) {
+ if (!Config::getParam('domainVerification')) {
$response
- ->addHeader('X-Fallback-Cookies', json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
+ ->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
;
}
$response
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
- ->json(array_merge($membership->getArrayCopy([
+ ->json(\array_merge($membership->getArrayCopy([
'$id',
'userId',
'teamId',
@@ -564,6 +575,7 @@ $utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status')
$utopia->delete('/v1/teams/:teamId/memberships/:inviteId')
->desc('Delete Team Membership')
+ ->groups(['api', 'teams'])
->label('scope', 'teams.write')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'teams')
@@ -594,7 +606,7 @@ $utopia->delete('/v1/teams/:teamId/memberships/:inviteId')
}
if ($membership->getAttribute('confirm')) { // Count only confirmed members
- $team = $projectDB->updateDocument(array_merge($team->getArrayCopy(), [
+ $team = $projectDB->updateDocument(\array_merge($team->getArrayCopy(), [
'sum' => $team->getAttribute('sum', 0) - 1,
]));
}
diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php
index 3eee6f5b02..c7be03e95b 100644
--- a/app/controllers/api/users.php
+++ b/app/controllers/api/users.php
@@ -21,10 +21,9 @@ use Appwrite\Database\Validator\UID;
use DeviceDetector\DeviceDetector;
use GeoIp2\Database\Reader;
-include_once __DIR__ . '/../shared/api.php';
-
$utopia->post('/v1/users')
->desc('Create User')
+ ->groups(['api', 'users'])
->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@@ -59,8 +58,8 @@ $utopia->post('/v1/users')
'emailVerification' => false,
'status' => Auth::USER_STATUS_UNACTIVATED,
'password' => Auth::passwordHash($password),
- 'password-update' => time(),
- 'registration' => time(),
+ 'password-update' => \time(),
+ 'registration' => \time(),
'reset' => false,
'name' => $name,
], ['email' => $email]);
@@ -75,13 +74,13 @@ $utopia->post('/v1/users')
continue;
}
- $oauth2Keys[] = 'oauth2'.ucfirst($key);
- $oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
+ $oauth2Keys[] = 'oauth2'.\ucfirst($key);
+ $oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
- ->json(array_merge($user->getArrayCopy(array_merge([
+ ->json(\array_merge($user->getArrayCopy(\array_merge([
'$id',
'status',
'email',
@@ -94,6 +93,7 @@ $utopia->post('/v1/users')
$utopia->get('/v1/users')
->desc('List Users')
+ ->groups(['api', 'users'])
->label('scope', 'users.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@@ -124,12 +124,12 @@ $utopia->get('/v1/users')
continue;
}
- $oauth2Keys[] = 'oauth2'.ucfirst($key);
- $oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
+ $oauth2Keys[] = 'oauth2'.\ucfirst($key);
+ $oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
- $results = array_map(function ($value) use ($oauth2Keys) { /* @var $value \Database\Document */
- return $value->getArrayCopy(array_merge(
+ $results = \array_map(function ($value) use ($oauth2Keys) { /* @var $value \Database\Document */
+ return $value->getArrayCopy(\array_merge(
[
'$id',
'status',
@@ -148,6 +148,7 @@ $utopia->get('/v1/users')
$utopia->get('/v1/users/:userId')
->desc('Get User')
+ ->groups(['api', 'users'])
->label('scope', 'users.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@@ -169,11 +170,11 @@ $utopia->get('/v1/users/:userId')
continue;
}
- $oauth2Keys[] = 'oauth2'.ucfirst($key);
- $oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
+ $oauth2Keys[] = 'oauth2'.\ucfirst($key);
+ $oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
- $response->json(array_merge($user->getArrayCopy(array_merge(
+ $response->json(\array_merge($user->getArrayCopy(\array_merge(
[
'$id',
'status',
@@ -189,6 +190,7 @@ $utopia->get('/v1/users/:userId')
$utopia->get('/v1/users/:userId/prefs')
->desc('Get User Preferences')
+ ->groups(['api', 'users'])
->label('scope', 'users.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@@ -206,7 +208,7 @@ $utopia->get('/v1/users/:userId/prefs')
$prefs = $user->getAttribute('prefs', '');
try {
- $prefs = json_decode($prefs, true);
+ $prefs = \json_decode($prefs, true);
$prefs = ($prefs) ? $prefs : [];
} catch (\Exception $error) {
throw new Exception('Failed to parse prefs', 500);
@@ -218,6 +220,7 @@ $utopia->get('/v1/users/:userId/prefs')
$utopia->get('/v1/users/:userId/sessions')
->desc('Get User Sessions')
+ ->groups(['api', 'users'])
->label('scope', 'users.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@@ -265,7 +268,7 @@ $utopia->get('/v1/users/:userId/sessions')
try {
$record = $reader->country($token->getAttribute('ip', ''));
- $sessions[$index]['geo']['isoCode'] = strtolower($record->country->isoCode);
+ $sessions[$index]['geo']['isoCode'] = \strtolower($record->country->isoCode);
$sessions[$index]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown');
} catch (\Exception $e) {
$sessions[$index]['geo']['isoCode'] = '--';
@@ -281,6 +284,7 @@ $utopia->get('/v1/users/:userId/sessions')
$utopia->get('/v1/users/:userId/logs')
->desc('Get User Logs')
+ ->groups(['api', 'users'])
->label('scope', 'users.read')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@@ -335,7 +339,7 @@ $utopia->get('/v1/users/:userId/logs')
$output[$i] = [
'event' => $log['event'],
'ip' => $log['ip'],
- 'time' => strtotime($log['time']),
+ 'time' => \strtotime($log['time']),
'OS' => $dd->getOs(),
'client' => $dd->getClient(),
'device' => $dd->getDevice(),
@@ -346,7 +350,7 @@ $utopia->get('/v1/users/:userId/logs')
try {
$record = $reader->country($log['ip']);
- $output[$i]['geo']['isoCode'] = strtolower($record->country->isoCode);
+ $output[$i]['geo']['isoCode'] = \strtolower($record->country->isoCode);
$output[$i]['geo']['country'] = $record->country->name;
$output[$i]['geo']['country'] = (isset($countries[$record->country->isoCode])) ? $countries[$record->country->isoCode] : Locale::getText('locale.country.unknown');
} catch (\Exception $e) {
@@ -361,6 +365,7 @@ $utopia->get('/v1/users/:userId/logs')
$utopia->patch('/v1/users/:userId/status')
->desc('Update User Status')
+ ->groups(['api', 'users'])
->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@@ -376,7 +381,7 @@ $utopia->patch('/v1/users/:userId/status')
throw new Exception('User not found', 404);
}
- $user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
+ $user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
'status' => (int)$status,
]));
@@ -391,12 +396,12 @@ $utopia->patch('/v1/users/:userId/status')
continue;
}
- $oauth2Keys[] = 'oauth2'.ucfirst($key);
- $oauth2Keys[] = 'oauth2'.ucfirst($key).'AccessToken';
+ $oauth2Keys[] = 'oauth2'.\ucfirst($key);
+ $oauth2Keys[] = 'oauth2'.\ucfirst($key).'AccessToken';
}
$response
- ->json(array_merge($user->getArrayCopy(array_merge([
+ ->json(\array_merge($user->getArrayCopy(\array_merge([
'$id',
'status',
'email',
@@ -409,6 +414,7 @@ $utopia->patch('/v1/users/:userId/status')
$utopia->patch('/v1/users/:userId/prefs')
->desc('Update User Preferences')
+ ->groups(['api', 'users'])
->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@@ -424,11 +430,11 @@ $utopia->patch('/v1/users/:userId/prefs')
throw new Exception('User not found', 404);
}
- $old = json_decode($user->getAttribute('prefs', '{}'), true);
+ $old = \json_decode($user->getAttribute('prefs', '{}'), true);
$old = ($old) ? $old : [];
- $user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
- 'prefs' => json_encode(array_merge($old, $prefs)),
+ $user = $projectDB->updateDocument(\array_merge($user->getArrayCopy(), [
+ 'prefs' => \json_encode(\array_merge($old, $prefs)),
]));
if (false === $user) {
@@ -438,7 +444,7 @@ $utopia->patch('/v1/users/:userId/prefs')
$prefs = $user->getAttribute('prefs', '');
try {
- $prefs = json_decode($prefs, true);
+ $prefs = \json_decode($prefs, true);
$prefs = ($prefs) ? $prefs : [];
} catch (\Exception $error) {
throw new Exception('Failed to parse prefs', 500);
@@ -450,6 +456,7 @@ $utopia->patch('/v1/users/:userId/prefs')
$utopia->delete('/v1/users/:userId/sessions/:sessionId')
->desc('Delete User Session')
+ ->groups(['api', 'users'])
->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
@@ -482,6 +489,7 @@ $utopia->delete('/v1/users/:userId/sessions/:sessionId')
$utopia->delete('/v1/users/:userId/sessions')
->desc('Delete User Sessions')
+ ->groups(['api', 'users'])
->label('scope', 'users.write')
->label('sdk.platform', [APP_PLATFORM_SERVER])
->label('sdk.namespace', 'users')
diff --git a/app/controllers/mock.php b/app/controllers/mock.php
index 1600fa857e..097501fbba 100644
--- a/app/controllers/mock.php
+++ b/app/controllers/mock.php
@@ -22,7 +22,6 @@ $utopia->get('/v1/mock/tests/foo')
->param('z', null, function () { return new ArrayList(new Text(256)); }, 'Sample array param')
->action(
function ($x, $y, $z) {
-
}
);
@@ -37,7 +36,6 @@ $utopia->post('/v1/mock/tests/foo')
->param('z', null, function () { return new ArrayList(new Text(256)); }, 'Sample array param')
->action(
function ($x, $y, $z) {
-
}
);
@@ -52,7 +50,6 @@ $utopia->patch('/v1/mock/tests/foo')
->param('z', null, function () { return new ArrayList(new Text(256)); }, 'Sample array param')
->action(
function ($x, $y, $z) {
-
}
);
@@ -67,7 +64,6 @@ $utopia->put('/v1/mock/tests/foo')
->param('z', null, function () { return new ArrayList(new Text(256)); }, 'Sample array param')
->action(
function ($x, $y, $z) {
-
}
);
@@ -82,7 +78,6 @@ $utopia->delete('/v1/mock/tests/foo')
->param('z', null, function () { return new ArrayList(new Text(256)); }, 'Sample array param')
->action(
function ($x, $y, $z) {
-
}
);
@@ -97,7 +92,6 @@ $utopia->get('/v1/mock/tests/bar')
->param('z', null, function () { return new ArrayList(new Text(256)); }, 'Sample array param')
->action(
function ($x, $y, $z) {
-
}
);
@@ -112,7 +106,6 @@ $utopia->post('/v1/mock/tests/bar')
->param('z', null, function () { return new ArrayList(new Text(256)); }, 'Sample array param')
->action(
function ($x, $y, $z) {
-
}
);
@@ -127,7 +120,6 @@ $utopia->patch('/v1/mock/tests/bar')
->param('z', null, function () { return new ArrayList(new Text(256)); }, 'Sample array param')
->action(
function ($x, $y, $z) {
-
}
);
@@ -142,7 +134,6 @@ $utopia->put('/v1/mock/tests/bar')
->param('z', null, function () { return new ArrayList(new Text(256)); }, 'Sample array param')
->action(
function ($x, $y, $z) {
-
}
);
@@ -157,7 +148,6 @@ $utopia->delete('/v1/mock/tests/bar')
->param('z', null, function () { return new ArrayList(new Text(256)); }, 'Sample array param')
->action(
function ($x, $y, $z) {
-
}
);
@@ -175,24 +165,24 @@ $utopia->post('/v1/mock/tests/general/upload')
->action(
function ($x, $y, $z, $file) use ($request) {
$file = $request->getFiles('file');
- $file['tmp_name'] = (is_array($file['tmp_name'])) ? $file['tmp_name'] : [$file['tmp_name']];
- $file['name'] = (is_array($file['name'])) ? $file['name'] : [$file['name']];
- $file['size'] = (is_array($file['size'])) ? $file['size'] : [$file['size']];
+ $file['tmp_name'] = (\is_array($file['tmp_name'])) ? $file['tmp_name'] : [$file['tmp_name']];
+ $file['name'] = (\is_array($file['name'])) ? $file['name'] : [$file['name']];
+ $file['size'] = (\is_array($file['size'])) ? $file['size'] : [$file['size']];
foreach ($file['name'] as $i => $name) {
- if($name !== 'file.png') {
+ if ($name !== 'file.png') {
throw new Exception('Wrong file name', 400);
}
}
foreach ($file['size'] as $i => $size) {
- if($size !== 38756) {
+ if ($size !== 38756) {
throw new Exception('Wrong file size', 400);
}
}
foreach ($file['tmp_name'] as $i => $tmpName) {
- if(md5(file_get_contents($tmpName)) !== 'd80e7e6999a3eb2ae0d631a96fe135a4') {
+ if (\md5(\file_get_contents($tmpName)) !== 'd80e7e6999a3eb2ae0d631a96fe135a4') {
throw new Exception('Wrong file uploaded', 400);
}
}
@@ -230,7 +220,7 @@ $utopia->get('/v1/mock/tests/general/set-cookie')
->label('sdk.description', 'Mock a set cookie request for SDK tests')
->action(
function () use ($response) {
- $response->addCookie('cookieName', 'cookieValue', time() + 31536000, '/', 'localhost', true, true);
+ $response->addCookie('cookieName', 'cookieValue', \time() + 31536000, '/', 'localhost', true, true);
}
);
@@ -242,7 +232,7 @@ $utopia->get('/v1/mock/tests/general/get-cookie')
->label('sdk.description', 'Mock a get cookie request for SDK tests')
->action(
function () use ($request) {
- if($request->getCookie('cookieName', '') !== 'cookieValue') {
+ if ($request->getCookie('cookieName', '') !== 'cookieValue') {
throw new Exception('Missing cookie value', 400);
}
}
@@ -271,7 +261,7 @@ $utopia->get('/v1/mock/tests/general/oauth2')
->param('state', '', function () { return new Text(1024); }, 'OAuth2 state.')
->action(
function ($clientId, $redirectURI, $scope, $state) use ($response) {
- $response->redirect($redirectURI.'?'.http_build_query(['code' => 'abcdef', 'state' => $state]));
+ $response->redirect($redirectURI.'?'.\http_build_query(['code' => 'abcdef', 'state' => $state]));
}
);
@@ -285,15 +275,15 @@ $utopia->get('/v1/mock/tests/general/oauth2/token')
->param('code', '', function () { return new Text(100); }, 'OAuth2 state.')
->action(
function ($clientId, $redirectURI, $clientSecret, $code) use ($response) {
- if($clientId != '1') {
+ if ($clientId != '1') {
throw new Exception('Invalid client ID');
}
- if($clientSecret != '123456') {
+ if ($clientSecret != '123456') {
throw new Exception('Invalid client secret');
}
- if($code != 'abcdef') {
+ if ($code != 'abcdef') {
throw new Exception('Invalid token');
}
@@ -308,7 +298,7 @@ $utopia->get('/v1/mock/tests/general/oauth2/user')
->param('token', '', function () { return new Text(100); }, 'OAuth2 Access Token.')
->action(
function ($token) use ($response) {
- if($token != '123456') {
+ if ($token != '123456') {
throw new Exception('Invalid token');
}
@@ -345,22 +335,21 @@ $utopia->get('/v1/mock/tests/general/oauth2/failure')
);
$utopia->shutdown(function() use ($response, $request, &$result, $utopia) {
-
$route = $utopia->match($request);
$path = APP_STORAGE_CACHE.'/tests.json';
- $tests = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
+ $tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : [];
- if(!is_array($tests)) {
+ if (!\is_array($tests)) {
throw new Exception('Failed to read results', 500);
}
$result[$route->getMethod() . ':' . $route->getURL()] = true;
- $tests = array_merge($tests, $result);
+ $tests = \array_merge($tests, $result);
- if(!file_put_contents($path, json_encode($tests), LOCK_EX)) {
+ if (!\file_put_contents($path, \json_encode($tests), LOCK_EX)) {
throw new Exception('Failed to save resutls', 500);
}
$response->json(['result' => $route->getMethod() . ':' . $route->getURL() . ':passed']);
-});
\ No newline at end of file
+}, 'mock');
\ No newline at end of file
diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php
index 0430212be7..e911c2c556 100644
--- a/app/controllers/shared/api.php
+++ b/app/controllers/shared/api.php
@@ -7,9 +7,12 @@ use Utopia\Abuse\Adapters\TimeLimit;
global $utopia, $request, $response, $register, $user, $project;
$utopia->init(function () use ($utopia, $request, $response, $register, $user, $project) {
-
$route = $utopia->match($request);
+ if (empty($project->getId()) && $route->getLabel('abuse-limit', 0) > 0) { // Abuse limit requires an active project scope
+ throw new Exception('Missing or unknown project ID', 400);
+ }
+
/*
* Abuse Check
*/
@@ -27,7 +30,7 @@ $utopia->init(function () use ($utopia, $request, $response, $register, $user, $
//TODO make sure we get array here
foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
- $timeLimit->setParam('{param-'.$key.'}', (is_array($value)) ? json_encode($value) : $value);
+ $timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value);
}
$abuse = new Abuse($timeLimit);
@@ -43,4 +46,4 @@ $utopia->init(function () use ($utopia, $request, $response, $register, $user, $
if ($abuse->check() && $request->getServer('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') {
throw new Exception('Too many requests', 429);
}
-});
\ No newline at end of file
+}, 'api');
\ No newline at end of file
diff --git a/app/controllers/shared/web.php b/app/controllers/shared/web.php
index 42afb283e6..a2f85bcb0a 100644
--- a/app/controllers/shared/web.php
+++ b/app/controllers/shared/web.php
@@ -2,42 +2,37 @@
use Utopia\View;
use Utopia\Config\Config;
-use Utopia\Locale\Locale;
-
-Locale::$exceptions = false;
-
-$roles = [
- ['type' => 'owner', 'label' => 'Owner'],
- ['type' => 'developer', 'label' => 'Developer'],
- ['type' => 'admin', 'label' => 'Admin'],
-];
$layout = new View(__DIR__.'/../../views/layouts/default.phtml');
-/* AJAX check */
-if (!empty($request->getQuery('version', ''))) {
- $layout->setPath(__DIR__.'/../../views/layouts/empty.phtml');
-}
-
-$layout
- ->setParam('title', APP_NAME)
- ->setParam('protocol', Config::getParam('protocol'))
- ->setParam('domain', $domain)
- ->setParam('home', $request->getServer('_APP_HOME'))
- ->setParam('setup', $request->getServer('_APP_SETUP'))
- ->setParam('class', 'unknown')
- ->setParam('icon', '/images/favicon.png')
- ->setParam('roles', $roles)
- ->setParam('env', $utopia->getEnv())
-;
-
$utopia->init(function () use ($utopia, $response, $request, $layout) {
+
+ /* AJAX check */
+ if (!empty($request->getQuery('version', ''))) {
+ $layout->setPath(__DIR__.'/../../views/layouts/empty.phtml');
+ }
+ $layout
+ ->setParam('title', APP_NAME)
+ ->setParam('protocol', Config::getParam('protocol'))
+ ->setParam('domain', Config::getParam('domain'))
+ ->setParam('home', $request->getServer('_APP_HOME'))
+ ->setParam('setup', $request->getServer('_APP_SETUP'))
+ ->setParam('class', 'unknown')
+ ->setParam('icon', '/images/favicon.png')
+ ->setParam('roles', [
+ ['type' => 'owner', 'label' => 'Owner'],
+ ['type' => 'developer', 'label' => 'Developer'],
+ ['type' => 'admin', 'label' => 'Admin'],
+ ])
+ ->setParam('env', $utopia->getMode())
+ ;
+
$time = (60 * 60 * 24 * 45); // 45 days cache
- $isDev = (\Utopia\App::ENV_TYPE_DEVELOPMENT == Config::getParam('env'));
+ $isDev = (\Utopia\App::MODE_TYPE_DEVELOPMENT == Config::getParam('env'));
$response
->addHeader('Cache-Control', 'public, max-age='.$time)
- ->addHeader('Expires', date('D, d M Y H:i:s', time() + $time).' GMT') // 45 days cache
+ ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache
->addHeader('X-UA-Compatible', 'IE=Edge'); // Deny IE browsers from going into quirks mode
$route = $utopia->match($request);
@@ -47,4 +42,4 @@ $utopia->init(function () use ($utopia, $response, $request, $layout) {
->setParam('isDev', $isDev)
->setParam('class', $scope)
;
-});
+}, 'web');
diff --git a/app/controllers/web/console.php b/app/controllers/web/console.php
index 5ef32d1f4d..71f7a94f1a 100644
--- a/app/controllers/web/console.php
+++ b/app/controllers/web/console.php
@@ -1,7 +1,5 @@
init(function () use ($layout) {
->setParam('description', 'Appwrite Console allows you to easily manage, monitor, and control your entire backend API and tools.')
->setParam('analytics', 'UA-26264668-5')
;
-});
+}, 'console');
$utopia->shutdown(function () use ($response, $request, $layout) {
$header = new View(__DIR__.'/../../views/console/comps/header.phtml');
@@ -34,10 +32,10 @@ $utopia->shutdown(function () use ($response, $request, $layout) {
;
$response->send($layout->render());
-});
+}, 'console');
$utopia->get('/error/:code')
- ->desc('Error page')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'home')
->param('code', null, new \Utopia\Validator\Numeric(), 'Valid status code number', false)
@@ -54,6 +52,7 @@ $utopia->get('/error/:code')
});
$utopia->get('/console')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout, $request) {
@@ -69,6 +68,7 @@ $utopia->get('/console')
});
$utopia->get('/console/account')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
@@ -86,7 +86,7 @@ $utopia->get('/console/account')
});
$utopia->get('/console/notifications')
- ->desc('Platform console notifications')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
@@ -98,7 +98,7 @@ $utopia->get('/console/notifications')
});
$utopia->get('/console/home')
- ->desc('Platform console project home')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
@@ -110,7 +110,7 @@ $utopia->get('/console/home')
});
$utopia->get('/console/settings')
- ->desc('Platform console project settings')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($request, $layout) {
@@ -129,7 +129,7 @@ $utopia->get('/console/settings')
});
$utopia->get('/console/webhooks')
- ->desc('Platform console project webhooks')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
@@ -145,7 +145,7 @@ $utopia->get('/console/webhooks')
});
$utopia->get('/console/keys')
- ->desc('Platform console project keys')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
@@ -160,7 +160,7 @@ $utopia->get('/console/keys')
});
$utopia->get('/console/tasks')
- ->desc('Platform console project tasks')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
@@ -172,7 +172,7 @@ $utopia->get('/console/tasks')
});
$utopia->get('/console/database')
- ->desc('Platform console project settings')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
@@ -184,7 +184,7 @@ $utopia->get('/console/database')
});
$utopia->get('/console/database/collection')
- ->desc('Platform console project database collection')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->param('id', '', function () { return new UID(); }, 'Collection unique ID.')
@@ -213,11 +213,10 @@ $utopia->get('/console/database/collection')
->addHeader('Expires', 0)
->addHeader('Pragma', 'no-cache')
;
-
});
$utopia->get('/console/database/document')
- ->desc('Platform console project database document')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->param('collection', '', function () { return new UID(); }, 'Collection unique ID.')
@@ -247,7 +246,7 @@ $utopia->get('/console/database/document')
});
$utopia->get('/console/storage')
- ->desc('Platform console project settings')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($request, $layout) {
@@ -265,7 +264,7 @@ $utopia->get('/console/storage')
});
$utopia->get('/console/users')
- ->desc('Platform console project settings')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
@@ -279,7 +278,7 @@ $utopia->get('/console/users')
});
$utopia->get('/console/users/user')
- ->desc('Platform console project user')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
@@ -291,7 +290,7 @@ $utopia->get('/console/users/user')
});
$utopia->get('/console/users/teams/team')
- ->desc('Platform console project team')
+ ->groups(['web', 'console'])
->label('permission', 'public')
->label('scope', 'console')
->action(function () use ($layout) {
diff --git a/app/controllers/web/home.php b/app/controllers/web/home.php
index 093faf3eb7..65097d36c3 100644
--- a/app/controllers/web/home.php
+++ b/app/controllers/web/home.php
@@ -1,7 +1,5 @@
init(function () use ($layout) {
+ $header = new View(__DIR__.'/../../views/home/comps/header.phtml');
+ $footer = new View(__DIR__.'/../../views/home/comps/footer.phtml');
-$footer
- ->setParam('version', Config::getParam('version'))
-;
+ $footer
+ ->setParam('version', Config::getParam('version'))
+ ;
-$layout
- ->setParam('title', APP_NAME)
- ->setParam('description', '')
- ->setParam('class', 'home')
- ->setParam('platforms', Config::getParam('platforms'))
- ->setParam('header', [$header])
- ->setParam('footer', [$footer])
-;
+ $layout
+ ->setParam('title', APP_NAME)
+ ->setParam('description', '')
+ ->setParam('class', 'home')
+ ->setParam('platforms', Config::getParam('platforms'))
+ ->setParam('header', [$header])
+ ->setParam('footer', [$footer])
+ ;
+}, 'home');
$utopia->shutdown(function () use ($response, $layout) {
$response->send($layout->render());
-});
+}, 'home');
$utopia->get('/')
+ ->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(
@@ -39,7 +40,7 @@ $utopia->get('/')
);
$utopia->get('/auth/signin')
- ->desc('Login page')
+ ->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
@@ -51,7 +52,7 @@ $utopia->get('/auth/signin')
});
$utopia->get('/auth/signup')
- ->desc('Registration page')
+ ->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
@@ -63,7 +64,7 @@ $utopia->get('/auth/signup')
});
$utopia->get('/auth/recovery')
- ->desc('Password recovery page')
+ ->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($request, $layout) {
@@ -75,7 +76,7 @@ $utopia->get('/auth/recovery')
});
$utopia->get('/auth/confirm')
- ->desc('Account confirmation page')
+ ->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
@@ -87,7 +88,7 @@ $utopia->get('/auth/confirm')
});
$utopia->get('/auth/join')
- ->desc('Account team join page')
+ ->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
@@ -99,7 +100,7 @@ $utopia->get('/auth/join')
});
$utopia->get('/auth/recovery/reset')
- ->desc('Password recovery page')
+ ->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
@@ -112,7 +113,7 @@ $utopia->get('/auth/recovery/reset')
$utopia->get('/auth/oauth2/success')
- ->desc('Registration page')
+ ->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
@@ -127,7 +128,7 @@ $utopia->get('/auth/oauth2/success')
});
$utopia->get('/auth/oauth2/failure')
- ->desc('Registration page')
+ ->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->action(function () use ($layout) {
@@ -142,7 +143,7 @@ $utopia->get('/auth/oauth2/failure')
});
$utopia->get('/error/:code')
- ->desc('Error page')
+ ->groups(['web', 'home'])
->label('permission', 'public')
->label('scope', 'home')
->param('code', null, new \Utopia\Validator\Numeric(), 'Valid status code number', false)
@@ -159,35 +160,38 @@ $utopia->get('/error/:code')
});
$utopia->get('/open-api-2.json')
+ ->groups(['web', 'home'])
->label('scope', 'public')
->label('docs', false)
->param('platform', APP_PLATFORM_CLIENT, function () {return new WhiteList([APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER, APP_PLATFORM_CONSOLE]);}, 'Choose target platform.', true)
->param('extensions', 0, function () {return new Range(0, 1);}, 'Show extra data.', true)
->param('tests', 0, function () {return new Range(0, 1);}, 'Include only test services.', true)
->action(
- function ($platform, $extensions, $tests) use ($response, $request, $utopia, $services) {
+ function ($platform, $extensions, $tests) use ($response, $request, $utopia) {
+ $services = Config::getParam('services', []);
+
function fromCamelCase($input)
{
- preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
+ \preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
$ret = $matches[0];
foreach ($ret as &$match) {
- $match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
+ $match = $match == \strtoupper($match) ? \strtolower($match) : \lcfirst($match);
}
- return implode('_', $ret);
+ return \implode('_', $ret);
}
function fromCamelCaseToDash($input)
{
- return str_replace([' ', '_'], '-', strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $input)));
+ return \str_replace([' ', '_'], '-', \strtolower(\preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $input)));
}
foreach ($services as $service) { /* @noinspection PhpIncludeInspection */
- if($tests && !isset($service['tests'])) {
+ if ($tests && !isset($service['tests'])) {
continue;
}
- if($tests && !$service['tests']) {
+ if ($tests && !$service['tests']) {
continue;
}
@@ -196,7 +200,7 @@ $utopia->get('/open-api-2.json')
}
/** @noinspection PhpIncludeInspection */
- include_once realpath(__DIR__.'/../../'.$service['controller']);
+ include_once \realpath(__DIR__.'/../../'.$service['controller']);
}
$security = [
@@ -295,7 +299,7 @@ $utopia->get('/open-api-2.json')
'url' => 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE',
],
],
- 'host' => parse_url($request->getServer('_APP_HOME', Config::getParam('domain')), PHP_URL_HOST),
+ 'host' => \parse_url($request->getServer('_APP_HOME', Config::getParam('domain')), PHP_URL_HOST),
'basePath' => '/v1',
'schemes' => ['https'],
'consumes' => ['application/json', 'multipart/form-data'],
@@ -347,19 +351,19 @@ $utopia->get('/open-api-2.json')
];
if ($extensions) {
- if(isset($output['securityDefinitions']['Project'])) {
+ if (isset($output['securityDefinitions']['Project'])) {
$output['securityDefinitions']['Project']['extensions'] = ['demo' => '5df5acd0d48c2'];
}
- if(isset($output['securityDefinitions']['Key'])) {
+ if (isset($output['securityDefinitions']['Key'])) {
$output['securityDefinitions']['Key']['extensions'] = ['demo' => '919c2d18fb5d4...a2ae413da83346ad2'];
}
- if(isset($output['securityDefinitions']['Locale'])) {
+ if (isset($output['securityDefinitions']['Locale'])) {
$output['securityDefinitions']['Locale']['extensions'] = ['demo' => 'en'];
}
- if(isset($output['securityDefinitions']['Mode'])) {
+ if (isset($output['securityDefinitions']['Mode'])) {
$output['securityDefinitions']['Mode']['extensions'] = ['demo' => ''];
}
}
@@ -374,11 +378,11 @@ $utopia->get('/open-api-2.json')
continue;
}
- if($platform !== APP_PLATFORM_CONSOLE && !in_array($platforms[$platform], $route->getLabel('sdk.platform', []))) {
+ if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $route->getLabel('sdk.platform', []))) {
continue;
}
- $url = str_replace('/v1', '', $route->getURL());
+ $url = \str_replace('/v1', '', $route->getURL());
$scope = $route->getLabel('scope', '');
$hide = $route->getLabel('sdk.hide', false);
$consumes = ['application/json'];
@@ -387,14 +391,14 @@ $utopia->get('/open-api-2.json')
continue;
}
- $desc = (!empty($route->getLabel('sdk.description', ''))) ? realpath('../'.$route->getLabel('sdk.description', '')) : null;
+ $desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath('../'.$route->getLabel('sdk.description', '')) : null;
$temp = [
'summary' => $route->getDesc(),
- 'operationId' => $route->getLabel('sdk.method', uniqid()),
+ 'operationId' => $route->getLabel('sdk.method', \uniqid()),
'consumes' => [],
'tags' => [$route->getLabel('sdk.namespace', 'default')],
- 'description' => ($desc) ? file_get_contents($desc) : '',
+ 'description' => ($desc) ? \file_get_contents($desc) : '',
// 'responses' => [
// 200 => [
@@ -440,7 +444,7 @@ $utopia->get('/open-api-2.json')
];
foreach ($route->getParams() as $name => $param) {
- $validator = (is_callable($param['validator'])) ? $param['validator']() : $param['validator']; /* @var $validator \Utopia\Validator */
+ $validator = (\is_callable($param['validator'])) ? $param['validator']() : $param['validator']; /* @var $validator \Utopia\Validator */
$node = [
'name' => $name,
@@ -448,14 +452,14 @@ $utopia->get('/open-api-2.json')
'required' => !$param['optional'],
];
- switch ((!empty($validator)) ? get_class($validator) : '') {
+ switch ((!empty($validator)) ? \get_class($validator) : '') {
case 'Utopia\Validator\Text':
$node['type'] = 'string';
- $node['x-example'] = '['.strtoupper(fromCamelCase($node['name'])).']';
+ $node['x-example'] = '['.\strtoupper(fromCamelCase($node['name'])).']';
break;
case 'Appwrite\Database\Validator\UID':
$node['type'] = 'string';
- $node['x-example'] = '['.strtoupper(fromCamelCase($node['name'])).']';
+ $node['x-example'] = '['.\strtoupper(fromCamelCase($node['name'])).']';
break;
case 'Utopia\Validator\Email':
$node['type'] = 'string';
@@ -517,11 +521,11 @@ $utopia->get('/open-api-2.json')
break;
}
- if ($param['optional'] && !is_null($param['default'])) { // Param has default value
+ if ($param['optional'] && !\is_null($param['default'])) { // Param has default value
$node['default'] = $param['default'];
}
- if (false !== strpos($url, ':'.$name)) { // Param is in URL path
+ if (false !== \strpos($url, ':'.$name)) { // Param is in URL path
$node['in'] = 'path';
$temp['parameters'][] = $node;
} elseif ($key == 'GET') { // Param is in query
@@ -537,12 +541,12 @@ $utopia->get('/open-api-2.json')
}
}
- $url = str_replace(':'.$name, '{'.$name.'}', $url);
+ $url = \str_replace(':'.$name, '{'.$name.'}', $url);
}
$temp['consumes'] = $consumes;
- $output['paths'][$url][strtolower($route->getMethod())] = $temp;
+ $output['paths'][$url][\strtolower($route->getMethod())] = $temp;
}
}
@@ -550,7 +554,7 @@ $utopia->get('/open-api-2.json')
var_dump($mock['name']);
}*/
- ksort($output['paths']);
+ \ksort($output['paths']);
$response
->json($output);
diff --git a/app/init.php b/app/init.php
index 62ea435166..ffa2171472 100644
--- a/app/init.php
+++ b/app/init.php
@@ -7,7 +7,7 @@
* Set configuration, framework resources, app constants
*
*/
-if (file_exists(__DIR__.'/../vendor/autoload.php')) {
+if (\file_exists(__DIR__.'/../vendor/autoload.php')) {
require_once __DIR__.'/../vendor/autoload.php';
}
@@ -51,6 +51,9 @@ const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
$register = new Registry();
$request = new Request();
$response = new Response();
+$utopia = new App('Asia/Tel_Aviv');
+
+$utopia->setMode($utopia->getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
/*
* ENV vars
@@ -61,30 +64,30 @@ Config::load('platforms', __DIR__.'/../app/config/platforms.php');
Config::load('locales', __DIR__.'/../app/config/locales.php');
Config::load('collections', __DIR__.'/../app/config/collections.php');
Config::load('environments', __DIR__.'/../app/config/environments.php');
+Config::load('roles', __DIR__.'/../app/config/roles.php'); // User roles and scopes
+Config::load('services', __DIR__.'/../app/config/services.php'); // List of services
-Config::setParam('env', $request->getServer('_APP_ENV', App::ENV_TYPE_PRODUCTION));
+Config::setParam('env', $utopia->getMode());
Config::setParam('domain', $request->getServer('HTTP_HOST', ''));
Config::setParam('domainVerification', false);
Config::setParam('version', $request->getServer('_APP_VERSION', 'UNKNOWN'));
Config::setParam('protocol', $request->getServer('HTTP_X_FORWARDED_PROTO', $request->getServer('REQUEST_SCHEME', 'https')));
-Config::setParam('port', (string) parse_url(Config::getParam('protocol').'://'.$request->getServer('HTTP_HOST', ''), PHP_URL_PORT));
-Config::setParam('hostname', parse_url(Config::getParam('protocol').'://'.$request->getServer('HTTP_HOST', null), PHP_URL_HOST));
-
-$utopia = new App('Asia/Tel_Aviv', Config::getParam('env'));
+Config::setParam('port', (string) \parse_url(Config::getParam('protocol').'://'.$request->getServer('HTTP_HOST', ''), PHP_URL_PORT));
+Config::setParam('hostname', \parse_url(Config::getParam('protocol').'://'.$request->getServer('HTTP_HOST', null), PHP_URL_HOST));
Resque::setBackend($request->getServer('_APP_REDIS_HOST', '')
.':'.$request->getServer('_APP_REDIS_PORT', ''));
-define('COOKIE_DOMAIN',
+\define('COOKIE_DOMAIN',
(
$request->getServer('HTTP_HOST', null) === 'localhost' ||
$request->getServer('HTTP_HOST', null) === 'localhost:'.Config::getParam('port') ||
- (filter_var(Config::getParam('hostname'), FILTER_VALIDATE_IP) !== false)
+ (\filter_var(Config::getParam('hostname'), FILTER_VALIDATE_IP) !== false)
)
? null
: '.'.Config::getParam('hostname')
);
-define('COOKIE_SAMESITE', Response::COOKIE_SAMESITE_NONE);
+\define('COOKIE_SAMESITE', Response::COOKIE_SAMESITE_NONE);
/*
* Registry
@@ -151,8 +154,9 @@ $register->set('smtp', function () use ($request) {
$mail->Password = $password;
$mail->SMTPSecure = $request->getServer('_APP_SMTP_SECURE', false);
$mail->SMTPAutoTLS = false;
+ $mail->CharSet = 'UTF-8';
- $from = urldecode($request->getServer('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server'));
+ $from = \urldecode($request->getServer('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server'));
$email = $request->getServer('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$mail->setFrom($email, $from);
@@ -166,7 +170,7 @@ $register->set('smtp', function () use ($request) {
/*
* Localization
*/
-$locale = $request->getParam('locale', $request->getHeader('X-Appwrite-Locale', null));
+$locale = $request->getParam('locale', $request->getHeader('X-Appwrite-Locale', ''));
Locale::$exceptions = false;
@@ -219,14 +223,14 @@ Locale::setLanguage('zh-tw', include __DIR__.'/config/locales/zh-tw.php');
Locale::setDefault('en');
-if (in_array($locale, Config::getParam('locales'))) {
+if (\in_array($locale, Config::getParam('locales'))) {
Locale::setDefault($locale);
}
-stream_context_set_default([ // Set global user agent and http settings
+\stream_context_set_default([ // Set global user agent and http settings
'http' => [
'method' => 'GET',
- 'user_agent' => sprintf(APP_USERAGENT,
+ 'user_agent' => \sprintf(APP_USERAGENT,
Config::getParam('version'),
$request->getServer('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)),
'timeout' => 2,
@@ -280,7 +284,7 @@ $consoleDB->setNamespace('app_console'); // Should be replaced with param if we
$consoleDB->setMocks(Config::getParam('collections', []));
Authorization::disable();
-$project = $consoleDB->getDocument($request->getParam('project', $request->getHeader('X-Appwrite-Project', null)));
+$project = $consoleDB->getDocument($request->getParam('project', $request->getHeader('X-Appwrite-Project', '')));
Authorization::enable();
@@ -304,8 +308,8 @@ $response->addHeader('X-Debug-Fallback', 'false');
if(empty($session['id']) && empty($session['secret'])) {
$response->addHeader('X-Debug-Fallback', 'true');
- $fallback = $request->getHeader('X-Fallback-Cookies', null);
- $fallback = json_decode($fallback, true);
+ $fallback = $request->getHeader('X-Fallback-Cookies', '');
+ $fallback = \json_decode($fallback, true);
$session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : ''));
}
@@ -317,9 +321,10 @@ $projectDB->setAdapter(new RedisAdapter(new MySQLAdapter($register), $register))
$projectDB->setNamespace('app_'.$project->getId());
$projectDB->setMocks(Config::getParam('collections', []));
-$user = $projectDB->getDocument(Auth::$unique);
-
-if (APP_MODE_ADMIN === $mode) {
+if (APP_MODE_ADMIN !== $mode) {
+ $user = $projectDB->getDocument(Auth::$unique);
+}
+else {
$user = $consoleDB->getDocument(Auth::$unique);
$user
@@ -346,7 +351,7 @@ $register->get('smtp')
->setFrom(
$request->getServer('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM),
($project->getId() === 'console')
- ? urldecode($request->getServer('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server'))
- : sprintf(Locale::getText('account.emails.team'), $project->getAttribute('name')
+ ? \urldecode($request->getServer('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server'))
+ : \sprintf(Locale::getText('account.emails.team'), $project->getAttribute('name')
)
);
\ No newline at end of file
diff --git a/app/sdks/client-flutter/README.md b/app/sdks/client-flutter/README.md
index b0c261c115..9a5c8c5c1a 100644
--- a/app/sdks/client-flutter/README.md
+++ b/app/sdks/client-flutter/README.md
@@ -2,9 +2,9 @@
[](https://pub.dartlang.org/packages/appwrite)

-
+
-**This SDK is compatible with Appwrite server version 0.6.1. For older versions, please check previous releases.**
+**This SDK is compatible with Appwrite server version 0.6.2. For older versions, please check previous releases.**
Appwrite is an open-source backend as a service server that abstract and simplify complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way.
Use the Flutter SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools.
diff --git a/app/sdks/client-flutter/docs/examples/account/create-o-auth2session.md b/app/sdks/client-flutter/docs/examples/account/create-o-auth2session.md
index c617c34d4d..0e26dd27af 100644
--- a/app/sdks/client-flutter/docs/examples/account/create-o-auth2session.md
+++ b/app/sdks/client-flutter/docs/examples/account/create-o-auth2session.md
@@ -10,7 +10,7 @@ void main() { // Init SDK
;
Future result = account.createOAuth2Session(
- provider: 'bitbucket',
+ provider: 'amazon',
);
result
diff --git a/app/sdks/client-flutter/docs/examples/avatars/get-initials.md b/app/sdks/client-flutter/docs/examples/avatars/get-initials.md
new file mode 100644
index 0000000000..b2e70788d6
--- /dev/null
+++ b/app/sdks/client-flutter/docs/examples/avatars/get-initials.md
@@ -0,0 +1,16 @@
+import 'package:appwrite/appwrite.dart';
+
+void main() { // Init SDK
+ Client client = Client();
+ Avatars avatars = Avatars(client);
+
+ client
+ .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
+ .setProject('5df5acd0d48c2') // Your project ID
+ ;
+
+ String result = avatars.getInitials(
+ );
+
+ print(result); // Resource URL string
+}
\ No newline at end of file
diff --git a/app/sdks/client-flutter/docs/examples/locale/get-languages.md b/app/sdks/client-flutter/docs/examples/locale/get-languages.md
new file mode 100644
index 0000000000..a4124d3cbd
--- /dev/null
+++ b/app/sdks/client-flutter/docs/examples/locale/get-languages.md
@@ -0,0 +1,20 @@
+import 'package:appwrite/appwrite.dart';
+
+void main() { // Init SDK
+ Client client = Client();
+ Locale locale = Locale(client);
+
+ client
+ .setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
+ .setProject('5df5acd0d48c2') // Your project ID
+ ;
+
+ Future result = locale.getLanguages( );
+
+ result
+ .then((response) {
+ print(response);
+ }).catchError((error) {
+ print(error.response);
+ });
+}
\ No newline at end of file
diff --git a/app/sdks/client-flutter/lib/services/account.dart b/app/sdks/client-flutter/lib/services/account.dart
index 8cc237d104..c2083da526 100644
--- a/app/sdks/client-flutter/lib/services/account.dart
+++ b/app/sdks/client-flutter/lib/services/account.dart
@@ -33,10 +33,10 @@ class Account extends Service {
///
/// Use this endpoint to allow a new user to register a new account in your
/// project. After the user registration completes successfully, you can use
- /// the [/account/verfication](/docs/account#createVerification) route to start
- /// verifying the user email address. To allow your new user to login to his
- /// new account, you need to create a new [account
- /// session](/docs/account#createSession).
+ /// the [/account/verfication](/docs/client/account#createVerification) route
+ /// to start verifying the user email address. To allow your new user to login
+ /// to his new account, you need to create a new [account
+ /// session](/docs/client/account#createSession).
///
Future create({@required String email, @required String password, String name = ''}) {
final String path = '/account';
@@ -195,7 +195,7 @@ class Account extends Service {
/// When the user clicks the confirmation link he is redirected back to your
/// app password reset URL with the secret key and email address values
/// attached to the URL query string. Use the query string params to submit a
- /// request to the [PUT /account/recovery](/docs/account#updateRecovery)
+ /// request to the [PUT /account/recovery](/docs/client/account#updateRecovery)
/// endpoint to complete the process.
///
Future createRecovery({@required String email, @required String url}) {
@@ -218,7 +218,7 @@ class Account extends Service {
/// Use this endpoint to complete the user account password reset. Both the
/// **userId** and **secret** arguments will be passed as query parameters to
/// the redirect URL you have provided when sending your request to the [POST
- /// /account/recovery](/docs/account#createRecovery) endpoint.
+ /// /account/recovery](/docs/client/account#createRecovery) endpoint.
///
/// Please note that in order to avoid a [Redirect
/// Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
@@ -305,21 +305,36 @@ class Account extends Service {
/// first. Use the success and failure arguments to provide a redirect URL's
/// back to your app when login is completed.
///
- Future createOAuth2Session({@required String provider, String success = 'https://appwrite.io/auth/oauth2/success', String failure = 'https://appwrite.io/auth/oauth2/failure'}) {
+ Future createOAuth2Session({@required String provider, String success = 'https://appwrite.io/auth/oauth2/success', String failure = 'https://appwrite.io/auth/oauth2/failure', List scopes = const []}) {
final String path = '/account/sessions/oauth2/{provider}'.replaceAll(RegExp('{provider}'), provider);
final Map params = {
'success': success,
'failure': failure,
+ 'scopes': scopes,
'project': client.config['project'],
};
+
+ final List query = [];
+
+ params.forEach((key, value) {
+ if (value is List) {
+ for (var item in value) {
+ query.add(Uri.encodeComponent(key + '[]') + '=' + Uri.encodeComponent(item));
+ }
+ }
+ else {
+ query.add(Uri.encodeComponent(key) + '=' + Uri.encodeComponent(value));
+ }
+ });
+
Uri endpoint = Uri.parse(client.endPoint);
Uri url = new Uri(scheme: endpoint.scheme,
host: endpoint.host,
port: endpoint.port,
path: endpoint.path + path,
- queryParameters:params,
+ query: query.join('&')
);
return FlutterWebAuth.authenticate(
@@ -361,7 +376,7 @@ class Account extends Service {
/// should redirect the user back for your app and allow you to complete the
/// verification process by verifying both the **userId** and **secret**
/// parameters. Learn more about how to [complete the verification
- /// process](/docs/account#updateAccountVerification).
+ /// process](/docs/client/account#updateAccountVerification).
///
/// Please note that in order to avoid a [Redirect
/// Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
diff --git a/app/sdks/client-flutter/lib/services/avatars.dart b/app/sdks/client-flutter/lib/services/avatars.dart
index fc9c403d90..16c7ce59ff 100644
--- a/app/sdks/client-flutter/lib/services/avatars.dart
+++ b/app/sdks/client-flutter/lib/services/avatars.dart
@@ -145,6 +145,42 @@ class Avatars extends Service {
return location.toString();
}
+ /// Get User Initials
+ ///
+ /// Use this endpoint to show your user initials avatar icon on your website or
+ /// app. By default, this route will try to print your logged-in user name or
+ /// email initials. You can also overwrite the user name if you pass the 'name'
+ /// parameter. If no name is given and no user is logged, an empty avatar will
+ /// be returned.
+ ///
+ /// You can use the color and background params to change the avatar colors. By
+ /// default, a random theme will be selected. The random theme will persist for
+ /// the user's initials when reloading the same theme will always return for
+ /// the same initials.
+ ///
+ String getInitials({String name = '', int width = 500, int height = 500, String color = '', String background = ''}) {
+ final String path = '/avatars/initials';
+
+ final Map params = {
+ 'name': name,
+ 'width': width,
+ 'height': height,
+ 'color': color,
+ 'background': background,
+ 'project': client.config['project'],
+ };
+
+ Uri endpoint = Uri.parse(client.endPoint);
+ Uri location = new Uri(scheme: endpoint.scheme,
+ host: endpoint.host,
+ port: endpoint.port,
+ path: endpoint.path + path,
+ queryParameters:params,
+ );
+
+ return location.toString();
+ }
+
/// Get QR Code
///
/// Converts a given plain text to a QR code image. You can use the query
diff --git a/app/sdks/client-flutter/lib/services/database.dart b/app/sdks/client-flutter/lib/services/database.dart
index 1163c9dbb5..88082feef7 100644
--- a/app/sdks/client-flutter/lib/services/database.dart
+++ b/app/sdks/client-flutter/lib/services/database.dart
@@ -41,7 +41,10 @@ class Database extends Service {
/// Create Document
///
- /// Create a new Document.
+ /// Create a new Document. Before using this route, you should create a new
+ /// collection resource using either a [server
+ /// integration](/docs/server/database?sdk=nodejs#createCollection) API or
+ /// directly from your database console.
///
Future createDocument({@required String collectionId, @required dynamic data, @required List read, @required List write, String parentDocument = '', String parentProperty = '', String parentPropertyType = 'assign'}) {
final String path = '/database/collections/{collectionId}/documents'.replaceAll(RegExp('{collectionId}'), collectionId);
diff --git a/app/sdks/client-flutter/lib/services/locale.dart b/app/sdks/client-flutter/lib/services/locale.dart
index 91ea1567ef..095af4792c 100644
--- a/app/sdks/client-flutter/lib/services/locale.dart
+++ b/app/sdks/client-flutter/lib/services/locale.dart
@@ -106,9 +106,9 @@ class Locale extends Service {
/// List Currencies
///
- /// List of all currencies, including currency symol, name, plural, and decimal
- /// digits for all major and minor currencies. You can use the locale header to
- /// get the data in a supported language.
+ /// List of all currencies, including currency symbol, name, plural, and
+ /// decimal digits for all major and minor currencies. You can use the locale
+ /// header to get the data in a supported language.
///
Future getCurrencies() {
final String path = '/locale/currencies';
@@ -120,6 +120,24 @@ class Locale extends Service {
'content-type': 'application/json',
};
+ return client.call(HttpMethod.get, path: path, params: params, headers: headers);
+ }
+
+ /// List Languages
+ ///
+ /// List of all languages classified by ISO 639-1 including 2-letter code, name
+ /// in English, and name in the respective language.
+ ///
+ Future getLanguages() {
+ final String path = '/locale/languages';
+
+ final Map params = {
+ };
+
+ final Map headers = {
+ 'content-type': 'application/json',
+ };
+
return client.call(HttpMethod.get, path: path, params: params, headers: headers);
}
}
\ No newline at end of file
diff --git a/app/sdks/client-flutter/lib/services/teams.dart b/app/sdks/client-flutter/lib/services/teams.dart
index 56e0731687..b9205da455 100644
--- a/app/sdks/client-flutter/lib/services/teams.dart
+++ b/app/sdks/client-flutter/lib/services/teams.dart
@@ -115,10 +115,14 @@ class Teams extends Service {
/// Get team members by the team unique ID. All team members have read access
/// for this list of resources.
///
- Future getMemberships({@required String teamId}) {
+ Future getMemberships({@required String teamId, String search = '', int limit = 25, int offset = 0, OrderType orderType = OrderType.asc}) {
final String path = '/teams/{teamId}/memberships'.replaceAll(RegExp('{teamId}'), teamId);
final Map params = {
+ 'search': search,
+ 'limit': limit,
+ 'offset': offset,
+ 'orderType': orderType.name(),
};
final Map headers = {
@@ -136,8 +140,8 @@ class Teams extends Service {
///
/// Use the 'URL' parameter to redirect the user from the invitation email back
/// to your app. When the user is redirected, use the [Update Team Membership
- /// Status](/docs/teams#updateMembershipStatus) endpoint to allow the user to
- /// accept the invitation to the team.
+ /// Status](/docs/client/teams#updateMembershipStatus) endpoint to allow the
+ /// user to accept the invitation to the team.
///
/// Please note that in order to avoid a [Redirect
/// Attacks](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md)
diff --git a/app/sdks/client-web/docs/examples/account/create-o-auth2session.md b/app/sdks/client-web/docs/examples/account/create-o-auth2session.md
index 0c7430741c..25b02e632e 100644
--- a/app/sdks/client-web/docs/examples/account/create-o-auth2session.md
+++ b/app/sdks/client-web/docs/examples/account/create-o-auth2session.md
@@ -5,10 +5,6 @@ sdk
.setProject('5df5acd0d48c2') // Your project ID
;
-let promise = sdk.account.createOAuth2Session('amazon');
+// Go to OAuth provider login page
+sdk.account.createOAuth2Session('amazon');
-promise.then(function (response) {
- console.log(response); // Success
-}, function (error) {
- console.log(error); // Failure
-});
\ No newline at end of file
diff --git a/app/sdks/client-web/docs/examples/avatars/get-browser.md b/app/sdks/client-web/docs/examples/avatars/get-browser.md
index 86d0df4a23..0855209f58 100644
--- a/app/sdks/client-web/docs/examples/avatars/get-browser.md
+++ b/app/sdks/client-web/docs/examples/avatars/get-browser.md
@@ -7,4 +7,4 @@ sdk
let result = sdk.avatars.getBrowser('aa');
-console.log(result); // Resource URL
+console.log(result); // Resource URL
\ No newline at end of file
diff --git a/app/sdks/client-web/docs/examples/avatars/get-credit-card.md b/app/sdks/client-web/docs/examples/avatars/get-credit-card.md
index 83a1381adf..1aa595bd65 100644
--- a/app/sdks/client-web/docs/examples/avatars/get-credit-card.md
+++ b/app/sdks/client-web/docs/examples/avatars/get-credit-card.md
@@ -7,4 +7,4 @@ sdk
let result = sdk.avatars.getCreditCard('amex');
-console.log(result); // Resource URL
+console.log(result); // Resource URL
\ No newline at end of file
diff --git a/app/sdks/client-web/docs/examples/avatars/get-favicon.md b/app/sdks/client-web/docs/examples/avatars/get-favicon.md
index c0ff93307a..a945e50bb2 100644
--- a/app/sdks/client-web/docs/examples/avatars/get-favicon.md
+++ b/app/sdks/client-web/docs/examples/avatars/get-favicon.md
@@ -7,4 +7,4 @@ sdk
let result = sdk.avatars.getFavicon('https://example.com');
-console.log(result); // Resource URL
+console.log(result); // Resource URL
\ No newline at end of file
diff --git a/app/sdks/client-web/docs/examples/avatars/get-flag.md b/app/sdks/client-web/docs/examples/avatars/get-flag.md
index 762aeb348e..222b388afc 100644
--- a/app/sdks/client-web/docs/examples/avatars/get-flag.md
+++ b/app/sdks/client-web/docs/examples/avatars/get-flag.md
@@ -7,4 +7,4 @@ sdk
let result = sdk.avatars.getFlag('af');
-console.log(result); // Resource URL
+console.log(result); // Resource URL
\ No newline at end of file
diff --git a/app/sdks/client-web/docs/examples/avatars/get-image.md b/app/sdks/client-web/docs/examples/avatars/get-image.md
index 58f0eb8077..3f15981c71 100644
--- a/app/sdks/client-web/docs/examples/avatars/get-image.md
+++ b/app/sdks/client-web/docs/examples/avatars/get-image.md
@@ -7,4 +7,4 @@ sdk
let result = sdk.avatars.getImage('https://example.com');
-console.log(result); // Resource URL
+console.log(result); // Resource URL
\ No newline at end of file
diff --git a/app/sdks/client-web/docs/examples/avatars/get-q-r.md b/app/sdks/client-web/docs/examples/avatars/get-q-r.md
index fb17032949..9d9ee3fea2 100644
--- a/app/sdks/client-web/docs/examples/avatars/get-q-r.md
+++ b/app/sdks/client-web/docs/examples/avatars/get-q-r.md
@@ -7,4 +7,4 @@ sdk
let result = sdk.avatars.getQR('[TEXT]');
-console.log(result); // Resource URL
+console.log(result); // Resource URL
\ No newline at end of file
diff --git a/app/sdks/client-web/docs/examples/storage/get-file-download.md b/app/sdks/client-web/docs/examples/storage/get-file-download.md
index ae3a643529..1323b8b8df 100644
--- a/app/sdks/client-web/docs/examples/storage/get-file-download.md
+++ b/app/sdks/client-web/docs/examples/storage/get-file-download.md
@@ -7,4 +7,4 @@ sdk
let result = sdk.storage.getFileDownload('[FILE_ID]');
-console.log(result); // Resource URL
+console.log(result); // Resource URL
\ No newline at end of file
diff --git a/app/sdks/client-web/docs/examples/storage/get-file-preview.md b/app/sdks/client-web/docs/examples/storage/get-file-preview.md
index 30461e4d9a..5ede9fdb93 100644
--- a/app/sdks/client-web/docs/examples/storage/get-file-preview.md
+++ b/app/sdks/client-web/docs/examples/storage/get-file-preview.md
@@ -7,4 +7,4 @@ sdk
let result = sdk.storage.getFilePreview('[FILE_ID]');
-console.log(result); // Resource URL
+console.log(result); // Resource URL
\ No newline at end of file
diff --git a/app/sdks/client-web/docs/examples/storage/get-file-view.md b/app/sdks/client-web/docs/examples/storage/get-file-view.md
index 341dd502de..53877ae352 100644
--- a/app/sdks/client-web/docs/examples/storage/get-file-view.md
+++ b/app/sdks/client-web/docs/examples/storage/get-file-view.md
@@ -7,4 +7,4 @@ sdk
let result = sdk.storage.getFileView('[FILE_ID]');
-console.log(result); // Resource URL
+console.log(result); // Resource URL
\ No newline at end of file
diff --git a/app/sdks/client-web/src/sdk.js b/app/sdks/client-web/src/sdk.js
index 35f9ef352f..4bc64c99d2 100644
--- a/app/sdks/client-web/src/sdk.js
+++ b/app/sdks/client-web/src/sdk.js
@@ -190,28 +190,27 @@
}
request.onload = function () {
+ let data = request.response;
+ let contentType = this.getResponseHeader('content-type') || '';
+ contentType = contentType.substring(0, contentType.indexOf(';'));
+
+ switch (contentType) {
+ case 'application/json':
+ data = JSON.parse(data);
+ break;
+ }
+
+ let cookieFallback = this.getResponseHeader('X-Fallback-Cookies') || '';
+
+ if(window.localStorage && cookieFallback) {
+ window.console.warn('Appwrite is using localStorage for session management. Increase your security by adding a custom domain as your API endpoint.');
+ window.localStorage.setItem('cookieFallback', cookieFallback);
+ }
+
if (4 === request.readyState && 399 >= request.status) {
- let data = request.response;
- let contentType = this.getResponseHeader('content-type') || '';
- contentType = contentType.substring(0, contentType.indexOf(';'));
-
- switch (contentType) {
- case 'application/json':
- data = JSON.parse(data);
- break;
- }
-
- let cookieFallback = this.getResponseHeader('X-Fallback-Cookies') || '';
-
- if(window.localStorage && cookieFallback) {
- window.console.warn('Appwrite is using localStorage for session management. Increase your security by adding a custom domain as your API endpoint.');
- window.localStorage.setItem('cookieFallback', cookieFallback);
- }
-
resolve(data);
-
} else {
- reject(new Error(request.statusText));
+ reject(data);
}
};
@@ -709,10 +708,11 @@
* @param {string} provider
* @param {string} success
* @param {string} failure
+ * @param {string[]} scopes
* @throws {Error}
* @return {Promise}
*/
- createOAuth2Session: function(provider, success = 'https://appwrite.io/auth/oauth2/success', failure = 'https://appwrite.io/auth/oauth2/failure') {
+ createOAuth2Session: function(provider, success = 'https://appwrite.io/auth/oauth2/success', failure = 'https://appwrite.io/auth/oauth2/failure', scopes = []) {
if(provider === undefined) {
throw new Error('Missing required parameter: "provider"');
}
@@ -729,9 +729,28 @@
payload['failure'] = failure;
}
+ if(scopes) {
+ payload['scopes'] = scopes;
+ }
+
payload['project'] = config.project;
- let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
+
+ let query = [];
+
+ for (let p in payload) {
+ if(Array.isArray(payload[p])) {
+ for (let index = 0; index < payload[p].length; index++) {
+ let param = payload[p][index];
+ query.push(encodeURIComponent(p + '[]') + "=" + encodeURIComponent(param));
+ }
+ }
+ else {
+ query.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p]));
+ }
+ }
+
+ query = query.join("&");
window.location = config.endpoint + path + ((query) ? '?' + query : '');
},
@@ -883,7 +902,22 @@
payload['project'] = config.project;
- let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
+
+ let query = [];
+
+ for (let p in payload) {
+ if(Array.isArray(payload[p])) {
+ for (let index = 0; index < payload[p].length; index++) {
+ let param = payload[p][index];
+ query.push(encodeURIComponent(p + '[]') + "=" + encodeURIComponent(param));
+ }
+ }
+ else {
+ query.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p]));
+ }
+ }
+
+ query = query.join("&");
return config.endpoint + path + ((query) ? '?' + query : '');
},
@@ -926,7 +960,22 @@
payload['project'] = config.project;
- let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
+
+ let query = [];
+
+ for (let p in payload) {
+ if(Array.isArray(payload[p])) {
+ for (let index = 0; index < payload[p].length; index++) {
+ let param = payload[p][index];
+ query.push(encodeURIComponent(p + '[]') + "=" + encodeURIComponent(param));
+ }
+ }
+ else {
+ query.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p]));
+ }
+ }
+
+ query = query.join("&");
return config.endpoint + path + ((query) ? '?' + query : '');
},
@@ -956,7 +1005,22 @@
payload['project'] = config.project;
- let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
+
+ let query = [];
+
+ for (let p in payload) {
+ if(Array.isArray(payload[p])) {
+ for (let index = 0; index < payload[p].length; index++) {
+ let param = payload[p][index];
+ query.push(encodeURIComponent(p + '[]') + "=" + encodeURIComponent(param));
+ }
+ }
+ else {
+ query.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p]));
+ }
+ }
+
+ query = query.join("&");
return config.endpoint + path + ((query) ? '?' + query : '');
},
@@ -998,7 +1062,22 @@
payload['project'] = config.project;
- let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
+
+ let query = [];
+
+ for (let p in payload) {
+ if(Array.isArray(payload[p])) {
+ for (let index = 0; index < payload[p].length; index++) {
+ let param = payload[p][index];
+ query.push(encodeURIComponent(p + '[]') + "=" + encodeURIComponent(param));
+ }
+ }
+ else {
+ query.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p]));
+ }
+ }
+
+ query = query.join("&");
return config.endpoint + path + ((query) ? '?' + query : '');
},
@@ -1040,7 +1119,91 @@
payload['project'] = config.project;
- let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
+
+ let query = [];
+
+ for (let p in payload) {
+ if(Array.isArray(payload[p])) {
+ for (let index = 0; index < payload[p].length; index++) {
+ let param = payload[p][index];
+ query.push(encodeURIComponent(p + '[]') + "=" + encodeURIComponent(param));
+ }
+ }
+ else {
+ query.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p]));
+ }
+ }
+
+ query = query.join("&");
+
+ return config.endpoint + path + ((query) ? '?' + query : '');
+ },
+
+ /**
+ * Get User Initials
+ *
+ * Use this endpoint to show your user initials avatar icon on your website or
+ * app. By default, this route will try to print your logged-in user name or
+ * email initials. You can also overwrite the user name if you pass the 'name'
+ * parameter. If no name is given and no user is logged, an empty avatar will
+ * be returned.
+ *
+ * You can use the color and background params to change the avatar colors. By
+ * default, a random theme will be selected. The random theme will persist for
+ * the user's initials when reloading the same theme will always return for
+ * the same initials.
+ *
+ * @param {string} name
+ * @param {number} width
+ * @param {number} height
+ * @param {string} color
+ * @param {string} background
+ * @throws {Error}
+ * @return {string}
+ */
+ getInitials: function(name = '', width = 500, height = 500, color = '', background = '') {
+ let path = '/avatars/initials';
+
+ let payload = {};
+
+ if(name) {
+ payload['name'] = name;
+ }
+
+ if(width) {
+ payload['width'] = width;
+ }
+
+ if(height) {
+ payload['height'] = height;
+ }
+
+ if(color) {
+ payload['color'] = color;
+ }
+
+ if(background) {
+ payload['background'] = background;
+ }
+
+ payload['project'] = config.project;
+
+
+ let query = [];
+
+ for (let p in payload) {
+ if(Array.isArray(payload[p])) {
+ for (let index = 0; index < payload[p].length; index++) {
+ let param = payload[p][index];
+ query.push(encodeURIComponent(p + '[]') + "=" + encodeURIComponent(param));
+ }
+ }
+ else {
+ query.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p]));
+ }
+ }
+
+ query = query.join("&");
return config.endpoint + path + ((query) ? '?' + query : '');
},
@@ -1139,7 +1302,22 @@
payload['project'] = config.project;
- let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
+
+ let query = [];
+
+ for (let p in payload) {
+ if(Array.isArray(payload[p])) {
+ for (let index = 0; index < payload[p].length; index++) {
+ let param = payload[p][index];
+ query.push(encodeURIComponent(p + '[]') + "=" + encodeURIComponent(param));
+ }
+ }
+ else {
+ query.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p]));
+ }
+ }
+
+ query = query.join("&");
return config.endpoint + path + ((query) ? '?' + query : '');
}
@@ -1759,7 +1937,22 @@
payload['project'] = config.project;
- let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
+
+ let query = [];
+
+ for (let p in payload) {
+ if(Array.isArray(payload[p])) {
+ for (let index = 0; index < payload[p].length; index++) {
+ let param = payload[p][index];
+ query.push(encodeURIComponent(p + '[]') + "=" + encodeURIComponent(param));
+ }
+ }
+ else {
+ query.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p]));
+ }
+ }
+
+ query = query.join("&");
return config.endpoint + path + ((query) ? '?' + query : '');
},
@@ -1812,7 +2005,22 @@
payload['project'] = config.project;
- let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
+
+ let query = [];
+
+ for (let p in payload) {
+ if(Array.isArray(payload[p])) {
+ for (let index = 0; index < payload[p].length; index++) {
+ let param = payload[p][index];
+ query.push(encodeURIComponent(p + '[]') + "=" + encodeURIComponent(param));
+ }
+ }
+ else {
+ query.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p]));
+ }
+ }
+
+ query = query.join("&");
return config.endpoint + path + ((query) ? '?' + query : '');
},
@@ -1843,7 +2051,22 @@
payload['project'] = config.project;
- let query = Object.keys(payload).map(key => key + '=' + encodeURIComponent(payload[key])).join('&');
+
+ let query = [];
+
+ for (let p in payload) {
+ if(Array.isArray(payload[p])) {
+ for (let index = 0; index < payload[p].length; index++) {
+ let param = payload[p][index];
+ query.push(encodeURIComponent(p + '[]') + "=" + encodeURIComponent(param));
+ }
+ }
+ else {
+ query.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p]));
+ }
+ }
+
+ query = query.join("&");
return config.endpoint + path + ((query) ? '?' + query : '');
}
diff --git a/app/sdks/client-web/src/sdk.min.js b/app/sdks/client-web/src/sdk.min.js
index fa08385239..a19dd9b755 100644
--- a/app/sdks/client-web/src/sdk.min.js
+++ b/app/sdks/client-web/src/sdk.min.js
@@ -9,9 +9,9 @@ if(method==='GET'){for(let param in params){if(param.hasOwnProperty(key)){path=a
switch(headers['content-type']){case 'application/json':params=JSON.stringify(params);break;case 'multipart/form-data':let formData=new FormData();Object.keys(params).forEach(function(key){let param=params[key];formData.append(key+(Array.isArray(param)?'[]':''),param)});params=formData;break}
return new Promise(function(resolve,reject){let request=new XMLHttpRequest(),key;request.withCredentials=!0;request.open(method,path,!0);for(key in headers){if(headers.hasOwnProperty(key)){if(key==='content-type'&&headers[key]==='multipart/form-data'){continue}
request.setRequestHeader(key,headers[key])}}
-request.onload=function(){if(4===request.readyState&&399>=request.status){let data=request.response;let contentType=this.getResponseHeader('content-type')||'';contentType=contentType.substring(0,contentType.indexOf(';'));switch(contentType){case 'application/json':data=JSON.parse(data);break}
+request.onload=function(){let data=request.response;let contentType=this.getResponseHeader('content-type')||'';contentType=contentType.substring(0,contentType.indexOf(';'));switch(contentType){case 'application/json':data=JSON.parse(data);break}
let cookieFallback=this.getResponseHeader('X-Fallback-Cookies')||'';if(window.localStorage&&cookieFallback){window.console.warn('Appwrite is using localStorage for session management. Increase your security by adding a custom domain as your API endpoint.');window.localStorage.setItem('cookieFallback',cookieFallback)}
-resolve(data)}else{reject(new Error(request.statusText))}};if(progress){request.addEventListener('progress',progress);request.upload.addEventListener('progress',progress,!1)}
+if(4===request.readyState&&399>=request.status){resolve(data)}else{reject(data)}};if(progress){request.addEventListener('progress',progress);request.upload.addEventListener('progress',progress,!1)}
request.onerror=function(){reject(new Error("Network Error"))};request.send(params)})};return{'get':function(path,headers={},params={}){return call('GET',path+((Object.keys(params).length>0)?'?'+buildQuery(params):''),headers,{})},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress)},'put':function(path,headers={},params={},progress=null){return call('PUT',path,headers,params,progress)},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress)},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress)},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let account={get:function(){let path='/account';let payload={};return http.get(path,{'content-type':'application/json',},payload)},create:function(email,password,name=''){if(email===undefined){throw new Error('Missing required parameter: "email"')}
if(password===undefined){throw new Error('Missing required parameter: "password"')}
let path='/account';let payload={};if(email){payload.email=email}
@@ -45,10 +45,12 @@ return http.put(path,{'content-type':'application/json',},payload)},getSessions:
if(password===undefined){throw new Error('Missing required parameter: "password"')}
let path='/account/sessions';let payload={};if(email){payload.email=email}
if(password){payload.password=password}
-return http.post(path,{'content-type':'application/json',},payload)},deleteSessions:function(){let path='/account/sessions';let payload={};return http.delete(path,{'content-type':'application/json',},payload)},createOAuth2Session:function(provider,success='https://appwrite.io/auth/oauth2/success',failure='https://appwrite.io/auth/oauth2/failure'){if(provider===undefined){throw new Error('Missing required parameter: "provider"')}
+return http.post(path,{'content-type':'application/json',},payload)},deleteSessions:function(){let path='/account/sessions';let payload={};return http.delete(path,{'content-type':'application/json',},payload)},createOAuth2Session:function(provider,success='https://appwrite.io/auth/oauth2/success',failure='https://appwrite.io/auth/oauth2/failure',scopes=[]){if(provider===undefined){throw new Error('Missing required parameter: "provider"')}
let path='/account/sessions/oauth2/{provider}'.replace(new RegExp('{provider}','g'),provider);let payload={};if(success){payload.success=success}
if(failure){payload.failure=failure}
-payload.project=config.project;let query=Object.keys(payload).map(key=>key+'='+encodeURIComponent(payload[key])).join('&');window.location=config.endpoint+path+((query)?'?'+query:'')},deleteSession:function(sessionId){if(sessionId===undefined){throw new Error('Missing required parameter: "sessionId"')}
+if(scopes){payload.scopes=scopes}
+payload.project=config.project;let query=[];for(let p in payload){if(Array.isArray(payload[p])){for(let index=0;indexkey+'='+encodeURIComponent(payload[key])).join('&');return config.endpoint+path+((query)?'?'+query:'')},getCreditCard:function(code,width=100,height=100,quality=100){if(code===undefined){throw new Error('Missing required parameter: "code"')}
+payload.project=config.project;let query=[];for(let p in payload){if(Array.isArray(payload[p])){for(let index=0;indexkey+'='+encodeURIComponent(payload[key])).join('&');return config.endpoint+path+((query)?'?'+query:'')},getFavicon:function(url){if(url===undefined){throw new Error('Missing required parameter: "url"')}
+payload.project=config.project;let query=[];for(let p in payload){if(Array.isArray(payload[p])){for(let index=0;indexkey+'='+encodeURIComponent(payload[key])).join('&');return config.endpoint+path+((query)?'?'+query:'')},getFlag:function(code,width=100,height=100,quality=100){if(code===undefined){throw new Error('Missing required parameter: "code"')}
+payload.project=config.project;let query=[];for(let p in payload){if(Array.isArray(payload[p])){for(let index=0;indexkey+'='+encodeURIComponent(payload[key])).join('&');return config.endpoint+path+((query)?'?'+query:'')},getImage:function(url,width=400,height=400){if(url===undefined){throw new Error('Missing required parameter: "url"')}
+payload.project=config.project;let query=[];for(let p in payload){if(Array.isArray(payload[p])){for(let index=0;indexkey+'='+encodeURIComponent(payload[key])).join('&');return config.endpoint+path+((query)?'?'+query:'')}};let database={listDocuments:function(collectionId,filters=[],offset=0,limit=50,orderField='$id',orderType='ASC',orderCast='string',search='',first=0,last=0){if(collectionId===undefined){throw new Error('Missing required parameter: "collectionId"')}
+payload.project=config.project;let query=[];for(let p in payload){if(Array.isArray(payload[p])){for(let index=0;indexkey+'='+encodeURIComponent(payload[key])).join('&');return config.endpoint+path+((query)?'?'+query:'')},getFilePreview:function(fileId,width=0,height=0,quality=100,background='',output=''){if(fileId===undefined){throw new Error('Missing required parameter: "fileId"')}
+let path='/storage/files/{fileId}/download'.replace(new RegExp('{fileId}','g'),fileId);let payload={};payload.project=config.project;let query=[];for(let p in payload){if(Array.isArray(payload[p])){for(let index=0;indexkey+'='+encodeURIComponent(payload[key])).join('&');return config.endpoint+path+((query)?'?'+query:'')},getFileView:function(fileId,as=''){if(fileId===undefined){throw new Error('Missing required parameter: "fileId"')}
+payload.project=config.project;let query=[];for(let p in payload){if(Array.isArray(payload[p])){for(let index=0;indexkey+'='+encodeURIComponent(payload[key])).join('&');return config.endpoint+path+((query)?'?'+query:'')}};let teams={list:function(search='',limit=25,offset=0,orderType='ASC'){let path='/teams';let payload={};if(search){payload.search=search}
+payload.project=config.project;let query=[];for(let p in payload){if(Array.isArray(payload[p])){for(let index=0;index;
+ createOAuth2Session(provider: string, success: string, failure: string, scopes: string[]): Promise