Compare commits

...
Author SHA1 Message Date
shimon fb3e772774 debug 2024-10-29 17:51:07 +02:00
shimon 1c18e90b8a Merge branch '1.6.x' of https://github.com/appwrite/appwrite into refactor-view-api 2024-10-29 14:55:37 +02:00
Steven NguyenandGitHub 894b6f478d Merge pull request #8889 from appwrite/fix-migrations-queries
Fix missing allow attribute
2024-10-28 20:40:40 -07:00
Jake Barnby 64dd4bdce1 Add response model rule 2024-10-29 15:56:08 +13:00
Jake Barnby 299664d18b Fix missing allow attribute 2024-10-29 15:23:46 +13:00
shimon 7c727ea818 cache 2024-10-27 16:57:47 +02:00
shimon 88075a75b9 make view api use file cache 2024-10-25 19:03:29 +03:00
shimon c02b484128 make view api use file cache 2024-10-25 19:03:07 +03:00
shimon 705bd8a385 make view api use file cache 2024-10-25 18:38:25 +03:00
Christy JacobandGitHub 52936d7eb5 Merge pull request #8855 from appwrite/PLA-1776
fix(blocks): check if resource is blocked inside functions worker
2024-10-25 15:38:41 +04:00
Fabian Gruber 4313162b5e fix(blocks): check if resource is blocked inside functions worker 2024-10-25 13:25:54 +02:00
Christy JacobandGitHub 0df3bbec24 Merge pull request #8812 from appwrite/PLA-1776
feat: allow blocking based on resource attributes
2024-10-24 14:47:44 +04:00
Fabian Gruber 225e2c21c3 feat: allow blocking based on resource attributes 2024-10-24 11:26:25 +02:00
Christy JacobandGitHub 3cc53acbf2 Merge pull request #8848 from appwrite/feat-change-default-function-spec
feat: use 1 instead of 0.5 cpu
2024-10-23 18:56:17 +04:00
loks0n 51fe894a3f Revert "Merge pull request #8757 from ItzNotABug/update-walter-references"
This reverts commit a49c3a33f0, reversing
changes made to 3887ba69d7.
2024-10-23 15:20:45 +01:00
loks0n 6f7d8d7f62 chore: add comment 2024-10-23 15:16:15 +01:00
loks0n f7633c9c3b Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-change-default-function-spec 2024-10-23 15:10:33 +01:00
Christy JacobandGitHub 5fa4711247 Merge pull request #8846 from appwrite/PLA-1797
fix: allow '.wav' as 'audio/x-wav' as well
2024-10-23 18:08:41 +04:00
loks0n 62096dc008 chore: use 1 instead of 0.5 cpu 2024-10-23 14:22:37 +01:00
Fabian Gruber de8d0d4406 fix: allow '.wav' as 'audio/x-wav' as well 2024-10-23 11:57:50 +02:00
Christy JacobandGitHub 5bec7a2c75 Merge pull request #8836 from appwrite/PLA-1790
chore: replace 'Expires' with 'Cache-Control: private' header to avoid CDN caching
2024-10-22 14:30:16 +04:00
Fabian Gruber a9b648b2a9 chore: replace 'Expires' with 'Cache-Control: private' header to avoid CDN caching 2024-10-22 11:38:05 +02:00
Jake BarnbyandGitHub f6cfc80a1d Merge pull request #8833 from appwrite/fix-target-deletes
Fix target deletes
2024-10-22 20:21:14 +13:00
Jake Barnby 6403048acd Fix tests 2024-10-22 20:00:24 +13:00
Jake Barnby f203800dd9 Fix tests 2024-10-22 19:52:38 +13:00
Jake Barnby 58658f335a Fix health cert test 2024-10-22 19:18:48 +13:00
Jake Barnby 1ed3dee135 Fix tests 2024-10-22 14:54:34 +13:00
Jake Barnby 0a22292d1b Update tests 2024-10-22 14:35:11 +13:00
Jake Barnby 8ab1600417 Update users API 2024-10-22 14:35:04 +13:00
Jake Barnby 99710e7b74 Force expired false if updating target identifier 2024-10-22 14:08:16 +13:00
Jake Barnby 0f11c249b9 Update messaging for FCM response fix 2024-10-22 14:07:32 +13:00
Matej BačoandGitHub f9b868b339 Merge pull request #8815 from appwrite/feat-add-proj-vars-tests
Add tests for project variables
2024-10-18 12:35:50 +02:00
Khushboo Verma 73918b137e Replace sizeof with assertCount 2024-10-18 15:48:59 +05:30
Khushboo Verma 5a74f351dc Add tests to update non-existent var 2024-10-18 00:07:44 +05:30
Khushboo Verma 766b335410 Merge branch '1.6.x' into feat-add-proj-vars-tests 2024-10-17 19:39:28 +05:30
Khushboo Verma 4cd951ae41 Add tests for project variables 2024-10-17 19:35:17 +05:30
Jake BarnbyandGitHub 0d64f06aa4 Merge pull request #8109 from appwrite/check-cursor-value
Invalid cursor value
2024-10-17 19:10:14 +13:00
Jake Barnby b32cb30737 Force CI 2024-10-17 18:51:41 +13:00
Jake Barnby ea2192a83c Force CI 2024-10-17 18:51:26 +13:00
Jake Barnby a7f1cc128a Validate cursor queries to avoid getDocument type error 2024-10-17 18:41:24 +13:00
Jake Barnby f6658c4f5a Merge remote-tracking branch 'origin/1.6.x' into check-cursor-value 2024-10-17 18:32:11 +13:00
Steven NguyenandGitHub 38b3333349 Merge pull request #8756 from appwrite/fix-typo-blr-region
Fix typo in BLR region
2024-10-16 19:11:29 -07:00
Christy JacobandGitHub 16b360e77e Merge pull request #8801 from appwrite/chore-bump-assistant
chore: bump assistant
2024-10-16 13:55:02 +04:00
Jake BarnbyandGitHub 8b6529934d Merge pull request #8802 from appwrite/fix-max-queries-size
Fix max queries values
2024-10-16 22:37:17 +13:00
Jake Barnby 3d991ede3e Merge remote-tracking branch 'origin/1.6.x' into fix-max-queries-size 2024-10-16 21:36:56 +13:00
Jake BarnbyandGitHub 62817f5ca7 Merge pull request #8809 from appwrite/chore-update-database
Update database
2024-10-16 21:36:39 +13:00
Jake Barnby 45c8584903 Update migration 2024-10-16 21:36:18 +13:00
Jake Barnby 16d65f32b3 Review fixes 2024-10-16 21:35:32 +13:00
Jake Barnby 481c0b9d7a Update database 2024-10-16 21:22:36 +13:00
Christy JacobandGitHub d5bb5d4c0e Merge pull request #8803 from appwrite/add-missing-index
chore: add missing indexes
2024-10-15 21:51:13 +04:00
Christy Jacob 0bbfcd3888 chore: update dependencies 2024-10-15 16:37:01 +00:00
Christy Jacob 1b93c78c5f chore: add missing indexes 2024-10-15 16:34:33 +00:00
fogelito 7a64987da7 Fix message 2024-10-15 17:25:10 +03:00
fogelito c9e2be0e08 Fix failing test 2024-10-15 16:35:26 +03:00
fogelito 20800d760b Set to 500 2024-10-15 15:56:12 +03:00
fogelito 801724e90e APP_DATABASE_QUERY_VALUES 2024-10-15 15:50:02 +03:00
loks0n c3d6dcad86 chore: bump assistant 2024-10-15 13:46:00 +01:00
Matej BačoandGitHub 8a5a1609fe Merge pull request #8776 from appwrite/fix-move-write-execution-to-worker
Move createDocument from api to worker
2024-10-14 15:26:36 +02:00
Matej Bačo 408dcbc263 Add async exec doc test 2024-10-14 13:06:08 +00:00
Matej BačoandGitHub 16a8fdce4e Merge branch '1.6.x' into fix-move-write-execution-to-worker 2024-10-13 21:08:13 +02:00
Steven NguyenandGitHub c247f4cc88 Merge pull request #8766 from appwrite/remove-audit-deletes
remove audits deletion
2024-10-11 16:25:26 -07:00
Christy JacobandGitHub 9c1f93d47b Merge pull request #8780 from appwrite/update-system-0.9.0
chore: update utopia-php/system to 0.9.0
2024-10-11 18:00:23 +04:00
Fabian Gruber de06af6c4b chore: update utopia-php/system to 0.9.0 2024-10-11 15:59:04 +02:00
Matej Bačo 8fea73d3bf Fix db issues 2024-10-11 13:32:48 +00:00
Jake BarnbyandGitHub 290ebbfc33 Merge pull request #8782 from appwrite/feat-migration-success
Call migration success on success
2024-10-10 21:23:42 +13:00
Jake Barnby a246ba1a9e Lint 2024-10-10 21:23:11 +13:00
Jake Barnby 4fa8f01ce2 Call migration success on success 2024-10-10 21:15:18 +13:00
Jake Barnby 244942dd4e Generate specs 2024-10-10 15:55:35 +13:00
Jake Barnby ecfe962d43 Fix execution model 2024-10-10 15:53:54 +13:00
Jake BarnbyandGitHub 212222ef74 Merge pull request #8767 from appwrite/chore-release-ruby
Chore release ruby
2024-10-10 14:44:03 +13:00
Jake Barnby b0f3a99b4b Merge remote-tracking branch 'origin/cl-1.6.0-16-update-deno' into 1.6.x
# Conflicts:
#	app/config/function-templates.php
#	composer.lock
2024-10-10 14:23:50 +13:00
Matej Bačo 4c1d539e04 Change order 2024-10-09 19:18:40 +02:00
Matej Bačo 119834fc96 Fix deno order 2024-10-09 18:21:35 +02:00
Christy Jacob d33ecfa7f5 chore: update php runtimes 2024-10-09 16:06:34 +00:00
Christy Jacob e0ebeb837a chore: update php runtimes 2024-10-09 16:04:36 +00:00
Christy JacobandGitHub c7a0e26439 Merge pull request #8772 from appwrite/fix-cache-docker
fix: cache docker
2024-10-09 19:30:48 +04:00
loks0n 3b4fea655a chore: revert logs 2024-10-09 14:27:38 +01:00
loks0n 8f4594babb debug: ci docker logs 2024-10-09 14:12:46 +01:00
loks0n e90df389dd debug: non override 2024-10-09 14:02:55 +01:00
loks0n 348d748039 Merge branch 'fix-cache-docker' of https://github.com/appwrite/appwrite into fix-cache-docker 2024-10-09 13:05:59 +01:00
loks0n 7c2b6d9330 fix: remove cache scope 2024-10-09 13:05:13 +01:00
Luke B. SilverandGitHub 09410bfe97 Merge branch '1.6.x' into fix-cache-docker 2024-10-09 12:13:08 +01:00
Christy JacobandGitHub dc6e4159e9 Merge pull request #8761 from appwrite/feat-ping-endpoint
feat: ping endpoint
2024-10-09 14:57:25 +04:00
loks0n 7a507f00bd Merge branch '1.6.x' of https://github.com/appwrite/appwrite into fix-cache-docker 2024-10-09 11:45:17 +01:00
loks0n 919bb9f4bd chore: remove restore-keys 2024-10-09 11:43:38 +01:00
Jake BarnbyandGitHub 76c29f72f0 Merge pull request #8777 from appwrite/feat-move-attributes
Move new attributes
2024-10-09 23:00:09 +13:00
Jake Barnby 473f97fe92 Update migration 2024-10-09 22:55:50 +13:00
Jake Barnby 18c25aa03a Move new attributes 2024-10-09 22:25:55 +13:00
Matej BačoandGitHub a040bf754e Merge pull request #8771 from appwrite/feat-add-runtimes
Feat: Add new runtimes
2024-10-09 10:58:27 +02:00
Khushboo Verma d1ab8c4df8 Move createDocument from api to worker 2024-10-09 14:04:08 +05:30
shimon 24283dff7f removed audit deletion leftovers in deletes worker 2024-10-09 10:13:49 +03:00
Matej Bačo a63acb7f23 Revert unwanted changes 2024-10-08 21:11:25 +02:00
loks0n 5524fdb7b6 fix: cache-docker 2024-10-08 18:48:11 +01:00
Luke B. SilverandGitHub 786e9576ce Merge branch '1.6.x' into feat-ping-endpoint 2024-10-08 17:51:11 +01:00
Matej Bačo d38383bf78 Upgrade console SDK version 2024-10-08 18:16:38 +02:00
Matej Bačo 5287e70f9a Add new runtimes 2024-10-08 16:20:04 +02:00
Jake Barnby a6a67301a4 Update packages 2024-10-08 23:09:19 +13:00
Jake BarnbyandGitHub 1df1bf34eb Merge pull request #8769 from appwrite/chore-update-database
Update database
2024-10-08 23:03:45 +13:00
Jake Barnby 325c9d61cc Lock 2024-10-08 23:02:55 +13:00
Jake Barnby c8cbcfefbb Update database 2024-10-08 23:01:54 +13:00
Jake BarnbyandGitHub 6502c91877 Merge pull request #8768 from appwrite/revert-8741-feat-adding-coroutines
Revert "Feat adding coroutines"
2024-10-08 22:55:37 +13:00
Jake BarnbyandGitHub 8bc37026cd Revert "Feat adding coroutines" 2024-10-08 20:54:40 +13:00
Christy JacobandGitHub 0c3fed9ef1 Merge pull request #8741 from appwrite/feat-adding-coroutines
Feat adding coroutines
2024-10-08 07:35:38 +04:00
Jake Barnby ba24b668c9 Lint 2024-10-08 14:43:06 +13:00
Jake Barnby 2a6572ec4a Merge remote-tracking branch 'origin/1.6.x' into chore-release-ruby
# Conflicts:
#	app/controllers/api/functions.php
#	composer.lock
2024-10-08 14:39:30 +13:00
Jake Barnby 03dd5a1579 Fix versions 2024-10-08 14:17:58 +13:00
Jake Barnby eea9e22923 Release ruby 2024-10-08 14:17:49 +13:00
Jake Barnby be1cada848 Fix specs 2024-10-08 14:17:34 +13:00
Binyamin Yawitz 61dba4eefb revert: reverting commit 39490e9 2024-10-07 21:16:31 -04:00
Binyamin Yawitz 25650e7501 chore: trigger ci 2024-10-07 21:01:59 -04:00
Binyamin Yawitz 40266481e0 chore: package update 2024-10-07 20:48:55 -04:00
Binyamin Yawitz 60e0554fdf fix: remove leftover print_r 2024-10-07 20:41:44 -04:00
Binyamin Yawitz c34f8afa81 fix: wrong document check 2024-10-07 20:34:27 -04:00
Binyamin Yawitz ab5632bfcd fix: wrong document check 2024-10-07 20:17:45 -04:00
Steven NguyenandGitHub 7efa5b59f5 fix: update blr region name to the new preferred name 2024-10-07 22:18:29 +00:00
Steven NguyenandGitHub a49c3a33f0 Merge pull request #8757 from ItzNotABug/update-walter-references
Fix Walter References
2024-10-07 13:51:47 -07:00
Binyamin Yawitz 3e9a24232c fix: introducing local variable 2024-10-07 15:26:01 -04:00
Binyamin Yawitz 160b6c3386 chore: updates 2024-10-07 15:11:39 -04:00
Binyamin Yawitz d1729404c1 fix: adjusting test 2024-10-07 13:29:35 -04:00
Binyamin Yawitz c06948b266 fixes: phpstan 2024-10-07 12:57:07 -04:00
Binyamin Yawitz 16835f96bf chore: merge 2024-10-07 11:02:18 -04:00
loks0n e0bd500aa2 fix: realtime event 2024-10-07 15:58:34 +01:00
loks0n de509c6928 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-ping-endpoint 2024-10-07 14:00:25 +01:00
loks0n 6dab42e59a chore: use project injectable 2024-10-07 14:00:19 +01:00
shimon 4590ae11ee revert last commit 2024-10-07 13:57:02 +03:00
Jake BarnbyandGitHub 6e1848f52f Merge pull request #8764 from appwrite/databases-originalId
originalId
2024-10-07 22:09:39 +13:00
shimon fa513d6242 debug 2024-10-07 11:06:08 +03:00
shimon 424f24af04 remove resource 2024-10-06 21:02:24 +03:00
shimon e06b77cf39 remove audits deletion 2024-10-06 20:25:51 +03:00
shimon a7c0ef3d09 remove audits deletion 2024-10-06 20:22:25 +03:00
fogelito e87a66c426 originalId required false 2024-10-06 17:29:35 +03:00
fogelito 0a4b86214a originalId 2024-10-06 17:20:10 +03:00
fogelito 78eb1eec25 originalId 2024-10-06 14:15:31 +03:00
loks0n ac5bc22817 feat: ping endpoint 2024-10-04 23:23:18 +01:00
ItzNotABug 6080a363ee update: names. 2024-10-04 13:28:38 +05:30
Steven NguyenandGitHub 3887ba69d7 Merge pull request #8755 from appwrite/EVDOG4LIFE-patch-3
Version Bump
2024-10-03 15:47:12 -07:00
EvanandGitHub d19f205207 Version Bump 2024-10-03 14:38:03 -07:00
Matej BačoandGitHub 36d110b809 Merge pull request #8697 from appwrite/chore-request-response-filter-tests
test for response and request filters
2024-10-03 13:14:59 +02:00
Khushboo Verma 34527efc79 Use helper functions 2024-10-03 16:07:01 +05:30
Khushboo Verma d037ced578 Merge branch '1.6.x' into chore-request-response-filter-tests 2024-10-03 15:55:21 +05:30
Torsten DittmannandGitHub 5faea67631 Merge pull request #8186 from appwrite/migrations-backups
Migrations Backups
2024-10-03 12:10:41 +02:00
Khushboo Verma ef6ed675b3 Fix request response filter tests 2024-10-03 15:26:31 +05:30
Khushboo Verma b96bd3bff6 Resolve merge conflicts 2024-10-03 15:03:05 +05:30
Torsten Dittmann 04bc24f42b tests: fix merge conflict 2024-10-03 11:20:47 +02:00
Torsten Dittmann ec77ab1007 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into migrations-backups 2024-10-03 11:13:44 +02:00
Matej BačoandGitHub a6ceda3543 Merge pull request #8682 from appwrite/fix-flaky-tests
fix: flaky functions tests
2024-10-03 11:07:40 +02:00
Matej BačoandGitHub a142c5f094 Merge branch '1.6.x' into fix-flaky-tests 2024-10-03 09:48:12 +02:00
Steven NguyenandGitHub 490d8d435e Merge pull request #8749 from appwrite/fix-missing-protocol
fix: missing protocol
2024-10-02 17:26:29 -07:00
Binyamin Yawitz 671f801cd5 fix: missing protocol 2024-10-02 13:27:02 -04:00
Torsten Dittmann 4f4edc48af Merge branch '1.6.x' of https://github.com/appwrite/appwrite into migrations-backups 2024-10-02 17:38:50 +02:00
Torsten Dittmann 48e57be312 chore: update composer lock 2024-10-02 17:18:10 +02:00
loks0n 886687ed34 fix: reorder assertions 2024-10-02 15:19:15 +01:00
loks0n a45db60313 fix: falsy values 2024-10-02 14:16:25 +01:00
fogelito a408cd3b57 set setResource project 2024-10-02 14:36:09 +03:00
fogelito c3b186b3f9 set setResource project 2024-10-02 14:32:56 +03:00
loks0n ff174238e8 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into fix-flaky-tests 2024-10-02 11:54:42 +01:00
Damodar LohaniandGitHub bbe39299ac Merge pull request #7718 from appwrite/feat-heic-support
Feat heic and avif support
2024-10-02 12:23:45 +05:45
Damodar Lohani 825c32eb17 remove submodule 2024-10-02 06:13:09 +00:00
Damodar LohaniandGitHub 12f8cf5c00 Merge branch '1.6.x' into feat-heic-support 2024-10-02 11:55:52 +05:45
Damodar Lohani d290460202 disable heic in mime 2024-10-02 06:06:49 +00:00
Damodar LohaniandGitHub 36aa38c58e Merge pull request #8746 from appwrite/fix-remove-jpeg-fallback
Remove JPEG fallback for webp
2024-10-02 11:49:30 +05:45
Damodar Lohani d4195aa3a6 update utopia image 2024-10-02 06:02:10 +00:00
Damodar Lohani 5f3e986ed3 Merge remote-tracking branch 'origin/1.6.x' into feat-heic-support 2024-10-02 06:00:28 +00:00
Damodar Lohani 14cd43ddfd disable heic 2024-10-02 05:59:46 +00:00
Damodar LohaniandGitHub 769a8ee6c9 Remove JPEG fallback for webp
- Now that safari supports webp but without the appropriate header, remove the fallback
2024-10-02 11:23:27 +05:45
Binyamin Yawitz 6ce8c6a73d chore: merge 2024-10-01 21:39:40 -04:00
Christy JacobandGitHub d89676bd3b Merge pull request #8720 from appwrite/feat-payload-response-type
feat: payload response type
2024-10-01 23:55:50 +04:00
Christy JacobandGitHub 5ec88389d6 Merge pull request #8742 from appwrite/messaging-adapter-default-values
Messaging adapter default values
2024-10-01 23:54:38 +04:00
loks0n 3cfc062d8d Merge branch '1.6.x' of https://github.com/appwrite/appwrite into fix-flaky-tests 2024-10-01 20:53:08 +01:00
shimon 7e2fde3a0a update validation 2024-10-01 22:53:08 +03:00
loks0n 634a2a37c4 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-payload-response-type 2024-10-01 20:27:10 +01:00
shimon 49a0095cd5 Merge branch '1.6.x' of github.com:appwrite/appwrite into messaging-adapter-default-values 2024-10-01 22:19:54 +03:00
Christy JacobandGitHub 1abd096347 Merge pull request #8745 from appwrite/update-database-metrics
chore: remove db disk storage calculation
2024-10-01 23:15:50 +04:00
loks0n ac2fa382a7 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-payload-response-type 2024-10-01 20:02:18 +01:00
shimon 32dbafa353 update validation 2024-10-01 22:00:45 +03:00
fogelito e1a772fd3e Merge branch '1.6.x' of https://github.com/appwrite/appwrite into migrations-backups 2024-10-01 21:45:10 +03:00
shimon 3cb27d7b2c update validation 2024-10-01 21:01:48 +03:00
Christy Jacob d9946c399b chore: linter and specs 2024-10-01 21:55:01 +04:00
Christy Jacob 4069ae7165 chore: remove db disk storage calculation 2024-10-01 21:45:34 +04:00
shimon 2af4ef705d composer 2024-10-01 19:09:43 +03:00
shimon 1551de729b messaging adapter default values 2024-10-01 18:56:59 +03:00
Binyamin Yawitz 2703ca1106 chore: merge 2024-10-01 11:44:01 -04:00
Christy JacobandGitHub c610c9aff1 Merge pull request #8565 from appwrite/feat-console-roles-dl
chore: Changes for console roles support
2024-10-01 19:20:04 +04:00
Binyamin Yawitz 5e8ef5523d Reapply "Fix auth injection"
This reverts commit 904e4c8650.
2024-10-01 11:08:47 -04:00
Binyamin Yawitz 579d3d7f14 chore: lint & updates 2024-10-01 10:39:43 -04:00
Binyamin Yawitz d14edf4207 reverting: to set coroutines as head 2024-10-01 10:30:47 -04:00
fogelito 54f55f65f4 deletes-worker-set-project 2024-10-01 17:17:36 +03:00
loks0n 38a02a9e16 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-payload-response-type 2024-10-01 10:50:13 +01:00
fogelito 51152ae879 Ignore junction tables 2024-09-30 23:07:37 +03:00
Christy JacobandGitHub b8d84ccaaf Merge pull request #8724 from appwrite/fix-logger-throwing-error
Fix: Logger throwing fatal error
2024-09-30 21:10:20 +04:00
Torsten DittmannandGitHub 80449c5c10 Merge pull request #8735 from appwrite/feat-realtime-projects-channel
feat(realtime): projects channels
2024-09-30 17:37:24 +02:00
Torsten Dittmann e6d8221c89 tests(realtime): fix argument types 2024-09-30 17:08:17 +02:00
Torsten Dittmann 333682c4dc tests: add projects channel assertions 2024-09-30 16:53:25 +02:00
Torsten Dittmann bb20b3f52c feat(realtime): projects channels 2024-09-30 16:32:50 +02:00
Matej Bačo c0a489311c Merge branch '1.6.x' into fix-logger-throwing-error 2024-09-30 16:22:01 +02:00
loks0n 05bcb92f73 test: extra sleep 2024-09-30 14:45:06 +01:00
Matej Bačo 2b3cab887f Merge branch 'main' into 1.6.x 2024-09-30 15:33:15 +02:00
loks0n d3e400a21e fix: graphql 2024-09-30 14:26:16 +01:00
Christy JacobandGitHub d31810d08b Merge pull request #8728 from appwrite/fix-delete-project
Ignore junction tables
2024-09-30 15:26:22 +04:00
loks0n f236b8e09a Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-payload-response-type 2024-09-30 11:41:37 +01:00
fogelito 6625d4e4a8 Add console error 2024-09-30 11:01:01 +03:00
fogelito 744cf4fbb7 Merge remote-tracking branch 'origin/migrations-backups' into migrations-backups 2024-09-30 10:44:34 +03:00
fogelito 032fec3cae Add id 2024-09-30 10:43:50 +03:00
Damodar Lohani e9a5299547 Merge remote-tracking branch 'origin/1.6.x' into feat-console-roles-dl 2024-09-29 02:01:42 +00:00
Damodar LohaniandGitHub 8e4ceebaef Merge pull request #8391 from appwrite/lohanidamodar-patch-3
Fix: throwing exception when optional array attribute does not exist
2024-09-29 07:28:21 +05:45
Damodar LohaniandGitHub df06cd9139 Merge pull request #8732 from appwrite/fix-webp-dl
Fix webp
2024-09-28 15:04:37 +05:45
Damodar Lohani 36cfc4bee0 Fix webp 2024-09-28 08:55:51 +00:00
Torsten Dittmann 5f15c34ad5 Merge branch 'feat-console-roles-dl' of https://github.com/appwrite/appwrite into migrations-backups 2024-09-27 14:55:57 +02:00
fogelito 0b6467291b Ignore junction tables 2024-09-27 15:43:21 +03:00
Torsten Dittmann 2841875714 Merge branch 'main' of https://github.com/appwrite/appwrite into migrations-backups 2024-09-27 12:15:11 +02:00
fogelito d5bf134c92 delete deleteCollection 2024-09-27 13:14:38 +03:00
fogelito e627cab92e Check deviceForFiles 2024-09-27 11:43:22 +03:00
fogelito 7389c9a5fe Catch Throwable 2024-09-27 10:50:09 +03:00
fogelito 26f0efc34d Ignore junction tables 2024-09-27 10:45:23 +03:00
fogelito a8bb54a33e var_dump $junctions 2024-09-26 18:24:40 +03:00
fogelito d642111bfa relatedCollection 2024-09-26 18:13:27 +03:00
fogelito e000234dbd relatedCollection 2024-09-26 18:11:41 +03:00
fogelito 2b08fb59c3 relations 2024-09-26 16:50:42 +03:00
loks0n 9f7d474455 chore: remove unused model 2024-09-26 13:00:55 +01:00
loks0n 6c2e407ffb fix: openapi v3 use string 2024-09-26 12:59:41 +01:00
Damodar LohaniandGitHub d4b480b014 catch error pushing error logs everywhere 2024-09-26 16:36:51 +05:45
Damodar LohaniandGitHub 77ccec337e Fix: Logger throwing fatal error 2024-09-26 16:29:07 +05:45
Matej BačoandGitHub eb81474962 Merge branch 'main' into fix-flaky-tests 2024-09-26 12:33:38 +02:00
loks0n 4eb971d3b6 chore: specs 2024-09-26 10:56:48 +01:00
loks0n c4a764d175 fix: use model instead of type 2024-09-26 10:56:21 +01:00
Luke B. SilverandGitHub 43c06c6b70 Merge branch 'main' into feat-payload-response-type 2024-09-26 10:31:18 +01:00
fogelito 672c8b2730 lock file 2024-09-26 10:52:53 +03:00
fogelito b1cfd1c0ff Merge branch '1.6.x' of https://github.com/appwrite/appwrite into migrations-backups
# Conflicts:
#	app/controllers/api/databases.php
#	composer.lock
2024-09-26 10:42:15 +03:00
fogelito 226c3c0372 Remove var_dumps 2024-09-26 10:38:36 +03:00
fogelito 5c7e4393a7 revert 2024-09-26 09:54:26 +03:00
fogelito 17d0053f37 use Authorization skip 2024-09-26 09:45:00 +03:00
Steven NguyenandGitHub cfc36a9f47 Merge pull request #8617 from appwrite/docs-fix-endpoints
Sentence casing endpoint API reference
2024-09-25 15:31:30 -07:00
Steven NguyenandGitHub 52e5947822 Merge branch 'main' into docs-fix-endpoints 2024-09-25 14:05:59 -07:00
Khushboo Verma fdf73f8a7a Add test for request filters 2024-09-25 23:36:00 +05:30
fogelito 1cf26b1f46 Dbg $projectCollectionIds 2024-09-25 19:08:24 +03:00
fogelito 0ace58f00b Dbg $projectCollectionIds 2024-09-25 19:04:47 +03:00
loks0n fc1e54c046 feat: payload response type 2024-09-25 16:48:23 +01:00
fogelito 3228ccb4f9 Dbg $projectCollectionIds 2024-09-25 18:21:47 +03:00
fogelito 868c416202 Dbg $projectCollectionIds 2024-09-25 18:19:41 +03:00
fogelito 7116405793 Dbg $projectCollectionIds 2024-09-25 18:16:57 +03:00
fogelito a9469e8734 projectId schedules 2024-09-25 16:49:49 +03:00
fogelito 71ffe2bf52 Deletes schedules 2024-09-25 16:39:31 +03:00
Khushboo Verma 5f97aafe46 More asserts 2024-09-25 17:38:30 +05:30
Matej Bačo 0753163066 PR review changes 2024-09-25 13:32:09 +02:00
Luke B. SilverandGitHub 186f84830a Merge branch 'main' into fix-flaky-tests 2024-09-25 11:35:27 +01:00
loks0n 8978442039 Merge branch 'main' of https://github.com/appwrite/appwrite into fix-flaky-tests 2024-09-25 09:41:13 +01:00
Damodar Lohani 1a9bf23181 Merge remote-tracking branch 'origin/main' into feat-console-roles-dl 2024-09-25 07:36:09 +00:00
Damodar Lohani 7c3ac9e014 Merge remote-tracking branch 'origin/1.6.x' into lohanidamodar-patch-3 2024-09-25 01:46:42 +00:00
fogelito a42a669d6e equal equal 2024-09-24 22:02:38 +03:00
fogelito 12d119069e equal 2024-09-24 22:02:18 +03:00
fogelito d532c874d5 specs 1.6.x 2024-09-24 19:43:05 +03:00
fogelito a2c1ce4e8f specs 2024-09-24 19:39:39 +03:00
fogelito bd83351092 lock.file 2024-09-24 19:18:40 +03:00
fogelito 3ff6ccbda1 Remove and add in init.php 2024-09-24 19:18:05 +03:00
fogelito 75d681e6e2 Remove Breadcrumb 2024-09-24 19:01:01 +03:00
3121cd1c07 Update src/Appwrite/Platform/Workers/Migrations.php
Co-authored-by: Christy Jacob <christyjacob4@gmail.com>
2024-09-24 18:52:38 +03:00
fogelito ab908aa9cb Add const 2024-09-24 18:20:00 +03:00
fogelito 77aad0bfce Bumb migration tag 2024-09-24 12:58:09 +03:00
fogelito 831472fc64 destination size 2024-09-24 12:48:18 +03:00
Damodar Lohani 3fe031d223 remove console modules 2024-09-24 02:44:04 +00:00
Damodar Lohani abe145dae2 fix formatting 2024-09-24 02:40:01 +00:00
Damodar LohaniandGitHub f2eb1c3b3e Update api.php 2024-09-24 07:04:05 +05:45
fogelito a95cd51be0 destination false 2024-09-23 11:20:38 +03:00
loks0n f6e3759f6b chore: add async 2024-09-22 20:14:58 +01:00
loks0n 097b342869 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into fix-flaky-tests 2024-09-22 20:14:37 +01:00
fogelito 6f5b144e70 Add info resourceType 2024-09-22 14:15:01 +03:00
fogelito 8213b37cf4 Add info resourceType 2024-09-22 13:29:42 +03:00
fogelito 0eb0054f38 Add info resourceType 2024-09-22 10:50:13 +03:00
fogelito 4d24c08220 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into migrations-backups
# Conflicts:
#	src/Appwrite/Platform/Tasks/ScheduleBase.php
2024-09-22 10:44:06 +03:00
Damodar Lohani c31e928def fix linter 2024-09-22 02:44:53 +00:00
Damodar Lohani 9b3f1a2c4a fix linter 2024-09-22 02:43:29 +00:00
Damodar LohaniandGitHub 985a31ca04 Merge branch '1.6.x' into feat-console-roles-dl 2024-09-22 08:26:14 +05:45
loks0n f0485ca870 fix: use boolean false 2024-09-20 18:30:26 +01:00
loks0n df9e80a962 feat: handle bools 2024-09-20 17:02:23 +01:00
loks0n 42ae429153 chore: remove test 2024-09-20 13:49:29 +01:00
loks0n cefc7a1d4b chore: fmt 2024-09-20 13:44:34 +01:00
loks0n 2d102a122c fix: tests 2024-09-20 13:42:08 +01:00
loks0n 41490602f7 fix: template 2024-09-20 10:38:43 +01:00
loks0n 8e13aafbc8 fixes 2024-09-20 10:29:49 +01:00
loks0n 39490e9819 chore: bump tests 2024-09-20 10:06:58 +01:00
loks0n 1509aa544d debug: output fail deployment 2024-09-20 09:43:41 +01:00
loks0n f24863349e fixes 2024-09-20 09:41:34 +01:00
loks0n e4ad2ed566 fix: realtime tests 2024-09-20 09:33:26 +01:00
loks0n 72ddef855e chore: fmt 2024-09-20 09:16:18 +01:00
loks0n 151cb78993 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into fix-flaky-tests 2024-09-20 09:15:58 +01:00
loks0n 321227db15 fix: seperate tests 2024-09-20 09:15:05 +01:00
Khushboo Verma b621ec2e8b test for response and request filters 2024-09-20 12:50:30 +05:30
loks0n f04e47b3d4 chore: fmt 2024-09-19 22:11:44 +01:00
loks0n 92ce8d711d feat: split scheduled tests 2024-09-19 22:10:04 +01:00
loks0n 19ea682a52 chore: timeouts 2024-09-19 21:54:25 +01:00
loks0n 906ef59d95 chore: fmt 2024-09-19 21:36:19 +01:00
loks0n 410a35deb5 fix: spacing 2024-09-19 21:29:18 +01:00
loks0n cb0416be7b chore: fmt 2024-09-19 21:08:04 +01:00
loks0n 65c0db3285 fix: package function 2024-09-19 20:58:09 +01:00
loks0n 504711845a fix: stdout 2024-09-19 20:46:05 +01:00
loks0n 32657e22cc fix: conflicts 2024-09-19 20:34:38 +01:00
loks0n cb66c9073c Merge branch '1.6.x' of https://github.com/appwrite/appwrite into fix-flaky-tests 2024-09-19 20:31:00 +01:00
loks0n 964b616e05 fix: refactor custom server test 2024-09-19 20:21:00 +01:00
fogelito 9a0c72294c composer.lock 2024-09-19 18:37:09 +03:00
fogelito d6d7cf1d9f Pull 1.6.x 2024-09-19 18:27:02 +03:00
fogelito b519f5e23a Merge branch '1.6.x' of https://github.com/appwrite/appwrite into migrations-backups
# Conflicts:
#	composer.json
#	composer.lock
#	src/Appwrite/Platform/Tasks/ScheduleBase.php
#	src/Appwrite/Specification/Format/Swagger2.php
2024-09-19 18:19:45 +03:00
loks0n af509f3479 fix: flakyness 2024-09-19 14:17:38 +01:00
loks0n 496eb5bd12 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into fix-flaky-tests 2024-09-19 14:03:29 +01:00
loks0n 979fcacd6c fix: abstract requests 2024-09-19 12:35:52 +01:00
Damodar LohaniandGitHub 8c46f73f69 Merge branch 'main' into lohanidamodar-patch-3 2024-09-19 10:10:24 +05:45
loks0n 121b9f32b8 fix: allowFailure 2024-09-18 14:50:36 +01:00
loks0n 20fa8b3017 fix: flaky functions shot 1 2024-09-18 14:29:44 +01:00
Damodar LohaniandGitHub 1aa99a0f3e Merge branch '1.6.x' into feat-console-roles-dl 2024-09-18 11:58:09 +05:45
Damodar LohaniandGitHub e1238d0fe9 Merge branch '1.6.x' into feat-console-roles-dl 2024-09-17 14:52:29 +05:45
fogelito 82df6bf936 Add preserve dates 2024-09-15 11:50:49 +03:00
fogelito 707d9554df Add $createdAt $updatedAt 2024-09-10 12:39:52 +02:00
Jake Barnby 1848de14cc Revert migrations upgrade 2024-09-10 19:30:08 +12:00
Jake Barnby c4b742ac0d Allow overriding realtime instance 2024-09-09 20:52:37 +12:00
fogelito 6f55068838 Fix getMessage 2024-09-05 19:40:04 +03:00
fogelito c5b93ce231 Debug info 2024-09-05 19:33:05 +03:00
fogelito a008c7c67b Debug info 2024-09-05 19:16:15 +03:00
fogelito 626b2390e4 Debug info 2024-09-05 19:06:48 +03:00
fogelito 745968ebd4 Debug info 2024-09-05 19:06:19 +03:00
fogelito 1ce6a85026 Use preserveDates branch 2024-09-05 17:25:41 +03:00
Damodar Lohani b30e5d4cda fix test 2024-09-05 03:52:00 +00:00
Damodar Lohani 70bf8e2c41 fix project scope 2024-09-05 03:47:58 +00:00
Damodar Lohani cd40df8388 fix tests and membership update 2024-09-05 03:43:02 +00:00
Damodar Lohani 59453873b7 fix team test 2024-09-05 03:29:41 +00:00
Damodar Lohani 3075cbe626 make mock public 2024-09-05 03:28:17 +00:00
Damodar Lohani d335aee549 fix 2024-09-05 03:24:57 +00:00
Damodar Lohani b2359786e6 fix test 2024-09-05 03:22:10 +00:00
Damodar Lohani 72d720f83e remove projects.write from user scope 2024-09-05 03:20:19 +00:00
Damodar LohaniandGitHub 7dc64c12a6 fix team resource 2024-09-05 08:32:50 +05:45
Damodar LohaniandGitHub 59760bf529 Update api.php 2024-09-05 08:28:47 +05:45
Damodar LohaniandGitHub 42b85fad54 remove dumps 2024-09-05 07:12:59 +05:45
Damodar Lohani b263ed7ce6 fix team res 2024-09-04 23:48:51 +00:00
Damodar Lohani e0a30fd25a fix memberships test 2024-09-04 23:44:26 +00:00
Damodar Lohani 7c1c025f9e remove console roles test 2024-09-04 23:35:31 +00:00
Jake Barnby e79e2857f9 Remove console 2024-09-04 20:27:29 +12:00
Jake Barnby db269d8b8f Merge remote-tracking branch 'origin/1.6.x' into migrations-backups 2024-09-04 20:23:14 +12:00
Damodar Lohani fdc69e77f4 remove validator 2024-09-04 07:45:51 +00:00
Damodar Lohani bad70b8257 remove comment 2024-09-04 07:44:39 +00:00
Damodar Lohani c439fb08fd reset change 2024-09-04 07:43:53 +00:00
Damodar Lohani cb537534e3 remove dump 2024-09-04 07:42:35 +00:00
Damodar Lohani d633c3e278 gitmodule 2024-09-04 07:41:44 +00:00
Damodar LohaniandGitHub 555f3f9406 Update api.php 2024-09-04 12:29:56 +05:45
Damodar LohaniandGitHub a89cf443aa Update api.php 2024-09-04 12:28:52 +05:45
Damodar LohaniandGitHub 269efbe2f0 Update api.php 2024-09-04 12:25:51 +05:45
Damodar Lohani 107fc1d069 fix check 2024-09-04 02:45:05 +00:00
Damodar Lohani 1752c6be44 fix team resource 2024-09-04 02:40:53 +00:00
Damodar Lohani 1f83dab9e1 fix roles for /projects/:projectId endpoints 2024-09-04 02:16:18 +00:00
Damodar Lohani bc5ac28b84 update validator and formatting 2024-09-04 02:01:56 +00:00
Damodar Lohani 413f3c20ab use proper scope for platforms and keys endpoint 2024-09-04 01:51:32 +00:00
Richard Choi 08b539ba05 change API reference endpoints to sentence case 2024-09-03 16:32:11 +00:00
Richard Choi 4e780c0f75 Change API reference endpoints to sentence casing 2024-09-03 16:22:30 +00:00
Jake Barnby 6854451565 Fix execution scheduler merge 2024-09-03 15:07:53 +12:00
Jake Barnby 2c2e88f645 Merge remote-tracking branch 'origin/1.6.x' into migrations-backups
# Conflicts:
#	app/init.php
#	composer.json
#	composer.lock
#	src/Appwrite/Platform/Tasks/ScheduleBase.php
#	src/Appwrite/Specification/Format/Swagger2.php
2024-09-03 14:48:57 +12:00
Damodar Lohani 7aacaad94d remove dump 2024-09-03 02:07:41 +00:00
Damodar Lohani 550a479532 Merge remote-tracking branch 'origin/1.6.x' into feat-console-roles-dl 2024-09-03 02:05:43 +00:00
fogelito 982ccf8873 logger update 2024-09-02 14:20:02 +03:00
fogelito 850e4c420b Merge remote-tracking branch 'origin/migrations-backups' into migrations-backups 2024-09-02 14:18:26 +03:00
fogelito d356d52c05 Merge branch 'main' of https://github.com/appwrite/appwrite into migrations-backups
# Conflicts:
#	composer.lock
2024-09-02 14:17:54 +03:00
Jake Barnby a93fa2cb0c Check base class for query validator descendants 2024-09-02 21:43:44 +12:00
fogelito 964be49c3a revert roles.php 2024-09-02 10:26:03 +03:00
fogelito 209c610cbe composer.lock 2024-09-01 13:15:51 +03:00
fogelito ff702d774f Merge remote-tracking branch 'origin/migrations-backups' into migrations-backups 2024-09-01 13:13:16 +03:00
fogelito 79fd72ead4 Add backups scopes 2024-09-01 13:13:06 +03:00
Jake Barnby f3a5572b59 Update SDK generator 2024-08-28 22:27:00 +12:00
Jake Barnby c868c669ca Move const to init to avoid redefine warnings 2024-08-28 21:35:16 +12:00
Jake Barnby 2d03ce598b Allow overriding request/response types for spec generation 2024-08-28 18:00:44 +12:00
fogelito d8148c5491 Change TimeLimit namespace 2024-08-25 11:19:25 +03:00
fogelito 6b1196fb26 update composer.lock 2024-08-25 11:10:42 +03:00
fogelito e11e922587 Move scopes 2024-08-25 09:58:54 +03:00
fogelito 3d3248cbbb Merge remote-tracking branch 'origin/migrations-backups' into migrations-backups 2024-08-25 09:49:55 +03:00
fogelito b35f906384 Longtext test 2024-08-25 09:49:44 +03:00
732a8938ce Update composer.json
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2024-08-25 09:48:50 +03:00
fogelito ed0652f86a database 52 2024-08-22 19:11:06 +03:00
fogelito 2fc483b9e0 rm migration version 2024-08-22 19:01:19 +03:00
fogelito b0e218cb3b database 50 2024-08-22 18:58:16 +03:00
fogelito 404ad5b75d database 49 2024-08-22 18:52:06 +03:00
fogelito cf7e1121fa audit 2024-08-22 18:43:42 +03:00
fogelito 9419e36de0 abuse 2024-08-22 18:43:04 +03:00
fogelito 1cdac28631 0.10.* cache 2024-08-22 18:42:22 +03:00
fogelito 43e7c1e9a6 database 50 2024-08-22 18:36:41 +03:00
fogelito fd52a172a7 audit abuse 2024-08-22 18:20:57 +03:00
fogelito 4b0c782891 databases 0.52.* 2024-08-22 18:18:56 +03:00
fogelito c43177093e Merge remote-tracking branch 'origin/migrations-backups' into migrations-backups 2024-08-22 17:31:05 +03:00
fogelito 5f27d75c11 Address comments 2024-08-22 17:30:50 +03:00
f1da0b2e61 Update src/Appwrite/Platform/Tasks/Maintenance.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2024-08-22 17:21:11 +03:00
Damodar Lohani 2491f5c70c fix scope merging 2024-08-21 05:51:37 +00:00
Damodar Lohani cd70d74233 remove per project roles 2024-08-21 05:45:19 +00:00
Damodar Lohani 1ca4904132 reset auth and roles 2024-08-21 05:14:17 +00:00
fogelito 2d84816bc9 composer.lock 2024-08-20 16:39:39 +03:00
fogelito ff1a679c49 Merge branch 'main' of https://github.com/appwrite/appwrite into migrations-backups-v2
# Conflicts:
#	composer.json
#	composer.lock
#	src/Appwrite/Platform/Tasks/Maintenance.php
#	src/Appwrite/Platform/Workers/Migrations.php
#	tests/e2e/Services/Databases/DatabasesBase.php
2024-08-20 16:36:54 +03:00
fogelito 9b20802b4c Only comment 2024-08-20 10:09:02 +03:00
fogelito 42671400bb Remove interface 2024-08-20 10:08:07 +03:00
fogelito 04967763e7 Add Iprojects 2024-08-19 18:46:20 +03:00
fogelito b47fd08f71 Remove interface 2024-08-19 18:26:54 +03:00
fogelito 49b659a9ae Remove interface 2024-08-19 18:01:29 +03:00
fogelito aa5ce6200d Appwrite notifyProjects 2024-08-19 16:33:15 +03:00
fogelito 9265ba743e Add implements Projects 2024-08-19 15:19:06 +03:00
fogelito ccc34b049a notifyProjects 2024-08-19 14:03:49 +03:00
fogelito ab4ac2e568 Change private to protected 2024-08-12 17:44:05 +03:00
fogelito 919b5ac06d Use document credentials 2024-08-12 11:15:14 +03:00
Damodar Lohani 9a140f90d4 update image library 2024-08-09 14:12:10 +00:00
Damodar Lohani 0a396b4dfa Merge remote-tracking branch 'origin/1.6.x' into feat-heic-support 2024-08-09 14:00:44 +00:00
fogelito 3800ccee1d credentials 2024-08-08 19:46:44 +03:00
fogelito d29740f0d7 credentials 2024-08-08 15:43:11 +03:00
fogelito 41beb8af9c credentials 2024-08-08 15:14:43 +03:00
fogelito f12a578efe Update on failed 2024-08-07 16:58:00 +03:00
fogelito 0ac5e2aa76 error no params 2024-08-07 16:33:54 +03:00
fogelito 7593537f3d Add error hook 2024-08-07 16:02:24 +03:00
fogelito 96274e9c77 Collections resourceType 2024-08-06 13:55:59 +03:00
fogelito 0bb34e0d01 Add resourceType 2024-08-06 12:46:13 +03:00
fogelito 85b43d1f5c restorations.read 2024-08-05 14:48:47 +03:00
fogelito 0ed7827fe4 Add backups scopes 2024-07-30 10:26:13 +03:00
fogelito 4f87d72bcd Migrations errors 2024-07-28 14:44:37 +03:00
fogelito ebb8d4a23b getTraceAsString 2024-07-28 11:58:36 +03:00
fogelito 1a97848d9c getTraceAsString 2024-07-28 11:58:04 +03:00
fogelito 5620f360c8 dbg error 2024-07-28 09:52:47 +03:00
fogelito 854f243fb1 dbg error 2024-07-28 09:51:22 +03:00
fogelito a98ff71188 processDestination constructor 2024-07-28 09:29:02 +03:00
fogelito 965cf9aac6 processDestination params 2024-07-28 09:27:18 +03:00
fogelito 1ff717ecef Change to getResourceName 2024-07-25 14:34:19 +03:00
fogelito 203021ecb7 "utopia-php/abuse": "0.38.*", 2024-07-25 12:38:54 +03:00
fogelito d67e9eec34 composer.json 2024-07-25 12:08:30 +03:00
fogelito a0c57d20d8 Abuse Audit upgrade 2024-07-25 11:58:15 +03:00
fogelito b217221cef Database 0.50.* 2024-07-25 11:53:35 +03:00
fogelito 6fb318c788 policies scope 2024-07-24 12:04:11 +03:00
Christy Jacob c167201d7c feat: add role override for admins 2024-07-24 02:47:46 +04:00
Christy Jacob eea5d610ba feat: linter 2024-07-23 23:54:52 +04:00
Christy Jacob 3b7ce7691e feat: add support for console role 2024-07-23 23:49:11 +04:00
fogelito 8de8ae03f3 Add header x-appwrite-preserve-dates 2024-07-22 15:38:58 +03:00
Christy Jacob 1bbce77c92 chore: poc for new roles 2024-07-18 01:18:15 +04:00
fogelito 865d84f4e7 ResourceId default value 2024-07-15 17:28:40 +03:00
fogelito d9da0dd671 Add resourceId to migration collections.php 2024-07-15 17:22:36 +03:00
Damodar LohaniandGitHub f54c468f51 Merge branch 'main' into lohanidamodar-patch-3 2024-07-15 14:26:25 +05:45
Damodar Lohani 1c7e6d7d81 fix test 2024-07-15 07:08:22 +00:00
Damodar Lohani 03c48a5ef3 set null when not required and data key is not set 2024-07-15 05:35:04 +00:00
Damodar Lohani b3aaca317c format and update test 2024-07-14 06:05:51 +00:00
Damodar LohaniandGitHub c0efc232ad Fix warning when optional attribute doesn't exist
When an attribute required is set to `false` and the attribute doesn't exist in the response, we get warning `undefined array key "attribute"` in the console, this update prevents that error
2024-07-14 11:31:02 +05:45
fogelito 6a717f2272 Add resourceId to migration collections.php 2024-07-11 18:47:03 +03:00
fogelito 475e2a64ce DuplicateException dbg 2024-07-11 13:36:28 +03:00
fogelito 865bfdbd34 DuplicateException dbg 2024-07-11 12:38:55 +03:00
fogelito b4ab1c2c69 Debug create document 2024-07-10 16:34:05 +03:00
Jake Barnby ed1a5b600d Fix oldAttribute key length 2024-07-10 19:00:10 +12:00
Damodar LohaniandGitHub 069ada8c4a Merge branch 'main' into feat-heic-support 2024-07-08 12:13:42 +05:45
fogelito 72c647fba8 Add source shutDown 2024-06-26 10:54:57 +03:00
fogelito 2412946939 Comment init $destination init 2024-06-19 17:26:48 +03:00
fogelito b84fe6891c lint 2024-06-19 13:26:52 +03:00
fogelito 4c4b95a39f composer.lock 2024-06-19 13:19:44 +03:00
fogelito b31a1e2584 Remove comments 2024-06-19 13:17:14 +03:00
fogelito 33a3674989 0.4.4 2024-06-19 13:14:57 +03:00
fogelito e457ed194f Minimum stable 2024-06-19 13:13:56 +03:00
fogelito ef6bc042fd Migrations 0.4.4 2024-06-19 13:11:21 +03:00
fogelito 2ba6916e53 Undefineds 2024-06-19 12:03:32 +03:00
fogelito 2d1299bb46 Remove migration 2024-06-19 11:46:32 +03:00
fogelito ec71fb22de Change processSource processDestination params 2024-06-19 11:45:07 +03:00
fogelito f1bff1b62c add migrations document 2024-06-19 11:09:37 +03:00
fogelito d39060e0a8 getDevice 2024-06-16 12:49:34 +03:00
fogelito 610edc9584 composer.lock 2024-06-16 09:52:53 +03:00
fogelito e58033aff0 Downgrade utopia-php/platform to 0.5 2024-06-13 19:03:51 +03:00
fogelito 2c80823d96 composer.lock 2024-06-13 18:26:13 +03:00
fogelito ccd0e863db Merge branch 'refactor-usage-sn' of https://github.com/appwrite/appwrite into migrations-backups
 Conflicts:
	composer.lock
2024-06-13 18:25:28 +03:00
fogelito 82be39af3d Update cache 2024-06-13 15:14:56 +03:00
fogelito 91cc3a3ff5 Remove archive 2024-06-09 19:36:41 +03:00
fogelito b23f7b0725 Revert archive 2024-06-09 19:25:49 +03:00
fogelito 25b661b908 Move Archive event to cloud 2024-06-09 17:15:17 +03:00
fogelito d5025e7db3 add todo 2024-06-09 16:59:23 +03:00
fogelito 0e33fb2b64 remove $backup 2024-06-09 16:19:02 +03:00
fogelito abde5393df Set Archive 2024-06-09 16:18:40 +03:00
fogelito 26c714da48 dbg 2024-05-29 19:30:52 +03:00
fogelito d000c160b6 Fix bug? 2024-05-29 19:29:03 +03:00
fogelito ed0995fa49 Fix bug? 2024-05-29 19:26:44 +03:00
fogelito 5764ea9ade static getCollectionId 2024-05-29 19:09:14 +03:00
fogelito 07bfe3823c getCollectionId 2024-05-29 18:59:10 +03:00
fogelito 97703b0332 Question 2024-05-29 18:16:29 +03:00
fogelito 3ebbc5976f hint update 2024-05-29 11:31:00 +03:00
fogelito 15ffce36de add hooks 2024-05-28 20:52:32 +03:00
fogelito 68f5947b40 Remove backup 2024-05-28 20:40:57 +03:00
fogelito d503a436d0 add project 2024-05-28 20:37:37 +03:00
fogelito 75ec86eed4 SourceAppwrite 2024-05-28 20:12:58 +03:00
fogelito 73280fd173 processSource 2024-05-28 20:05:36 +03:00
fogelito 6149f46621 processDestination 2024-05-28 19:59:54 +03:00
fogelito bdb7350994 change private to protected 2024-05-28 19:50:37 +03:00
fogelito c08d34ead9 add destinations to api's 2024-05-27 18:56:28 +03:00
fogelito ddb0ce3175 add destinations 2024-05-27 18:52:33 +03:00
fogelito f68f21a264 Add debug 2024-05-27 17:53:50 +03:00
fogelito bc62405810 Add debug 2024-05-27 17:42:51 +03:00
fogelito b5061da8c3 add resourceType backup 2024-05-27 17:17:58 +03:00
fogelito 9c591bd42d add resourceType backup 2024-05-27 17:06:08 +03:00
fogelito bbcab38590 add config backups scopes 2024-05-26 12:14:40 +03:00
fogelito a7b7059916 revert scope 2024-05-26 11:37:16 +03:00
fogelito d4b0ca7804 add scopes 2024-05-26 11:05:18 +03:00
fogelito 552c729c5a fix enqueueResources 2024-05-23 18:22:25 +03:00
fogelito 0994b0f2de trigger migration 2024-05-23 18:14:00 +03:00
fogelito ecfc5d68c2 accept null 2024-05-23 17:49:41 +03:00
fogelito 06a5e76c50 ScheduleBase refactor 2024-05-23 17:42:03 +03:00
fogelito 87e0d167e2 Merge branch 'refactor-usage-sn' of https://github.com/appwrite/appwrite into check-cursor-value
 Conflicts:
	composer.lock
2024-05-15 10:57:25 +03:00
fogelito 1a5ae40e15 revert 2024-05-15 10:56:02 +03:00
fogelito 6a4581671f Use original delete throw 2024-05-15 10:52:45 +03:00
fogelito 8bf1d4924b Use original delete throw 2024-05-15 10:36:18 +03:00
fogelito 661329b977 Invalid cursor value 2024-05-09 19:54:28 +03:00
Damodar Lohani 4fd0e9e02d supporting mime types fo avif and heic images 2024-03-03 13:10:25 +00:00
106 changed files with 6597 additions and 5504 deletions
+15 -14
View File
@@ -16,15 +16,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Build Appwrite
uses: docker/build-push-action@v3
uses: docker/build-push-action@v6
with:
context: .
push: false
@@ -39,7 +39,7 @@ jobs:
VERSION=dev
- name: Cache Docker Image
uses: actions/cache@v3
uses: actions/cache@v4
with:
key: ${{ env.CACHE_KEY }}
path: /tmp/${{ env.IMAGE }}.tar
@@ -51,10 +51,10 @@ jobs:
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Load Cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
key: ${{ env.CACHE_KEY }}
path: /tmp/${{ env.IMAGE }}.tar
@@ -81,10 +81,10 @@ jobs:
needs: setup
steps:
- name: checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Load Cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
key: ${{ env.CACHE_KEY }}
path: /tmp/${{ env.IMAGE }}.tar
@@ -113,6 +113,7 @@ jobs:
Console,
Databases,
Functions,
FunctionsSchedule,
GraphQL,
Health,
Locale,
@@ -128,10 +129,10 @@ jobs:
steps:
- name: checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Load Cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
key: ${{ env.CACHE_KEY }}
path: /tmp/${{ env.IMAGE }}.tar
@@ -141,7 +142,7 @@ jobs:
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose up -d
sleep 25
sleep 30
- name: Run ${{matrix.service}} Tests
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
@@ -149,15 +150,15 @@ jobs:
- name: Run ${{matrix.service}} Shared Tables Tests
run: _APP_DATABASE_SHARED_TABLES=database_db_main docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
benchamrking:
benchmarking:
name: Benchmark
runs-on: ubuntu-latest
needs: setup
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Load Cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
key: ${{ env.CACHE_KEY }}
path: /tmp/${{ env.IMAGE }}.tar
-3
View File
@@ -320,15 +320,12 @@ These are the current metrics we collect usage stats for:
| executions | Total number of executions per project |
| databases | Total number of databases per project |
| databases.storage | Total amount of storage used by all databases per project (in bytes) |
| databases.storage_disk | Total amount of storage used by all database per project on disk (in bytes) |
| collections | Total number of collections per project |
| {databaseInternalId}.collections | Total number of collections per database|
| {databaseInternalId}.storage | Sum of database storage (in bytes) |
| {databaseInternalId}.storage_disk | Sum of database storage on disk (in bytes) |
| documents | Total number of documents per project |
| {databaseInternalId}.{collectionInternalId}.documents | Total number of documents per collection |
| {databaseInternalId}.{collectionInternalId}.storage | Sum of database storage used by the collection (in bytes) |
| {databsaeInternalId}.{collectionInternalId}.storage_disk | Sum of database storage used by the collection on disk (in bytes) |
| buckets | Total number of buckets per project |
| files | Total number of files per project |
| {bucketInternalId}.files.storage | Sum of files.storage per bucket (in bytes) |
+2
View File
@@ -28,6 +28,8 @@ RUN \
apk add boost boost-dev; \
fi
RUN apk add libwebp
WORKDIR /usr/src/code
COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor
+1
View File
@@ -11,6 +11,7 @@
| 1.3.x | :white_check_mark: |
| 1.4.x | :white_check_mark: |
| 1.5.x | :white_check_mark: |
| 1.6.x | :white_check_mark: |
## Reporting a Vulnerability
+6 -2
View File
@@ -197,8 +197,12 @@ CLI::setResource('logError', function (Registry $register) {
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('Usage stats log pushed with status code: ' . $responseCode);
try {
$responseCode = $logger->addLog($log);
Console::info('Error log pushed with status code: ' . $responseCode);
} catch (Throwable $th) {
Console::error('Error pushing log: ' . $th->getMessage());
}
}
Console::warning("Failed: {$error->getMessage()}");
+63 -5
View File
@@ -46,15 +46,15 @@ $commonCollections = [
'filters' => [],
],
[
'$id' => ID::custom('mimeType'),
'$id' => ID::custom('headers'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 255, // https://tools.ietf.org/html/rfc4288#section-4.2
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'default' => [],
'array' => false,
'filters' => [],
'filters' => ['json'],
],
[
'$id' => 'accessedAt',
@@ -2406,6 +2406,17 @@ $projectCollections = array_merge([
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('originalId'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => Database::LENGTH_KEY,
'format' => '',
'filters' => [],
'required' => false,
'default' => null,
'array' => false,
],
],
'indexes' => [
[
@@ -4109,13 +4120,24 @@ $projectCollections = array_merge([
'$id' => ID::custom('source'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 8192,
'size' => 8192, // reduce size
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('destination'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false, // make true after patch script
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('credentials'),
'type' => Database::VAR_STRING,
@@ -4508,6 +4530,28 @@ $consoleCollections = array_merge([
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('pingCount'),
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => 0,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('pingedAt'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
]
],
'indexes' => [
[
@@ -4531,6 +4575,20 @@ $consoleCollections = array_merge([
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_pingCount'),
'type' => Database::INDEX_KEY,
'attributes' => ['pingCount'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_pingedAt'),
'type' => Database::INDEX_KEY,
'attributes' => ['pingedAt'],
'lengths' => [],
'orders' => [],
]
],
],
+5
View File
@@ -24,6 +24,11 @@ return [
'description' => 'Access to this API is forbidden.',
'code' => 401,
],
Exception::GENERAL_RESOURCE_BLOCKED => [
'name' => Exception::GENERAL_RESOURCE_BLOCKED,
'description' => 'Access to this resource is blocked.',
'code' => 401,
],
Exception::GENERAL_UNKNOWN_ORIGIN => [
'name' => Exception::GENERAL_UNKNOWN_ORIGIN,
'description' => 'The request originated from an unknown origin. If you trust this domain, please list it as a trusted platform in the Appwrite console.',
+15 -10
View File
@@ -3,7 +3,7 @@
const TEMPLATE_RUNTIMES = [
'NODE' => [
'name' => 'node',
'versions' => ['21.0', '20.0', '19.0', '18.0', '16.0', '14.5']
'versions' => ['22', '21.0', '20.0', '19.0', '18.0', '16.0', '14.5']
],
'PYTHON' => [
'name' => 'python',
@@ -11,7 +11,7 @@ const TEMPLATE_RUNTIMES = [
],
'DART' => [
'name' => 'dart',
'versions' => ['3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16', '2.16']
'versions' => ['3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16', '2.16']
],
'GO' => [
'name' => 'go',
@@ -21,9 +21,13 @@ const TEMPLATE_RUNTIMES = [
'name' => 'php',
'versions' => ['8.3', '8.2', '8.1', '8.0']
],
'DENO' => [
'name' => 'deno',
'versions' => ['2.0', '1.46', '1.40', '1.35', '1.24', '1.21']
],
'BUN' => [
'name' => 'bun',
'versions' => ['1.0']
'versions' => ['1.1', '1.0']
],
'RUBY' => [
'name' => 'ruby',
@@ -51,7 +55,7 @@ return [
'id' => 'starter',
'name' => 'Starter function',
'tagline' =>
'A simple function to get started. Edit this function to explore endless possibilities with Appwrite Functions.',
'A simple function to get started. Edit this function to explore endless possibilities with Appwrite Functions.',
'permissions' => ['any'],
'events' => [],
'cron' => '',
@@ -73,6 +77,7 @@ return [
'src/index.php',
'php/starter'
),
...getRuntimes(TEMPLATE_RUNTIMES['DENO'], 'deno cache src/main.ts', 'src/main.ts', 'deno/starter'),
...getRuntimes(TEMPLATE_RUNTIMES['BUN'], 'bun install', 'src/main.ts', 'bun/starter'),
...getRuntimes(TEMPLATE_RUNTIMES['RUBY'], 'bundle install', 'lib/main.rb', 'ruby/starter'),
],
@@ -121,7 +126,7 @@ return [
'description' => 'Authentication token to access your Upstash Vector database. <a class="u-bold" target="_blank" href="https://upstash.com/docs/vector/overall/getstarted">Learn more</a>.',
'value' => '',
'placeholder' =>
'oe4wNTbwHVLcDNa6oceZfhBEABsCNYh43ii6Xdq4bKBH7mq7qJkUmc4cs3ABbYyuVKWZTxVQjiNjYgydn2dkhABNes4NAuDpj7qxUAmZYqGJT78',
'oe4wNTbwHVLcDNa6oceZfhBEABsCNYh43ii6Xdq4bKBH7mq7qJkUmc4cs3ABbYyuVKWZTxVQjiNjYgydn2dkhABNes4NAuDpj7qxUAmZYqGJT78',
'required' => true,
'type' => 'password'
]
@@ -227,7 +232,7 @@ return [
'id' => 'query-mongo-atlas',
'name' => 'Query MongoDB Atlas',
'tagline' =>
'Realtime NoSQL document database with geospecial, graph, search, and vector suport.',
'Realtime NoSQL document database with geospecial, graph, search, and vector suport.',
'permissions' => ['any'],
'events' => [],
'cron' => '',
@@ -252,7 +257,7 @@ return [
'description' => 'The endpoint to connect to your Mongo database. <a class="u-bold" target="_blank" href="https://www.mongodb.com/docs/atlas/getting-started/">Learn more</a>.',
'value' => '',
'placeholder' =>
'mongodb+srv://appwrite:Yx42hafg7Q4fgkxe@cluster0.7mslfog.mongodb.net/?retryWrites=true&w=majority&appName=Appwrite',
'mongodb+srv://appwrite:Yx42hafg7Q4fgkxe@cluster0.7mslfog.mongodb.net/?retryWrites=true&w=majority&appName=Appwrite',
'required' => true,
'type' => 'password'
]
@@ -264,7 +269,7 @@ return [
'id' => 'query-neon-postgres',
'name' => 'Query Neon Postgres',
'tagline' =>
'Reliable SQL database with replication, point-in-time recovery, and pgvector support.',
'Reliable SQL database with replication, point-in-time recovery, and pgvector support.',
'permissions' => ['any'],
'events' => [],
'cron' => '',
@@ -491,7 +496,7 @@ return [
'id' => 'censor-with-redact',
'name' => 'Censor with Redact',
'tagline' =>
'Censor sensitive information from a provided text string using Redact API by Pangea.',
'Censor sensitive information from a provided text string using Redact API by Pangea.',
'permissions' => ['any'],
'events' => [],
'cron' => '',
@@ -560,7 +565,7 @@ return [
'id' => 'github-issue-bot',
'name' => 'GitHub issue bot',
'tagline' =>
'Automate the process of responding to newly opened issues in a GitHub repository.',
'Automate the process of responding to newly opened issues in a GitHub repository.',
'permissions' => ['any'],
'events' => [],
'cron' => '',
+5 -9
View File
@@ -1,9 +1,5 @@
<?php
const APP_PLATFORM_SERVER = 'server';
const APP_PLATFORM_CLIENT = 'client';
const APP_PLATFORM_CONSOLE = 'console';
return [
APP_PLATFORM_CLIENT => [
'key' => APP_PLATFORM_CLIENT,
@@ -15,7 +11,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '16.0.0',
'version' => '16.0.2',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -203,7 +199,7 @@ return [
[
'key' => 'web',
'name' => 'Console',
'version' => '1.1.0',
'version' => '1.2.1',
'url' => 'https://github.com/appwrite/sdk-for-console',
'package' => '',
'enabled' => true,
@@ -285,7 +281,7 @@ return [
[
'key' => 'php',
'name' => 'PHP',
'version' => '12.0.0',
'version' => '12.1.0',
'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true,
@@ -321,7 +317,7 @@ return [
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '12.1.0',
'version' => '12.1.1',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@@ -357,7 +353,7 @@ return [
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.10.0',
'version' => '0.10.1',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
+1 -1
View File
@@ -31,7 +31,7 @@ return [
],
'blr' => [
'$id' => 'blr',
'name' => 'Banglore',
'name' => 'Bengaluru',
'disabled' => true,
'flag' => 'in',
'default' => true,
+2 -2
View File
@@ -17,7 +17,6 @@ $member = [
'files.read',
'files.write',
'projects.read',
'projects.write',
'locale.read',
'avatars.read',
'execution.read',
@@ -49,6 +48,7 @@ $admins = [
'collections.write',
'platforms.read',
'platforms.write',
'projects.write',
'keys.read',
'keys.write',
'webhooks.read',
@@ -75,7 +75,7 @@ $admins = [
'topics.write',
'topics.read',
'subscribers.write',
'subscribers.read'
'subscribers.read',
];
return [
+1 -1
View File
@@ -6,7 +6,7 @@ return [
Specification::S_05VCPU_512MB => [
'slug' => Specification::S_05VCPU_512MB,
'memory' => 512,
'cpus' => 0.5
'cpus' => 1 // TODO: revert this, it's a temporary change to test function performance.
],
Specification::S_1VCPU_512MB => [
'slug' => Specification::S_1VCPU_512MB,
+111 -110
View File
@@ -43,7 +43,7 @@
},
"x-appwrite": {
"method": "get",
"weight": 8,
"weight": 9,
"cookies": false,
"type": "",
"deprecated": false,
@@ -94,7 +94,7 @@
},
"x-appwrite": {
"method": "create",
"weight": 7,
"weight": 8,
"cookies": false,
"type": "",
"deprecated": false,
@@ -181,7 +181,7 @@
},
"x-appwrite": {
"method": "updateEmail",
"weight": 33,
"weight": 34,
"cookies": false,
"type": "",
"deprecated": false,
@@ -239,7 +239,7 @@
},
"\/account\/identities": {
"get": {
"summary": "List Identities",
"summary": "List identities",
"operationId": "accountListIdentities",
"tags": [
"account"
@@ -259,7 +259,7 @@
},
"x-appwrite": {
"method": "listIdentities",
"weight": 56,
"weight": 57,
"cookies": false,
"type": "",
"deprecated": false,
@@ -320,7 +320,7 @@
},
"x-appwrite": {
"method": "deleteIdentity",
"weight": 57,
"weight": 58,
"cookies": false,
"type": "",
"deprecated": false,
@@ -385,7 +385,7 @@
},
"x-appwrite": {
"method": "createJWT",
"weight": 28,
"weight": 29,
"cookies": false,
"type": "",
"deprecated": false,
@@ -436,7 +436,7 @@
},
"x-appwrite": {
"method": "listLogs",
"weight": 30,
"weight": 31,
"cookies": false,
"type": "",
"deprecated": false,
@@ -504,7 +504,7 @@
},
"x-appwrite": {
"method": "updateMFA",
"weight": 43,
"weight": 44,
"cookies": false,
"type": "",
"deprecated": false,
@@ -556,7 +556,7 @@
},
"\/account\/mfa\/authenticators\/{type}": {
"post": {
"summary": "Create Authenticator",
"summary": "Create authenticator",
"operationId": "accountCreateMfaAuthenticator",
"tags": [
"account"
@@ -576,7 +576,7 @@
},
"x-appwrite": {
"method": "createMfaAuthenticator",
"weight": 45,
"weight": 46,
"cookies": false,
"type": "",
"deprecated": false,
@@ -624,7 +624,7 @@
]
},
"put": {
"summary": "Verify Authenticator",
"summary": "Verify authenticator",
"operationId": "accountUpdateMfaAuthenticator",
"tags": [
"account"
@@ -644,7 +644,7 @@
},
"x-appwrite": {
"method": "updateMfaAuthenticator",
"weight": 46,
"weight": 47,
"cookies": false,
"type": "",
"deprecated": false,
@@ -711,7 +711,7 @@
}
},
"delete": {
"summary": "Delete Authenticator",
"summary": "Delete authenticator",
"operationId": "accountDeleteMfaAuthenticator",
"tags": [
"account"
@@ -724,7 +724,7 @@
},
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"weight": 50,
"weight": 51,
"cookies": false,
"type": "",
"deprecated": false,
@@ -774,7 +774,7 @@
},
"\/account\/mfa\/challenge": {
"post": {
"summary": "Create MFA Challenge",
"summary": "Create MFA challenge",
"operationId": "accountCreateMfaChallenge",
"tags": [
"account"
@@ -794,7 +794,7 @@
},
"x-appwrite": {
"method": "createMfaChallenge",
"weight": 51,
"weight": 52,
"cookies": false,
"type": "",
"deprecated": false,
@@ -850,7 +850,7 @@
}
},
"put": {
"summary": "Create MFA Challenge (confirmation)",
"summary": "Create MFA challenge (confirmation)",
"operationId": "accountUpdateMfaChallenge",
"tags": [
"account"
@@ -870,7 +870,7 @@
},
"x-appwrite": {
"method": "updateMfaChallenge",
"weight": 52,
"weight": 53,
"cookies": false,
"type": "",
"deprecated": false,
@@ -928,7 +928,7 @@
},
"\/account\/mfa\/factors": {
"get": {
"summary": "List Factors",
"summary": "List factors",
"operationId": "accountListMfaFactors",
"tags": [
"account"
@@ -948,7 +948,7 @@
},
"x-appwrite": {
"method": "listMfaFactors",
"weight": 44,
"weight": 45,
"cookies": false,
"type": "",
"deprecated": false,
@@ -981,7 +981,7 @@
},
"\/account\/mfa\/recovery-codes": {
"get": {
"summary": "Get MFA Recovery Codes",
"summary": "Get MFA recovery codes",
"operationId": "accountGetMfaRecoveryCodes",
"tags": [
"account"
@@ -1001,7 +1001,7 @@
},
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"weight": 49,
"weight": 50,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1032,7 +1032,7 @@
]
},
"post": {
"summary": "Create MFA Recovery Codes",
"summary": "Create MFA recovery codes",
"operationId": "accountCreateMfaRecoveryCodes",
"tags": [
"account"
@@ -1052,7 +1052,7 @@
},
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"weight": 47,
"weight": 48,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1083,7 +1083,7 @@
]
},
"patch": {
"summary": "Regenerate MFA Recovery Codes",
"summary": "Regenerate MFA recovery codes",
"operationId": "accountUpdateMfaRecoveryCodes",
"tags": [
"account"
@@ -1103,7 +1103,7 @@
},
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"weight": 48,
"weight": 49,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1156,7 +1156,7 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 31,
"weight": 32,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1228,7 +1228,7 @@
},
"x-appwrite": {
"method": "updatePassword",
"weight": 32,
"weight": 33,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1305,7 +1305,7 @@
},
"x-appwrite": {
"method": "updatePhone",
"weight": 34,
"weight": 35,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1383,7 +1383,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 29,
"weight": 30,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1434,7 +1434,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 35,
"weight": 36,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1506,7 +1506,7 @@
},
"x-appwrite": {
"method": "createRecovery",
"weight": 37,
"weight": 38,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1585,7 +1585,7 @@
},
"x-appwrite": {
"method": "updateRecovery",
"weight": 38,
"weight": 39,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1669,7 +1669,7 @@
},
"x-appwrite": {
"method": "listSessions",
"weight": 10,
"weight": 11,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1713,7 +1713,7 @@
},
"x-appwrite": {
"method": "deleteSessions",
"weight": 11,
"weight": 12,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1766,7 +1766,7 @@
},
"x-appwrite": {
"method": "createAnonymousSession",
"weight": 16,
"weight": 17,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1817,7 +1817,7 @@
},
"x-appwrite": {
"method": "createEmailPasswordSession",
"weight": 15,
"weight": 16,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1893,7 +1893,7 @@
},
"x-appwrite": {
"method": "updateMagicURLSession",
"weight": 25,
"weight": 26,
"cookies": false,
"type": "",
"deprecated": true,
@@ -1962,7 +1962,7 @@
},
"x-appwrite": {
"method": "createOAuth2Session",
"weight": 18,
"weight": 19,
"cookies": false,
"type": "webAuth",
"deprecated": false,
@@ -2105,7 +2105,7 @@
},
"x-appwrite": {
"method": "updatePhoneSession",
"weight": 26,
"weight": 27,
"cookies": false,
"type": "",
"deprecated": true,
@@ -2181,7 +2181,7 @@
},
"x-appwrite": {
"method": "createSession",
"weight": 17,
"weight": 18,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2257,7 +2257,7 @@
},
"x-appwrite": {
"method": "getSession",
"weight": 12,
"weight": 13,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2320,7 +2320,7 @@
},
"x-appwrite": {
"method": "updateSession",
"weight": 14,
"weight": 15,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2376,7 +2376,7 @@
},
"x-appwrite": {
"method": "deleteSession",
"weight": 13,
"weight": 14,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2441,7 +2441,7 @@
},
"x-appwrite": {
"method": "updateStatus",
"weight": 36,
"weight": 37,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2494,7 +2494,7 @@
},
"x-appwrite": {
"method": "createPushTarget",
"weight": 53,
"weight": 54,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2575,7 +2575,7 @@
},
"x-appwrite": {
"method": "updatePushTarget",
"weight": 54,
"weight": 55,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2655,7 +2655,7 @@
},
"x-appwrite": {
"method": "deletePushTarget",
"weight": 55,
"weight": 56,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2718,7 +2718,7 @@
},
"x-appwrite": {
"method": "createEmailToken",
"weight": 24,
"weight": 25,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2799,7 +2799,7 @@
},
"x-appwrite": {
"method": "createMagicURLToken",
"weight": 23,
"weight": 24,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2881,7 +2881,7 @@
},
"x-appwrite": {
"method": "createOAuth2Token",
"weight": 22,
"weight": 23,
"cookies": false,
"type": "webAuth",
"deprecated": false,
@@ -3024,7 +3024,7 @@
},
"x-appwrite": {
"method": "createPhoneToken",
"weight": 27,
"weight": 28,
"cookies": false,
"type": "",
"deprecated": false,
@@ -3103,7 +3103,7 @@
},
"x-appwrite": {
"method": "createVerification",
"weight": 39,
"weight": 40,
"cookies": false,
"type": "",
"deprecated": false,
@@ -3173,7 +3173,7 @@
},
"x-appwrite": {
"method": "updateVerification",
"weight": 40,
"weight": 41,
"cookies": false,
"type": "",
"deprecated": false,
@@ -3251,7 +3251,7 @@
},
"x-appwrite": {
"method": "createPhoneVerification",
"weight": 41,
"weight": 42,
"cookies": false,
"type": "",
"deprecated": false,
@@ -3305,7 +3305,7 @@
},
"x-appwrite": {
"method": "updatePhoneVerification",
"weight": 42,
"weight": 43,
"cookies": false,
"type": "",
"deprecated": false,
@@ -3376,7 +3376,7 @@
},
"x-appwrite": {
"method": "getBrowser",
"weight": 59,
"weight": 60,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -3504,7 +3504,7 @@
},
"x-appwrite": {
"method": "getCreditCard",
"weight": 58,
"weight": 59,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -3636,7 +3636,7 @@
},
"x-appwrite": {
"method": "getFavicon",
"weight": 62,
"weight": 63,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -3696,7 +3696,7 @@
},
"x-appwrite": {
"method": "getFlag",
"weight": 60,
"weight": 61,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -4186,7 +4186,7 @@
},
"x-appwrite": {
"method": "getImage",
"weight": 61,
"weight": 62,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -4270,7 +4270,7 @@
},
"x-appwrite": {
"method": "getInitials",
"weight": 64,
"weight": 65,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -4364,7 +4364,7 @@
},
"x-appwrite": {
"method": "getQR",
"weight": 63,
"weight": 64,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -4465,7 +4465,7 @@
},
"x-appwrite": {
"method": "listDocuments",
"weight": 108,
"weight": 109,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4552,7 +4552,7 @@
},
"x-appwrite": {
"method": "createDocument",
"weight": 107,
"weight": 108,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4661,7 +4661,7 @@
},
"x-appwrite": {
"method": "getDocument",
"weight": 109,
"weight": 110,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4758,7 +4758,7 @@
},
"x-appwrite": {
"method": "updateDocument",
"weight": 111,
"weight": 112,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4859,7 +4859,7 @@
},
"x-appwrite": {
"method": "deleteDocument",
"weight": 112,
"weight": 113,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4945,7 +4945,7 @@
},
"x-appwrite": {
"method": "listExecutions",
"weight": 304,
"weight": 305,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5033,7 +5033,7 @@
},
"x-appwrite": {
"method": "createExecution",
"weight": 303,
"weight": 304,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5084,7 +5084,7 @@
"body": {
"type": "string",
"description": "HTTP body of execution. Default value is empty string.",
"x-example": null
"x-example": "<BODY>"
},
"async": {
"type": "boolean",
@@ -5150,7 +5150,7 @@
},
"x-appwrite": {
"method": "getExecution",
"weight": 305,
"weight": 306,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5226,7 +5226,7 @@
},
"x-appwrite": {
"method": "query",
"weight": 329,
"weight": 330,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5280,7 +5280,7 @@
},
"x-appwrite": {
"method": "mutation",
"weight": 328,
"weight": 329,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5334,7 +5334,7 @@
},
"x-appwrite": {
"method": "get",
"weight": 116,
"weight": 117,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5368,7 +5368,7 @@
},
"\/locale\/codes": {
"get": {
"summary": "List Locale Codes",
"summary": "List locale codes",
"operationId": "localeListCodes",
"tags": [
"locale"
@@ -5388,7 +5388,7 @@
},
"x-appwrite": {
"method": "listCodes",
"weight": 117,
"weight": 118,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5442,7 +5442,7 @@
},
"x-appwrite": {
"method": "listContinents",
"weight": 121,
"weight": 122,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5496,7 +5496,7 @@
},
"x-appwrite": {
"method": "listCountries",
"weight": 118,
"weight": 119,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5550,7 +5550,7 @@
},
"x-appwrite": {
"method": "listCountriesEU",
"weight": 119,
"weight": 120,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5604,7 +5604,7 @@
},
"x-appwrite": {
"method": "listCountriesPhones",
"weight": 120,
"weight": 121,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5658,7 +5658,7 @@
},
"x-appwrite": {
"method": "listCurrencies",
"weight": 122,
"weight": 123,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5712,7 +5712,7 @@
},
"x-appwrite": {
"method": "listLanguages",
"weight": 123,
"weight": 124,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5766,7 +5766,7 @@
},
"x-appwrite": {
"method": "createSubscriber",
"weight": 380,
"weight": 381,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5851,7 +5851,7 @@
},
"x-appwrite": {
"method": "deleteSubscriber",
"weight": 384,
"weight": 385,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5928,7 +5928,7 @@
},
"x-appwrite": {
"method": "listFiles",
"weight": 206,
"weight": 207,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6016,7 +6016,7 @@
},
"x-appwrite": {
"method": "createFile",
"weight": 205,
"weight": 206,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -6116,7 +6116,7 @@
},
"x-appwrite": {
"method": "getFile",
"weight": 207,
"weight": 208,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6190,7 +6190,7 @@
},
"x-appwrite": {
"method": "updateFile",
"weight": 212,
"weight": 213,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6268,7 +6268,7 @@
}
},
"delete": {
"summary": "Delete File",
"summary": "Delete file",
"operationId": "storageDeleteFile",
"tags": [
"storage"
@@ -6281,7 +6281,7 @@
},
"x-appwrite": {
"method": "deleteFile",
"weight": 213,
"weight": 214,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6350,7 +6350,7 @@
},
"x-appwrite": {
"method": "getFileDownload",
"weight": 209,
"weight": 210,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6419,7 +6419,7 @@
},
"x-appwrite": {
"method": "getFilePreview",
"weight": 208,
"weight": 209,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6610,7 +6610,8 @@
"jpeg",
"gif",
"png",
"webp"
"webp",
"avif"
],
"x-enum-name": "ImageFormat",
"x-enum-keys": [],
@@ -6636,7 +6637,7 @@
},
"x-appwrite": {
"method": "getFileView",
"weight": 210,
"weight": 211,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6712,7 +6713,7 @@
},
"x-appwrite": {
"method": "list",
"weight": 217,
"weight": 218,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6790,7 +6791,7 @@
},
"x-appwrite": {
"method": "create",
"weight": 216,
"weight": 217,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6877,7 +6878,7 @@
},
"x-appwrite": {
"method": "get",
"weight": 218,
"weight": 219,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6941,7 +6942,7 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 220,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7017,7 +7018,7 @@
},
"x-appwrite": {
"method": "delete",
"weight": 222,
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7083,7 +7084,7 @@
},
"x-appwrite": {
"method": "listMemberships",
"weight": 224,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7171,7 +7172,7 @@
},
"x-appwrite": {
"method": "createMembership",
"weight": 223,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7284,7 +7285,7 @@
},
"x-appwrite": {
"method": "getMembership",
"weight": 225,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7358,7 +7359,7 @@
},
"x-appwrite": {
"method": "updateMembership",
"weight": 226,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7447,7 +7448,7 @@
},
"x-appwrite": {
"method": "deleteMembership",
"weight": 228,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7523,7 +7524,7 @@
},
"x-appwrite": {
"method": "updateMembershipStatus",
"weight": 227,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7623,7 +7624,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 219,
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7686,7 +7687,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 221,
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
@@ -9405,7 +9406,7 @@
"responseBody": {
"type": "string",
"description": "HTTP response body. This will return empty unless execution is created as synchronous.",
"x-example": "Developers are awesome."
"x-example": ""
},
"responseHeaders": {
"type": "array",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+36 -35
View File
@@ -166,7 +166,7 @@
"tags": [
"account"
],
"description": "Update currently logged in user account email address. After changing user address, the user confirmation status will get reset. A new confirmation email is not sent automatically however you can use the send confirmation email endpoint again to send the confirmation email. For security measures, user password is required to complete this request.\nThis endpoint can also be used to convert an anonymous account to a normal one, by passing an email address and a new password.\n",
"description": "Update currently logged in user account email address. After changing user address, the user confirmation status will get reset. A new confirmation email is not sent automatically however you can use the send confirmation email endpoint again to send the confirmation email. For security measures, user password is required to complete this request.\r\nThis endpoint can also be used to convert an anonymous account to a normal one, by passing an email address and a new password.\r\n",
"responses": {
"200": {
"description": "User",
@@ -239,7 +239,7 @@
},
"\/account\/identities": {
"get": {
"summary": "List Identities",
"summary": "List identities",
"operationId": "accountListIdentities",
"tags": [
"account"
@@ -556,7 +556,7 @@
},
"\/account\/mfa\/authenticators\/{type}": {
"post": {
"summary": "Create Authenticator",
"summary": "Create authenticator",
"operationId": "accountCreateMfaAuthenticator",
"tags": [
"account"
@@ -624,7 +624,7 @@
]
},
"put": {
"summary": "Verify Authenticator",
"summary": "Verify authenticator",
"operationId": "accountUpdateMfaAuthenticator",
"tags": [
"account"
@@ -711,7 +711,7 @@
}
},
"delete": {
"summary": "Delete Authenticator",
"summary": "Delete authenticator",
"operationId": "accountDeleteMfaAuthenticator",
"tags": [
"account"
@@ -774,7 +774,7 @@
},
"\/account\/mfa\/challenge": {
"post": {
"summary": "Create MFA Challenge",
"summary": "Create MFA challenge",
"operationId": "accountCreateMfaChallenge",
"tags": [
"account"
@@ -850,7 +850,7 @@
}
},
"put": {
"summary": "Create MFA Challenge (confirmation)",
"summary": "Create MFA challenge (confirmation)",
"operationId": "accountUpdateMfaChallenge",
"tags": [
"account"
@@ -928,7 +928,7 @@
},
"\/account\/mfa\/factors": {
"get": {
"summary": "List Factors",
"summary": "List factors",
"operationId": "accountListMfaFactors",
"tags": [
"account"
@@ -981,7 +981,7 @@
},
"\/account\/mfa\/recovery-codes": {
"get": {
"summary": "Get MFA Recovery Codes",
"summary": "Get MFA recovery codes",
"operationId": "accountGetMfaRecoveryCodes",
"tags": [
"account"
@@ -1032,7 +1032,7 @@
]
},
"post": {
"summary": "Create MFA Recovery Codes",
"summary": "Create MFA recovery codes",
"operationId": "accountCreateMfaRecoveryCodes",
"tags": [
"account"
@@ -1083,7 +1083,7 @@
]
},
"patch": {
"summary": "Regenerate MFA Recovery Codes",
"summary": "Regenerate MFA recovery codes",
"operationId": "accountUpdateMfaRecoveryCodes",
"tags": [
"account"
@@ -1570,7 +1570,7 @@
"tags": [
"account"
],
"description": "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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createRecovery) endpoint.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.",
"description": "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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createRecovery) endpoint.\r\n\r\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.",
"responses": {
"200": {
"description": "Token",
@@ -1802,7 +1802,7 @@
"tags": [
"account"
],
"description": "Allow the user to login into their account by providing a valid email and password combination. This route will create a new session for the user.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Allow the user to login into their account by providing a valid email and password combination. This route will create a new session for the user.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"201": {
"description": "Session",
@@ -1954,7 +1954,7 @@
"tags": [
"account"
],
"description": "Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed.\n\nIf there is already an active session, the new session will be attached to the logged-in account. If there are no active sessions, the server will attempt to look for a user with the same email address as the email received from the OAuth2 provider and attach the new session to the existing user. If no matching user is found - the server will create a new user.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).\n",
"description": "Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed.\r\n\r\nIf there is already an active session, the new session will be attached to the logged-in account. If there are no active sessions, the server will attempt to look for a user with the same email address as the email received from the OAuth2 provider and attach the new session to the existing user. If no matching user is found - the server will create a new user.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).\r\n",
"responses": {
"301": {
"description": "File"
@@ -2703,7 +2703,7 @@
"tags": [
"account"
],
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's email is valid for 15 minutes.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's email is valid for 15 minutes.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"201": {
"description": "Token",
@@ -2784,7 +2784,7 @@
"tags": [
"account"
],
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not been registered, a new user will be created. When the user clicks the link in the email, the user is redirected back to the URL you provided with the secret key and userId values attached to the URL query string. Use the query string parameters to submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The link sent to the user's email address is valid for 1 hour. If you are on a mobile device you can leave the URL parameter empty, so that the login completion will be handled by your Appwrite instance by default.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).\n",
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not been registered, a new user will be created. When the user clicks the link in the email, the user is redirected back to the URL you provided with the secret key and userId values attached to the URL query string. Use the query string parameters to submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The link sent to the user's email address is valid for 1 hour. If you are on a mobile device you can leave the URL parameter empty, so that the login completion will be handled by your Appwrite instance by default.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).\r\n",
"responses": {
"201": {
"description": "Token",
@@ -2873,7 +2873,7 @@
"tags": [
"account"
],
"description": "Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed. \n\nIf authentication succeeds, `userId` and `secret` of a token will be appended to the success URL as query parameters. These can be used to create a new session using the [Create session](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed. \r\n\r\nIf authentication succeeds, `userId` and `secret` of a token will be appended to the success URL as query parameters. These can be used to create a new session using the [Create session](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"301": {
"description": "File"
@@ -3009,7 +3009,7 @@
"tags": [
"account"
],
"description": "Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"201": {
"description": "Token",
@@ -3088,7 +3088,7 @@
"tags": [
"account"
],
"description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to 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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n",
"description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to 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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\r\n\r\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\r\n",
"responses": {
"201": {
"description": "Token",
@@ -3368,7 +3368,7 @@
"tags": [
"avatars"
],
"description": "You can use this endpoint to show different browser icons to your users. The code argument receives the browser code as it appears in your user [GET \/account\/sessions](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#getSessions) endpoint. Use width, height and quality arguments to change the output settings.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.",
"description": "You can use this endpoint to show different browser icons to your users. The code argument receives the browser code as it appears in your user [GET \/account\/sessions](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#getSessions) endpoint. Use width, height and quality arguments to change the output settings.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.",
"responses": {
"200": {
"description": "Image"
@@ -3496,7 +3496,7 @@
"tags": [
"avatars"
],
"description": "The credit card endpoint will return you the icon of the credit card provider you need. Use width, height and quality arguments to change the output settings.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\n",
"description": "The credit card endpoint will return you the icon of the credit card provider you need. Use width, height and quality arguments to change the output settings.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\r\n",
"responses": {
"200": {
"description": "Image"
@@ -3628,7 +3628,7 @@
"tags": [
"avatars"
],
"description": "Use this endpoint to fetch the favorite icon (AKA favicon) of any remote website URL.\n\nThis endpoint does not follow HTTP redirects.",
"description": "Use this endpoint to fetch the favorite icon (AKA favicon) of any remote website URL.\r\n\r\nThis endpoint does not follow HTTP redirects.",
"responses": {
"200": {
"description": "Image"
@@ -3688,7 +3688,7 @@
"tags": [
"avatars"
],
"description": "You can use this endpoint to show different country flags icons to your users. The code argument receives the 2 letter country code. Use width, height and quality arguments to change the output settings. Country codes follow the [ISO 3166-1](https:\/\/en.wikipedia.org\/wiki\/ISO_3166-1) standard.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\n",
"description": "You can use this endpoint to show different country flags icons to your users. The code argument receives the 2 letter country code. Use width, height and quality arguments to change the output settings. Country codes follow the [ISO 3166-1](https:\/\/en.wikipedia.org\/wiki\/ISO_3166-1) standard.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\r\n",
"responses": {
"200": {
"description": "Image"
@@ -4178,7 +4178,7 @@
"tags": [
"avatars"
],
"description": "Use this endpoint to fetch a remote image URL and crop it to any image size you want. This endpoint is very useful if you need to crop and display remote images in your app or in case you want to make sure a 3rd party image is properly served using a TLS protocol.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 400x400px.\n\nThis endpoint does not follow HTTP redirects.",
"description": "Use this endpoint to fetch a remote image URL and crop it to any image size you want. This endpoint is very useful if you need to crop and display remote images in your app or in case you want to make sure a 3rd party image is properly served using a TLS protocol.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 400x400px.\r\n\r\nThis endpoint does not follow HTTP redirects.",
"responses": {
"200": {
"description": "Image"
@@ -4262,7 +4262,7 @@
"tags": [
"avatars"
],
"description": "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.\n\nYou 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.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\n",
"description": "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.\r\n\r\nYou 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.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\r\n",
"responses": {
"200": {
"description": "Image"
@@ -4356,7 +4356,7 @@
"tags": [
"avatars"
],
"description": "Converts a given plain text to a QR code image. You can use the query parameters to change the size and style of the resulting image.\n",
"description": "Converts a given plain text to a QR code image. You can use the query parameters to change the size and style of the resulting image.\r\n",
"responses": {
"200": {
"description": "Image"
@@ -5319,7 +5319,7 @@
"tags": [
"locale"
],
"description": "Get the current user location based on IP. Returns an object with user country code, country name, continent name, continent code, ip address and suggested currency. You can use the locale header to get the data in a supported language.\n\n([IP Geolocation by DB-IP](https:\/\/db-ip.com))",
"description": "Get the current user location based on IP. Returns an object with user country code, country name, continent name, continent code, ip address and suggested currency. You can use the locale header to get the data in a supported language.\r\n\r\n([IP Geolocation by DB-IP](https:\/\/db-ip.com))",
"responses": {
"200": {
"description": "Locale",
@@ -5368,7 +5368,7 @@
},
"\/locale\/codes": {
"get": {
"summary": "List Locale Codes",
"summary": "List locale codes",
"operationId": "localeListCodes",
"tags": [
"locale"
@@ -6001,7 +6001,7 @@
"tags": [
"storage"
],
"description": "Create a new file. Before using this route, you should create a new bucket resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/storage#storageCreateBucket) API or directly from your Appwrite console.\n\nLarger files should be uploaded using multiple requests with the [content-range](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Range) header to send a partial request with a maximum supported chunk of `5MB`. The `content-range` header values should always be in bytes.\n\nWhen the first request is sent, the server will return the **File** object, and the subsequent part request must include the file's **id** in `x-appwrite-id` header to allow the server to know that the partial upload is for the existing file and not for a new one.\n\nIf you're creating a new file using one of the Appwrite SDKs, all the chunking logic will be managed by the SDK internally.\n",
"description": "Create a new file. Before using this route, you should create a new bucket resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/storage#storageCreateBucket) API or directly from your Appwrite console.\r\n\r\nLarger files should be uploaded using multiple requests with the [content-range](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Range) header to send a partial request with a maximum supported chunk of `5MB`. The `content-range` header values should always be in bytes.\r\n\r\nWhen the first request is sent, the server will return the **File** object, and the subsequent part request must include the file's **id** in `x-appwrite-id` header to allow the server to know that the partial upload is for the existing file and not for a new one.\r\n\r\nIf you're creating a new file using one of the Appwrite SDKs, all the chunking logic will be managed by the SDK internally.\r\n",
"responses": {
"201": {
"description": "File",
@@ -6268,7 +6268,7 @@
}
},
"delete": {
"summary": "Delete File",
"summary": "Delete file",
"operationId": "storageDeleteFile",
"tags": [
"storage"
@@ -6610,7 +6610,8 @@
"jpeg",
"gif",
"png",
"webp"
"webp",
"avif"
],
"x-enum-name": "ImageFormat",
"x-enum-keys": [],
@@ -7156,7 +7157,7 @@
"tags": [
"teams"
],
"description": "Invite a new member to join your team. Provide an ID for existing users, or invite unregistered users using an email or phone number. If initiated from a Client SDK, Appwrite will send an email or sms with a link to join the team to the invited user, and an account will be created for them if one doesn't exist. If initiated from a Server SDK, the new member will be added automatically to the team.\n\nYou only need to provide one of a user ID, email, or phone number. Appwrite will prioritize accepting the user ID > email > phone number if you provide more than one of these parameters.\n\nUse the `url` parameter to redirect the user from the invitation email to your app. After the user is redirected, use the [Update Team Membership Status](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/teams#updateMembershipStatus) endpoint to allow the user to accept the invitation to the team. \n\nPlease note that to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) Appwrite will accept the only redirect URLs under the domains you have added as a platform on the Appwrite Console.\n",
"description": "Invite a new member to join your team. Provide an ID for existing users, or invite unregistered users using an email or phone number. If initiated from a Client SDK, Appwrite will send an email or sms with a link to join the team to the invited user, and an account will be created for them if one doesn't exist. If initiated from a Server SDK, the new member will be added automatically to the team.\r\n\r\nYou only need to provide one of a user ID, email, or phone number. Appwrite will prioritize accepting the user ID > email > phone number if you provide more than one of these parameters.\r\n\r\nUse the `url` parameter to redirect the user from the invitation email to your app. After the user is redirected, use the [Update Team Membership Status](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/teams#updateMembershipStatus) endpoint to allow the user to accept the invitation to the team. \r\n\r\nPlease note that to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) Appwrite will accept the only redirect URLs under the domains you have added as a platform on the Appwrite Console.\r\n",
"responses": {
"201": {
"description": "Membership",
@@ -7343,7 +7344,7 @@
"tags": [
"teams"
],
"description": "Modify the roles of a team member. Only team members with the owner role have access to this endpoint. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions).\n",
"description": "Modify the roles of a team member. Only team members with the owner role have access to this endpoint. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions).\r\n",
"responses": {
"200": {
"description": "Membership",
@@ -7508,7 +7509,7 @@
"tags": [
"teams"
],
"description": "Use this endpoint to allow a user to accept an invitation to join a team after being redirected back to your app from the invitation email received by the user.\n\nIf the request is successful, a session for the user is automatically created.\n",
"description": "Use this endpoint to allow a user to accept an invitation to join a team after being redirected back to your app from the invitation email received by the user.\r\n\r\nIf the request is successful, a session for the user is automatically created.\r\n",
"responses": {
"200": {
"description": "Membership",
@@ -9405,7 +9406,7 @@
"responseBody": {
"type": "string",
"description": "HTTP response body. This will return empty unless execution is created as synchronous.",
"x-example": "Developers are awesome."
"x-example": ""
},
"responseHeaders": {
"type": "array",
File diff suppressed because it is too large Load Diff
+215 -82
View File
@@ -167,7 +167,7 @@
"tags": [
"account"
],
"description": "Update currently logged in user account email address. After changing user address, the user confirmation status will get reset. A new confirmation email is not sent automatically however you can use the send confirmation email endpoint again to send the confirmation email. For security measures, user password is required to complete this request.\nThis endpoint can also be used to convert an anonymous account to a normal one, by passing an email address and a new password.\n",
"description": "Update currently logged in user account email address. After changing user address, the user confirmation status will get reset. A new confirmation email is not sent automatically however you can use the send confirmation email endpoint again to send the confirmation email. For security measures, user password is required to complete this request.\r\nThis endpoint can also be used to convert an anonymous account to a normal one, by passing an email address and a new password.\r\n",
"responses": {
"200": {
"description": "User",
@@ -241,7 +241,7 @@
},
"\/account\/identities": {
"get": {
"summary": "List Identities",
"summary": "List identities",
"operationId": "accountListIdentities",
"tags": [
"account"
@@ -562,7 +562,7 @@
},
"\/account\/mfa\/authenticators\/{type}": {
"post": {
"summary": "Create Authenticator",
"summary": "Create authenticator",
"operationId": "accountCreateMfaAuthenticator",
"tags": [
"account"
@@ -631,7 +631,7 @@
]
},
"put": {
"summary": "Verify Authenticator",
"summary": "Verify authenticator",
"operationId": "accountUpdateMfaAuthenticator",
"tags": [
"account"
@@ -719,7 +719,7 @@
}
},
"delete": {
"summary": "Delete Authenticator",
"summary": "Delete authenticator",
"operationId": "accountDeleteMfaAuthenticator",
"tags": [
"account"
@@ -783,7 +783,7 @@
},
"\/account\/mfa\/challenge": {
"post": {
"summary": "Create MFA Challenge",
"summary": "Create MFA challenge",
"operationId": "accountCreateMfaChallenge",
"tags": [
"account"
@@ -859,7 +859,7 @@
}
},
"put": {
"summary": "Create MFA Challenge (confirmation)",
"summary": "Create MFA challenge (confirmation)",
"operationId": "accountUpdateMfaChallenge",
"tags": [
"account"
@@ -938,7 +938,7 @@
},
"\/account\/mfa\/factors": {
"get": {
"summary": "List Factors",
"summary": "List factors",
"operationId": "accountListMfaFactors",
"tags": [
"account"
@@ -992,7 +992,7 @@
},
"\/account\/mfa\/recovery-codes": {
"get": {
"summary": "Get MFA Recovery Codes",
"summary": "Get MFA recovery codes",
"operationId": "accountGetMfaRecoveryCodes",
"tags": [
"account"
@@ -1044,7 +1044,7 @@
]
},
"post": {
"summary": "Create MFA Recovery Codes",
"summary": "Create MFA recovery codes",
"operationId": "accountCreateMfaRecoveryCodes",
"tags": [
"account"
@@ -1096,7 +1096,7 @@
]
},
"patch": {
"summary": "Regenerate MFA Recovery Codes",
"summary": "Regenerate MFA recovery codes",
"operationId": "accountUpdateMfaRecoveryCodes",
"tags": [
"account"
@@ -1590,7 +1590,7 @@
"tags": [
"account"
],
"description": "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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createRecovery) endpoint.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.",
"description": "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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createRecovery) endpoint.\r\n\r\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.",
"responses": {
"200": {
"description": "Token",
@@ -1825,7 +1825,7 @@
"tags": [
"account"
],
"description": "Allow the user to login into their account by providing a valid email and password combination. This route will create a new session for the user.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Allow the user to login into their account by providing a valid email and password combination. This route will create a new session for the user.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"201": {
"description": "Session",
@@ -2370,7 +2370,7 @@
"tags": [
"account"
],
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's email is valid for 15 minutes.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's email is valid for 15 minutes.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"201": {
"description": "Token",
@@ -2451,7 +2451,7 @@
"tags": [
"account"
],
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not been registered, a new user will be created. When the user clicks the link in the email, the user is redirected back to the URL you provided with the secret key and userId values attached to the URL query string. Use the query string parameters to submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The link sent to the user's email address is valid for 1 hour. If you are on a mobile device you can leave the URL parameter empty, so that the login completion will be handled by your Appwrite instance by default.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).\n",
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not been registered, a new user will be created. When the user clicks the link in the email, the user is redirected back to the URL you provided with the secret key and userId values attached to the URL query string. Use the query string parameters to submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The link sent to the user's email address is valid for 1 hour. If you are on a mobile device you can leave the URL parameter empty, so that the login completion will be handled by your Appwrite instance by default.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).\r\n",
"responses": {
"201": {
"description": "Token",
@@ -2540,7 +2540,7 @@
"tags": [
"account"
],
"description": "Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed. \n\nIf authentication succeeds, `userId` and `secret` of a token will be appended to the success URL as query parameters. These can be used to create a new session using the [Create session](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed. \r\n\r\nIf authentication succeeds, `userId` and `secret` of a token will be appended to the success URL as query parameters. These can be used to create a new session using the [Create session](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"301": {
"description": "File"
@@ -2676,7 +2676,7 @@
"tags": [
"account"
],
"description": "Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"201": {
"description": "Token",
@@ -2755,7 +2755,7 @@
"tags": [
"account"
],
"description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to 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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n",
"description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to 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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\r\n\r\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\r\n",
"responses": {
"201": {
"description": "Token",
@@ -3039,7 +3039,7 @@
"tags": [
"avatars"
],
"description": "You can use this endpoint to show different browser icons to your users. The code argument receives the browser code as it appears in your user [GET \/account\/sessions](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#getSessions) endpoint. Use width, height and quality arguments to change the output settings.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.",
"description": "You can use this endpoint to show different browser icons to your users. The code argument receives the browser code as it appears in your user [GET \/account\/sessions](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#getSessions) endpoint. Use width, height and quality arguments to change the output settings.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.",
"responses": {
"200": {
"description": "Image"
@@ -3169,7 +3169,7 @@
"tags": [
"avatars"
],
"description": "The credit card endpoint will return you the icon of the credit card provider you need. Use width, height and quality arguments to change the output settings.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\n",
"description": "The credit card endpoint will return you the icon of the credit card provider you need. Use width, height and quality arguments to change the output settings.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\r\n",
"responses": {
"200": {
"description": "Image"
@@ -3303,7 +3303,7 @@
"tags": [
"avatars"
],
"description": "Use this endpoint to fetch the favorite icon (AKA favicon) of any remote website URL.\n\nThis endpoint does not follow HTTP redirects.",
"description": "Use this endpoint to fetch the favorite icon (AKA favicon) of any remote website URL.\r\n\r\nThis endpoint does not follow HTTP redirects.",
"responses": {
"200": {
"description": "Image"
@@ -3365,7 +3365,7 @@
"tags": [
"avatars"
],
"description": "You can use this endpoint to show different country flags icons to your users. The code argument receives the 2 letter country code. Use width, height and quality arguments to change the output settings. Country codes follow the [ISO 3166-1](https:\/\/en.wikipedia.org\/wiki\/ISO_3166-1) standard.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\n",
"description": "You can use this endpoint to show different country flags icons to your users. The code argument receives the 2 letter country code. Use width, height and quality arguments to change the output settings. Country codes follow the [ISO 3166-1](https:\/\/en.wikipedia.org\/wiki\/ISO_3166-1) standard.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\r\n",
"responses": {
"200": {
"description": "Image"
@@ -3857,7 +3857,7 @@
"tags": [
"avatars"
],
"description": "Use this endpoint to fetch a remote image URL and crop it to any image size you want. This endpoint is very useful if you need to crop and display remote images in your app or in case you want to make sure a 3rd party image is properly served using a TLS protocol.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 400x400px.\n\nThis endpoint does not follow HTTP redirects.",
"description": "Use this endpoint to fetch a remote image URL and crop it to any image size you want. This endpoint is very useful if you need to crop and display remote images in your app or in case you want to make sure a 3rd party image is properly served using a TLS protocol.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 400x400px.\r\n\r\nThis endpoint does not follow HTTP redirects.",
"responses": {
"200": {
"description": "Image"
@@ -3943,7 +3943,7 @@
"tags": [
"avatars"
],
"description": "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.\n\nYou 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.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\n",
"description": "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.\r\n\r\nYou 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.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\r\n",
"responses": {
"200": {
"description": "Image"
@@ -4039,7 +4039,7 @@
"tags": [
"avatars"
],
"description": "Converts a given plain text to a QR code image. You can use the query parameters to change the size and style of the resulting image.\n",
"description": "Converts a given plain text to a QR code image. You can use the query parameters to change the size and style of the resulting image.\r\n",
"responses": {
"200": {
"description": "Image"
@@ -4211,7 +4211,7 @@
"tags": [
"databases"
],
"description": "Create a new Database.\n",
"description": "Create a new Database.\r\n",
"responses": {
"201": {
"description": "Database",
@@ -5026,7 +5026,7 @@
"tags": [
"databases"
],
"description": "Create a boolean attribute.\n",
"description": "Create a boolean attribute.\r\n",
"responses": {
"202": {
"description": "AttributeBoolean",
@@ -5472,7 +5472,7 @@
"tags": [
"databases"
],
"description": "Create an email attribute.\n",
"description": "Create an email attribute.\r\n",
"responses": {
"202": {
"description": "AttributeEmail",
@@ -5581,7 +5581,7 @@
"tags": [
"databases"
],
"description": "Update an email attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update an email attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeEmail",
@@ -5695,7 +5695,7 @@
"tags": [
"databases"
],
"description": "Create an enumeration attribute. The `elements` param acts as a white-list of accepted values for this attribute. \n",
"description": "Create an enumeration attribute. The `elements` param acts as a white-list of accepted values for this attribute. \r\n",
"responses": {
"202": {
"description": "AttributeEnum",
@@ -5813,7 +5813,7 @@
"tags": [
"databases"
],
"description": "Update an enum attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update an enum attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeEnum",
@@ -5936,7 +5936,7 @@
"tags": [
"databases"
],
"description": "Create a float attribute. Optionally, minimum and maximum values can be provided.\n",
"description": "Create a float attribute. Optionally, minimum and maximum values can be provided.\r\n",
"responses": {
"202": {
"description": "AttributeFloat",
@@ -6055,7 +6055,7 @@
"tags": [
"databases"
],
"description": "Update a float attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update a float attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeFloat",
@@ -6181,7 +6181,7 @@
"tags": [
"databases"
],
"description": "Create an integer attribute. Optionally, minimum and maximum values can be provided.\n",
"description": "Create an integer attribute. Optionally, minimum and maximum values can be provided.\r\n",
"responses": {
"202": {
"description": "AttributeInteger",
@@ -6300,7 +6300,7 @@
"tags": [
"databases"
],
"description": "Update an integer attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update an integer attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeInteger",
@@ -6426,7 +6426,7 @@
"tags": [
"databases"
],
"description": "Create IP address attribute.\n",
"description": "Create IP address attribute.\r\n",
"responses": {
"202": {
"description": "AttributeIP",
@@ -6535,7 +6535,7 @@
"tags": [
"databases"
],
"description": "Update an ip attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update an ip attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeIP",
@@ -6649,7 +6649,7 @@
"tags": [
"databases"
],
"description": "Create relationship attribute. [Learn more about relationship attributes](https:\/\/appwrite.io\/docs\/databases-relationships#relationship-attributes).\n",
"description": "Create relationship attribute. [Learn more about relationship attributes](https:\/\/appwrite.io\/docs\/databases-relationships#relationship-attributes).\r\n",
"responses": {
"202": {
"description": "AttributeRelationship",
@@ -6783,7 +6783,7 @@
"tags": [
"databases"
],
"description": "Create a string attribute.\n",
"description": "Create a string attribute.\r\n",
"responses": {
"202": {
"description": "AttributeString",
@@ -6903,7 +6903,7 @@
"tags": [
"databases"
],
"description": "Update a string attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update a string attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeString",
@@ -7022,7 +7022,7 @@
"tags": [
"databases"
],
"description": "Create a URL attribute.\n",
"description": "Create a URL attribute.\r\n",
"responses": {
"202": {
"description": "AttributeURL",
@@ -7131,7 +7131,7 @@
"tags": [
"databases"
],
"description": "Update an url attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update an url attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeURL",
@@ -7433,7 +7433,7 @@
"tags": [
"databases"
],
"description": "Update relationship attribute. [Learn more about relationship attributes](https:\/\/appwrite.io\/docs\/databases-relationships#relationship-attributes).\n",
"description": "Update relationship attribute. [Learn more about relationship attributes](https:\/\/appwrite.io\/docs\/databases-relationships#relationship-attributes).\r\n",
"responses": {
"200": {
"description": "AttributeRelationship",
@@ -8119,7 +8119,7 @@
"tags": [
"databases"
],
"description": "Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request.\nAttributes can be `key`, `fulltext`, and `unique`.",
"description": "Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request.\r\nAttributes can be `key`, `fulltext`, and `unique`.",
"responses": {
"202": {
"description": "Index",
@@ -8767,7 +8767,7 @@
"tags": [
"functions"
],
"description": "List allowed function specifications for this instance.\n",
"description": "List allowed function specifications for this instance.\r\n",
"responses": {
"200": {
"description": "Specifications List",
@@ -9249,7 +9249,7 @@
"tags": [
"functions"
],
"description": "Create a new function code deployment. Use this endpoint to upload a new version of your code function. To execute your newly uploaded code, you'll need to update the function's deployment to use your new deployment UID.\n\nThis endpoint accepts a tar.gz file compressed with your code. Make sure to include any dependencies your code has within the compressed file. You can learn more about code packaging in the [Appwrite Cloud Functions tutorial](https:\/\/appwrite.io\/docs\/functions).\n\nUse the \"command\" param to set the entrypoint used to execute your code.",
"description": "Create a new function code deployment. Use this endpoint to upload a new version of your code function. To execute your newly uploaded code, you'll need to update the function's deployment to use your new deployment UID.\r\n\r\nThis endpoint accepts a tar.gz file compressed with your code. Make sure to include any dependencies your code has within the compressed file. You can learn more about code packaging in the [Appwrite Cloud Functions tutorial](https:\/\/appwrite.io\/docs\/functions).\r\n\r\nUse the \"command\" param to set the entrypoint used to execute your code.",
"responses": {
"202": {
"description": "Deployment",
@@ -10068,7 +10068,7 @@
"tags": [
"functions"
],
"description": "Delete a function execution by its unique ID.\n",
"description": "Delete a function execution by its unique ID.\r\n",
"responses": {
"204": {
"description": "No content"
@@ -11279,7 +11279,7 @@
"tags": [
"health"
],
"description": "Returns the amount of failed jobs in a given queue.\n",
"description": "Returns the amount of failed jobs in a given queue.\r\n",
"responses": {
"200": {
"description": "Health Queue",
@@ -12046,7 +12046,7 @@
"tags": [
"locale"
],
"description": "Get the current user location based on IP. Returns an object with user country code, country name, continent name, continent code, ip address and suggested currency. You can use the locale header to get the data in a supported language.\n\n([IP Geolocation by DB-IP](https:\/\/db-ip.com))",
"description": "Get the current user location based on IP. Returns an object with user country code, country name, continent name, continent code, ip address and suggested currency. You can use the locale header to get the data in a supported language.\r\n\r\n([IP Geolocation by DB-IP](https:\/\/db-ip.com))",
"responses": {
"200": {
"description": "Locale",
@@ -12097,7 +12097,7 @@
},
"\/locale\/codes": {
"get": {
"summary": "List Locale Codes",
"summary": "List locale codes",
"operationId": "localeListCodes",
"tags": [
"locale"
@@ -12720,7 +12720,7 @@
"tags": [
"messaging"
],
"description": "Update an email message by its unique ID.\n",
"description": "Update an email message by its unique ID.\r\n",
"responses": {
"200": {
"description": "Message",
@@ -13027,7 +13027,7 @@
"tags": [
"messaging"
],
"description": "Update a push notification by its unique ID.\n",
"description": "Update a push notification by its unique ID.\r\n",
"responses": {
"200": {
"description": "Message",
@@ -13299,7 +13299,7 @@
"tags": [
"messaging"
],
"description": "Update an email message by its unique ID.\n",
"description": "Update an email message by its unique ID.\r\n",
"responses": {
"200": {
"description": "Message",
@@ -13414,7 +13414,7 @@
"tags": [
"messaging"
],
"description": "Get a message by its unique ID.\n",
"description": "Get a message by its unique ID.\r\n",
"responses": {
"200": {
"description": "Message",
@@ -15915,7 +15915,7 @@
"tags": [
"messaging"
],
"description": "Get a provider by its unique ID.\n",
"description": "Get a provider by its unique ID.\r\n",
"responses": {
"200": {
"description": "Provider",
@@ -16355,7 +16355,7 @@
"tags": [
"messaging"
],
"description": "Get a topic by its unique ID.\n",
"description": "Get a topic by its unique ID.\r\n",
"responses": {
"200": {
"description": "Topic",
@@ -16418,7 +16418,7 @@
"tags": [
"messaging"
],
"description": "Update a topic by its unique ID.\n",
"description": "Update a topic by its unique ID.\r\n",
"responses": {
"200": {
"description": "Topic",
@@ -16822,7 +16822,7 @@
"tags": [
"messaging"
],
"description": "Get a subscriber by its unique ID.\n",
"description": "Get a subscriber by its unique ID.\r\n",
"responses": {
"200": {
"description": "Subscriber",
@@ -17516,7 +17516,7 @@
"tags": [
"storage"
],
"description": "Create a new file. Before using this route, you should create a new bucket resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/storage#storageCreateBucket) API or directly from your Appwrite console.\n\nLarger files should be uploaded using multiple requests with the [content-range](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Range) header to send a partial request with a maximum supported chunk of `5MB`. The `content-range` header values should always be in bytes.\n\nWhen the first request is sent, the server will return the **File** object, and the subsequent part request must include the file's **id** in `x-appwrite-id` header to allow the server to know that the partial upload is for the existing file and not for a new one.\n\nIf you're creating a new file using one of the Appwrite SDKs, all the chunking logic will be managed by the SDK internally.\n",
"description": "Create a new file. Before using this route, you should create a new bucket resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/storage#storageCreateBucket) API or directly from your Appwrite console.\r\n\r\nLarger files should be uploaded using multiple requests with the [content-range](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Range) header to send a partial request with a maximum supported chunk of `5MB`. The `content-range` header values should always be in bytes.\r\n\r\nWhen the first request is sent, the server will return the **File** object, and the subsequent part request must include the file's **id** in `x-appwrite-id` header to allow the server to know that the partial upload is for the existing file and not for a new one.\r\n\r\nIf you're creating a new file using one of the Appwrite SDKs, all the chunking logic will be managed by the SDK internally.\r\n",
"responses": {
"201": {
"description": "File",
@@ -17789,7 +17789,7 @@
}
},
"delete": {
"summary": "Delete File",
"summary": "Delete file",
"operationId": "storageDeleteFile",
"tags": [
"storage"
@@ -18137,7 +18137,8 @@
"jpeg",
"gif",
"png",
"webp"
"webp",
"avif"
],
"x-enum-name": "ImageFormat",
"x-enum-keys": [],
@@ -18697,7 +18698,7 @@
"tags": [
"teams"
],
"description": "Invite a new member to join your team. Provide an ID for existing users, or invite unregistered users using an email or phone number. If initiated from a Client SDK, Appwrite will send an email or sms with a link to join the team to the invited user, and an account will be created for them if one doesn't exist. If initiated from a Server SDK, the new member will be added automatically to the team.\n\nYou only need to provide one of a user ID, email, or phone number. Appwrite will prioritize accepting the user ID > email > phone number if you provide more than one of these parameters.\n\nUse the `url` parameter to redirect the user from the invitation email to your app. After the user is redirected, use the [Update Team Membership Status](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/teams#updateMembershipStatus) endpoint to allow the user to accept the invitation to the team. \n\nPlease note that to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) Appwrite will accept the only redirect URLs under the domains you have added as a platform on the Appwrite Console.\n",
"description": "Invite a new member to join your team. Provide an ID for existing users, or invite unregistered users using an email or phone number. If initiated from a Client SDK, Appwrite will send an email or sms with a link to join the team to the invited user, and an account will be created for them if one doesn't exist. If initiated from a Server SDK, the new member will be added automatically to the team.\r\n\r\nYou only need to provide one of a user ID, email, or phone number. Appwrite will prioritize accepting the user ID > email > phone number if you provide more than one of these parameters.\r\n\r\nUse the `url` parameter to redirect the user from the invitation email to your app. After the user is redirected, use the [Update Team Membership Status](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/teams#updateMembershipStatus) endpoint to allow the user to accept the invitation to the team. \r\n\r\nPlease note that to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) Appwrite will accept the only redirect URLs under the domains you have added as a platform on the Appwrite Console.\r\n",
"responses": {
"201": {
"description": "Membership",
@@ -18888,7 +18889,7 @@
"tags": [
"teams"
],
"description": "Modify the roles of a team member. Only team members with the owner role have access to this endpoint. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions).\n",
"description": "Modify the roles of a team member. Only team members with the owner role have access to this endpoint. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions).\r\n",
"responses": {
"200": {
"description": "Membership",
@@ -19057,7 +19058,7 @@
"tags": [
"teams"
],
"description": "Use this endpoint to allow a user to accept an invitation to join a team after being redirected back to your app from the invitation email received by the user.\n\nIf the request is successful, a session for the user is automatically created.\n",
"description": "Use this endpoint to allow a user to accept an invitation to join a team after being redirected back to your app from the invitation email received by the user.\r\n\r\nIf the request is successful, a session for the user is automatically created.\r\n",
"responses": {
"200": {
"description": "Membership",
@@ -19645,7 +19646,7 @@
},
"\/users\/identities": {
"get": {
"summary": "List Identities",
"summary": "List identities",
"operationId": "usersListIdentities",
"tags": [
"users"
@@ -20580,7 +20581,7 @@
"tags": [
"users"
],
"description": "Update the user labels by its unique ID. \n\nLabels can be used to grant access to resources. While teams are a way for user's to share access to a resource, labels can be defined by the developer to grant access without an invitation. See the [Permissions docs](https:\/\/appwrite.io\/docs\/permissions) for more info.",
"description": "Update the user labels by its unique ID. \r\n\r\nLabels can be used to grant access to resources. While teams are a way for user's to share access to a resource, labels can be defined by the developer to grant access without an invitation. See the [Permissions docs](https:\/\/appwrite.io\/docs\/permissions) for more info.",
"responses": {
"200": {
"description": "User",
@@ -20885,7 +20886,7 @@
},
"\/users\/{userId}\/mfa\/authenticators\/{type}": {
"delete": {
"summary": "Delete Authenticator",
"summary": "Delete authenticator",
"operationId": "usersDeleteMfaAuthenticator",
"tags": [
"users"
@@ -20964,7 +20965,7 @@
},
"\/users\/{userId}\/mfa\/factors": {
"get": {
"summary": "List Factors",
"summary": "List factors",
"operationId": "usersListMfaFactors",
"tags": [
"users"
@@ -21028,7 +21029,7 @@
},
"\/users\/{userId}\/mfa\/recovery-codes": {
"get": {
"summary": "Get MFA Recovery Codes",
"summary": "Get MFA recovery codes",
"operationId": "usersGetMfaRecoveryCodes",
"tags": [
"users"
@@ -21090,7 +21091,7 @@
]
},
"put": {
"summary": "Regenerate MFA Recovery Codes",
"summary": "Regenerate MFA recovery codes",
"operationId": "usersUpdateMfaRecoveryCodes",
"tags": [
"users"
@@ -21152,7 +21153,7 @@
]
},
"patch": {
"summary": "Create MFA Recovery Codes",
"summary": "Create MFA recovery codes",
"operationId": "usersCreateMfaRecoveryCodes",
"tags": [
"users"
@@ -21677,7 +21678,7 @@
"tags": [
"users"
],
"description": "Creates a session for a user. Returns an immediately usable session object.\n\nIf you want to generate a token for a custom authentication flow, use the [POST \/users\/{userId}\/tokens](https:\/\/appwrite.io\/docs\/server\/users#createToken) endpoint.",
"description": "Creates a session for a user. Returns an immediately usable session object.\r\n\r\nIf you want to generate a token for a custom authentication flow, use the [POST \/users\/{userId}\/tokens](https:\/\/appwrite.io\/docs\/server\/users#createToken) endpoint.",
"responses": {
"201": {
"description": "Session",
@@ -21941,7 +21942,7 @@
},
"\/users\/{userId}\/targets": {
"get": {
"summary": "List User Targets",
"summary": "List user targets",
"operationId": "usersListTargets",
"tags": [
"users"
@@ -22017,7 +22018,7 @@
]
},
"post": {
"summary": "Create User Target",
"summary": "Create user target",
"operationId": "usersCreateTarget",
"tags": [
"users"
@@ -22130,7 +22131,7 @@
},
"\/users\/{userId}\/targets\/{targetId}": {
"get": {
"summary": "Get User Target",
"summary": "Get user target",
"operationId": "usersGetTarget",
"tags": [
"users"
@@ -22203,7 +22204,7 @@
]
},
"patch": {
"summary": "Update User target",
"summary": "Update user target",
"operationId": "usersUpdateTarget",
"tags": [
"users"
@@ -22375,7 +22376,7 @@
"tags": [
"users"
],
"description": "Returns a token with a secret key for creating a session. Use the user ID and secret and submit a request to the [PUT \/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process.\n",
"description": "Returns a token with a secret key for creating a session. Use the user ID and secret and submit a request to the [PUT \/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process.\r\n",
"responses": {
"201": {
"description": "Token",
@@ -23677,6 +23678,16 @@
"x-example": false,
"nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"size": {
"type": "integer",
"description": "Attribute size.",
@@ -23696,6 +23707,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"size"
]
},
@@ -23734,6 +23747,16 @@
"x-example": false,
"nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"min": {
"type": "integer",
"description": "Minimum value to enforce for new documents.",
@@ -23761,7 +23784,9 @@
"type",
"status",
"error",
"required"
"required",
"$createdAt",
"$updatedAt"
]
},
"attributeFloat": {
@@ -23799,6 +23824,16 @@
"x-example": false,
"nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"min": {
"type": "number",
"description": "Minimum value to enforce for new documents.",
@@ -23826,7 +23861,9 @@
"type",
"status",
"error",
"required"
"required",
"$createdAt",
"$updatedAt"
]
},
"attributeBoolean": {
@@ -23864,6 +23901,16 @@
"x-example": false,
"nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"default": {
"type": "boolean",
"description": "Default value for attribute when not provided. Cannot be set when attribute is required.",
@@ -23876,7 +23923,9 @@
"type",
"status",
"error",
"required"
"required",
"$createdAt",
"$updatedAt"
]
},
"attributeEmail": {
@@ -23914,6 +23963,16 @@
"x-example": false,
"nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"format": {
"type": "string",
"description": "String format.",
@@ -23932,6 +23991,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"format"
]
},
@@ -23970,6 +24031,16 @@
"x-example": false,
"nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"elements": {
"type": "array",
"description": "Array of elements in enumerated type.",
@@ -23996,6 +24067,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"elements",
"format"
]
@@ -24035,6 +24108,16 @@
"x-example": false,
"nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"format": {
"type": "string",
"description": "String format.",
@@ -24053,6 +24136,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"format"
]
},
@@ -24091,6 +24176,16 @@
"x-example": false,
"nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"format": {
"type": "string",
"description": "String format.",
@@ -24109,6 +24204,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"format"
]
},
@@ -24147,6 +24244,16 @@
"x-example": false,
"nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"format": {
"type": "string",
"description": "ISO 8601 format.",
@@ -24165,6 +24272,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"format"
]
},
@@ -24203,6 +24312,16 @@
"x-example": false,
"nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"relatedCollection": {
"type": "string",
"description": "The ID of the related collection.",
@@ -24240,6 +24359,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"relatedCollection",
"relationType",
"twoWay",
@@ -24288,6 +24409,16 @@
},
"x-example": [],
"nullable": true
},
"$createdAt": {
"type": "string",
"description": "Index creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Index update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
}
},
"required": [
@@ -24295,7 +24426,9 @@
"type",
"status",
"error",
"attributes"
"attributes",
"$createdAt",
"$updatedAt"
]
},
"document": {
@@ -25966,7 +26099,7 @@
"responseBody": {
"type": "string",
"description": "HTTP response body. This will return empty unless execution is created as synchronous.",
"x-example": "Developers are awesome."
"x-example": ""
},
"responseHeaders": {
"type": "array",
+164 -169
View File
@@ -87,7 +87,7 @@
},
"x-appwrite": {
"method": "get",
"weight": 8,
"weight": 9,
"cookies": false,
"type": "",
"deprecated": false,
@@ -140,7 +140,7 @@
},
"x-appwrite": {
"method": "create",
"weight": 7,
"weight": 8,
"cookies": false,
"type": "",
"deprecated": false,
@@ -233,7 +233,7 @@
},
"x-appwrite": {
"method": "updateEmail",
"weight": 33,
"weight": 34,
"cookies": false,
"type": "",
"deprecated": false,
@@ -293,7 +293,7 @@
},
"\/account\/identities": {
"get": {
"summary": "List Identities",
"summary": "List identities",
"operationId": "accountListIdentities",
"consumes": [
"application\/json"
@@ -315,7 +315,7 @@
},
"x-appwrite": {
"method": "listIdentities",
"weight": 56,
"weight": 57,
"cookies": false,
"type": "",
"deprecated": false,
@@ -379,7 +379,7 @@
},
"x-appwrite": {
"method": "deleteIdentity",
"weight": 57,
"weight": 58,
"cookies": false,
"type": "",
"deprecated": false,
@@ -444,7 +444,7 @@
},
"x-appwrite": {
"method": "createJWT",
"weight": 28,
"weight": 29,
"cookies": false,
"type": "",
"deprecated": false,
@@ -497,7 +497,7 @@
},
"x-appwrite": {
"method": "listLogs",
"weight": 30,
"weight": 31,
"cookies": false,
"type": "",
"deprecated": false,
@@ -566,7 +566,7 @@
},
"x-appwrite": {
"method": "updateMFA",
"weight": 43,
"weight": 44,
"cookies": false,
"type": "",
"deprecated": false,
@@ -619,7 +619,7 @@
},
"\/account\/mfa\/authenticators\/{type}": {
"post": {
"summary": "Create Authenticator",
"summary": "Create authenticator",
"operationId": "accountCreateMfaAuthenticator",
"consumes": [
"application\/json"
@@ -641,7 +641,7 @@
},
"x-appwrite": {
"method": "createMfaAuthenticator",
"weight": 45,
"weight": 46,
"cookies": false,
"type": "",
"deprecated": false,
@@ -687,7 +687,7 @@
]
},
"put": {
"summary": "Verify Authenticator",
"summary": "Verify authenticator",
"operationId": "accountUpdateMfaAuthenticator",
"consumes": [
"application\/json"
@@ -709,7 +709,7 @@
},
"x-appwrite": {
"method": "updateMfaAuthenticator",
"weight": 46,
"weight": 47,
"cookies": false,
"type": "",
"deprecated": false,
@@ -773,7 +773,7 @@
]
},
"delete": {
"summary": "Delete Authenticator",
"summary": "Delete authenticator",
"operationId": "accountDeleteMfaAuthenticator",
"consumes": [
"application\/json"
@@ -790,7 +790,7 @@
},
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"weight": 50,
"weight": 51,
"cookies": false,
"type": "",
"deprecated": false,
@@ -838,7 +838,7 @@
},
"\/account\/mfa\/challenge": {
"post": {
"summary": "Create MFA Challenge",
"summary": "Create MFA challenge",
"operationId": "accountCreateMfaChallenge",
"consumes": [
"application\/json"
@@ -860,7 +860,7 @@
},
"x-appwrite": {
"method": "createMfaChallenge",
"weight": 51,
"weight": 52,
"cookies": false,
"type": "",
"deprecated": false,
@@ -917,7 +917,7 @@
]
},
"put": {
"summary": "Create MFA Challenge (confirmation)",
"summary": "Create MFA challenge (confirmation)",
"operationId": "accountUpdateMfaChallenge",
"consumes": [
"application\/json"
@@ -934,7 +934,7 @@
},
"x-appwrite": {
"method": "updateMfaChallenge",
"weight": 52,
"weight": 53,
"cookies": false,
"type": "",
"deprecated": false,
@@ -994,7 +994,7 @@
},
"\/account\/mfa\/factors": {
"get": {
"summary": "List Factors",
"summary": "List factors",
"operationId": "accountListMfaFactors",
"consumes": [
"application\/json"
@@ -1016,7 +1016,7 @@
},
"x-appwrite": {
"method": "listMfaFactors",
"weight": 44,
"weight": 45,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1049,7 +1049,7 @@
},
"\/account\/mfa\/recovery-codes": {
"get": {
"summary": "Get MFA Recovery Codes",
"summary": "Get MFA recovery codes",
"operationId": "accountGetMfaRecoveryCodes",
"consumes": [
"application\/json"
@@ -1071,7 +1071,7 @@
},
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"weight": 49,
"weight": 50,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1102,7 +1102,7 @@
]
},
"post": {
"summary": "Create MFA Recovery Codes",
"summary": "Create MFA recovery codes",
"operationId": "accountCreateMfaRecoveryCodes",
"consumes": [
"application\/json"
@@ -1124,7 +1124,7 @@
},
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"weight": 47,
"weight": 48,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1155,7 +1155,7 @@
]
},
"patch": {
"summary": "Regenerate MFA Recovery Codes",
"summary": "Regenerate MFA recovery codes",
"operationId": "accountUpdateMfaRecoveryCodes",
"consumes": [
"application\/json"
@@ -1177,7 +1177,7 @@
},
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"weight": 48,
"weight": 49,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1232,7 +1232,7 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 31,
"weight": 32,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1307,7 +1307,7 @@
},
"x-appwrite": {
"method": "updatePassword",
"weight": 32,
"weight": 33,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1388,7 +1388,7 @@
},
"x-appwrite": {
"method": "updatePhone",
"weight": 34,
"weight": 35,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1470,7 +1470,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 29,
"weight": 30,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1523,7 +1523,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 35,
"weight": 36,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1598,7 +1598,7 @@
},
"x-appwrite": {
"method": "createRecovery",
"weight": 37,
"weight": 38,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1681,7 +1681,7 @@
},
"x-appwrite": {
"method": "updateRecovery",
"weight": 38,
"weight": 39,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1770,7 +1770,7 @@
},
"x-appwrite": {
"method": "listSessions",
"weight": 10,
"weight": 11,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1818,7 +1818,7 @@
},
"x-appwrite": {
"method": "deleteSessions",
"weight": 11,
"weight": 12,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1873,7 +1873,7 @@
},
"x-appwrite": {
"method": "createAnonymousSession",
"weight": 16,
"weight": 17,
"cookies": false,
"type": "",
"deprecated": false,
@@ -1926,7 +1926,7 @@
},
"x-appwrite": {
"method": "createEmailPasswordSession",
"weight": 15,
"weight": 16,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2006,7 +2006,7 @@
},
"x-appwrite": {
"method": "updateMagicURLSession",
"weight": 25,
"weight": 26,
"cookies": false,
"type": "",
"deprecated": true,
@@ -2083,7 +2083,7 @@
},
"x-appwrite": {
"method": "createOAuth2Session",
"weight": 18,
"weight": 19,
"cookies": false,
"type": "webAuth",
"deprecated": false,
@@ -2221,7 +2221,7 @@
},
"x-appwrite": {
"method": "updatePhoneSession",
"weight": 26,
"weight": 27,
"cookies": false,
"type": "",
"deprecated": true,
@@ -2301,7 +2301,7 @@
},
"x-appwrite": {
"method": "createSession",
"weight": 17,
"weight": 18,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2381,7 +2381,7 @@
},
"x-appwrite": {
"method": "getSession",
"weight": 12,
"weight": 13,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2444,7 +2444,7 @@
},
"x-appwrite": {
"method": "updateSession",
"weight": 14,
"weight": 15,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2502,7 +2502,7 @@
},
"x-appwrite": {
"method": "deleteSession",
"weight": 13,
"weight": 14,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2567,7 +2567,7 @@
},
"x-appwrite": {
"method": "updateStatus",
"weight": 36,
"weight": 37,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2622,7 +2622,7 @@
},
"x-appwrite": {
"method": "createPushTarget",
"weight": 53,
"weight": 54,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2708,7 +2708,7 @@
},
"x-appwrite": {
"method": "updatePushTarget",
"weight": 54,
"weight": 55,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2784,7 +2784,7 @@
},
"x-appwrite": {
"method": "deletePushTarget",
"weight": 55,
"weight": 56,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2847,7 +2847,7 @@
},
"x-appwrite": {
"method": "createEmailToken",
"weight": 24,
"weight": 25,
"cookies": false,
"type": "",
"deprecated": false,
@@ -2933,7 +2933,7 @@
},
"x-appwrite": {
"method": "createMagicURLToken",
"weight": 23,
"weight": 24,
"cookies": false,
"type": "",
"deprecated": false,
@@ -3025,7 +3025,7 @@
},
"x-appwrite": {
"method": "createOAuth2Token",
"weight": 22,
"weight": 23,
"cookies": false,
"type": "webAuth",
"deprecated": false,
@@ -3163,7 +3163,7 @@
},
"x-appwrite": {
"method": "createPhoneToken",
"weight": 27,
"weight": 28,
"cookies": false,
"type": "",
"deprecated": false,
@@ -3246,7 +3246,7 @@
},
"x-appwrite": {
"method": "createVerification",
"weight": 39,
"weight": 40,
"cookies": false,
"type": "",
"deprecated": false,
@@ -3319,7 +3319,7 @@
},
"x-appwrite": {
"method": "updateVerification",
"weight": 40,
"weight": 41,
"cookies": false,
"type": "",
"deprecated": false,
@@ -3401,7 +3401,7 @@
},
"x-appwrite": {
"method": "createPhoneVerification",
"weight": 41,
"weight": 42,
"cookies": false,
"type": "",
"deprecated": false,
@@ -3457,7 +3457,7 @@
},
"x-appwrite": {
"method": "updatePhoneVerification",
"weight": 42,
"weight": 43,
"cookies": false,
"type": "",
"deprecated": false,
@@ -3539,7 +3539,7 @@
},
"x-appwrite": {
"method": "getBrowser",
"weight": 59,
"weight": 60,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -3668,7 +3668,7 @@
},
"x-appwrite": {
"method": "getCreditCard",
"weight": 58,
"weight": 59,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -3801,7 +3801,7 @@
},
"x-appwrite": {
"method": "getFavicon",
"weight": 62,
"weight": 63,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -3868,7 +3868,7 @@
},
"x-appwrite": {
"method": "getFlag",
"weight": 60,
"weight": 61,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -4359,7 +4359,7 @@
},
"x-appwrite": {
"method": "getImage",
"weight": 61,
"weight": 62,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -4446,7 +4446,7 @@
},
"x-appwrite": {
"method": "getInitials",
"weight": 64,
"weight": 65,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -4541,7 +4541,7 @@
},
"x-appwrite": {
"method": "getQR",
"weight": 63,
"weight": 64,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -4636,7 +4636,7 @@
},
"x-appwrite": {
"method": "listDocuments",
"weight": 108,
"weight": 109,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4720,7 +4720,7 @@
},
"x-appwrite": {
"method": "createDocument",
"weight": 107,
"weight": 108,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4828,7 +4828,7 @@
},
"x-appwrite": {
"method": "getDocument",
"weight": 109,
"weight": 110,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4920,7 +4920,7 @@
},
"x-appwrite": {
"method": "updateDocument",
"weight": 111,
"weight": 112,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5019,7 +5019,7 @@
},
"x-appwrite": {
"method": "deleteDocument",
"weight": 112,
"weight": 113,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5101,7 +5101,7 @@
},
"x-appwrite": {
"method": "listExecutions",
"weight": 304,
"weight": 305,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5167,7 +5167,7 @@
"summary": "Create execution",
"operationId": "functionsCreateExecution",
"consumes": [
"multipart\/form-data"
"application\/json"
],
"produces": [
"multipart\/form-data"
@@ -5186,7 +5186,7 @@
},
"x-appwrite": {
"method": "createExecution",
"weight": 303,
"weight": 304,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5226,65 +5226,59 @@
"in": "path"
},
{
"name": "body",
"description": "HTTP body of execution. Default value is empty string.",
"required": false,
"type": "payload",
"default": "",
"in": "formData"
},
{
"name": "async",
"description": "Execute code in the background. Default value is false.",
"required": false,
"type": "boolean",
"x-example": false,
"default": false,
"in": "formData"
},
{
"name": "path",
"description": "HTTP path of execution. Path can include query params. Default value is \/",
"required": false,
"type": "string",
"x-example": "<PATH>",
"default": "\/",
"in": "formData"
},
{
"name": "method",
"description": "HTTP method of execution. Default value is GET.",
"required": false,
"type": "string",
"x-example": "GET",
"enum": [
"GET",
"POST",
"PUT",
"PATCH",
"DELETE",
"OPTIONS"
],
"x-enum-name": "ExecutionMethod",
"x-enum-keys": [],
"default": "POST",
"in": "formData"
},
{
"name": "headers",
"description": "HTTP headers of execution. Defaults to empty.",
"required": false,
"type": "object",
"default": [],
"x-example": "{}",
"in": "formData"
},
{
"name": "scheduledAt",
"description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.",
"required": false,
"type": "string",
"in": "formData"
"name": "payload",
"in": "body",
"schema": {
"type": "object",
"properties": {
"body": {
"type": "string",
"description": "HTTP body of execution. Default value is empty string.",
"default": "",
"x-example": "<BODY>"
},
"async": {
"type": "boolean",
"description": "Execute code in the background. Default value is false.",
"default": false,
"x-example": false
},
"path": {
"type": "string",
"description": "HTTP path of execution. Path can include query params. Default value is \/",
"default": "\/",
"x-example": "<PATH>"
},
"method": {
"type": "string",
"description": "HTTP method of execution. Default value is GET.",
"default": "POST",
"x-example": "GET",
"enum": [
"GET",
"POST",
"PUT",
"PATCH",
"DELETE",
"OPTIONS"
],
"x-enum-name": "ExecutionMethod",
"x-enum-keys": []
},
"headers": {
"type": "object",
"description": "HTTP headers of execution. Defaults to empty.",
"default": [],
"x-example": "{}"
},
"scheduledAt": {
"type": "string",
"description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.",
"default": null,
"x-example": null
}
}
}
}
]
}
@@ -5313,7 +5307,7 @@
},
"x-appwrite": {
"method": "getExecution",
"weight": 305,
"weight": 306,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5387,7 +5381,7 @@
},
"x-appwrite": {
"method": "query",
"weight": 329,
"weight": 330,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5463,7 +5457,7 @@
},
"x-appwrite": {
"method": "mutation",
"weight": 328,
"weight": 329,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5539,7 +5533,7 @@
},
"x-appwrite": {
"method": "get",
"weight": 116,
"weight": 117,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5573,7 +5567,7 @@
},
"\/locale\/codes": {
"get": {
"summary": "List Locale Codes",
"summary": "List locale codes",
"operationId": "localeListCodes",
"consumes": [
"application\/json"
@@ -5595,7 +5589,7 @@
},
"x-appwrite": {
"method": "listCodes",
"weight": 117,
"weight": 118,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5651,7 +5645,7 @@
},
"x-appwrite": {
"method": "listContinents",
"weight": 121,
"weight": 122,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5707,7 +5701,7 @@
},
"x-appwrite": {
"method": "listCountries",
"weight": 118,
"weight": 119,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5763,7 +5757,7 @@
},
"x-appwrite": {
"method": "listCountriesEU",
"weight": 119,
"weight": 120,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5819,7 +5813,7 @@
},
"x-appwrite": {
"method": "listCountriesPhones",
"weight": 120,
"weight": 121,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5875,7 +5869,7 @@
},
"x-appwrite": {
"method": "listCurrencies",
"weight": 122,
"weight": 123,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5931,7 +5925,7 @@
},
"x-appwrite": {
"method": "listLanguages",
"weight": 123,
"weight": 124,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5987,7 +5981,7 @@
},
"x-appwrite": {
"method": "createSubscriber",
"weight": 380,
"weight": 381,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6076,7 +6070,7 @@
},
"x-appwrite": {
"method": "deleteSubscriber",
"weight": 384,
"weight": 385,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6151,7 +6145,7 @@
},
"x-appwrite": {
"method": "listFiles",
"weight": 206,
"weight": 207,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6236,7 +6230,7 @@
},
"x-appwrite": {
"method": "createFile",
"weight": 205,
"weight": 206,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -6330,7 +6324,7 @@
},
"x-appwrite": {
"method": "getFile",
"weight": 207,
"weight": 208,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6402,7 +6396,7 @@
},
"x-appwrite": {
"method": "updateFile",
"weight": 212,
"weight": 213,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6476,7 +6470,7 @@
]
},
"delete": {
"summary": "Delete File",
"summary": "Delete file",
"operationId": "storageDeleteFile",
"consumes": [
"application\/json"
@@ -6493,7 +6487,7 @@
},
"x-appwrite": {
"method": "deleteFile",
"weight": 213,
"weight": 214,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6567,7 +6561,7 @@
},
"x-appwrite": {
"method": "getFileDownload",
"weight": 209,
"weight": 210,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6641,7 +6635,7 @@
},
"x-appwrite": {
"method": "getFilePreview",
"weight": 208,
"weight": 209,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6807,7 +6801,8 @@
"jpeg",
"gif",
"png",
"webp"
"webp",
"avif"
],
"x-enum-name": "ImageFormat",
"x-enum-keys": [],
@@ -6841,7 +6836,7 @@
},
"x-appwrite": {
"method": "getFileView",
"weight": 210,
"weight": 211,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6915,7 +6910,7 @@
},
"x-appwrite": {
"method": "list",
"weight": 217,
"weight": 218,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6992,7 +6987,7 @@
},
"x-appwrite": {
"method": "create",
"weight": 216,
"weight": 217,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7086,7 +7081,7 @@
},
"x-appwrite": {
"method": "get",
"weight": 218,
"weight": 219,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7150,7 +7145,7 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 220,
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7227,7 +7222,7 @@
},
"x-appwrite": {
"method": "delete",
"weight": 222,
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7293,7 +7288,7 @@
},
"x-appwrite": {
"method": "listMemberships",
"weight": 224,
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7378,7 +7373,7 @@
},
"x-appwrite": {
"method": "createMembership",
"weight": 223,
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7495,7 +7490,7 @@
},
"x-appwrite": {
"method": "getMembership",
"weight": 225,
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7567,7 +7562,7 @@
},
"x-appwrite": {
"method": "updateMembership",
"weight": 226,
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7655,7 +7650,7 @@
},
"x-appwrite": {
"method": "deleteMembership",
"weight": 228,
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7729,7 +7724,7 @@
},
"x-appwrite": {
"method": "updateMembershipStatus",
"weight": 227,
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7827,7 +7822,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 219,
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7890,7 +7885,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 221,
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
@@ -9591,7 +9586,7 @@
"responseBody": {
"type": "string",
"description": "HTTP response body. This will return empty unless execution is created as synchronous.",
"x-example": "Developers are awesome."
"x-example": ""
},
"responseHeaders": {
"type": "array",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+37 -36
View File
@@ -222,7 +222,7 @@
"tags": [
"account"
],
"description": "Update currently logged in user account email address. After changing user address, the user confirmation status will get reset. A new confirmation email is not sent automatically however you can use the send confirmation email endpoint again to send the confirmation email. For security measures, user password is required to complete this request.\nThis endpoint can also be used to convert an anonymous account to a normal one, by passing an email address and a new password.\n",
"description": "Update currently logged in user account email address. After changing user address, the user confirmation status will get reset. A new confirmation email is not sent automatically however you can use the send confirmation email endpoint again to send the confirmation email. For security measures, user password is required to complete this request.\r\nThis endpoint can also be used to convert an anonymous account to a normal one, by passing an email address and a new password.\r\n",
"responses": {
"200": {
"description": "User",
@@ -293,7 +293,7 @@
},
"\/account\/identities": {
"get": {
"summary": "List Identities",
"summary": "List identities",
"operationId": "accountListIdentities",
"consumes": [
"application\/json"
@@ -619,7 +619,7 @@
},
"\/account\/mfa\/authenticators\/{type}": {
"post": {
"summary": "Create Authenticator",
"summary": "Create authenticator",
"operationId": "accountCreateMfaAuthenticator",
"consumes": [
"application\/json"
@@ -687,7 +687,7 @@
]
},
"put": {
"summary": "Verify Authenticator",
"summary": "Verify authenticator",
"operationId": "accountUpdateMfaAuthenticator",
"consumes": [
"application\/json"
@@ -773,7 +773,7 @@
]
},
"delete": {
"summary": "Delete Authenticator",
"summary": "Delete authenticator",
"operationId": "accountDeleteMfaAuthenticator",
"consumes": [
"application\/json"
@@ -838,7 +838,7 @@
},
"\/account\/mfa\/challenge": {
"post": {
"summary": "Create MFA Challenge",
"summary": "Create MFA challenge",
"operationId": "accountCreateMfaChallenge",
"consumes": [
"application\/json"
@@ -917,7 +917,7 @@
]
},
"put": {
"summary": "Create MFA Challenge (confirmation)",
"summary": "Create MFA challenge (confirmation)",
"operationId": "accountUpdateMfaChallenge",
"consumes": [
"application\/json"
@@ -994,7 +994,7 @@
},
"\/account\/mfa\/factors": {
"get": {
"summary": "List Factors",
"summary": "List factors",
"operationId": "accountListMfaFactors",
"consumes": [
"application\/json"
@@ -1049,7 +1049,7 @@
},
"\/account\/mfa\/recovery-codes": {
"get": {
"summary": "Get MFA Recovery Codes",
"summary": "Get MFA recovery codes",
"operationId": "accountGetMfaRecoveryCodes",
"consumes": [
"application\/json"
@@ -1102,7 +1102,7 @@
]
},
"post": {
"summary": "Create MFA Recovery Codes",
"summary": "Create MFA recovery codes",
"operationId": "accountCreateMfaRecoveryCodes",
"consumes": [
"application\/json"
@@ -1155,7 +1155,7 @@
]
},
"patch": {
"summary": "Regenerate MFA Recovery Codes",
"summary": "Regenerate MFA recovery codes",
"operationId": "accountUpdateMfaRecoveryCodes",
"consumes": [
"application\/json"
@@ -1670,7 +1670,7 @@
"tags": [
"account"
],
"description": "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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createRecovery) endpoint.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.",
"description": "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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createRecovery) endpoint.\r\n\r\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.",
"responses": {
"200": {
"description": "Token",
@@ -1915,7 +1915,7 @@
"tags": [
"account"
],
"description": "Allow the user to login into their account by providing a valid email and password combination. This route will create a new session for the user.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Allow the user to login into their account by providing a valid email and password combination. This route will create a new session for the user.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"201": {
"description": "Session",
@@ -2075,7 +2075,7 @@
"tags": [
"account"
],
"description": "Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed.\n\nIf there is already an active session, the new session will be attached to the logged-in account. If there are no active sessions, the server will attempt to look for a user with the same email address as the email received from the OAuth2 provider and attach the new session to the existing user. If no matching user is found - the server will create a new user.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).\n",
"description": "Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed.\r\n\r\nIf there is already an active session, the new session will be attached to the logged-in account. If there are no active sessions, the server will attempt to look for a user with the same email address as the email received from the OAuth2 provider and attach the new session to the existing user. If no matching user is found - the server will create a new user.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).\r\n",
"responses": {
"301": {
"description": "No content"
@@ -2836,7 +2836,7 @@
"tags": [
"account"
],
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's email is valid for 15 minutes.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's email is valid for 15 minutes.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"201": {
"description": "Token",
@@ -2922,7 +2922,7 @@
"tags": [
"account"
],
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not been registered, a new user will be created. When the user clicks the link in the email, the user is redirected back to the URL you provided with the secret key and userId values attached to the URL query string. Use the query string parameters to submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The link sent to the user's email address is valid for 1 hour. If you are on a mobile device you can leave the URL parameter empty, so that the login completion will be handled by your Appwrite instance by default.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).\n",
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not been registered, a new user will be created. When the user clicks the link in the email, the user is redirected back to the URL you provided with the secret key and userId values attached to the URL query string. Use the query string parameters to submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The link sent to the user's email address is valid for 1 hour. If you are on a mobile device you can leave the URL parameter empty, so that the login completion will be handled by your Appwrite instance by default.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).\r\n",
"responses": {
"201": {
"description": "Token",
@@ -3017,7 +3017,7 @@
"tags": [
"account"
],
"description": "Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed. \n\nIf authentication succeeds, `userId` and `secret` of a token will be appended to the success URL as query parameters. These can be used to create a new session using the [Create session](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed. \r\n\r\nIf authentication succeeds, `userId` and `secret` of a token will be appended to the success URL as query parameters. These can be used to create a new session using the [Create session](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"301": {
"description": "No content"
@@ -3152,7 +3152,7 @@
"tags": [
"account"
],
"description": "Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"201": {
"description": "Token",
@@ -3235,7 +3235,7 @@
"tags": [
"account"
],
"description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to 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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n",
"description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to 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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\r\n\r\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\r\n",
"responses": {
"201": {
"description": "Token",
@@ -3528,7 +3528,7 @@
"tags": [
"avatars"
],
"description": "You can use this endpoint to show different browser icons to your users. The code argument receives the browser code as it appears in your user [GET \/account\/sessions](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#getSessions) endpoint. Use width, height and quality arguments to change the output settings.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.",
"description": "You can use this endpoint to show different browser icons to your users. The code argument receives the browser code as it appears in your user [GET \/account\/sessions](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#getSessions) endpoint. Use width, height and quality arguments to change the output settings.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.",
"responses": {
"200": {
"description": "Image",
@@ -3657,7 +3657,7 @@
"tags": [
"avatars"
],
"description": "The credit card endpoint will return you the icon of the credit card provider you need. Use width, height and quality arguments to change the output settings.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\n",
"description": "The credit card endpoint will return you the icon of the credit card provider you need. Use width, height and quality arguments to change the output settings.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\r\n",
"responses": {
"200": {
"description": "Image",
@@ -3790,7 +3790,7 @@
"tags": [
"avatars"
],
"description": "Use this endpoint to fetch the favorite icon (AKA favicon) of any remote website URL.\n\nThis endpoint does not follow HTTP redirects.",
"description": "Use this endpoint to fetch the favorite icon (AKA favicon) of any remote website URL.\r\n\r\nThis endpoint does not follow HTTP redirects.",
"responses": {
"200": {
"description": "Image",
@@ -3857,7 +3857,7 @@
"tags": [
"avatars"
],
"description": "You can use this endpoint to show different country flags icons to your users. The code argument receives the 2 letter country code. Use width, height and quality arguments to change the output settings. Country codes follow the [ISO 3166-1](https:\/\/en.wikipedia.org\/wiki\/ISO_3166-1) standard.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\n",
"description": "You can use this endpoint to show different country flags icons to your users. The code argument receives the 2 letter country code. Use width, height and quality arguments to change the output settings. Country codes follow the [ISO 3166-1](https:\/\/en.wikipedia.org\/wiki\/ISO_3166-1) standard.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\r\n",
"responses": {
"200": {
"description": "Image",
@@ -4348,7 +4348,7 @@
"tags": [
"avatars"
],
"description": "Use this endpoint to fetch a remote image URL and crop it to any image size you want. This endpoint is very useful if you need to crop and display remote images in your app or in case you want to make sure a 3rd party image is properly served using a TLS protocol.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 400x400px.\n\nThis endpoint does not follow HTTP redirects.",
"description": "Use this endpoint to fetch a remote image URL and crop it to any image size you want. This endpoint is very useful if you need to crop and display remote images in your app or in case you want to make sure a 3rd party image is properly served using a TLS protocol.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 400x400px.\r\n\r\nThis endpoint does not follow HTTP redirects.",
"responses": {
"200": {
"description": "Image",
@@ -4435,7 +4435,7 @@
"tags": [
"avatars"
],
"description": "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.\n\nYou 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.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\n",
"description": "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.\r\n\r\nYou 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.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\r\n",
"responses": {
"200": {
"description": "Image",
@@ -4530,7 +4530,7 @@
"tags": [
"avatars"
],
"description": "Converts a given plain text to a QR code image. You can use the query parameters to change the size and style of the resulting image.\n",
"description": "Converts a given plain text to a QR code image. You can use the query parameters to change the size and style of the resulting image.\r\n",
"responses": {
"200": {
"description": "Image",
@@ -5528,7 +5528,7 @@
"tags": [
"locale"
],
"description": "Get the current user location based on IP. Returns an object with user country code, country name, continent name, continent code, ip address and suggested currency. You can use the locale header to get the data in a supported language.\n\n([IP Geolocation by DB-IP](https:\/\/db-ip.com))",
"description": "Get the current user location based on IP. Returns an object with user country code, country name, continent name, continent code, ip address and suggested currency. You can use the locale header to get the data in a supported language.\r\n\r\n([IP Geolocation by DB-IP](https:\/\/db-ip.com))",
"responses": {
"200": {
"description": "Locale",
@@ -5573,7 +5573,7 @@
},
"\/locale\/codes": {
"get": {
"summary": "List Locale Codes",
"summary": "List locale codes",
"operationId": "localeListCodes",
"consumes": [
"application\/json"
@@ -6225,7 +6225,7 @@
"tags": [
"storage"
],
"description": "Create a new file. Before using this route, you should create a new bucket resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/storage#storageCreateBucket) API or directly from your Appwrite console.\n\nLarger files should be uploaded using multiple requests with the [content-range](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Range) header to send a partial request with a maximum supported chunk of `5MB`. The `content-range` header values should always be in bytes.\n\nWhen the first request is sent, the server will return the **File** object, and the subsequent part request must include the file's **id** in `x-appwrite-id` header to allow the server to know that the partial upload is for the existing file and not for a new one.\n\nIf you're creating a new file using one of the Appwrite SDKs, all the chunking logic will be managed by the SDK internally.\n",
"description": "Create a new file. Before using this route, you should create a new bucket resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/storage#storageCreateBucket) API or directly from your Appwrite console.\r\n\r\nLarger files should be uploaded using multiple requests with the [content-range](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Range) header to send a partial request with a maximum supported chunk of `5MB`. The `content-range` header values should always be in bytes.\r\n\r\nWhen the first request is sent, the server will return the **File** object, and the subsequent part request must include the file's **id** in `x-appwrite-id` header to allow the server to know that the partial upload is for the existing file and not for a new one.\r\n\r\nIf you're creating a new file using one of the Appwrite SDKs, all the chunking logic will be managed by the SDK internally.\r\n",
"responses": {
"201": {
"description": "File",
@@ -6476,7 +6476,7 @@
]
},
"delete": {
"summary": "Delete File",
"summary": "Delete file",
"operationId": "storageDeleteFile",
"consumes": [
"application\/json"
@@ -6807,7 +6807,8 @@
"jpeg",
"gif",
"png",
"webp"
"webp",
"avif"
],
"x-enum-name": "ImageFormat",
"x-enum-keys": [],
@@ -7367,7 +7368,7 @@
"tags": [
"teams"
],
"description": "Invite a new member to join your team. Provide an ID for existing users, or invite unregistered users using an email or phone number. If initiated from a Client SDK, Appwrite will send an email or sms with a link to join the team to the invited user, and an account will be created for them if one doesn't exist. If initiated from a Server SDK, the new member will be added automatically to the team.\n\nYou only need to provide one of a user ID, email, or phone number. Appwrite will prioritize accepting the user ID > email > phone number if you provide more than one of these parameters.\n\nUse the `url` parameter to redirect the user from the invitation email to your app. After the user is redirected, use the [Update Team Membership Status](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/teams#updateMembershipStatus) endpoint to allow the user to accept the invitation to the team. \n\nPlease note that to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) Appwrite will accept the only redirect URLs under the domains you have added as a platform on the Appwrite Console.\n",
"description": "Invite a new member to join your team. Provide an ID for existing users, or invite unregistered users using an email or phone number. If initiated from a Client SDK, Appwrite will send an email or sms with a link to join the team to the invited user, and an account will be created for them if one doesn't exist. If initiated from a Server SDK, the new member will be added automatically to the team.\r\n\r\nYou only need to provide one of a user ID, email, or phone number. Appwrite will prioritize accepting the user ID > email > phone number if you provide more than one of these parameters.\r\n\r\nUse the `url` parameter to redirect the user from the invitation email to your app. After the user is redirected, use the [Update Team Membership Status](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/teams#updateMembershipStatus) endpoint to allow the user to accept the invitation to the team. \r\n\r\nPlease note that to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) Appwrite will accept the only redirect URLs under the domains you have added as a platform on the Appwrite Console.\r\n",
"responses": {
"201": {
"description": "Membership",
@@ -7556,7 +7557,7 @@
"tags": [
"teams"
],
"description": "Modify the roles of a team member. Only team members with the owner role have access to this endpoint. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions).\n",
"description": "Modify the roles of a team member. Only team members with the owner role have access to this endpoint. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions).\r\n",
"responses": {
"200": {
"description": "Membership",
@@ -7718,7 +7719,7 @@
"tags": [
"teams"
],
"description": "Use this endpoint to allow a user to accept an invitation to join a team after being redirected back to your app from the invitation email received by the user.\n\nIf the request is successful, a session for the user is automatically created.\n",
"description": "Use this endpoint to allow a user to accept an invitation to join a team after being redirected back to your app from the invitation email received by the user.\r\n\r\nIf the request is successful, a session for the user is automatically created.\r\n",
"responses": {
"200": {
"description": "Membership",
@@ -9589,9 +9590,9 @@
"format": "int32"
},
"responseBody": {
"type": "string",
"type": "payload",
"description": "HTTP response body. This will return empty unless execution is created as synchronous.",
"x-example": "Developers are awesome."
"x-example": ""
},
"responseHeaders": {
"type": "array",
File diff suppressed because it is too large Load Diff
+216 -83
View File
@@ -238,7 +238,7 @@
"tags": [
"account"
],
"description": "Update currently logged in user account email address. After changing user address, the user confirmation status will get reset. A new confirmation email is not sent automatically however you can use the send confirmation email endpoint again to send the confirmation email. For security measures, user password is required to complete this request.\nThis endpoint can also be used to convert an anonymous account to a normal one, by passing an email address and a new password.\n",
"description": "Update currently logged in user account email address. After changing user address, the user confirmation status will get reset. A new confirmation email is not sent automatically however you can use the send confirmation email endpoint again to send the confirmation email. For security measures, user password is required to complete this request.\r\nThis endpoint can also be used to convert an anonymous account to a normal one, by passing an email address and a new password.\r\n",
"responses": {
"200": {
"description": "User",
@@ -310,7 +310,7 @@
},
"\/account\/identities": {
"get": {
"summary": "List Identities",
"summary": "List identities",
"operationId": "accountListIdentities",
"consumes": [
"application\/json"
@@ -640,7 +640,7 @@
},
"\/account\/mfa\/authenticators\/{type}": {
"post": {
"summary": "Create Authenticator",
"summary": "Create authenticator",
"operationId": "accountCreateMfaAuthenticator",
"consumes": [
"application\/json"
@@ -709,7 +709,7 @@
]
},
"put": {
"summary": "Verify Authenticator",
"summary": "Verify authenticator",
"operationId": "accountUpdateMfaAuthenticator",
"consumes": [
"application\/json"
@@ -796,7 +796,7 @@
]
},
"delete": {
"summary": "Delete Authenticator",
"summary": "Delete authenticator",
"operationId": "accountDeleteMfaAuthenticator",
"consumes": [
"application\/json"
@@ -862,7 +862,7 @@
},
"\/account\/mfa\/challenge": {
"post": {
"summary": "Create MFA Challenge",
"summary": "Create MFA challenge",
"operationId": "accountCreateMfaChallenge",
"consumes": [
"application\/json"
@@ -941,7 +941,7 @@
]
},
"put": {
"summary": "Create MFA Challenge (confirmation)",
"summary": "Create MFA challenge (confirmation)",
"operationId": "accountUpdateMfaChallenge",
"consumes": [
"application\/json"
@@ -1019,7 +1019,7 @@
},
"\/account\/mfa\/factors": {
"get": {
"summary": "List Factors",
"summary": "List factors",
"operationId": "accountListMfaFactors",
"consumes": [
"application\/json"
@@ -1075,7 +1075,7 @@
},
"\/account\/mfa\/recovery-codes": {
"get": {
"summary": "Get MFA Recovery Codes",
"summary": "Get MFA recovery codes",
"operationId": "accountGetMfaRecoveryCodes",
"consumes": [
"application\/json"
@@ -1129,7 +1129,7 @@
]
},
"post": {
"summary": "Create MFA Recovery Codes",
"summary": "Create MFA recovery codes",
"operationId": "accountCreateMfaRecoveryCodes",
"consumes": [
"application\/json"
@@ -1183,7 +1183,7 @@
]
},
"patch": {
"summary": "Regenerate MFA Recovery Codes",
"summary": "Regenerate MFA recovery codes",
"operationId": "accountUpdateMfaRecoveryCodes",
"consumes": [
"application\/json"
@@ -1705,7 +1705,7 @@
"tags": [
"account"
],
"description": "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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createRecovery) endpoint.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.",
"description": "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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createRecovery) endpoint.\r\n\r\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.",
"responses": {
"200": {
"description": "Token",
@@ -1953,7 +1953,7 @@
"tags": [
"account"
],
"description": "Allow the user to login into their account by providing a valid email and password combination. This route will create a new session for the user.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Allow the user to login into their account by providing a valid email and password combination. This route will create a new session for the user.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"201": {
"description": "Session",
@@ -2518,7 +2518,7 @@
"tags": [
"account"
],
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's email is valid for 15 minutes.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's email is valid for 15 minutes.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"201": {
"description": "Token",
@@ -2604,7 +2604,7 @@
"tags": [
"account"
],
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not been registered, a new user will be created. When the user clicks the link in the email, the user is redirected back to the URL you provided with the secret key and userId values attached to the URL query string. Use the query string parameters to submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The link sent to the user's email address is valid for 1 hour. If you are on a mobile device you can leave the URL parameter empty, so that the login completion will be handled by your Appwrite instance by default.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).\n",
"description": "Sends the user an email with a secret key for creating a session. If the provided user ID has not been registered, a new user will be created. When the user clicks the link in the email, the user is redirected back to the URL you provided with the secret key and userId values attached to the URL query string. Use the query string parameters to submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The link sent to the user's email address is valid for 1 hour. If you are on a mobile device you can leave the URL parameter empty, so that the login completion will be handled by your Appwrite instance by default.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).\r\n",
"responses": {
"201": {
"description": "Token",
@@ -2699,7 +2699,7 @@
"tags": [
"account"
],
"description": "Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed. \n\nIf authentication succeeds, `userId` and `secret` of a token will be appended to the success URL as query parameters. These can be used to create a new session using the [Create session](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed. \r\n\r\nIf authentication succeeds, `userId` and `secret` of a token will be appended to the success URL as query parameters. These can be used to create a new session using the [Create session](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"301": {
"description": "No content"
@@ -2834,7 +2834,7 @@
"tags": [
"account"
],
"description": "Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes.\n\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"description": "Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [POST \/v1\/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes.\r\n\r\nA user is limited to 10 active sessions at a time by default. [Learn more about session limits](https:\/\/appwrite.io\/docs\/authentication-security#limits).",
"responses": {
"201": {
"description": "Token",
@@ -2917,7 +2917,7 @@
"tags": [
"account"
],
"description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to 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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\n\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\n",
"description": "Use this endpoint to send a verification message to your user email address to confirm they are the valid owners of that address. Both the **userId** and **secret** arguments will be passed as query parameters to the URL you have provided to be attached to the verification email. The provided URL should redirect the user back to 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](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#updateVerification). The verification link sent to the user's email address is valid for 7 days.\r\n\r\nPlease note that in order to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md), the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface.\r\n",
"responses": {
"201": {
"description": "Token",
@@ -3214,7 +3214,7 @@
"tags": [
"avatars"
],
"description": "You can use this endpoint to show different browser icons to your users. The code argument receives the browser code as it appears in your user [GET \/account\/sessions](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#getSessions) endpoint. Use width, height and quality arguments to change the output settings.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.",
"description": "You can use this endpoint to show different browser icons to your users. The code argument receives the browser code as it appears in your user [GET \/account\/sessions](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#getSessions) endpoint. Use width, height and quality arguments to change the output settings.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.",
"responses": {
"200": {
"description": "Image",
@@ -3345,7 +3345,7 @@
"tags": [
"avatars"
],
"description": "The credit card endpoint will return you the icon of the credit card provider you need. Use width, height and quality arguments to change the output settings.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\n",
"description": "The credit card endpoint will return you the icon of the credit card provider you need. Use width, height and quality arguments to change the output settings.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\r\n",
"responses": {
"200": {
"description": "Image",
@@ -3480,7 +3480,7 @@
"tags": [
"avatars"
],
"description": "Use this endpoint to fetch the favorite icon (AKA favicon) of any remote website URL.\n\nThis endpoint does not follow HTTP redirects.",
"description": "Use this endpoint to fetch the favorite icon (AKA favicon) of any remote website URL.\r\n\r\nThis endpoint does not follow HTTP redirects.",
"responses": {
"200": {
"description": "Image",
@@ -3549,7 +3549,7 @@
"tags": [
"avatars"
],
"description": "You can use this endpoint to show different country flags icons to your users. The code argument receives the 2 letter country code. Use width, height and quality arguments to change the output settings. Country codes follow the [ISO 3166-1](https:\/\/en.wikipedia.org\/wiki\/ISO_3166-1) standard.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\n",
"description": "You can use this endpoint to show different country flags icons to your users. The code argument receives the 2 letter country code. Use width, height and quality arguments to change the output settings. Country codes follow the [ISO 3166-1](https:\/\/en.wikipedia.org\/wiki\/ISO_3166-1) standard.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\r\n",
"responses": {
"200": {
"description": "Image",
@@ -4042,7 +4042,7 @@
"tags": [
"avatars"
],
"description": "Use this endpoint to fetch a remote image URL and crop it to any image size you want. This endpoint is very useful if you need to crop and display remote images in your app or in case you want to make sure a 3rd party image is properly served using a TLS protocol.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 400x400px.\n\nThis endpoint does not follow HTTP redirects.",
"description": "Use this endpoint to fetch a remote image URL and crop it to any image size you want. This endpoint is very useful if you need to crop and display remote images in your app or in case you want to make sure a 3rd party image is properly served using a TLS protocol.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 400x400px.\r\n\r\nThis endpoint does not follow HTTP redirects.",
"responses": {
"200": {
"description": "Image",
@@ -4131,7 +4131,7 @@
"tags": [
"avatars"
],
"description": "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.\n\nYou 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.\n\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\n",
"description": "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.\r\n\r\nYou 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.\r\n\r\nWhen one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.\r\n",
"responses": {
"200": {
"description": "Image",
@@ -4228,7 +4228,7 @@
"tags": [
"avatars"
],
"description": "Converts a given plain text to a QR code image. You can use the query parameters to change the size and style of the resulting image.\n",
"description": "Converts a given plain text to a QR code image. You can use the query parameters to change the size and style of the resulting image.\r\n",
"responses": {
"200": {
"description": "Image",
@@ -4400,7 +4400,7 @@
"tags": [
"databases"
],
"description": "Create a new Database.\n",
"description": "Create a new Database.\r\n",
"responses": {
"201": {
"description": "Database",
@@ -5217,7 +5217,7 @@
"tags": [
"databases"
],
"description": "Create a boolean attribute.\n",
"description": "Create a boolean attribute.\r\n",
"responses": {
"202": {
"description": "AttributeBoolean",
@@ -5653,7 +5653,7 @@
"tags": [
"databases"
],
"description": "Create an email attribute.\n",
"description": "Create an email attribute.\r\n",
"responses": {
"202": {
"description": "AttributeEmail",
@@ -5760,7 +5760,7 @@
"tags": [
"databases"
],
"description": "Update an email attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update an email attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeEmail",
@@ -5871,7 +5871,7 @@
"tags": [
"databases"
],
"description": "Create an enumeration attribute. The `elements` param acts as a white-list of accepted values for this attribute. \n",
"description": "Create an enumeration attribute. The `elements` param acts as a white-list of accepted values for this attribute. \r\n",
"responses": {
"202": {
"description": "AttributeEnum",
@@ -5988,7 +5988,7 @@
"tags": [
"databases"
],
"description": "Update an enum attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update an enum attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeEnum",
@@ -6109,7 +6109,7 @@
"tags": [
"databases"
],
"description": "Create a float attribute. Optionally, minimum and maximum values can be provided.\n",
"description": "Create a float attribute. Optionally, minimum and maximum values can be provided.\r\n",
"responses": {
"202": {
"description": "AttributeFloat",
@@ -6228,7 +6228,7 @@
"tags": [
"databases"
],
"description": "Update a float attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update a float attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeFloat",
@@ -6353,7 +6353,7 @@
"tags": [
"databases"
],
"description": "Create an integer attribute. Optionally, minimum and maximum values can be provided.\n",
"description": "Create an integer attribute. Optionally, minimum and maximum values can be provided.\r\n",
"responses": {
"202": {
"description": "AttributeInteger",
@@ -6472,7 +6472,7 @@
"tags": [
"databases"
],
"description": "Update an integer attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update an integer attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeInteger",
@@ -6597,7 +6597,7 @@
"tags": [
"databases"
],
"description": "Create IP address attribute.\n",
"description": "Create IP address attribute.\r\n",
"responses": {
"202": {
"description": "AttributeIP",
@@ -6704,7 +6704,7 @@
"tags": [
"databases"
],
"description": "Update an ip attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update an ip attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeIP",
@@ -6815,7 +6815,7 @@
"tags": [
"databases"
],
"description": "Create relationship attribute. [Learn more about relationship attributes](https:\/\/appwrite.io\/docs\/databases-relationships#relationship-attributes).\n",
"description": "Create relationship attribute. [Learn more about relationship attributes](https:\/\/appwrite.io\/docs\/databases-relationships#relationship-attributes).\r\n",
"responses": {
"202": {
"description": "AttributeRelationship",
@@ -6951,7 +6951,7 @@
"tags": [
"databases"
],
"description": "Create a string attribute.\n",
"description": "Create a string attribute.\r\n",
"responses": {
"202": {
"description": "AttributeString",
@@ -7071,7 +7071,7 @@
"tags": [
"databases"
],
"description": "Update a string attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update a string attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeString",
@@ -7188,7 +7188,7 @@
"tags": [
"databases"
],
"description": "Create a URL attribute.\n",
"description": "Create a URL attribute.\r\n",
"responses": {
"202": {
"description": "AttributeURL",
@@ -7295,7 +7295,7 @@
"tags": [
"databases"
],
"description": "Update an url attribute. Changing the `default` value will not update already existing documents.\n",
"description": "Update an url attribute. Changing the `default` value will not update already existing documents.\r\n",
"responses": {
"200": {
"description": "AttributeURL",
@@ -7586,7 +7586,7 @@
"tags": [
"databases"
],
"description": "Update relationship attribute. [Learn more about relationship attributes](https:\/\/appwrite.io\/docs\/databases-relationships#relationship-attributes).\n",
"description": "Update relationship attribute. [Learn more about relationship attributes](https:\/\/appwrite.io\/docs\/databases-relationships#relationship-attributes).\r\n",
"responses": {
"200": {
"description": "AttributeRelationship",
@@ -8250,7 +8250,7 @@
"tags": [
"databases"
],
"description": "Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request.\nAttributes can be `key`, `fulltext`, and `unique`.",
"description": "Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request.\r\nAttributes can be `key`, `fulltext`, and `unique`.",
"responses": {
"202": {
"description": "Index",
@@ -8917,7 +8917,7 @@
"tags": [
"functions"
],
"description": "List allowed function specifications for this instance.\n",
"description": "List allowed function specifications for this instance.\r\n",
"responses": {
"200": {
"description": "Specifications List",
@@ -9415,7 +9415,7 @@
"tags": [
"functions"
],
"description": "Create a new function code deployment. Use this endpoint to upload a new version of your code function. To execute your newly uploaded code, you'll need to update the function's deployment to use your new deployment UID.\n\nThis endpoint accepts a tar.gz file compressed with your code. Make sure to include any dependencies your code has within the compressed file. You can learn more about code packaging in the [Appwrite Cloud Functions tutorial](https:\/\/appwrite.io\/docs\/functions).\n\nUse the \"command\" param to set the entrypoint used to execute your code.",
"description": "Create a new function code deployment. Use this endpoint to upload a new version of your code function. To execute your newly uploaded code, you'll need to update the function's deployment to use your new deployment UID.\r\n\r\nThis endpoint accepts a tar.gz file compressed with your code. Make sure to include any dependencies your code has within the compressed file. You can learn more about code packaging in the [Appwrite Cloud Functions tutorial](https:\/\/appwrite.io\/docs\/functions).\r\n\r\nUse the \"command\" param to set the entrypoint used to execute your code.",
"responses": {
"202": {
"description": "Deployment",
@@ -10231,7 +10231,7 @@
"tags": [
"functions"
],
"description": "Delete a function execution by its unique ID.\n",
"description": "Delete a function execution by its unique ID.\r\n",
"responses": {
"204": {
"description": "No content"
@@ -11494,7 +11494,7 @@
"tags": [
"health"
],
"description": "Returns the amount of failed jobs in a given queue.\n",
"description": "Returns the amount of failed jobs in a given queue.\r\n",
"responses": {
"200": {
"description": "Health Queue",
@@ -12265,7 +12265,7 @@
"tags": [
"locale"
],
"description": "Get the current user location based on IP. Returns an object with user country code, country name, continent name, continent code, ip address and suggested currency. You can use the locale header to get the data in a supported language.\n\n([IP Geolocation by DB-IP](https:\/\/db-ip.com))",
"description": "Get the current user location based on IP. Returns an object with user country code, country name, continent name, continent code, ip address and suggested currency. You can use the locale header to get the data in a supported language.\r\n\r\n([IP Geolocation by DB-IP](https:\/\/db-ip.com))",
"responses": {
"200": {
"description": "Locale",
@@ -12312,7 +12312,7 @@
},
"\/locale\/codes": {
"get": {
"summary": "List Locale Codes",
"summary": "List locale codes",
"operationId": "localeListCodes",
"consumes": [
"application\/json"
@@ -12968,7 +12968,7 @@
"tags": [
"messaging"
],
"description": "Update an email message by its unique ID.\n",
"description": "Update an email message by its unique ID.\r\n",
"responses": {
"200": {
"description": "Message",
@@ -13302,7 +13302,7 @@
"tags": [
"messaging"
],
"description": "Update a push notification by its unique ID.\n",
"description": "Update a push notification by its unique ID.\r\n",
"responses": {
"200": {
"description": "Message",
@@ -13596,7 +13596,7 @@
"tags": [
"messaging"
],
"description": "Update an email message by its unique ID.\n",
"description": "Update an email message by its unique ID.\r\n",
"responses": {
"200": {
"description": "Message",
@@ -13715,7 +13715,7 @@
"tags": [
"messaging"
],
"description": "Get a message by its unique ID.\n",
"description": "Get a message by its unique ID.\r\n",
"responses": {
"200": {
"description": "Message",
@@ -16355,7 +16355,7 @@
"tags": [
"messaging"
],
"description": "Get a provider by its unique ID.\n",
"description": "Get a provider by its unique ID.\r\n",
"responses": {
"200": {
"description": "Provider",
@@ -16803,7 +16803,7 @@
"tags": [
"messaging"
],
"description": "Get a topic by its unique ID.\n",
"description": "Get a topic by its unique ID.\r\n",
"responses": {
"200": {
"description": "Topic",
@@ -16866,7 +16866,7 @@
"tags": [
"messaging"
],
"description": "Update a topic by its unique ID.\n",
"description": "Update a topic by its unique ID.\r\n",
"responses": {
"200": {
"description": "Topic",
@@ -17270,7 +17270,7 @@
"tags": [
"messaging"
],
"description": "Get a subscriber by its unique ID.\n",
"description": "Get a subscriber by its unique ID.\r\n",
"responses": {
"200": {
"description": "Subscriber",
@@ -17981,7 +17981,7 @@
"tags": [
"storage"
],
"description": "Create a new file. Before using this route, you should create a new bucket resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/storage#storageCreateBucket) API or directly from your Appwrite console.\n\nLarger files should be uploaded using multiple requests with the [content-range](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Range) header to send a partial request with a maximum supported chunk of `5MB`. The `content-range` header values should always be in bytes.\n\nWhen the first request is sent, the server will return the **File** object, and the subsequent part request must include the file's **id** in `x-appwrite-id` header to allow the server to know that the partial upload is for the existing file and not for a new one.\n\nIf you're creating a new file using one of the Appwrite SDKs, all the chunking logic will be managed by the SDK internally.\n",
"description": "Create a new file. Before using this route, you should create a new bucket resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/storage#storageCreateBucket) API or directly from your Appwrite console.\r\n\r\nLarger files should be uploaded using multiple requests with the [content-range](https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Content-Range) header to send a partial request with a maximum supported chunk of `5MB`. The `content-range` header values should always be in bytes.\r\n\r\nWhen the first request is sent, the server will return the **File** object, and the subsequent part request must include the file's **id** in `x-appwrite-id` header to allow the server to know that the partial upload is for the existing file and not for a new one.\r\n\r\nIf you're creating a new file using one of the Appwrite SDKs, all the chunking logic will be managed by the SDK internally.\r\n",
"responses": {
"201": {
"description": "File",
@@ -18238,7 +18238,7 @@
]
},
"delete": {
"summary": "Delete File",
"summary": "Delete file",
"operationId": "storageDeleteFile",
"consumes": [
"application\/json"
@@ -18575,7 +18575,8 @@
"jpeg",
"gif",
"png",
"webp"
"webp",
"avif"
],
"x-enum-name": "ImageFormat",
"x-enum-keys": [],
@@ -19149,7 +19150,7 @@
"tags": [
"teams"
],
"description": "Invite a new member to join your team. Provide an ID for existing users, or invite unregistered users using an email or phone number. If initiated from a Client SDK, Appwrite will send an email or sms with a link to join the team to the invited user, and an account will be created for them if one doesn't exist. If initiated from a Server SDK, the new member will be added automatically to the team.\n\nYou only need to provide one of a user ID, email, or phone number. Appwrite will prioritize accepting the user ID > email > phone number if you provide more than one of these parameters.\n\nUse the `url` parameter to redirect the user from the invitation email to your app. After the user is redirected, use the [Update Team Membership Status](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/teams#updateMembershipStatus) endpoint to allow the user to accept the invitation to the team. \n\nPlease note that to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) Appwrite will accept the only redirect URLs under the domains you have added as a platform on the Appwrite Console.\n",
"description": "Invite a new member to join your team. Provide an ID for existing users, or invite unregistered users using an email or phone number. If initiated from a Client SDK, Appwrite will send an email or sms with a link to join the team to the invited user, and an account will be created for them if one doesn't exist. If initiated from a Server SDK, the new member will be added automatically to the team.\r\n\r\nYou only need to provide one of a user ID, email, or phone number. Appwrite will prioritize accepting the user ID > email > phone number if you provide more than one of these parameters.\r\n\r\nUse the `url` parameter to redirect the user from the invitation email to your app. After the user is redirected, use the [Update Team Membership Status](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/teams#updateMembershipStatus) endpoint to allow the user to accept the invitation to the team. \r\n\r\nPlease note that to avoid a [Redirect Attack](https:\/\/github.com\/OWASP\/CheatSheetSeries\/blob\/master\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) Appwrite will accept the only redirect URLs under the domains you have added as a platform on the Appwrite Console.\r\n",
"responses": {
"201": {
"description": "Membership",
@@ -19342,7 +19343,7 @@
"tags": [
"teams"
],
"description": "Modify the roles of a team member. Only team members with the owner role have access to this endpoint. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions).\n",
"description": "Modify the roles of a team member. Only team members with the owner role have access to this endpoint. Learn more about [roles and permissions](https:\/\/appwrite.io\/docs\/permissions).\r\n",
"responses": {
"200": {
"description": "Membership",
@@ -19508,7 +19509,7 @@
"tags": [
"teams"
],
"description": "Use this endpoint to allow a user to accept an invitation to join a team after being redirected back to your app from the invitation email received by the user.\n\nIf the request is successful, a session for the user is automatically created.\n",
"description": "Use this endpoint to allow a user to accept an invitation to join a team after being redirected back to your app from the invitation email received by the user.\r\n\r\nIf the request is successful, a session for the user is automatically created.\r\n",
"responses": {
"200": {
"description": "Membership",
@@ -20105,7 +20106,7 @@
},
"\/users\/identities": {
"get": {
"summary": "List Identities",
"summary": "List identities",
"operationId": "usersListIdentities",
"consumes": [
"application\/json"
@@ -21087,7 +21088,7 @@
"tags": [
"users"
],
"description": "Update the user labels by its unique ID. \n\nLabels can be used to grant access to resources. While teams are a way for user's to share access to a resource, labels can be defined by the developer to grant access without an invitation. See the [Permissions docs](https:\/\/appwrite.io\/docs\/permissions) for more info.",
"description": "Update the user labels by its unique ID. \r\n\r\nLabels can be used to grant access to resources. While teams are a way for user's to share access to a resource, labels can be defined by the developer to grant access without an invitation. See the [Permissions docs](https:\/\/appwrite.io\/docs\/permissions) for more info.",
"responses": {
"200": {
"description": "User",
@@ -21383,7 +21384,7 @@
},
"\/users\/{userId}\/mfa\/authenticators\/{type}": {
"delete": {
"summary": "Delete Authenticator",
"summary": "Delete authenticator",
"operationId": "usersDeleteMfaAuthenticator",
"consumes": [
"application\/json"
@@ -21460,7 +21461,7 @@
},
"\/users\/{userId}\/mfa\/factors": {
"get": {
"summary": "List Factors",
"summary": "List factors",
"operationId": "usersListMfaFactors",
"consumes": [
"application\/json"
@@ -21524,7 +21525,7 @@
},
"\/users\/{userId}\/mfa\/recovery-codes": {
"get": {
"summary": "Get MFA Recovery Codes",
"summary": "Get MFA recovery codes",
"operationId": "usersGetMfaRecoveryCodes",
"consumes": [
"application\/json"
@@ -21586,7 +21587,7 @@
]
},
"put": {
"summary": "Regenerate MFA Recovery Codes",
"summary": "Regenerate MFA recovery codes",
"operationId": "usersUpdateMfaRecoveryCodes",
"consumes": [
"application\/json"
@@ -21648,7 +21649,7 @@
]
},
"patch": {
"summary": "Create MFA Recovery Codes",
"summary": "Create MFA recovery codes",
"operationId": "usersCreateMfaRecoveryCodes",
"consumes": [
"application\/json"
@@ -22175,7 +22176,7 @@
"tags": [
"users"
],
"description": "Creates a session for a user. Returns an immediately usable session object.\n\nIf you want to generate a token for a custom authentication flow, use the [POST \/users\/{userId}\/tokens](https:\/\/appwrite.io\/docs\/server\/users#createToken) endpoint.",
"description": "Creates a session for a user. Returns an immediately usable session object.\r\n\r\nIf you want to generate a token for a custom authentication flow, use the [POST \/users\/{userId}\/tokens](https:\/\/appwrite.io\/docs\/server\/users#createToken) endpoint.",
"responses": {
"201": {
"description": "Session",
@@ -22434,7 +22435,7 @@
},
"\/users\/{userId}\/targets": {
"get": {
"summary": "List User Targets",
"summary": "List user targets",
"operationId": "usersListTargets",
"consumes": [
"application\/json"
@@ -22509,7 +22510,7 @@
]
},
"post": {
"summary": "Create User Target",
"summary": "Create user target",
"operationId": "usersCreateTarget",
"consumes": [
"application\/json"
@@ -22625,7 +22626,7 @@
},
"\/users\/{userId}\/targets\/{targetId}": {
"get": {
"summary": "Get User Target",
"summary": "Get user target",
"operationId": "usersGetTarget",
"consumes": [
"application\/json"
@@ -22696,7 +22697,7 @@
]
},
"patch": {
"summary": "Update User target",
"summary": "Update user target",
"operationId": "usersUpdateTarget",
"consumes": [
"application\/json"
@@ -22875,7 +22876,7 @@
"tags": [
"users"
],
"description": "Returns a token with a secret key for creating a session. Use the user ID and secret and submit a request to the [PUT \/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process.\n",
"description": "Returns a token with a secret key for creating a session. Use the user ID and secret and submit a request to the [PUT \/account\/sessions\/token](https:\/\/appwrite.io\/docs\/references\/cloud\/client-web\/account#createSession) endpoint to complete the login process.\r\n",
"responses": {
"201": {
"description": "Token",
@@ -24166,6 +24167,16 @@
"x-example": false,
"x-nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"size": {
"type": "integer",
"description": "Attribute size.",
@@ -24185,6 +24196,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"size"
]
},
@@ -24223,6 +24236,16 @@
"x-example": false,
"x-nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"min": {
"type": "integer",
"description": "Minimum value to enforce for new documents.",
@@ -24250,7 +24273,9 @@
"type",
"status",
"error",
"required"
"required",
"$createdAt",
"$updatedAt"
]
},
"attributeFloat": {
@@ -24288,6 +24313,16 @@
"x-example": false,
"x-nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"min": {
"type": "number",
"description": "Minimum value to enforce for new documents.",
@@ -24315,7 +24350,9 @@
"type",
"status",
"error",
"required"
"required",
"$createdAt",
"$updatedAt"
]
},
"attributeBoolean": {
@@ -24353,6 +24390,16 @@
"x-example": false,
"x-nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"default": {
"type": "boolean",
"description": "Default value for attribute when not provided. Cannot be set when attribute is required.",
@@ -24365,7 +24412,9 @@
"type",
"status",
"error",
"required"
"required",
"$createdAt",
"$updatedAt"
]
},
"attributeEmail": {
@@ -24403,6 +24452,16 @@
"x-example": false,
"x-nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"format": {
"type": "string",
"description": "String format.",
@@ -24421,6 +24480,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"format"
]
},
@@ -24459,6 +24520,16 @@
"x-example": false,
"x-nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"elements": {
"type": "array",
"description": "Array of elements in enumerated type.",
@@ -24485,6 +24556,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"elements",
"format"
]
@@ -24524,6 +24597,16 @@
"x-example": false,
"x-nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"format": {
"type": "string",
"description": "String format.",
@@ -24542,6 +24625,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"format"
]
},
@@ -24580,6 +24665,16 @@
"x-example": false,
"x-nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"format": {
"type": "string",
"description": "String format.",
@@ -24598,6 +24693,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"format"
]
},
@@ -24636,6 +24733,16 @@
"x-example": false,
"x-nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"format": {
"type": "string",
"description": "ISO 8601 format.",
@@ -24654,6 +24761,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"format"
]
},
@@ -24692,6 +24801,16 @@
"x-example": false,
"x-nullable": true
},
"$createdAt": {
"type": "string",
"description": "Attribute creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Attribute update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"relatedCollection": {
"type": "string",
"description": "The ID of the related collection.",
@@ -24729,6 +24848,8 @@
"status",
"error",
"required",
"$createdAt",
"$updatedAt",
"relatedCollection",
"relationType",
"twoWay",
@@ -24777,6 +24898,16 @@
},
"x-example": [],
"x-nullable": true
},
"$createdAt": {
"type": "string",
"description": "Index creation date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
},
"$updatedAt": {
"type": "string",
"description": "Index update date in ISO 8601 format.",
"x-example": "2020-10-15T06:38:00.000+00:00"
}
},
"required": [
@@ -24784,7 +24915,9 @@
"type",
"status",
"error",
"attributes"
"attributes",
"$createdAt",
"$updatedAt"
]
},
"document": {
@@ -26458,9 +26591,9 @@
"format": "int32"
},
"responseBody": {
"type": "string",
"type": "payload",
"description": "HTTP response body. This will return empty unless execution is created as synchronous.",
"x-example": "Developers are awesome."
"x-example": ""
},
"responseHeaders": {
"type": "array",
+3
View File
@@ -6,6 +6,8 @@ return [
'image/gif',
'image/png',
'image/webp',
// 'image/heic',
'image/avif',
// Video Files
'video/mp4',
@@ -31,6 +33,7 @@ return [
'audio/ogg', // Ogg Vorbis RFC 5334
'audio/vorbis', // Vorbis RFC 5215
'audio/vnd.wav', // wav RFC 2361
'audio/x-wav', // php reads .wav as this - https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
'audio/aac', //AAC audio
'audio/x-hx-aac-adts', // AAC audio
+3
View File
@@ -6,4 +6,7 @@ return [ // Accepted outputs files
'gif' => 'image/gif',
'png' => 'image/png',
'webp' => 'image/webp',
// 'heic' => 'image/heic',
// 'heics' => 'image/heic',
'avif' => 'image/avif'
];
+21 -12
View File
@@ -42,6 +42,7 @@ use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Queries;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
@@ -3683,7 +3684,7 @@ App::patch('/v1/account/mfa')
});
App::get('/v1/account/mfa/factors')
->desc('List Factors')
->desc('List factors')
->groups(['api', 'account', 'mfa'])
->label('scope', 'account')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -3715,7 +3716,7 @@ App::get('/v1/account/mfa/factors')
});
App::post('/v1/account/mfa/authenticators/:type')
->desc('Create Authenticator')
->desc('Create authenticator')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
@@ -3787,7 +3788,7 @@ App::post('/v1/account/mfa/authenticators/:type')
});
App::put('/v1/account/mfa/authenticators/:type')
->desc('Verify Authenticator')
->desc('Verify authenticator')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
@@ -3852,7 +3853,7 @@ App::put('/v1/account/mfa/authenticators/:type')
});
App::post('/v1/account/mfa/recovery-codes')
->desc('Create MFA Recovery Codes')
->desc('Create MFA recovery codes')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
@@ -3894,7 +3895,7 @@ App::post('/v1/account/mfa/recovery-codes')
});
App::patch('/v1/account/mfa/recovery-codes')
->desc('Regenerate MFA Recovery Codes')
->desc('Regenerate MFA recovery codes')
->groups(['api', 'account', 'mfaProtected'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
@@ -3935,7 +3936,7 @@ App::patch('/v1/account/mfa/recovery-codes')
});
App::get('/v1/account/mfa/recovery-codes')
->desc('Get MFA Recovery Codes')
->desc('Get MFA recovery codes')
->groups(['api', 'account', 'mfaProtected'])
->label('scope', 'account')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -3965,7 +3966,7 @@ App::get('/v1/account/mfa/recovery-codes')
});
App::delete('/v1/account/mfa/authenticators/:type')
->desc('Delete Authenticator')
->desc('Delete authenticator')
->groups(['api', 'account', 'mfaProtected'])
->label('event', 'users.[userId].delete.mfa')
->label('scope', 'account')
@@ -4003,7 +4004,7 @@ App::delete('/v1/account/mfa/authenticators/:type')
});
App::post('/v1/account/mfa/challenge')
->desc('Create MFA Challenge')
->desc('Create MFA challenge')
->groups(['api', 'account', 'mfa'])
->label('scope', 'account')
->label('event', 'users.[userId].challenges.[challengeId].create')
@@ -4191,7 +4192,7 @@ App::post('/v1/account/mfa/challenge')
});
App::put('/v1/account/mfa/challenge')
->desc('Create MFA Challenge (confirmation)')
->desc('Create MFA challenge (confirmation)')
->groups(['api', 'account', 'mfa'])
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[sessionId].create')
@@ -4314,7 +4315,7 @@ App::post('/v1/account/targets/push')
$device = $detector->getDevice();
$sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret);
$sessionId = Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret);
$session = $dbForProject->getDocument('sessions', $sessionId);
try {
@@ -4383,7 +4384,9 @@ App::put('/v1/account/targets/:targetId/push')
}
if ($identifier) {
$target->setAttribute('identifier', $identifier);
$target
->setAttribute('identifier', $identifier)
->setAttribute('expired', false);
}
$detector = new Detector($request->getUserAgent());
@@ -4452,7 +4455,7 @@ App::delete('/v1/account/targets/:targetId/push')
$response->noContent();
});
App::get('/v1/account/identities')
->desc('List Identities')
->desc('List identities')
->groups(['api', 'account'])
->label('scope', 'account')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@@ -4486,6 +4489,12 @@ App::get('/v1/account/identities')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$identityId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('identities', $identityId);
+12 -12
View File
@@ -55,7 +55,7 @@ $avatarCallback = function (string $type, string $code, int $width, int $height,
$output = (empty($output)) ? $type : $output;
$data = $image->output($output, $quality);
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType('image/png')
->file($data);
unset($image);
@@ -275,7 +275,7 @@ App::get('/v1/avatars/image')
$data = $image->output($output, $quality);
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType('image/png')
->file($data);
unset($image);
@@ -409,7 +409,7 @@ App::get('/v1/avatars/favicon')
throw new Exception(Exception::AVATAR_ICON_NOT_FOUND, 'Favicon not found');
}
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType('image/x-icon')
->file($data);
}
@@ -420,7 +420,7 @@ App::get('/v1/avatars/favicon')
$data = $image->output($output, $quality);
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType('image/png')
->file($data);
unset($image);
@@ -461,7 +461,7 @@ App::get('/v1/avatars/qr')
$image->crop((int) $size, (int) $size);
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->setContentType('image/png')
->send($image->output('png', 9));
});
@@ -544,13 +544,13 @@ App::get('/v1/avatars/initials')
$image->compositeImage($punch, Imagick::COMPOSITE_COPYOPACITY, 0, 0);
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->setContentType('image/png')
->file($image->getImageBlob());
});
App::get('/v1/cards/cloud')
->desc('Get Front Of Cloud Card')
->desc('Get front Of Cloud Card')
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
->label('cache', true)
@@ -751,13 +751,13 @@ App::get('/v1/cards/cloud')
}
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->setContentType('image/png')
->file($baseImage->getImageBlob());
});
App::get('/v1/cards/cloud-back')
->desc('Get Back Of Cloud Card')
->desc('Get back Of Cloud Card')
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
->label('cache', true)
@@ -829,13 +829,13 @@ App::get('/v1/cards/cloud-back')
}
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->setContentType('image/png')
->file($baseImage->getImageBlob());
});
App::get('/v1/cards/cloud-og')
->desc('Get OG Image From Cloud Card')
->desc('Get OG image From Cloud Card')
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
->label('cache', true)
@@ -1219,7 +1219,7 @@ App::get('/v1/cards/cloud-og')
}
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->setContentType('image/png')
->file($baseImage->getImageBlob());
});
+1 -1
View File
@@ -57,7 +57,7 @@ App::get('/v1/console/variables')
});
App::post('/v1/console/assistant')
->desc('Ask Query')
->desc('Ask query')
->groups(['api', 'assistant'])
->label('scope', 'assistant.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
+80 -7
View File
@@ -40,6 +40,7 @@ use Utopia\Database\Validator\Index as IndexValidator;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\Queries;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\Structure;
@@ -438,6 +439,7 @@ App::post('/v1/databases')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].create')
->label('scope', 'databases.write')
->label('resourceType', 'databases')
->label('audits.event', 'database.create')
->label('audits.resource', 'database/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -515,6 +517,7 @@ App::get('/v1/databases')
->desc('List databases')
->groups(['api', 'database'])
->label('scope', 'databases.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'list')
@@ -546,6 +549,13 @@ App::get('/v1/databases')
});
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$databaseId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('databases', $databaseId);
@@ -568,6 +578,7 @@ App::get('/v1/databases/:databaseId')
->desc('Get database')
->groups(['api', 'database'])
->label('scope', 'databases.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'get')
@@ -593,6 +604,7 @@ App::get('/v1/databases/:databaseId/logs')
->desc('List database logs')
->groups(['api', 'database'])
->label('scope', 'databases.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'listLogs')
@@ -684,6 +696,7 @@ App::put('/v1/databases/:databaseId')
->desc('Update database')
->groups(['api', 'database', 'schema'])
->label('scope', 'databases.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].update')
->label('audits.event', 'database.update')
->label('audits.resource', 'database/{response.$id}')
@@ -722,6 +735,7 @@ App::delete('/v1/databases/:databaseId')
->desc('Delete database')
->groups(['api', 'database', 'schema'])
->label('scope', 'databases.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].delete')
->label('audits.event', 'database.delete')
->label('audits.resource', 'database/{request.databaseId}')
@@ -771,6 +785,7 @@ App::post('/v1/databases/:databaseId/collections')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].create')
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('audits.event', 'collection.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -838,6 +853,7 @@ App::get('/v1/databases/:databaseId/collections')
->desc('List collections')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'listCollections')
@@ -878,6 +894,12 @@ App::get('/v1/databases/:databaseId/collections')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$collectionId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
@@ -901,6 +923,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId')
->desc('Get collection')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'getCollection')
@@ -935,6 +958,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
->desc('List collection logs')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'listCollectionLogs')
@@ -1035,6 +1059,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId')
->desc('Update collection')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].update')
->label('audits.event', 'collection.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -1098,6 +1123,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId')
->desc('Delete collection')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].delete')
->label('audits.event', 'collection.delete')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -1154,6 +1180,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -1211,6 +1238,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email'
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk.namespace', 'databases')
@@ -1253,6 +1281,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum')
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk.namespace', 'databases')
@@ -1300,6 +1329,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip')
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk.namespace', 'databases')
@@ -1342,6 +1372,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url')
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk.namespace', 'databases')
@@ -1384,6 +1415,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk.namespace', 'databases')
@@ -1455,6 +1487,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float'
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk.namespace', 'databases')
@@ -1529,6 +1562,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea
->groups(['api', 'database', 'schema'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk.namespace', 'databases')
@@ -1570,6 +1604,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk.namespace', 'databases')
@@ -1614,6 +1649,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk.namespace', 'databases')
@@ -1741,6 +1777,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
->desc('List attributes')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'listAttributes')
@@ -1788,6 +1825,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
$cursor = \reset($cursor);
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$attributeId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->find('attributes', [
Query::equal('collectionInternalId', [$collection->getInternalId()]),
@@ -1819,6 +1861,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key')
->desc('Get attribute')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'getAttribute')
@@ -1893,6 +1936,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
->desc('Update string attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -1936,6 +1980,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email
->desc('Update email attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -1977,6 +2022,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/
->desc('Update enum attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -2020,6 +2066,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k
->desc('Update IP address attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -2061,6 +2108,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:
->desc('Update URL attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -2102,6 +2150,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ
->desc('Update integer attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -2153,6 +2202,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float
->desc('Update float attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -2204,6 +2254,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole
->desc('Update boolean attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -2244,6 +2295,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet
->desc('Update dateTime attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -2284,6 +2336,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
->desc('Update relationship attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -2341,6 +2394,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->desc('Delete attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.delete')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -2455,6 +2509,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].create')
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('audits.event', 'index.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -2509,7 +2564,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
'required' => true,
'array' => false,
'default' => null,
'size' => 36
'size' => Database::LENGTH_KEY
];
$oldAttributes[] = [
@@ -2624,6 +2679,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes')
->desc('List indexes')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'listIndexes')
@@ -2667,6 +2723,11 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes')
$cursor = reset($cursor);
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$indexId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [
Query::equal('collectionInternalId', [$collection->getInternalId()]),
@@ -2694,6 +2755,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->desc('Get index')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'getIndex')
@@ -2733,6 +2795,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->desc('Delete index')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].indexes.[indexId].update')
->label('audits.event', 'index.delete')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
@@ -2798,6 +2861,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create')
->label('scope', 'documents.write')
->label('resourceType', 'databases')
->label('audits.event', 'document.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
@@ -3049,6 +3113,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
->desc('List documents')
->groups(['api', 'database'])
->label('scope', 'documents.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'listDocuments')
@@ -3094,6 +3159,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
$cursor = \reset($cursor);
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$documentId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId));
@@ -3204,6 +3275,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->desc('Get document')
->groups(['api', 'database'])
->label('scope', 'documents.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'getDocument')
@@ -3296,6 +3368,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->desc('List document logs')
->groups(['api', 'database'])
->label('scope', 'documents.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'listDocumentLogs')
@@ -3401,6 +3474,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
->groups(['api', 'database'])
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].update')
->label('scope', 'documents.write')
->label('resourceType', 'databases')
->label('audits.event', 'document.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{response.$id}')
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
@@ -3636,6 +3710,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->desc('Delete document')
->groups(['api', 'database'])
->label('scope', 'documents.write')
->label('resourceType', 'databases')
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].delete')
->label('audits.event', 'document.delete')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}/document/{request.documentId}')
@@ -3656,11 +3731,10 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->inject('requestTimestamp')
->inject('response')
->inject('dbForProject')
->inject('queueForDeletes')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('mode')
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Usage $queueForUsage, string $mode) {
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, Usage $queueForUsage, string $mode) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
@@ -3733,10 +3807,6 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
)
);
$queueForDeletes
->setType(DELETE_TYPE_AUDIT)
->setDocument($document);
$queueForEvents
->setParam('databaseId', $databaseId)
->setParam('collectionId', $collection->getId())
@@ -3755,6 +3825,7 @@ App::get('/v1/databases/usage')
->desc('Get databases usage stats')
->groups(['api', 'database', 'usage'])
->label('scope', 'collections.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'getUsage')
@@ -3836,6 +3907,7 @@ App::get('/v1/databases/:databaseId/usage')
->desc('Get database usage stats')
->groups(['api', 'database', 'usage'])
->label('scope', 'collections.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'getDatabaseUsage')
@@ -3923,6 +3995,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
->desc('Get collection usage stats')
->groups(['api', 'database', 'usage'])
->label('scope', 'collections.read')
->label('resourceType', 'databases')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'databases')
->label('sdk.method', 'getCollectionUsage')
+50 -3
View File
@@ -11,7 +11,6 @@ use Appwrite\Event\Validator\FunctionEvent;
use Appwrite\Extend\Exception;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Functions\Validator\Headers;
use Appwrite\Functions\Validator\Payload;
use Appwrite\Functions\Validator\RuntimeSpecification;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Platform\Tasks\ScheduleExecutions;
@@ -38,6 +37,7 @@ use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\Roles;
use Utopia\Database\Validator\UID;
use Utopia\Storage\Device;
@@ -138,6 +138,7 @@ App::post('/v1/functions')
->desc('Create function')
->label('scope', 'functions.write')
->label('event', 'functions.[functionId].create')
->label('resourceType', 'functions')
->label('audits.event', 'function.create')
->label('audits.resource', 'function/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -400,6 +401,7 @@ App::get('/v1/functions')
->groups(['api', 'functions'])
->desc('List functions')
->label('scope', 'functions.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'list')
@@ -432,6 +434,12 @@ App::get('/v1/functions')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$functionId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('functions', $functionId);
@@ -454,6 +462,7 @@ App::get('/v1/functions/runtimes')
->groups(['api', 'functions'])
->desc('List runtimes')
->label('scope', 'functions.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listRuntimes')
@@ -487,6 +496,7 @@ App::get('/v1/functions/specifications')
->groups(['api', 'functions'])
->desc('List available function runtime specifications')
->label('scope', 'functions.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listSpecifications')
@@ -523,6 +533,7 @@ App::get('/v1/functions/:functionId')
->groups(['api', 'functions'])
->desc('Get function')
->label('scope', 'functions.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'get')
@@ -547,6 +558,7 @@ App::get('/v1/functions/:functionId/usage')
->desc('Get function usage')
->groups(['api', 'functions', 'usage'])
->label('scope', 'functions.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getFunctionUsage')
@@ -651,6 +663,7 @@ App::get('/v1/functions/usage')
->desc('Get functions usage')
->groups(['api', 'functions'])
->label('scope', 'functions.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getUsage')
@@ -750,6 +763,7 @@ App::put('/v1/functions/:functionId')
->groups(['api', 'functions'])
->desc('Update function')
->label('scope', 'functions.write')
->label('resourceType', 'functions')
->label('event', 'functions.[functionId].update')
->label('audits.event', 'function.update')
->label('audits.resource', 'function/{response.$id}')
@@ -952,6 +966,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
->groups(['api', 'functions'])
->desc('Download deployment')
->label('scope', 'functions.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getDeploymentDownload')
@@ -988,7 +1003,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
$response
->setContentType('application/gzip')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->addHeader('X-Peak', \memory_get_peak_usage())
->addHeader('Content-Disposition', 'attachment; filename="' . $deploymentId . '.tar.gz"');
@@ -1037,6 +1052,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
->groups(['api', 'functions'])
->desc('Update deployment')
->label('scope', 'functions.write')
->label('resourceType', 'functions')
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
->label('audits.event', 'deployment.update')
->label('audits.resource', 'function/{request.functionId}')
@@ -1099,6 +1115,7 @@ App::delete('/v1/functions/:functionId')
->groups(['api', 'functions'])
->desc('Delete function')
->label('scope', 'functions.write')
->label('resourceType', 'functions')
->label('event', 'functions.[functionId].delete')
->label('audits.event', 'function.delete')
->label('audits.resource', 'function/{request.functionId}')
@@ -1146,6 +1163,7 @@ App::post('/v1/functions/:functionId/deployments')
->groups(['api', 'functions'])
->desc('Create deployment')
->label('scope', 'functions.write')
->label('resourceType', 'functions')
->label('event', 'functions.[functionId].deployments.[deploymentId].create')
->label('audits.event', 'deployment.create')
->label('audits.resource', 'function/{request.functionId}')
@@ -1365,6 +1383,7 @@ App::get('/v1/functions/:functionId/deployments')
->groups(['api', 'functions'])
->desc('List deployments')
->label('scope', 'functions.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listDeployments')
@@ -1408,6 +1427,12 @@ App::get('/v1/functions/:functionId/deployments')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$deploymentId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('deployments', $deploymentId);
@@ -1442,6 +1467,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId')
->groups(['api', 'functions'])
->desc('Get deployment')
->label('scope', 'functions.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getDeployment')
@@ -1485,6 +1511,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
->groups(['api', 'functions'])
->desc('Delete deployment')
->label('scope', 'functions.write')
->label('resourceType', 'functions')
->label('event', 'functions.[functionId].deployments.[deploymentId].delete')
->label('audits.event', 'deployment.delete')
->label('audits.resource', 'function/{request.functionId}')
@@ -1550,6 +1577,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
->groups(['api', 'functions'])
->desc('Rebuild deployment')
->label('scope', 'functions.write')
->label('resourceType', 'functions')
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
->label('audits.event', 'deployment.update')
->label('audits.resource', 'function/{request.functionId}')
@@ -1618,6 +1646,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
->groups(['api', 'functions'])
->desc('Cancel deployment')
->label('scope', 'functions.write')
->label('resourceType', 'functions')
->label('audits.event', 'deployment.update')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -1707,7 +1736,9 @@ App::post('/v1/functions/:functionId/executions')
->groups(['api', 'functions'])
->desc('Create execution')
->label('scope', 'execution.write')
->label('resourceType', 'functions')
->label('event', 'functions.[functionId].executions.[executionId].create')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'createExecution')
@@ -1717,7 +1748,7 @@ App::post('/v1/functions/:functionId/executions')
->label('sdk.response.model', Response::MODEL_EXECUTION)
->label('sdk.request.type', Response::CONTENT_TYPE_JSON)
->param('functionId', '', new UID(), 'Function ID.')
->param('body', '', new Payload(10485760, 0), 'HTTP body of execution. Default value is empty string.', true)
->param('body', '', new Text(10485760, 0), 'HTTP body of execution. Default value is empty string.', true)
->param('async', false, new Boolean(true), 'Execute code in the background. Default value is false.', true)
->param('path', '/', new Text(2048), 'HTTP path of execution. Path can include query params. Default value is /', true)
->param('method', 'POST', new Whitelist(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], true), 'HTTP method of execution. Default value is GET.', true)
@@ -2109,6 +2140,7 @@ App::get('/v1/functions/:functionId/executions')
->groups(['api', 'functions'])
->desc('List executions')
->label('scope', 'execution.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listExecutions')
@@ -2154,6 +2186,12 @@ App::get('/v1/functions/:functionId/executions')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$executionId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('executions', $executionId);
@@ -2190,6 +2228,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
->groups(['api', 'functions'])
->desc('Get execution')
->label('scope', 'execution.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getExecution')
@@ -2237,6 +2276,7 @@ App::delete('/v1/functions/:functionId/executions/:executionId')
->groups(['api', 'functions'])
->desc('Delete execution')
->label('scope', 'execution.write')
->label('resourceType', 'functions')
->label('event', 'functions.[functionId].executions.[executionId].delete')
->label('audits.event', 'executions.delete')
->label('audits.resource', 'function/{request.functionId}')
@@ -2307,6 +2347,7 @@ App::post('/v1/functions/:functionId/variables')
->desc('Create variable')
->groups(['api', 'functions'])
->label('scope', 'functions.write')
->label('resourceType', 'functions')
->label('audits.event', 'variable.create')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -2371,6 +2412,7 @@ App::get('/v1/functions/:functionId/variables')
->desc('List variables')
->groups(['api', 'functions'])
->label('scope', 'functions.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listVariables')
@@ -2398,6 +2440,7 @@ App::get('/v1/functions/:functionId/variables/:variableId')
->desc('Get variable')
->groups(['api', 'functions'])
->label('scope', 'functions.read')
->label('resourceType', 'functions')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getVariable')
@@ -2437,6 +2480,7 @@ App::put('/v1/functions/:functionId/variables/:variableId')
->desc('Update variable')
->groups(['api', 'functions'])
->label('scope', 'functions.write')
->label('resourceType', 'functions')
->label('audits.event', 'variable.update')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -2498,6 +2542,7 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
->desc('Delete variable')
->groups(['api', 'functions'])
->label('scope', 'functions.write')
->label('resourceType', 'functions')
->label('audits.event', 'variable.delete')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -2546,6 +2591,7 @@ App::get('/v1/functions/templates')
->groups(['api'])
->desc('List function templates')
->label('scope', 'public')
->label('resourceType', 'functions')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listTemplates')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
@@ -2583,6 +2629,7 @@ App::get('/v1/functions/templates')
App::get('/v1/functions/templates/:templateId')
->desc('Get function template')
->label('scope', 'public')
->label('resourceType', 'functions')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getTemplate')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
+2 -2
View File
@@ -63,13 +63,13 @@ App::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('Cache-Control', 'private, max-age=3888000') // 45 days
;
$response->dynamic(new Document($output), Response::MODEL_LOCALE);
});
App::get('/v1/locale/codes')
->desc('List Locale Codes')
->desc('List locale codes')
->groups(['api', 'locale'])
->label('scope', 'locale.read')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
+72
View File
@@ -32,6 +32,7 @@ use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Queries;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\Roles;
@@ -55,6 +56,7 @@ App::post('/v1/messaging/providers/mailgun')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].create')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createMailgunProvider')
@@ -142,6 +144,7 @@ App::post('/v1/messaging/providers/sendgrid')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].create')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createSendgridProvider')
@@ -217,6 +220,7 @@ App::post('/v1/messaging/providers/smtp')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].create')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createSmtpProvider')
@@ -304,6 +308,7 @@ App::post('/v1/messaging/providers/msg91')
->label('audits.event', 'provider.create')
->label('audits.resource', 'provider/{response.$id}')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('event', 'providers.[providerId].create')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
@@ -381,6 +386,7 @@ App::post('/v1/messaging/providers/telesign')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].create')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createTelesignProvider')
@@ -458,6 +464,7 @@ App::post('/v1/messaging/providers/textmagic')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].create')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createTextmagicProvider')
@@ -535,6 +542,7 @@ App::post('/v1/messaging/providers/twilio')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].create')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createTwilioProvider')
@@ -612,6 +620,7 @@ App::post('/v1/messaging/providers/vonage')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].create')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createVonageProvider')
@@ -689,6 +698,7 @@ App::post('/v1/messaging/providers/fcm')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].create')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createFcmProvider')
@@ -752,6 +762,7 @@ App::post('/v1/messaging/providers/apns')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].create')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createApnsProvider')
@@ -835,6 +846,7 @@ App::get('/v1/messaging/providers')
->desc('List providers')
->groups(['api', 'messaging'])
->label('scope', 'providers.read')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'listProviders')
@@ -866,6 +878,11 @@ App::get('/v1/messaging/providers')
$cursor = reset($cursor);
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$providerId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId));
@@ -886,6 +903,7 @@ App::get('/v1/messaging/providers/:providerId/logs')
->desc('List provider logs')
->groups(['api', 'messaging'])
->label('scope', 'providers.read')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'listProviderLogs')
@@ -974,6 +992,7 @@ App::get('/v1/messaging/providers/:providerId')
->desc('Get provider')
->groups(['api', 'messaging'])
->label('scope', 'providers.read')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'getProvider')
@@ -1001,6 +1020,7 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].update')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateMailgunProvider')
@@ -1107,6 +1127,7 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].update')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateSendgridProvider')
@@ -1198,6 +1219,7 @@ App::patch('/v1/messaging/providers/smtp/:providerId')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].update')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateSmtpProvider')
@@ -1320,6 +1342,7 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].update')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateMsg91Provider')
@@ -1400,6 +1423,7 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].update')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateTelesignProvider')
@@ -1482,6 +1506,7 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].update')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateTextmagicProvider')
@@ -1564,6 +1589,7 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].update')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateTwilioProvider')
@@ -1646,6 +1672,7 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].update')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateVonageProvider')
@@ -1728,6 +1755,7 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].update')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateFcmProvider')
@@ -1797,6 +1825,7 @@ App::patch('/v1/messaging/providers/apns/:providerId')
->label('audits.resource', 'provider/{response.$id}')
->label('event', 'providers.[providerId].update')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateApnsProvider')
@@ -1892,6 +1921,7 @@ App::delete('/v1/messaging/providers/:providerId')
->label('audits.resource', 'provider/{request.$providerId}')
->label('event', 'providers.[providerId].delete')
->label('scope', 'providers.write')
->label('resourceType', 'providers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'deleteProvider')
@@ -1927,6 +1957,7 @@ App::post('/v1/messaging/topics')
->label('audits.resource', 'topic/{response.$id}')
->label('event', 'topics.[topicId].create')
->label('scope', 'topics.write')
->label('resourceType', 'topics')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createTopic')
@@ -1967,6 +1998,7 @@ App::get('/v1/messaging/topics')
->desc('List topics')
->groups(['api', 'messaging'])
->label('scope', 'topics.read')
->label('resourceType', 'topics')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'listTopics')
@@ -1998,6 +2030,11 @@ App::get('/v1/messaging/topics')
$cursor = reset($cursor);
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$topicId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
@@ -2018,6 +2055,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
->desc('List topic logs')
->groups(['api', 'messaging'])
->label('scope', 'topics.read')
->label('resourceType', 'topics')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'listTopicLogs')
@@ -2107,6 +2145,7 @@ App::get('/v1/messaging/topics/:topicId')
->desc('Get topic')
->groups(['api', 'messaging'])
->label('scope', 'topics.read')
->label('resourceType', 'topics')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'getTopic')
@@ -2135,6 +2174,7 @@ App::patch('/v1/messaging/topics/:topicId')
->label('audits.resource', 'topic/{response.$id}')
->label('event', 'topics.[topicId].update')
->label('scope', 'topics.write')
->label('resourceType', 'topics')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateTopic')
@@ -2179,6 +2219,7 @@ App::delete('/v1/messaging/topics/:topicId')
->label('audits.resource', 'topic/{request.$topicId}')
->label('event', 'topics.[topicId].delete')
->label('scope', 'topics.write')
->label('resourceType', 'topics')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'deleteTopic')
@@ -2219,6 +2260,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
->label('audits.resource', 'subscriber/{response.$id}')
->label('event', 'topics.[topicId].subscribers.[subscriberId].create')
->label('scope', 'subscribers.write')
->label('resourceType', 'subscribers')
->label('sdk.auth', [APP_AUTH_TYPE_JWT, APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createSubscriber')
@@ -2312,6 +2354,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
->desc('List subscribers')
->groups(['api', 'messaging'])
->label('scope', 'subscribers.read')
->label('resourceType', 'subscribers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'listSubscribers')
@@ -2352,6 +2395,11 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
$cursor = reset($cursor);
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$subscriberId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId));
@@ -2386,6 +2434,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
->desc('List subscriber logs')
->groups(['api', 'messaging'])
->label('scope', 'subscribers.read')
->label('resourceType', 'subscribers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'listSubscriberLogs')
@@ -2475,6 +2524,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->desc('Get subscriber')
->groups(['api', 'messaging'])
->label('scope', 'subscribers.read')
->label('resourceType', 'subscribers')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'getSubscriber')
@@ -2517,6 +2567,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->label('audits.resource', 'subscriber/{request.$subscriberId}')
->label('event', 'topics.[topicId].subscribers.[subscriberId].delete')
->label('scope', 'subscribers.write')
->label('resourceType', 'subscribers')
->label('sdk.auth', [APP_AUTH_TYPE_JWT, APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'deleteSubscriber')
@@ -2576,6 +2627,7 @@ App::post('/v1/messaging/messages/email')
->label('audits.resource', 'message/{response.$id}')
->label('event', 'messages.[messageId].create')
->label('scope', 'messages.write')
->label('resourceType', 'messages')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createEmail')
@@ -2728,6 +2780,7 @@ App::post('/v1/messaging/messages/sms')
->label('audits.resource', 'message/{response.$id}')
->label('event', 'messages.[messageId].create')
->label('scope', 'messages.write')
->label('resourceType', 'messages')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createSms')
@@ -2844,6 +2897,7 @@ App::post('/v1/messaging/messages/push')
->label('audits.resource', 'message/{response.$id}')
->label('event', 'messages.[messageId].create')
->label('scope', 'messages.write')
->label('resourceType', 'messages')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'createPush')
@@ -3017,6 +3071,7 @@ App::get('/v1/messaging/messages')
->desc('List messages')
->groups(['api', 'messaging'])
->label('scope', 'messages.read')
->label('resourceType', 'messages')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'listMessages')
@@ -3048,6 +3103,11 @@ App::get('/v1/messaging/messages')
$cursor = reset($cursor);
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$messageId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('messages', $messageId));
@@ -3068,6 +3128,7 @@ App::get('/v1/messaging/messages/:messageId/logs')
->desc('List message logs')
->groups(['api', 'messaging'])
->label('scope', 'messages.read')
->label('resourceType', 'messages')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'listMessageLogs')
@@ -3157,6 +3218,7 @@ App::get('/v1/messaging/messages/:messageId/targets')
->desc('List message targets')
->groups(['api', 'messaging'])
->label('scope', 'messages.read')
->label('resourceType', 'messages')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'listTargets')
@@ -3202,6 +3264,11 @@ App::get('/v1/messaging/messages/:messageId/targets')
$cursor = reset($cursor);
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$targetId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('targets', $targetId);
@@ -3222,6 +3289,7 @@ App::get('/v1/messaging/messages/:messageId')
->desc('Get message')
->groups(['api', 'messaging'])
->label('scope', 'messages.read')
->label('resourceType', 'messages')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'getMessage')
@@ -3249,6 +3317,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
->label('audits.resource', 'message/{response.$id}')
->label('event', 'messages.[messageId].update')
->label('scope', 'messages.write')
->label('resourceType', 'messages')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateEmail')
@@ -3449,6 +3518,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
->label('audits.resource', 'message/{response.$id}')
->label('event', 'messages.[messageId].update')
->label('scope', 'messages.write')
->label('resourceType', 'messages')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updateSms')
@@ -3604,6 +3674,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
->label('audits.resource', 'message/{response.$id}')
->label('event', 'messages.[messageId].update')
->label('scope', 'messages.write')
->label('resourceType', 'messages')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'updatePush')
@@ -3842,6 +3913,7 @@ App::delete('/v1/messaging/messages/:messageId')
->label('audits.resource', 'message/{request.messageId}')
->label('event', 'messages.[messageId].delete')
->label('scope', 'messages.write')
->label('resourceType', 'messages')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'messaging')
->label('sdk.method', 'delete')
+27 -15
View File
@@ -16,6 +16,7 @@ use Utopia\Database\Document;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
use Utopia\Migration\Sources\Appwrite;
use Utopia\Migration\Sources\Firebase;
@@ -33,7 +34,7 @@ include_once __DIR__ . '/../shared/api.php';
App::post('/v1/migrations/appwrite')
->groups(['api', 'migrations'])
->desc('Migrate Appwrite Data')
->desc('Migrate Appwrite data')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
@@ -60,6 +61,7 @@ App::post('/v1/migrations/appwrite')
'status' => 'pending',
'stage' => 'init',
'source' => Appwrite::getName(),
'destination' => Appwrite::getName(),
'credentials' => [
'endpoint' => $endpoint,
'projectId' => $projectId,
@@ -87,7 +89,7 @@ App::post('/v1/migrations/appwrite')
App::post('/v1/migrations/firebase/oauth')
->groups(['api', 'migrations'])
->desc('Migrate Firebase Data (OAuth)')
->desc('Migrate Firebase data (OAuth)')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
@@ -164,6 +166,7 @@ App::post('/v1/migrations/firebase/oauth')
'status' => 'pending',
'stage' => 'init',
'source' => Firebase::getName(),
'destination' => Appwrite::getName(),
'credentials' => [
'serviceAccount' => json_encode($serviceAccount),
],
@@ -189,7 +192,7 @@ App::post('/v1/migrations/firebase/oauth')
App::post('/v1/migrations/firebase')
->groups(['api', 'migrations'])
->desc('Migrate Firebase Data (Service Account)')
->desc('Migrate Firebase data (Service Account)')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
@@ -224,6 +227,7 @@ App::post('/v1/migrations/firebase')
'status' => 'pending',
'stage' => 'init',
'source' => Firebase::getName(),
'destination' => Appwrite::getName(),
'credentials' => [
'serviceAccount' => $serviceAccount,
],
@@ -249,7 +253,7 @@ App::post('/v1/migrations/firebase')
App::post('/v1/migrations/supabase')
->groups(['api', 'migrations'])
->desc('Migrate Supabase Data')
->desc('Migrate Supabase data')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
@@ -279,6 +283,7 @@ App::post('/v1/migrations/supabase')
'status' => 'pending',
'stage' => 'init',
'source' => Supabase::getName(),
'destination' => Appwrite::getName(),
'credentials' => [
'endpoint' => $endpoint,
'apiKey' => $apiKey,
@@ -309,7 +314,7 @@ App::post('/v1/migrations/supabase')
App::post('/v1/migrations/nhost')
->groups(['api', 'migrations'])
->desc('Migrate NHost Data')
->desc('Migrate NHost data')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
@@ -340,6 +345,7 @@ App::post('/v1/migrations/nhost')
'status' => 'pending',
'stage' => 'init',
'source' => NHost::getName(),
'destination' => Appwrite::getName(),
'credentials' => [
'subdomain' => $subdomain,
'region' => $region,
@@ -371,7 +377,7 @@ App::post('/v1/migrations/nhost')
App::get('/v1/migrations')
->groups(['api', 'migrations'])
->desc('List Migrations')
->desc('List migrations')
->label('scope', 'migrations.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'migrations')
@@ -404,6 +410,12 @@ App::get('/v1/migrations')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$migrationId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('migrations', $migrationId);
@@ -424,7 +436,7 @@ App::get('/v1/migrations')
App::get('/v1/migrations/:migrationId')
->groups(['api', 'migrations'])
->desc('Get Migration')
->desc('Get migration')
->label('scope', 'migrations.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'migrations')
@@ -448,7 +460,7 @@ App::get('/v1/migrations/:migrationId')
App::get('/v1/migrations/appwrite/report')
->groups(['api', 'migrations'])
->desc('Generate a report on Appwrite Data')
->desc('Generate a report on Appwrite data')
->label('scope', 'migrations.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'migrations')
@@ -490,7 +502,7 @@ App::get('/v1/migrations/appwrite/report')
App::get('/v1/migrations/firebase/report')
->groups(['api', 'migrations'])
->desc('Generate a report on Firebase Data')
->desc('Generate a report on Firebase data')
->label('scope', 'migrations.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'migrations')
@@ -537,7 +549,7 @@ App::get('/v1/migrations/firebase/report')
App::get('/v1/migrations/firebase/report/oauth')
->groups(['api', 'migrations'])
->desc('Generate a report on Firebase Data using OAuth')
->desc('Generate a report on Firebase data using OAuth')
->label('scope', 'migrations.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'migrations')
@@ -627,7 +639,7 @@ App::get('/v1/migrations/firebase/report/oauth')
});
App::get('/v1/migrations/firebase/connect')
->desc('Authorize with firebase')
->desc('Authorize with Firebase')
->groups(['api', 'migrations'])
->label('scope', 'migrations.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
@@ -781,7 +793,7 @@ App::get('/v1/migrations/firebase/redirect')
});
App::get('/v1/migrations/firebase/projects')
->desc('List Firebase Projects')
->desc('List Firebase projects')
->groups(['api', 'migrations'])
->label('scope', 'migrations.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
@@ -870,7 +882,7 @@ App::get('/v1/migrations/firebase/projects')
});
App::get('/v1/migrations/firebase/deauthorize')
->desc('Revoke Appwrite\'s authorization to access Firebase Projects')
->desc('Revoke Appwrite\'s authorization to access Firebase projects')
->groups(['api', 'migrations'])
->label('scope', 'migrations.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
@@ -985,7 +997,7 @@ App::get('/v1/migrations/nhost/report')
App::patch('/v1/migrations/:migrationId')
->groups(['api', 'migrations'])
->desc('Retry Migration')
->desc('Retry migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].retry')
->label('audits.event', 'migration.retry')
@@ -1030,7 +1042,7 @@ App::patch('/v1/migrations/:migrationId')
App::delete('/v1/migrations/:migrationId')
->groups(['api', 'migrations'])
->desc('Delete Migration')
->desc('Delete migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].delete')
->label('audits.event', 'migrationId.delete')
+5 -5
View File
@@ -309,7 +309,7 @@ App::get('/v1/project/usage')
// Variables
App::post('/v1/project/variables')
->desc('Create Variable')
->desc('Create variable')
->groups(['api'])
->label('scope', 'projects.write')
->label('audits.event', 'variable.create')
@@ -364,7 +364,7 @@ App::post('/v1/project/variables')
});
App::get('/v1/project/variables')
->desc('List Variables')
->desc('List variables')
->groups(['api'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
@@ -389,7 +389,7 @@ App::get('/v1/project/variables')
});
App::get('/v1/project/variables/:variableId')
->desc('Get Variable')
->desc('Get variable')
->groups(['api'])
->label('scope', 'projects.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
@@ -413,7 +413,7 @@ App::get('/v1/project/variables/:variableId')
});
App::put('/v1/project/variables/:variableId')
->desc('Update Variable')
->desc('Update variable')
->groups(['api'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
@@ -459,7 +459,7 @@ App::put('/v1/project/variables/:variableId')
});
App::delete('/v1/project/variables/:variableId')
->desc('Delete Variable')
->desc('Delete variable')
->groups(['api'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
+19 -11
View File
@@ -31,6 +31,7 @@ use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
use Utopia\Domains\Validator\PublicDomain;
use Utopia\DSN\DSN;
@@ -279,6 +280,12 @@ App::get('/v1/projects')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$projectId = $cursor->getValue();
$cursorDocument = $dbForConsole->getDocument('projects', $projectId);
@@ -923,6 +930,7 @@ App::delete('/v1/projects/:projectId')
}
$queueForDeletes
->setProject($project)
->setType(DELETE_TYPE_DOCUMENT)
->setDocument($project);
@@ -1199,7 +1207,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
App::post('/v1/projects/:projectId/keys')
->desc('Create key')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('scope', 'keys.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createKey')
@@ -1249,7 +1257,7 @@ App::post('/v1/projects/:projectId/keys')
App::get('/v1/projects/:projectId/keys')
->desc('List keys')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('scope', 'keys.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listKeys')
@@ -1281,7 +1289,7 @@ App::get('/v1/projects/:projectId/keys')
App::get('/v1/projects/:projectId/keys/:keyId')
->desc('Get key')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('scope', 'keys.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getKey')
@@ -1315,7 +1323,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
App::put('/v1/projects/:projectId/keys/:keyId')
->desc('Update key')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('scope', 'keys.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateKey')
@@ -1361,7 +1369,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
App::delete('/v1/projects/:projectId/keys/:keyId')
->desc('Delete key')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('scope', 'keys.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deleteKey')
@@ -1436,7 +1444,7 @@ App::post('/v1/projects/:projectId/platforms')
->desc('Create platform')
->groups(['api', 'projects'])
->label('audits.event', 'platforms.create')
->label('scope', 'projects.write')
->label('scope', 'platforms.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createPlatform')
@@ -1486,7 +1494,7 @@ App::post('/v1/projects/:projectId/platforms')
App::get('/v1/projects/:projectId/platforms')
->desc('List platforms')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('scope', 'platforms.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'listPlatforms')
@@ -1518,7 +1526,7 @@ App::get('/v1/projects/:projectId/platforms')
App::get('/v1/projects/:projectId/platforms/:platformId')
->desc('Get platform')
->groups(['api', 'projects'])
->label('scope', 'projects.read')
->label('scope', 'platforms.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'getPlatform')
@@ -1552,7 +1560,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
App::put('/v1/projects/:projectId/platforms/:platformId')
->desc('Update platform')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('scope', 'platforms.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updatePlatform')
@@ -1600,7 +1608,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
->desc('Delete platform')
->groups(['api', 'projects'])
->label('audits.event', 'platforms.delete')
->label('scope', 'projects.write')
->label('scope', 'platforms.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'deletePlatform')
@@ -1744,7 +1752,7 @@ App::post('/v1/projects/:projectId/smtp/tests')
->param('port', 587, new Integer(), 'SMTP server port', true)
->param('username', '', new Text(0, 0), 'SMTP server username', true)
->param('password', '', new Text(0, 0), 'SMTP server password', true)
->param('secure', '', new WhiteList(['tls'], true), 'Does SMTP server use secure connection', true)
->param('secure', '', new WhiteList(['tls', 'ssl'], true), 'Does SMTP server use secure connection', true)
->inject('response')
->inject('dbForConsole')
->inject('queueForMails')
+12 -5
View File
@@ -13,6 +13,7 @@ use Utopia\Database\Document;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
use Utopia\Domains\Domain;
use Utopia\Logger\Log;
@@ -23,7 +24,7 @@ use Utopia\Validator\WhiteList;
App::post('/v1/proxy/rules')
->groups(['api', 'proxy'])
->desc('Create Rule')
->desc('Create rule')
->label('scope', 'rules.write')
->label('event', 'rules.[ruleId].create')
->label('audits.event', 'rule.create')
@@ -149,7 +150,7 @@ App::post('/v1/proxy/rules')
App::get('/v1/proxy/rules')
->groups(['api', 'proxy'])
->desc('List Rules')
->desc('List rules')
->label('scope', 'rules.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'proxy')
@@ -185,6 +186,12 @@ App::get('/v1/proxy/rules')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$ruleId = $cursor->getValue();
$cursorDocument = $dbForConsole->getDocument('rules', $ruleId);
@@ -212,7 +219,7 @@ App::get('/v1/proxy/rules')
App::get('/v1/proxy/rules/:ruleId')
->groups(['api', 'proxy'])
->desc('Get Rule')
->desc('Get rule')
->label('scope', 'rules.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'proxy')
@@ -241,7 +248,7 @@ App::get('/v1/proxy/rules/:ruleId')
App::delete('/v1/proxy/rules/:ruleId')
->groups(['api', 'proxy'])
->desc('Delete Rule')
->desc('Delete rule')
->label('scope', 'rules.write')
->label('event', 'rules.[ruleId].delete')
->label('audits.event', 'rules.delete')
@@ -277,7 +284,7 @@ App::delete('/v1/proxy/rules/:ruleId')
});
App::patch('/v1/proxy/rules/:ruleId/verification')
->desc('Update Rule Verification Status')
->desc('Update rule verification status')
->groups(['api', 'proxy'])
->label('scope', 'rules.write')
->label('event', 'rules.[ruleId].update')
+42 -13
View File
@@ -24,6 +24,7 @@ use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
use Utopia\Image\Image;
use Utopia\Storage\Compression\Algorithms\GZIP;
@@ -48,6 +49,7 @@ App::post('/v1/storage/buckets')
->desc('Create bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.write')
->label('resourceType', 'buckets')
->label('event', 'buckets.[bucketId].create')
->label('audits.event', 'bucket.create')
->label('audits.resource', 'bucket/{response.$id}')
@@ -146,6 +148,7 @@ App::get('/v1/storage/buckets')
->desc('List buckets')
->groups(['api', 'storage'])
->label('scope', 'buckets.read')
->label('resourceType', 'buckets')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'listBuckets')
@@ -178,6 +181,12 @@ App::get('/v1/storage/buckets')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$bucketId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('buckets', $bucketId);
@@ -200,6 +209,7 @@ App::get('/v1/storage/buckets/:bucketId')
->desc('Get bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.read')
->label('resourceType', 'buckets')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getBucket')
@@ -225,6 +235,7 @@ App::put('/v1/storage/buckets/:bucketId')
->desc('Update bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.write')
->label('resourceType', 'buckets')
->label('event', 'buckets.[bucketId].update')
->label('audits.event', 'bucket.update')
->label('audits.resource', 'bucket/{response.$id}')
@@ -292,6 +303,7 @@ App::delete('/v1/storage/buckets/:bucketId')
->desc('Delete bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.write')
->label('resourceType', 'buckets')
->label('audits.event', 'bucket.delete')
->label('event', 'buckets.[bucketId].delete')
->label('audits.resource', 'bucket/{request.bucketId}')
@@ -334,6 +346,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->desc('Create file')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('resourceType', 'buckets')
->label('audits.event', 'file.create')
->label('event', 'buckets.[bucketId].files.[fileId].create')
->label('audits.resource', 'file/{response.$id}')
@@ -695,6 +708,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
->desc('List files')
->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('resourceType', 'buckets')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'listFiles')
@@ -744,6 +758,12 @@ App::get('/v1/storage/buckets/:bucketId/files')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$fileId = $cursor->getValue();
if ($fileSecurity && !$valid) {
@@ -780,6 +800,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
->desc('Get file')
->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('resourceType', 'buckets')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFile')
@@ -827,6 +848,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->desc('Get file preview')
->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('resourceType', 'buckets')
->label('cache', true)
->label('cache.resourceType', 'bucket/{request.bucketId}')
->label('cache.resource', 'file/{request.fileId}')
@@ -889,10 +911,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
if ((\strpos($request->getAccept(), 'image/webp') === false) && ('webp' === $output)) { // Fallback webp to jpeg when no browser support
$output = 'jpg';
}
$inputs = Config::getParam('storage-inputs');
$outputs = Config::getParam('storage-outputs');
$fileLogos = Config::getParam('storage-logos');
@@ -990,7 +1008,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg'];
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType($contentType)
->file($data)
;
@@ -1003,6 +1021,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->desc('Get file for download')
->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('resourceType', 'buckets')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFileDownload')
@@ -1053,7 +1072,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
$response
->setContentType($file->getAttribute('mimeType'))
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->addHeader('X-Peak', \memory_get_peak_usage())
->addHeader('Content-Disposition', 'attachment; filename="' . $file->getAttribute('name', '') . '"')
;
@@ -1143,6 +1162,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->desc('Get file for view')
->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('resourceType', 'buckets')
->label('cache', true)
->label('cache.resourceType', 'bucket/{request.bucketId}')
->label('cache.resource', 'file/{request.fileId}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getFileView')
@@ -1203,7 +1226,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->addHeader('X-Peak', \memory_get_peak_usage())
;
@@ -1262,20 +1285,21 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
if (!empty($source)) {
if (!empty($rangeHeader)) {
$response->send(substr($source, $start, ($end - $start + 1)));
$response->file(substr($source, $start, ($end - $start + 1)));
}
$response->send($source);
$response->file($source);
return;
}
if (!empty($rangeHeader)) {
$response->send($deviceForFiles->read($path, $start, ($end - $start + 1)));
$response->file($deviceForFiles->read($path, $start, ($end - $start + 1)));
return;
}
$size = $deviceForFiles->getFileSize($path);
if ($size > APP_STORAGE_READ_BUFFER) {
for ($i = 0; $i < ceil($size / MAX_OUTPUT_CHUNK_SIZE); $i++) {
$response->chunk(
$deviceForFiles->read(
$path,
@@ -1286,7 +1310,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
);
}
} else {
$response->send($deviceForFiles->read($path));
$response->file($deviceForFiles->read($path));
}
});
@@ -1294,6 +1318,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->desc('Get file for push notification')
->groups(['api', 'storage'])
->label('scope', 'public')
->label('resourceType', 'buckets')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location')
@@ -1357,7 +1382,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->addHeader('Content-Security-Policy', 'script-src none;')
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->addHeader('X-Peak', \memory_get_peak_usage());
$size = $file->getAttribute('sizeOriginal', 0);
@@ -1448,6 +1473,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->desc('Update file')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('resourceType', 'buckets')
->label('event', 'buckets.[bucketId].files.[fileId].update')
->label('audits.event', 'file.update')
->label('audits.resource', 'file/{response.$id}')
@@ -1549,9 +1575,10 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
});
App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->desc('Delete File')
->desc('Delete file')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('resourceType', 'buckets')
->label('event', 'buckets.[bucketId].files.[fileId].delete')
->label('audits.event', 'file.delete')
->label('audits.resource', 'file/{request.fileId}')
@@ -1645,6 +1672,7 @@ App::get('/v1/storage/usage')
->desc('Get storage usage stats')
->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('resourceType', 'buckets')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getUsage')
@@ -1724,6 +1752,7 @@ App::get('/v1/storage/:bucketId/usage')
->desc('Get bucket usage stats')
->groups(['api', 'storage'])
->label('scope', 'files.read')
->label('resourceType', 'buckets')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'getBucketUsage')
+37 -2
View File
@@ -34,6 +34,7 @@ use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Queries;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
@@ -43,6 +44,7 @@ use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Host;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
App::post('/v1/teams')
->desc('Create team')
@@ -169,6 +171,12 @@ App::get('/v1/teams')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$teamId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('teams', $teamId);
@@ -394,7 +402,17 @@ App::post('/v1/teams/:teamId/memberships')
->param('email', '', new Email(), 'Email of the new team member.', true)
->param('userId', '', new UID(), 'ID of the user to be added to a team.', true)
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.')
->param('roles', [], function (Document $project) {
if ($project->getId() === 'console') {
;
$roles = array_keys(Config::getParam('roles', []));
array_filter($roles, function ($role) {
return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]);
});
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
}
return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE);
}, 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project'])
->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) // TODO add our own built-in confirm page
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
->inject('response')
@@ -740,6 +758,13 @@ App::get('/v1/teams/:teamId/memberships')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$membershipId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('memberships', $membershipId);
@@ -868,7 +893,17 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->label('sdk.response.model', Response::MODEL_MEMBERSHIP)
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.')
->param('roles', [], function (Document $project) {
if ($project->getId() === 'console') {
;
$roles = array_keys(Config::getParam('roles', []));
array_filter($roles, function ($role) {
return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]);
});
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
}
return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE);
}, 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project'])
->inject('request')
->inject('response')
->inject('user')
+34 -13
View File
@@ -36,6 +36,7 @@ use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Queries;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
@@ -452,7 +453,7 @@ App::post('/v1/users/scrypt-modified')
});
App::post('/v1/users/:userId/targets')
->desc('Create User Target')
->desc('Create user target')
->groups(['api', 'users'])
->label('audits.event', 'target.create')
->label('audits.resource', 'target/response.$id')
@@ -576,6 +577,12 @@ App::get('/v1/users')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$userId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('users', $userId);
@@ -647,7 +654,7 @@ App::get('/v1/users/:userId/prefs')
});
App::get('/v1/users/:userId/targets/:targetId')
->desc('Get User Target')
->desc('Get user target')
->groups(['api', 'users'])
->label('scope', 'targets.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
@@ -848,7 +855,7 @@ App::get('/v1/users/:userId/logs')
});
App::get('/v1/users/:userId/targets')
->desc('List User Targets')
->desc('List user targets')
->groups(['api', 'users'])
->label('scope', 'targets.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_ADMIN])
@@ -886,6 +893,11 @@ App::get('/v1/users/:userId/targets')
$cursor = reset($cursor);
if ($cursor) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$targetId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('targets', $targetId);
@@ -903,7 +915,7 @@ App::get('/v1/users/:userId/targets')
});
App::get('/v1/users/identities')
->desc('List Identities')
->desc('List identities')
->groups(['api', 'users'])
->label('scope', 'users.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@@ -938,6 +950,12 @@ App::get('/v1/users/identities')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$identityId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('identities', $identityId);
@@ -1425,7 +1443,7 @@ App::patch('/v1/users/:userId/prefs')
});
App::patch('/v1/users/:userId/targets/:targetId')
->desc('Update User target')
->desc('Update user target')
->groups(['api', 'users'])
->label('audits.event', 'target.update')
->label('audits.resource', 'target/{response.$id}')
@@ -1485,7 +1503,9 @@ App::patch('/v1/users/:userId/targets/:targetId')
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE);
}
$target->setAttribute('identifier', $identifier);
$target
->setAttribute('identifier', $identifier)
->setAttribute('expired', false);
}
if ($providerId) {
@@ -1499,8 +1519,9 @@ App::patch('/v1/users/:userId/targets/:targetId')
throw new Exception(Exception::PROVIDER_INCORRECT_TYPE);
}
$target->setAttribute('providerId', $provider->getId());
$target->setAttribute('providerInternalId', $provider->getInternalId());
$target
->setAttribute('providerId', $provider->getId())
->setAttribute('providerInternalId', $provider->getInternalId());
}
if ($name) {
@@ -1557,7 +1578,7 @@ App::patch('/v1/users/:userId/mfa')
});
App::get('/v1/users/:userId/mfa/factors')
->desc('List Factors')
->desc('List factors')
->groups(['api', 'users'])
->label('scope', 'users.read')
->label('usage.metric', 'users.{scope}.requests.read')
@@ -1590,7 +1611,7 @@ App::get('/v1/users/:userId/mfa/factors')
});
App::get('/v1/users/:userId/mfa/recovery-codes')
->desc('Get MFA Recovery Codes')
->desc('Get MFA recovery codes')
->groups(['api', 'users'])
->label('scope', 'users.read')
->label('usage.metric', 'users.{scope}.requests.read')
@@ -1625,7 +1646,7 @@ App::get('/v1/users/:userId/mfa/recovery-codes')
});
App::patch('/v1/users/:userId/mfa/recovery-codes')
->desc('Create MFA Recovery Codes')
->desc('Create MFA recovery codes')
->groups(['api', 'users'])
->label('event', 'users.[userId].create.mfa.recovery-codes')
->label('scope', 'users.write')
@@ -1671,7 +1692,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
});
App::put('/v1/users/:userId/mfa/recovery-codes')
->desc('Regenerate MFA Recovery Codes')
->desc('Regenerate MFA recovery codes')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.mfa.recovery-codes')
->label('scope', 'users.write')
@@ -1716,7 +1737,7 @@ App::put('/v1/users/:userId/mfa/recovery-codes')
});
App::delete('/v1/users/:userId/mfa/authenticators/:type')
->desc('Delete Authenticator')
->desc('Delete authenticator')
->groups(['api', 'users'])
->label('event', 'users.[userId].delete.mfa')
->label('scope', 'users.write')
+13 -6
View File
@@ -20,6 +20,7 @@ use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Detector\Adapter\Bun;
use Utopia\Detector\Adapter\CPP;
use Utopia\Detector\Adapter\Dart;
@@ -263,7 +264,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
};
App::get('/v1/vcs/github/authorize')
->desc('Install GitHub App')
->desc('Install GitHub app')
->groups(['api', 'vcs'])
->label('scope', 'vcs.read')
->label('sdk.namespace', 'vcs')
@@ -305,7 +306,7 @@ App::get('/v1/vcs/github/authorize')
});
App::get('/v1/vcs/github/callback')
->desc('Capture installation and authorization from GitHub App')
->desc('Capture installation and authorization from GitHub app')
->groups(['api', 'vcs'])
->label('scope', 'public')
->label('error', __DIR__ . '/../../views/general/error.phtml')
@@ -598,7 +599,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:pr
});
App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
->desc('List Repositories')
->desc('List repositories')
->groups(['api', 'vcs'])
->label('scope', 'vcs.read')
->label('sdk.namespace', 'vcs')
@@ -843,7 +844,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
});
App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/branches')
->desc('List Repository Branches')
->desc('List repository branches')
->groups(['api', 'vcs'])
->label('scope', 'vcs.read')
->label('sdk.namespace', 'vcs')
@@ -892,7 +893,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
});
App::post('/v1/vcs/github/events')
->desc('Create Event')
->desc('Create event')
->groups(['api', 'vcs'])
->label('scope', 'public')
->inject('gitHub')
@@ -1069,6 +1070,12 @@ App::get('/v1/vcs/installations')
$cursor = reset($cursor);
if ($cursor) {
/** @var Query $cursor */
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$installationId = $cursor->getValue();
$cursorDocument = $dbForConsole->getDocument('installations', $installationId);
@@ -1120,7 +1127,7 @@ App::get('/v1/vcs/installations/:installationId')
});
App::delete('/v1/vcs/installations/:installationId')
->desc('Delete Installation')
->desc('Delete installation')
->groups(['api', 'vcs'])
->label('scope', 'vcs.write')
->label('sdk.namespace', 'vcs')
+66 -12
View File
@@ -6,6 +6,7 @@ use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth;
use Appwrite\Event\Certificate;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Network\Validator\Origin;
@@ -25,6 +26,7 @@ use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
@@ -44,7 +46,7 @@ Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost');
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
function router(App $utopia, Database $dbForConsole, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, Usage $queueForUsage, Reader $geodb)
function router(App $utopia, Database $dbForConsole, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked)
{
$utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml');
@@ -135,6 +137,10 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
throw new AppwriteException(AppwriteException::FUNCTION_NOT_FOUND);
}
if ($isResourceBlocked($project, 'functions', $functionId)) {
throw new AppwriteException(AppwriteException::GENERAL_RESOURCE_BLOCKED);
}
$version = $function->getAttribute('version', 'v2');
$runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []);
$spec = Config::getParam('runtime-specifications')[$function->getAttribute('specification', APP_FUNCTION_SPECIFICATION_DEFAULT)];
@@ -366,7 +372,11 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
->trigger()
;
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
$queueForFunctions
->setType(Func::TYPE_ASYNC_WRITE)
->setExecution($execution)
->setProject($project)
->trigger();
}
$execution->setAttribute('logs', '');
@@ -450,7 +460,9 @@ App::init()
->inject('queueForUsage')
->inject('queueForEvents')
->inject('queueForCertificates')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates) {
->inject('queueForFunctions')
->inject('isResourceBlocked')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked) {
/*
* Appwrite Router
*/
@@ -458,7 +470,7 @@ App::init()
$mainDomain = System::getEnv('_APP_DOMAIN', '');
// Only run Router when external domain
if ($host !== $mainDomain) {
if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb)) {
if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked)) {
return;
}
}
@@ -666,8 +678,10 @@ App::options()
->inject('getProjectDB')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('queueForFunctions')
->inject('geodb')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) {
->inject('isResourceBlocked')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked) {
/*
* Appwrite Router
*/
@@ -675,7 +689,7 @@ App::options()
$mainDomain = System::getEnv('_APP_DOMAIN', '');
// Only run Router when external domain
if ($host !== $mainDomain) {
if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb)) {
if (router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked)) {
return;
}
}
@@ -863,8 +877,12 @@ App::error()
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('Log pushed with status code: ' . $responseCode);
try {
$responseCode = $logger->addLog($log);
Console::info('Error log pushed with status code: ' . $responseCode);
} catch (Throwable $th) {
Console::error('Error pushing log: ' . $th->getMessage());
}
}
/** Wrap all exceptions inside Appwrite\Extend\Exception */
@@ -953,8 +971,10 @@ App::get('/robots.txt')
->inject('getProjectDB')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('queueForFunctions')
->inject('geodb')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) {
->inject('isResourceBlocked')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked) {
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
@@ -962,7 +982,7 @@ App::get('/robots.txt')
$template = new View(__DIR__ . '/../views/general/robots.phtml');
$response->text($template->render(false));
} else {
router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb);
router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked);
}
});
@@ -978,8 +998,10 @@ App::get('/humans.txt')
->inject('getProjectDB')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('queueForFunctions')
->inject('geodb')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Reader $geodb) {
->inject('isResourceBlocked')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForConsole, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked) {
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
@@ -987,7 +1009,7 @@ App::get('/humans.txt')
$template = new View(__DIR__ . '/../views/general/humans.phtml');
$response->text($template->render(false));
} else {
router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $geodb);
router($utopia, $dbForConsole, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked);
}
});
@@ -1044,6 +1066,38 @@ App::get('/.well-known/acme-challenge/*')
include_once __DIR__ . '/shared/api.php';
include_once __DIR__ . '/shared/api/auth.php';
App::get('/v1/ping')
->groups(['api', 'general'])
->desc('Test the connection between the Appwrite and the SDK.')
->label('scope', 'global')
->label('event', 'projects.[projectId].ping')
->inject('response')
->inject('project')
->inject('dbForConsole')
->inject('queueForEvents')
->action(function (Response $response, Document $project, Database $dbForConsole, Event $queueForEvents) {
if ($project->isEmpty()) {
throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND);
}
$pingCount = $project->getAttribute('pingCount', 0) + 1;
$pingedAt = DateTime::now();
$project
->setAttribute('pingCount', $pingCount)
->setAttribute('pingedAt', $pingedAt);
Authorization::skip(function () use ($dbForConsole, $project) {
$dbForConsole->updateDocument('projects', $project->getId(), $project);
});
$queueForEvents
->setParam('projectId', $project->getId())
->setPayload($response->output($project, Response::MODEL_PROJECT));
$response->text('Pong!');
});
App::wildcard()
->groups(['api'])
->label('scope', 'global')
+1 -1
View File
@@ -158,7 +158,7 @@ App::patch('/v1/mock/functions-v2')
App::post('/v1/mock/api-key-unprefixed')
->desc('Create API Key (without standard prefix)')
->groups(['mock', 'api', 'projects'])
->label('scope', 'projects.write')
->label('scope', 'public')
->label('docs', false)
->param('projectId', '', new UID(), 'Project ID.')
->inject('response')
+69 -43
View File
@@ -160,42 +160,22 @@ App::init()
->inject('session')
->inject('servers')
->inject('mode')
->action(function (App $utopia, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode) {
->inject('team')
->action(function (App $utopia, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team) {
$route = $utopia->getRoute();
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
/**
* ACL Check
*/
/** Default role */
$roles = Config::getParam('roles', []);
$role = ($user->isEmpty())
? Role::guests()->toString()
: Role::users()->toString();
// Add user roles
$memberships = $user->find('teamId', $project->getAttribute('teamId'), 'memberships');
if ($memberships) {
foreach ($memberships->getAttribute('roles', []) as $memberRole) {
switch ($memberRole) {
case 'owner':
$role = Auth::USER_ROLE_OWNER;
break;
case 'admin':
$role = Auth::USER_ROLE_ADMIN;
break;
case 'developer':
$role = Auth::USER_ROLE_DEVELOPER;
break;
}
}
}
$roles = Config::getParam('roles', []);
$scope = $route->getLabel('scope', 'none'); // Allowed scope for chosen route
$scopes = $roles[$role]['scopes']; // Allowed scopes for user role
/** Allowed Scopes for the role */
$scopes = $roles[$role]['scopes'];
$apiKey = $request->getHeader('x-appwrite-key', '');
@@ -294,13 +274,38 @@ App::init()
}
}
}
// Admin User Authentication
elseif (($project->getId() === 'console' && !$team->isEmpty() && !$user->isEmpty()) || ($project->getId() !== 'console' && !$user->isEmpty() && $mode === APP_MODE_ADMIN)) {
$teamId = $team->getId();
$adminRoles = [];
$memberships = $user->getAttribute('memberships', []);
foreach ($memberships as $membership) {
if ($membership->getAttribute('confirm', false) === true && $membership->getAttribute('teamId') === $teamId) {
$adminRoles = $membership->getAttribute('roles', []);
break;
}
}
if (empty($adminRoles)) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$scopes = []; // reset scope if admin
foreach ($adminRoles as $role) {
$scopes = \array_merge($scopes, $roles[$role]['scopes']);
}
Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
}
$scopes = \array_unique($scopes);
Authorization::setRole($role);
foreach (Auth::getRoles($user) as $authRole) {
Authorization::setRole($authRole);
}
/** Do not allow access to disabled services */
$service = $route->getLabel('sdk.namespace', '');
if (!empty($service)) {
if (
@@ -311,14 +316,14 @@ App::init()
throw new Exception(Exception::GENERAL_SERVICE_DISABLED);
}
}
if (!\in_array($scope, $scopes)) {
if ($project->isEmpty()) { // Check if permission is denied because project is missing
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
/** Do now allow access if scope is not allowed */
$scope = $route->getLabel('scope', 'none');
if (!\in_array($scope, $scopes)) {
throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')');
}
/** Do not allow access to blocked accounts */
if (false === $user->getAttribute('status')) { // Account is blocked
throw new Exception(Exception::USER_BLOCKED);
}
@@ -457,7 +462,11 @@ App::init()
$useCache = $route->getLabel('cache', false);
if ($useCache) {
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
$keyParams = $request->getURI() . '*' . implode('*', $request->getParams());
if(!empty($request->getRangeStart())) $keyParams .= '*'.$request->getRangeStart();
if(!empty($request->getRangeEnd())) $keyParams .= '*'.$request->getRangeEnd();
$keyParams .= '*' . APP_CACHE_BUSTER;
$key = md5($keyParams);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
@@ -466,6 +475,7 @@ App::init()
$data = $cache->load($key, $timestamp);
if (!empty($data) && !$cacheLog->isEmpty()) {
$parts = explode('/', $cacheLog->getAttribute('resourceType'));
$type = $parts[0] ?? null;
@@ -501,17 +511,19 @@ App::init()
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
}
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $timestamp) . ' GMT')
->addHeader('X-Appwrite-Cache', 'hit')
->setContentType($cacheLog->getAttribute('mimeType'))
->send($data);
->addHeader('Cache-Control', sprintf('private, max-age=%d', $timestamp))
->addHeader('X-Appwrite-Cache', 'hit');
foreach ($cacheLog->getAttribute('headers') as $key => $value) {
$response->addHeader($key, $value);
}
$response->send($data);
} else {
$response
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
->addHeader('Pragma', 'no-cache')
->addHeader('Expires', 0)
->addHeader('Expires', '0')
->addHeader('X-Appwrite-Cache', 'miss')
;
}
@@ -699,7 +711,8 @@ App::shutdown()
if (!empty($queueForMessaging->getType())) {
$queueForMessaging->trigger();
}
var_dump($request->getRangeStart());
var_dump($request->getRangeEnd());
/**
* Cache label
*/
@@ -707,7 +720,9 @@ App::shutdown()
if ($useCache) {
$resource = $resourceType = null;
$data = $response->getPayload();
var_dump($response->getHeaders());
if (!empty($data['payload'])) {
$pattern = $route->getLabel('cache.resource', null);
if (!empty($pattern)) {
$resource = $parseLabel($pattern, $responsePayload, $requestParams, $user);
@@ -718,17 +733,30 @@ App::shutdown()
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
}
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
$keyParams = $request->getURI() . '*' . implode('*', $request->getParams());
if(!empty($request->getRangeStart())) $keyParams .= '*'.$request->getRangeStart();
if(!empty($request->getRangeEnd())) $keyParams .= '*'.$request->getRangeEnd();
$keyParams .= '*'. APP_CACHE_BUSTER;
$key = md5($keyParams);
$signature = md5($data['payload']);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
$now = DateTime::now();
if ($cacheLog->isEmpty()) {
$filterList = ['Cache-Control', 'Pragma', 'Expires', 'X-Appwrite-Cache'];
$headers = [];
foreach ($response->getHeaders() as $name => $value) {
if(!in_array($name, $filterList)) {
$headers[$name] = $value;
}
}
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
'$id' => $key,
'resource' => $resource,
'resourceType' => $resourceType,
'mimeType' => $response->getContentType(),
'headers' => $headers,
'accessedAt' => $now,
'signature' => $signature,
])));
@@ -746,8 +774,6 @@ App::shutdown()
}
}
if ($project->getId() !== 'console') {
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
$fileSize = 0;
+6 -2
View File
@@ -298,8 +298,12 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('Log pushed with status code: ' . $responseCode);
try {
$responseCode = $logger->addLog($log);
Console::info('Error log pushed with status code: ' . $responseCode);
} catch (Throwable $th) {
Console::error('Error pushing log: ' . $th->getMessage());
}
}
Console::error('[Error] Type: ' . get_class($th));
+59 -17
View File
@@ -41,6 +41,7 @@ use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Origin;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\URL\URL as AppwriteURL;
use Appwrite\Utopia\Request;
use MaxMind\Db\Reader;
use PHPMailer\PHPMailer\PHPMailer;
use Swoole\Database\PDOProxy;
@@ -129,6 +130,7 @@ const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange';
const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange';
const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char
const APP_DATABASE_TIMEOUT_MILLISECONDS = 15_000;
const APP_DATABASE_QUERY_MAX_VALUES = 500;
const APP_STORAGE_UPLOADS = '/storage/uploads';
const APP_STORAGE_FUNCTIONS = '/storage/functions';
const APP_STORAGE_BUILDS = '/storage/builds';
@@ -148,9 +150,12 @@ const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
const APP_HOSTNAME_INTERNAL = 'appwrite';
const APP_FUNCTION_SPECIFICATION_DEFAULT = Specification::S_05VCPU_512MB;
const APP_FUNCTION_SPECIFICATION_DEFAULT = Specification::S_1VCPU_512MB;
const APP_FUNCTION_CPUS_DEFAULT = 0.5;
const APP_FUNCTION_MEMORY_DEFAULT = 512;
const APP_PLATFORM_SERVER = 'server';
const APP_PLATFORM_CLIENT = 'client';
const APP_PLATFORM_CONSOLE = 'console';
// Database Reconnect
const DATABASE_RECONNECT_SLEEP = 2;
@@ -239,15 +244,12 @@ const METRIC_SESSIONS = 'sessions';
const METRIC_DATABASES = 'databases';
const METRIC_COLLECTIONS = 'collections';
const METRIC_DATABASES_STORAGE = 'databases.storage';
const METRIC_DATABASES_STORAGE_DISK = 'databases.storage_disk';
const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections';
const METRIC_DATABASE_ID_STORAGE = '{databaseInternalId}.databases.storage';
const METRIC_DATABASE_ID_STORAGE_DISK = '{databaseInternalId}.databases.storage_disk';
const METRIC_DOCUMENTS = 'documents';
const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE = '{databaseInternalId}.{collectionInternalId}.databases.storage';
const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE_DISK = '{databaseInternalId}.{collectionInternalId}.databases.storage_disk';
const METRIC_BUCKETS = 'buckets';
const METRIC_FILES = 'files';
const METRIC_FILES_STORAGE = 'files.storage';
@@ -1258,13 +1260,13 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
$user = new Document([]);
}
if (APP_MODE_ADMIN === $mode) {
if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) {
Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
} else {
$user = new Document([]);
}
}
// if (APP_MODE_ADMIN === $mode) {
// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) {
// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
// } else {
// $user = new Document([]);
// }
// }
$authJWT = $request->getHeader('x-appwrite-jwt', '');
@@ -1341,7 +1343,7 @@ App::setResource('console', function () {
'$collection' => ID::custom('projects'),
'description' => 'Appwrite core engine',
'logo' => '',
'teamId' => -1,
'teamId' => null,
'webhooks' => [],
'keys' => [],
'platforms' => [
@@ -1397,7 +1399,8 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForConsole,
$database
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
try {
$dsn = new DSN($project->getAttribute('database'));
@@ -1433,7 +1436,8 @@ App::setResource('dbForConsole', function (Group $pools, Cache $cache) {
->setNamespace('_console')
->setMetadata('host', \gethostname())
->setMetadata('project', 'console')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
return $database;
}, ['pools', 'cache']);
@@ -1457,7 +1461,8 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForConsole,
$database
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS);
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
if ($dsn->getHost() === System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$database
@@ -1522,9 +1527,9 @@ App::setResource('deviceForBuilds', function ($project) {
return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
}, ['project']);
function getDevice($root): Device
function getDevice(string $root, string $connection = ''): Device
{
$connection = System::getEnv('_APP_CONNECTIONS_STORAGE', '');
$connection = !empty($connection) ? $connection : System::getEnv('_APP_CONNECTIONS_STORAGE', '');
if (!empty($connection)) {
$acl = 'private';
@@ -1766,6 +1771,43 @@ App::setResource('requestTimestamp', function ($request) {
}
return $requestTimestamp;
}, ['request']);
App::setResource('plan', function (array $plan = []) {
return [];
});
App::setResource('team', function (Document $project, Database $dbForConsole, App $utopia, Request $request) {
$teamInternalId = '';
if ($project->getId() !== 'console') {
$teamInternalId = $project->getAttribute('teamInternalId', '');
} else {
$route = $utopia->match($request);
$path = $route->getPath();
if (str_starts_with($path, '/v1/projects/:projectId')) {
$uri = $request->getURI();
$pid = explode('/', $uri)[3];
$p = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $pid));
$teamInternalId = $p->getAttribute('teamInternalId', '');
} elseif ($path === '/v1/projects') {
$teamId = $request->getParam('teamId', '');
$team = Authorization::skip(fn () => $dbForConsole->getDocument('teams', $teamId));
return $team;
}
}
$team = Authorization::skip(function () use ($dbForConsole, $teamInternalId) {
return $dbForConsole->findOne('teams', [
Query::equal('$internalId', [$teamInternalId]),
]);
});
if (!$team) {
$team = new Document([]);
}
return $team;
}, ['project', 'dbForConsole', 'utopia', 'request']);
App::setResource(
'isResourceBlocked',
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
);
+17 -6
View File
@@ -40,7 +40,7 @@ require_once __DIR__ . '/init.php';
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
// Allows overriding
if (!function_exists("getConsoleDB")) {
if (!function_exists('getConsoleDB')) {
function getConsoleDB(): Database
{
global $register;
@@ -66,7 +66,7 @@ if (!function_exists("getConsoleDB")) {
}
// Allows overriding
if (!function_exists("getProjectDB")) {
if (!function_exists('getProjectDB')) {
function getProjectDB(Document $project): Database
{
global $register;
@@ -113,7 +113,7 @@ if (!function_exists("getProjectDB")) {
}
// Allows overriding
if (!function_exists("getCache")) {
if (!function_exists('getCache')) {
function getCache(): Cache
{
global $register;
@@ -135,7 +135,14 @@ if (!function_exists("getCache")) {
}
}
$realtime = new Realtime();
if (!function_exists('getRealtime')) {
function getRealtime(): Realtime
{
return new Realtime();
}
}
$realtime = getRealtime();
/**
* Table for statistics across all workers.
@@ -184,8 +191,12 @@ $logError = function (Throwable $error, string $action) use ($register) {
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('Realtime log pushed with status code: ' . $responseCode);
try {
$responseCode = $logger->addLog($log);
Console::info('Error log pushed with status code: ' . $responseCode);
} catch (Throwable $th) {
Console::error('Error pushing log: ' . $th->getMessage());
}
}
Console::error('[Error] Type: ' . get_class($error));
+11 -2
View File
@@ -272,6 +272,11 @@ Server::setResource('deviceForCache', function (Document $project) {
return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId());
}, ['project']);
Server::setResource(
'isResourceBlocked',
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
);
$pools = $register->get('pools');
$platform = new Appwrite();
@@ -346,8 +351,12 @@ $worker
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('Usage stats log pushed with status code: ' . $responseCode);
try {
$responseCode = $logger->addLog($log);
Console::info('Error log pushed with status code: ' . $responseCode);
} catch (Throwable $th) {
Console::error('Error pushing log: ' . $th->getMessage());
}
}
Console::error('[Error] Type: ' . get_class($error));
+5 -11
View File
@@ -43,7 +43,7 @@
"ext-openssl": "*",
"ext-zlib": "*",
"ext-sockets": "*",
"appwrite/php-runtimes": "0.15.*",
"appwrite/php-runtimes": "0.16.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "0.43.0",
"utopia-php/analytics": "0.10.*",
@@ -51,16 +51,16 @@
"utopia-php/cache": "0.10.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.53.5-rc1",
"utopia-php/database": "0.53.8",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
"utopia-php/fetch": "0.2.*",
"utopia-php/image": "0.6.*",
"utopia-php/image": "0.7.*",
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.12.*",
"utopia-php/migration": "0.5.*",
"utopia-php/migration": "0.6.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.5.*",
@@ -69,7 +69,7 @@
"utopia-php/registry": "0.5.*",
"utopia-php/storage": "0.18.*",
"utopia-php/swoole": "0.8.*",
"utopia-php/system": "0.8.*",
"utopia-php/system": "0.9.*",
"utopia-php/vcs": "0.8.*",
"utopia-php/websocket": "0.1.*",
"matomo/device-detector": "6.1.*",
@@ -97,11 +97,5 @@
"platform": {
"php": "8.3"
}
},
"repositories": {
"utopia-php/database": {
"type": "vcs",
"url": "https://github.com/utopia-php/database"
}
}
}
Generated
+100 -123
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "7066d9ca32e7a1a60614effdc4701970",
"content-hash": "18505aa5baca1170e7cbdbb2a355b173",
"packages": [
{
"name": "adhocore/jwt",
@@ -65,16 +65,16 @@
},
{
"name": "appwrite/appwrite",
"version": "10.1.0",
"version": "11.1.0",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-for-php.git",
"reference": "da579af70723cfc117b5af84375bdef117e27312"
"reference": "1d043f543acdb17b9fdb440b1b2dd208e400bad3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/da579af70723cfc117b5af84375bdef117e27312",
"reference": "da579af70723cfc117b5af84375bdef117e27312",
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/1d043f543acdb17b9fdb440b1b2dd208e400bad3",
"reference": "1d043f543acdb17b9fdb440b1b2dd208e400bad3",
"shasum": ""
},
"require": {
@@ -83,7 +83,8 @@
"php": ">=7.1.0"
},
"require-dev": {
"phpunit/phpunit": "3.7.35"
"mockery/mockery": "^1.6.6",
"phpunit/phpunit": "^10"
},
"type": "library",
"autoload": {
@@ -99,10 +100,10 @@
"support": {
"email": "team@appwrite.io",
"issues": "https://github.com/appwrite/sdk-for-php/issues",
"source": "https://github.com/appwrite/sdk-for-php/tree/10.1.0",
"source": "https://github.com/appwrite/sdk-for-php/tree/11.1.0",
"url": "https://appwrite.io/support"
},
"time": "2023-11-20T09:56:12+00:00"
"time": "2024-06-26T07:03:23+00:00"
},
{
"name": "appwrite/php-clamav",
@@ -156,21 +157,21 @@
},
{
"name": "appwrite/php-runtimes",
"version": "0.15.0",
"version": "0.16.2",
"source": {
"type": "git",
"url": "https://github.com/appwrite/runtimes.git",
"reference": "68ea5bcc24c513a6d641ddf9412bbab13e5dfb94"
"reference": "c33005e3eaaf2d427e9fd1077d5335e31f4d36f9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/68ea5bcc24c513a6d641ddf9412bbab13e5dfb94",
"reference": "68ea5bcc24c513a6d641ddf9412bbab13e5dfb94",
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/c33005e3eaaf2d427e9fd1077d5335e31f4d36f9",
"reference": "c33005e3eaaf2d427e9fd1077d5335e31f4d36f9",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/system": "0.8.*"
"utopia-php/system": "0.9.*"
},
"require-dev": {
"laravel/pint": "^1.15",
@@ -205,9 +206,9 @@
],
"support": {
"issues": "https://github.com/appwrite/runtimes/issues",
"source": "https://github.com/appwrite/runtimes/tree/0.15.0"
"source": "https://github.com/appwrite/runtimes/tree/0.16.2"
},
"time": "2024-08-21T10:23:45+00:00"
"time": "2024-10-09T15:02:52+00:00"
},
{
"name": "beberlei/assert",
@@ -1623,16 +1624,16 @@
},
{
"name": "utopia-php/cli",
"version": "0.15.0",
"version": "0.15.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cli.git",
"reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea"
"reference": "d69bbe51a6a94dc4e5bcdd542b5938038b985a65"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea",
"reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea",
"url": "https://api.github.com/repos/utopia-php/cli/zipball/d69bbe51a6a94dc4e5bcdd542b5938038b985a65",
"reference": "d69bbe51a6a94dc4e5bcdd542b5938038b985a65",
"shasum": ""
},
"require": {
@@ -1666,9 +1667,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/cli/issues",
"source": "https://github.com/utopia-php/cli/tree/0.15.0"
"source": "https://github.com/utopia-php/cli/tree/0.15.1"
},
"time": "2023-03-01T05:55:14+00:00"
"time": "2024-10-04T13:55:36+00:00"
},
{
"name": "utopia-php/config",
@@ -1723,16 +1724,16 @@
},
{
"name": "utopia-php/database",
"version": "0.53.5-rc1",
"version": "0.53.8",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "689ba22063bf46def385da8695ba7a921e81e38d"
"reference": "f4f9297d633b9f8407c6261535549bfd6024a468"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/689ba22063bf46def385da8695ba7a921e81e38d",
"reference": "689ba22063bf46def385da8695ba7a921e81e38d",
"url": "https://api.github.com/repos/utopia-php/database/zipball/f4f9297d633b9f8407c6261535549bfd6024a468",
"reference": "f4f9297d633b9f8407c6261535549bfd6024a468",
"shasum": ""
},
"require": {
@@ -1759,38 +1760,7 @@
"Utopia\\Database\\": "src/Database"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\E2E\\": "tests/e2e",
"Tests\\Unit\\": "tests/unit"
}
},
"scripts": {
"build": [
"Composer\\Config::disableProcessTimeout",
"docker compose build"
],
"start": [
"Composer\\Config::disableProcessTimeout",
"docker compose up -d"
],
"test": [
"Composer\\Config::disableProcessTimeout",
"docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml"
],
"lint": [
"./vendor/bin/pint --test"
],
"format": [
"./vendor/bin/pint"
],
"check": [
"./vendor/bin/phpstan analyse --level 7 src tests --memory-limit 512M"
],
"coverage": [
"./vendor/bin/coverage-check ./tmp/clover.xml 90"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@@ -1803,10 +1773,10 @@
"utopia"
],
"support": {
"source": "https://github.com/utopia-php/database/tree/0.53.5-rc1",
"issues": "https://github.com/utopia-php/database/issues"
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.53.8"
},
"time": "2024-09-24T08:43:10+00:00"
"time": "2024-10-16T08:16:33+00:00"
},
{
"name": "utopia-php/domains",
@@ -2001,21 +1971,21 @@
},
{
"name": "utopia-php/image",
"version": "0.6.1",
"version": "0.7.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/image.git",
"reference": "2d74c27e69e65a93cf94a16586598a04fe435bf0"
"reference": "fcea143edbad524bf871ddbebe801d981f91f181"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/image/zipball/2d74c27e69e65a93cf94a16586598a04fe435bf0",
"reference": "2d74c27e69e65a93cf94a16586598a04fe435bf0",
"url": "https://api.github.com/repos/utopia-php/image/zipball/fcea143edbad524bf871ddbebe801d981f91f181",
"reference": "fcea143edbad524bf871ddbebe801d981f91f181",
"shasum": ""
},
"require": {
"ext-imagick": "*",
"php": ">=8.0"
"php": ">=8.1"
},
"require-dev": {
"laravel/pint": "1.2.*",
@@ -2043,9 +2013,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/image/issues",
"source": "https://github.com/utopia-php/image/tree/0.6.1"
"source": "https://github.com/utopia-php/image/tree/0.7.0"
},
"time": "2024-02-05T13:31:44+00:00"
"time": "2024-10-02T05:45:38+00:00"
},
{
"name": "utopia-php/locale",
@@ -2100,16 +2070,16 @@
},
{
"name": "utopia-php/logger",
"version": "0.6.1",
"version": "0.6.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/logger.git",
"reference": "7e8ff512c6f04577aba1df67c7b9628971946f9c"
"reference": "25b5bd2ad8bb51292f76332faa7034644fd0941d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/logger/zipball/7e8ff512c6f04577aba1df67c7b9628971946f9c",
"reference": "7e8ff512c6f04577aba1df67c7b9628971946f9c",
"url": "https://api.github.com/repos/utopia-php/logger/zipball/25b5bd2ad8bb51292f76332faa7034644fd0941d",
"reference": "25b5bd2ad8bb51292f76332faa7034644fd0941d",
"shasum": ""
},
"require": {
@@ -2148,22 +2118,22 @@
],
"support": {
"issues": "https://github.com/utopia-php/logger/issues",
"source": "https://github.com/utopia-php/logger/tree/0.6.1"
"source": "https://github.com/utopia-php/logger/tree/0.6.2"
},
"time": "2024-09-20T14:02:12+00:00"
"time": "2024-10-14T16:02:49+00:00"
},
{
"name": "utopia-php/messaging",
"version": "0.12.0",
"version": "0.12.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/messaging.git",
"reference": "6e466d3511981291843c6ebf9ce3f44fc75e37b0"
"reference": "f6790fba1fcee12163d51c65d2c226a7856295d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/6e466d3511981291843c6ebf9ce3f44fc75e37b0",
"reference": "6e466d3511981291843c6ebf9ce3f44fc75e37b0",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/f6790fba1fcee12163d51c65d2c226a7856295d9",
"reference": "f6790fba1fcee12163d51c65d2c226a7856295d9",
"shasum": ""
},
"require": {
@@ -2199,33 +2169,41 @@
],
"support": {
"issues": "https://github.com/utopia-php/messaging/issues",
"source": "https://github.com/utopia-php/messaging/tree/0.12.0"
"source": "https://github.com/utopia-php/messaging/tree/0.12.2"
},
"time": "2024-05-30T14:58:25+00:00"
"time": "2024-10-22T01:02:20+00:00"
},
{
"name": "utopia-php/migration",
"version": "0.5.2",
"version": "0.6.9",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "f18d44d4459f78c292dac9edde856fd156fe497a"
"reference": "ce97cdf2ca82e7cec78e2ed484ef2c71ebe8744b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/f18d44d4459f78c292dac9edde856fd156fe497a",
"reference": "f18d44d4459f78c292dac9edde856fd156fe497a",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/ce97cdf2ca82e7cec78e2ed484ef2c71ebe8744b",
"reference": "ce97cdf2ca82e7cec78e2ed484ef2c71ebe8744b",
"shasum": ""
},
"require": {
"appwrite/appwrite": "10.1.0",
"php": "8.*"
"appwrite/appwrite": "11.1.*",
"ext-curl": "*",
"ext-openssl": "*",
"php": "8.3.*",
"utopia-php/database": "0.53.*",
"utopia-php/dsn": "0.2.*",
"utopia-php/framework": "0.33.*",
"utopia-php/storage": "0.18.*"
},
"require-dev": {
"laravel/pint": "1.*",
"phpunit/phpunit": "9.*",
"utopia-php/cli": "^0.18.0",
"vlucas/phpdotenv": "5.*"
"ext-pdo": "*",
"laravel/pint": "1.17.*",
"phpstan/phpstan": "1.11.*",
"phpunit/phpunit": "11.2.*",
"utopia-php/cli": "0.16.*",
"vlucas/phpdotenv": "5.6.*"
},
"type": "library",
"autoload": {
@@ -2247,9 +2225,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.5.2"
"source": "https://github.com/utopia-php/migration/tree/0.6.9"
},
"time": "2024-07-22T09:27:07+00:00"
"time": "2024-10-16T08:33:21+00:00"
},
{
"name": "utopia-php/mongo",
@@ -2736,16 +2714,16 @@
},
{
"name": "utopia-php/system",
"version": "0.8.0",
"version": "0.9.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/system.git",
"reference": "a2cbfb3c69b9ecb8b6f06c5774f3cf279ea7665e"
"reference": "8e4a7edaf2dfeb4c9524e9f766d27754f2c4b64d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/system/zipball/a2cbfb3c69b9ecb8b6f06c5774f3cf279ea7665e",
"reference": "a2cbfb3c69b9ecb8b6f06c5774f3cf279ea7665e",
"url": "https://api.github.com/repos/utopia-php/system/zipball/8e4a7edaf2dfeb4c9524e9f766d27754f2c4b64d",
"reference": "8e4a7edaf2dfeb4c9524e9f766d27754f2c4b64d",
"shasum": ""
},
"require": {
@@ -2786,9 +2764,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/system/issues",
"source": "https://github.com/utopia-php/system/tree/0.8.0"
"source": "https://github.com/utopia-php/system/tree/0.9.0"
},
"time": "2024-04-01T10:22:28+00:00"
"time": "2024-10-09T14:44:01+00:00"
},
{
"name": "utopia-php/vcs",
@@ -3024,16 +3002,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.39.21",
"version": "0.39.24",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "9754b190d33aaad56fdb8defc94f90248184c5ac"
"reference": "412451c87f6ef17e24e9a5cf41721043d74c60c8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9754b190d33aaad56fdb8defc94f90248184c5ac",
"reference": "9754b190d33aaad56fdb8defc94f90248184c5ac",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/412451c87f6ef17e24e9a5cf41721043d74c60c8",
"reference": "412451c87f6ef17e24e9a5cf41721043d74c60c8",
"shasum": ""
},
"require": {
@@ -3069,9 +3047,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/0.39.21"
"source": "https://github.com/appwrite/sdk-generator/tree/0.39.24"
},
"time": "2024-09-10T08:49:29+00:00"
"time": "2024-10-09T19:13:27+00:00"
},
{
"name": "doctrine/annotations",
@@ -3345,16 +3323,16 @@
},
{
"name": "laravel/pint",
"version": "v1.17.3",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "9d77be916e145864f10788bb94531d03e1f7b482"
"reference": "35c00c05ec43e6b46d295efc0f4386ceb30d50d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/9d77be916e145864f10788bb94531d03e1f7b482",
"reference": "9d77be916e145864f10788bb94531d03e1f7b482",
"url": "https://api.github.com/repos/laravel/pint/zipball/35c00c05ec43e6b46d295efc0f4386ceb30d50d9",
"reference": "35c00c05ec43e6b46d295efc0f4386ceb30d50d9",
"shasum": ""
},
"require": {
@@ -3407,7 +3385,7 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
"time": "2024-09-03T15:00:28+00:00"
"time": "2024-09-24T17:22:50+00:00"
},
{
"name": "matthiasmullie/minify",
@@ -3595,16 +3573,16 @@
},
{
"name": "nikic/php-parser",
"version": "v5.2.0",
"version": "v5.3.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb"
"reference": "8eea230464783aa9671db8eea6f8c6ac5285794b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb",
"reference": "23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b",
"reference": "8eea230464783aa9671db8eea6f8c6ac5285794b",
"shasum": ""
},
"require": {
@@ -3647,9 +3625,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.2.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1"
},
"time": "2024-09-15T16:40:33+00:00"
"time": "2024-10-08T18:51:32+00:00"
},
{
"name": "phar-io/manifest",
@@ -3869,6 +3847,7 @@
"issues": "https://github.com/phpbench/dom/issues",
"source": "https://github.com/phpbench/dom/tree/0.3.3"
},
"abandoned": true,
"time": "2023-03-06T23:46:57+00:00"
},
{
@@ -4216,16 +4195,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.31.0",
"version": "1.33.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "249f15fb843bf240cf058372dad29e100cee6c17"
"reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/249f15fb843bf240cf058372dad29e100cee6c17",
"reference": "249f15fb843bf240cf058372dad29e100cee6c17",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/82a311fd3690fb2bf7b64d5c98f912b3dd746140",
"reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140",
"shasum": ""
},
"require": {
@@ -4257,9 +4236,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.31.0"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.33.0"
},
"time": "2024-09-22T11:32:18+00:00"
"time": "2024-10-13T11:25:22+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -7026,9 +7005,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"utopia-php/database": 5
},
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
+1 -1
View File
@@ -863,7 +863,7 @@ services:
appwrite-assistant:
container_name: appwrite-assistant
image: appwrite/assistant:0.4.0
image: appwrite/assistant:0.5.0
networks:
- appwrite
environment:
+2
View File
@@ -8,6 +8,8 @@ use Utopia\Queue\Connection;
class Func extends Event
{
public const TYPE_ASYNC_WRITE = 'async_write';
protected string $jwt = '';
protected string $type = '';
protected string $body = '';
+1 -1
View File
@@ -81,7 +81,7 @@ class Migration extends Event
return $client->enqueue([
'project' => $this->project,
'user' => $this->user,
'migration' => $this->migration
'migration' => $this->migration,
]);
}
}
+1
View File
@@ -39,6 +39,7 @@ class Exception extends \Exception
public const GENERAL_UNKNOWN = 'general_unknown';
public const GENERAL_MOCK = 'general_mock';
public const GENERAL_ACCESS_FORBIDDEN = 'general_access_forbidden';
public const GENERAL_RESOURCE_BLOCKED = 'general_resource_blocked';
public const GENERAL_UNKNOWN_ORIGIN = 'general_unknown_origin';
public const GENERAL_API_DISABLED = 'general_api_disabled';
public const GENERAL_SERVICE_DISABLED = 'general_service_disabled';
+1
View File
@@ -50,6 +50,7 @@ class Mapper
$defaults = [
'boolean' => Type::boolean(),
'string' => Type::string(),
'payload' => Type::string(),
'integer' => Type::int(),
'double' => Type::float(),
'datetime' => Type::string(),
@@ -243,7 +243,11 @@ class Realtime extends Adapter
* @param string $event
* @param Document $payload
* @param Document|null $project
* @param Document|null $database
* @param Document|null $collection
* @param Document|null $bucket
* @return array
* @throws \Exception
*/
public static function fromPayload(string $event, Document $payload, Document $project = null, Document $database = null, Document $collection = null, Document $bucket = null): array
{
@@ -262,6 +266,13 @@ class Realtime extends Adapter
break;
case 'rules':
$channels[] = 'console';
$channels[] = 'projects.' . $project->getId();
$projectId = 'console';
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
break;
case 'projects':
$channels[] = 'console';
$channels[] = 'projects.' . $parts[1];
$projectId = 'console';
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
break;
@@ -280,6 +291,7 @@ class Realtime extends Adapter
case 'databases':
if (in_array($parts[4] ?? [], ['attributes', 'indexes'])) {
$channels[] = 'console';
$channels[] = 'projects.' . $project->getId();
$projectId = 'console';
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
} elseif (($parts[4] ?? '') === 'documents') {
@@ -319,6 +331,7 @@ class Realtime extends Adapter
if ($parts[2] === 'executions') {
if (!empty($payload->getRead())) {
$channels[] = 'console';
$channels[] = 'projects.' . $project->getId();
$channels[] = 'executions';
$channels[] = 'executions.' . $payload->getId();
$channels[] = 'functions.' . $payload->getAttribute('functionId');
@@ -326,6 +339,7 @@ class Realtime extends Adapter
}
} elseif ($parts[2] === 'deployments') {
$channels[] = 'console';
$channels[] = 'projects.' . $project->getId();
$projectId = 'console';
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
}
@@ -333,6 +347,7 @@ class Realtime extends Adapter
break;
case 'migrations':
$channels[] = 'console';
$channels[] = 'projects.' . $project->getId();
$projectId = 'console';
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
break;
+14 -6
View File
@@ -49,12 +49,7 @@ class Maintenance extends Action
$this->foreachProject($dbForConsole, function (Document $project) use ($queueForDeletes, $usageStatsRetentionHourly) {
$queueForDeletes->setProject($project);
$this->notifyDeleteTargets($queueForDeletes);
$this->notifyDeleteExecutionLogs($queueForDeletes);
$this->notifyDeleteAbuseLogs($queueForDeletes);
$this->notifyDeleteAuditLogs($queueForDeletes);
$this->notifyDeleteUsageStats($usageStatsRetentionHourly, $queueForDeletes);
$this->notifyDeleteExpiredSessions($queueForDeletes);
$this->notifyProjects($queueForDeletes, $usageStatsRetentionHourly);
});
$this->notifyDeleteConnections($queueForDeletes);
@@ -64,6 +59,19 @@ class Maintenance extends Action
}, $interval, $delay);
}
/**
* Hook to allow sub-classes to extend project-level maintenance functionality.
*/
protected function notifyProjects(Delete $queueForDeletes, int $usageStatsRetentionHourly): void
{
$this->notifyDeleteTargets($queueForDeletes);
$this->notifyDeleteExecutionLogs($queueForDeletes);
$this->notifyDeleteAbuseLogs($queueForDeletes);
$this->notifyDeleteAuditLogs($queueForDeletes);
$this->notifyDeleteUsageStats($usageStatsRetentionHourly, $queueForDeletes);
$this->notifyDeleteExpiredSessions($queueForDeletes);
}
protected function foreachProject(Database $dbForConsole, callable $callback): void
{
// TODO: @Meldiron name of this method no longer matches. It does not delete, and it gives whole document
+9 -23
View File
@@ -24,11 +24,8 @@ abstract class ScheduleBase extends Action
abstract public static function getName(): string;
abstract public static function getSupportedResource(): string;
abstract protected function enqueueResources(
Group $pools,
Database $dbForConsole
);
abstract public static function getCollectionId(): string;
abstract protected function enqueueResources(Group $pools, Database $dbForConsole, callable $getProjectDB): void;
public function __construct()
{
@@ -62,14 +59,8 @@ abstract class ScheduleBase extends Action
$getSchedule = function (Document $schedule) use ($dbForConsole, $getProjectDB): array {
$project = $dbForConsole->getDocument('projects', $schedule->getAttribute('projectId'));
$collectionId = match ($schedule->getAttribute('resourceType')) {
'function' => 'functions',
'message' => 'messages',
'execution' => 'executions'
};
$resource = $getProjectDB($project)->getDocument(
$collectionId,
static::getCollectionId(),
$schedule->getAttribute('resourceId')
);
@@ -113,12 +104,7 @@ abstract class ScheduleBase extends Action
try {
$this->schedules[$document->getInternalId()] = $getSchedule($document);
} catch (\Throwable $th) {
$collectionId = match ($document->getAttribute('resourceType')) {
'function' => 'functions',
'message' => 'messages',
'execution' => 'executions'
};
$collectionId = static::getCollectionId();
Console::error("Failed to load schedule for project {$document['projectId']} {$collectionId} {$document['resourceId']}");
Console::error($th->getMessage());
}
@@ -133,7 +119,7 @@ abstract class ScheduleBase extends Action
Console::success("Starting timers at " . DateTime::now());
run(function () use ($dbForConsole, &$lastSyncUpdate, $getSchedule, $pools) {
run(function () use ($dbForConsole, &$lastSyncUpdate, $getSchedule, $pools, $getProjectDB) {
/**
* The timer synchronize $schedules copy with database collection.
*/
@@ -172,10 +158,10 @@ abstract class ScheduleBase extends Action
$new = \strtotime($document['resourceUpdatedAt']);
if (!$document['active']) {
Console::info("Removing: {$document['resourceId']}");
Console::info("Removing: {$document['resourceType']}::{$document['resourceId']}");
unset($this->schedules[$document->getInternalId()]);
} elseif ($new !== $org) {
Console::info("Updating: {$document['resourceId']}");
Console::info("Updating: {$document['resourceType']}::{$document['resourceId']}");
$this->schedules[$document->getInternalId()] = $getSchedule($document);
}
}
@@ -193,10 +179,10 @@ abstract class ScheduleBase extends Action
Timer::tick(
static::ENQUEUE_TIMER * 1000,
fn () => $this->enqueueResources($pools, $dbForConsole)
fn () => $this->enqueueResources($pools, $dbForConsole, $getProjectDB)
);
$this->enqueueResources($pools, $dbForConsole);
$this->enqueueResources($pools, $dbForConsole, $getProjectDB);
});
}
}
@@ -22,7 +22,12 @@ class ScheduleExecutions extends ScheduleBase
return 'execution';
}
protected function enqueueResources(Group $pools, Database $dbForConsole): void
public static function getCollectionId(): string
{
return 'executions';
}
protected function enqueueResources(Group $pools, Database $dbForConsole, callable $getProjectDB): void
{
$queue = $pools->get('queue')->pop();
$connection = $queue->getResource();
@@ -26,7 +26,12 @@ class ScheduleFunctions extends ScheduleBase
return 'function';
}
protected function enqueueResources(Group $pools, Database $dbForConsole): void
public static function getCollectionId(): string
{
return 'functions';
}
protected function enqueueResources(Group $pools, Database $dbForConsole, callable $getProjectDB): void
{
$timerStart = \microtime(true);
$time = DateTime::now();
@@ -21,7 +21,12 @@ class ScheduleMessages extends ScheduleBase
return 'message';
}
protected function enqueueResources(Group $pools, Database $dbForConsole): void
public static function getCollectionId(): string
{
return 'messages';
}
protected function enqueueResources(Group $pools, Database $dbForConsole, callable $getProjectDB): void
{
foreach ($this->schedules as $schedule) {
if (!$schedule['active']) {
+19 -8
View File
@@ -5,9 +5,11 @@ namespace Appwrite\Platform\Tasks;
use Appwrite\Specification\Format\OpenAPI3;
use Appwrite\Specification\Format\Swagger2;
use Appwrite\Specification\Specification;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Request as AppwriteRequest;
use Appwrite\Utopia\Response as AppwriteResponse;
use Exception;
use Swoole\Http\Response as HttpResponse;
use Swoole\Http\Request as SwooleRequest;
use Swoole\Http\Response as SwooleResponse;
use Utopia\App;
use Utopia\Cache\Adapter\None;
use Utopia\Cache\Cache;
@@ -17,7 +19,8 @@ use Utopia\Database\Adapter\MySQL;
use Utopia\Database\Database;
use Utopia\Platform\Action;
use Utopia\Registry\Registry;
use Utopia\Request;
use Utopia\Request as UtopiaRequest;
use Utopia\Response as UtopiaResponse;
use Utopia\System\System;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
@@ -29,6 +32,16 @@ class Specs extends Action
return 'specs';
}
public function getRequest(): UtopiaRequest
{
return new AppwriteRequest(new SwooleRequest());
}
public function getResponse(): UtopiaResponse
{
return new AppwriteResponse(new SwooleResponse());
}
public function __construct()
{
$this
@@ -42,11 +55,11 @@ class Specs extends Action
public function action(string $version, string $mode, Registry $register): void
{
$appRoutes = App::getRoutes();
$response = new Response(new HttpResponse());
$response = $this->getResponse();
$mocks = ($mode === 'mocks');
// Mock dependencies
App::setResource('request', fn () => new Request());
App::setResource('request', fn () => $this->getRequest());
App::setResource('response', fn () => $response);
App::setResource('dbForConsole', fn () => new Database(new MySQL(''), new Cache(new None())));
App::setResource('dbForProject', fn () => new Database(new MySQL(''), new Cache(new None())));
@@ -183,10 +196,8 @@ class Specs extends Action
case APP_AUTH_TYPE_SESSION:
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
break;
case APP_AUTH_TYPE_KEY:
$sdkPlatforms[] = APP_PLATFORM_SERVER;
break;
case APP_AUTH_TYPE_JWT:
case APP_AUTH_TYPE_KEY:
$sdkPlatforms[] = APP_PLATFORM_SERVER;
break;
case APP_AUTH_TYPE_ADMIN:
@@ -5,7 +5,6 @@ namespace Appwrite\Platform\Workers;
use Appwrite\Event\Event;
use Appwrite\Messaging\Adapter\Realtime;
use Exception;
use Utopia\Audit\Audit;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Document;
@@ -492,8 +491,6 @@ class Databases extends Action
});
$dbForProject->deleteCollection('database_' . $database->getInternalId());
$this->deleteAuditLogsByResource('database/' . $database->getId(), $project, $dbForProject);
}
/**
@@ -549,23 +546,8 @@ class Databases extends Action
Query::equal('databaseInternalId', [$databaseInternalId]),
Query::equal('collectionInternalId', [$collectionInternalId])
], $dbForProject);
$this->deleteAuditLogsByResource('database/' . $databaseId . '/collection/' . $collectionId, $project, $dbForProject);
}
/**
* @param string $resource
* @param Document $project
* @param Database $dbForProject
* @return void
* @throws Exception
*/
protected function deleteAuditLogsByResource(string $resource, Document $project, Database $dbForProject): void
{
$this->deleteByGroup(Audit::COLLECTION, [
Query::equal('resource', [$resource])
], $dbForProject);
}
/**
* @param string $collection collectionID
+20 -23
View File
@@ -119,10 +119,6 @@ class Deletes extends Action
if (!$project->isEmpty()) {
$this->deleteAuditLogs($project, $getProjectDB, $auditRetention);
}
if (!$document->isEmpty()) {
$this->deleteAuditLogsByResource($getProjectDB, 'document/' . $document->getId(), $project);
}
break;
case DELETE_TYPE_ABUSE:
$this->deleteAbuseLogs($project, $getProjectDB, $abuseRetention);
@@ -480,6 +476,7 @@ class Deletes extends Action
private function deleteProject(Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, Document $document): void
{
$projectInternalId = $document->getInternalId();
$projectId = $document->getId();
try {
$dsn = new DSN($document->getAttribute('database', 'console'));
@@ -503,7 +500,18 @@ class Deletes extends Action
foreach ($collections as $collection) {
if ($dsn->getHost() !== System::getEnv('_APP_DATABASE_SHARED_TABLES', '') || !\in_array($collection->getId(), $projectCollectionIds)) {
$dbForProject->deleteCollection($collection->getId());
try {
$dbForProject->deleteCollection($collection->getId());
} catch (Throwable $e) {
Console::error('Error deleting '.$collection->getId().' '.$e->getMessage());
/**
* Ignore junction tables;
*/
if (!preg_match('/^_\d+_\d+$/', $collection->getId())) {
throw $e;
}
}
} else {
$this->deleteByGroup($collection->getId(), [], database: $dbForProject);
}
@@ -557,6 +565,11 @@ class Deletes extends Action
Query::equal('projectInternalId', [$projectInternalId]),
], $dbForConsole);
// Delete Schedules (No projectInternalId in this collection)
$this->deleteByGroup('schedules', [
Query::equal('projectId', [$projectId]),
], $dbForConsole);
// Delete metadata table
if ($dsn->getHost() !== System::getEnv('_APP_DATABASE_SHARED_TABLES', '')) {
$dbForProject->deleteCollection('_metadata');
@@ -716,22 +729,6 @@ class Deletes extends Action
}
}
/**
* @param callable $getProjectDB
* @param string $resource
* @param Document $project
* @return void
* @throws Exception
*/
private function deleteAuditLogsByResource(callable $getProjectDB, string $resource, Document $project): void
{
$dbForProject = $getProjectDB($project);
$this->deleteByGroup(Audit::COLLECTION, [
Query::equal('resource', [$resource])
], $dbForProject);
}
/**
* @param callable $getProjectDB
* @param Device $deviceForFunctions
@@ -955,7 +952,7 @@ class Deletes extends Action
* @return void
* @throws Exception
*/
private function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void
protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void
{
$count = 0;
$chunk = 0;
@@ -997,7 +994,7 @@ class Deletes extends Action
* @return void
* @throws Exception
*/
private function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void
protected function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void
{
$count = 0;
$chunk = 0;
+65 -36
View File
@@ -41,29 +41,33 @@ class Functions extends Action
$this
->desc('Functions worker')
->groups(['functions'])
->inject('project')
->inject('message')
->inject('dbForProject')
->inject('queueForFunctions')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('log')
->callback(fn (Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage, $log));
->inject('isResourceBlocked')
->callback(fn (Document $project, Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log, callable $isResourceBlocked) => $this->action($project, $message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage, $log, $isResourceBlocked));
}
/**
* @param Document $project
* @param Message $message
* @param Database $dbForProject
* @param Func $queueForFunctions
* @param Event $queueForEvents
* @param Usage $queueForUsage
* @param Log $log
* @param callable $isResourceBlocked
* @return void
* @throws Authorization
* @throws Structure
* @throws \Utopia\Database\Exception
* @throws Conflict
*/
public function action(Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log): void
public function action(Document $project, Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log, callable $isResourceBlocked): void
{
$payload = $message->getPayload() ?? [];
@@ -72,48 +76,22 @@ class Functions extends Action
}
$type = $payload['type'] ?? '';
$events = $payload['events'] ?? [];
$data = $payload['body'] ?? '';
$eventData = $payload['payload'] ?? '';
$project = new Document($payload['project'] ?? []);
$function = new Document($payload['function'] ?? []);
$functionId = $payload['functionId'] ?? '';
$user = new Document($payload['user'] ?? []);
$userId = $payload['userId'] ?? '';
$method = $payload['method'] ?? 'POST';
$headers = $payload['headers'] ?? [];
$path = $payload['path'] ?? '/';
$jwt = $payload['jwt'] ?? '';
if ($user->isEmpty() && !empty($userId)) {
$user = $dbForProject->getDocument('users', $userId);
}
if (empty($jwt) && !$user->isEmpty()) {
$jwtExpiry = $function->getAttribute('timeout', 900);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
$jwt = $jwtObj->encode([
'userId' => $user->getId(),
]);
}
if ($project->getId() === 'console') {
// Short-term solution to offhand write operation from API container
if ($type === Func::TYPE_ASYNC_WRITE) {
$execution = new Document($payload['execution'] ?? []);
$dbForProject->createDocument('executions', $execution);
return;
}
if ($function->isEmpty() && !empty($functionId)) {
$function = $dbForProject->getDocument('functions', $functionId);
}
$log->addTag('functionId', $function->getId());
$log->addTag('projectId', $project->getId());
$log->addTag('type', $type);
$eventData = $payload['payload'] ?? '';
$user = new Document($payload['user'] ?? []);
$events = $payload['events'] ?? [];
if (!empty($events)) {
$limit = 30;
$sum = 30;
$offset = 0;
/** @var Document[] $functions */
while ($sum >= $limit) {
$functions = $dbForProject->find('functions', [
Query::limit($limit),
@@ -130,6 +108,12 @@ class Functions extends Action
if (!array_intersect($events, $function->getAttribute('events', []))) {
continue;
}
if ($isResourceBlocked($project, 'functions', $function->getId())) {
Console::log('Function ' . $function->getId() . ' is blocked, skipping execution.');
continue;
}
Console::success('Iterating function: ' . $function->getAttribute('name'));
$this->execute(
@@ -160,6 +144,50 @@ class Functions extends Action
return;
}
$data = $payload['body'] ?? '';
$function = new Document($payload['function'] ?? []);
$functionId = $payload['functionId'] ?? '';
$userId = $payload['userId'] ?? '';
$method = $payload['method'] ?? 'POST';
$headers = $payload['headers'] ?? [];
$path = $payload['path'] ?? '/';
$jwt = $payload['jwt'] ?? '';
if ($user->isEmpty() && !empty($userId)) {
$user = $dbForProject->getDocument('users', $userId);
}
if (empty($jwt) && !$user->isEmpty()) {
$jwtExpiry = $function->getAttribute('timeout', 900);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
$jwt = $jwtObj->encode([
'userId' => $user->getId(),
]);
}
if ($project->getId() === 'console') {
return;
}
if ($function->isEmpty() && !empty($functionId)) {
$function = $dbForProject->getDocument('functions', $functionId);
}
// $function still empty, we can't execute this
if ($function->isEmpty()) {
Console::warning('Got empty function without functionId.');
return;
}
if ($isResourceBlocked($project, 'functions', $function->getId())) {
Console::log('Function ' . $function->getId() . ' is blocked, skipping execution.');
return;
}
$log->addTag('functionId', $function->getId());
$log->addTag('projectId', $project->getId());
$log->addTag('type', $type);
/**
* Handle Schedule and HTTP execution.
*/
@@ -587,7 +615,8 @@ class Functions extends Action
$target = Realtime::fromPayload(
// Pass first, most verbose event pattern
event: $allEvents[0],
payload: $execution
payload: $execution,
project: $project
);
Realtime::send(
projectId: 'console',
+40 -21
View File
@@ -489,11 +489,29 @@ class Messaging extends Action
return match ($provider->getAttribute('provider')) {
'mock' => new Mock('username', 'password'),
'twilio' => new Twilio($credentials['accountSid'], $credentials['authToken'], null, isset($credentials['messagingServiceSid']) ? $credentials['messagingServiceSid'] : null),
'textmagic' => new TextMagic($credentials['username'], $credentials['apiKey']),
'telesign' => new Telesign($credentials['customerId'], $credentials['apiKey']),
'msg91' => new Msg91($credentials['senderId'], $credentials['authKey'], $credentials['templateId']),
'vonage' => new Vonage($credentials['apiKey'], $credentials['apiSecret']),
'twilio' => new Twilio(
$credentials['accountSid'] ?? '',
$credentials['authToken'] ?? '',
null,
$credentials['messagingServiceSid'] ?? null
),
'textmagic' => new TextMagic(
$credentials['username'] ?? '',
$credentials['apiKey'] ?? ''
),
'telesign' => new Telesign(
$credentials['customerId'] ?? '',
$credentials['apiKey'] ?? ''
),
'msg91' => new Msg91(
$credentials['senderId'] ?? '',
$credentials['authKey'] ?? '',
$credentials['templateId'] ?? ''
),
'vonage' => new Vonage(
$credentials['apiKey'] ?? '',
$credentials['apiSecret'] ?? ''
),
default => null
};
}
@@ -506,11 +524,11 @@ class Messaging extends Action
return match ($provider->getAttribute('provider')) {
'mock' => new Mock('username', 'password'),
'apns' => new APNS(
$credentials['authKey'],
$credentials['authKeyId'],
$credentials['teamId'],
$credentials['bundleId'],
$options['sandbox']
$credentials['authKey'] ?? '',
$credentials['authKeyId'] ?? '',
$credentials['teamId'] ?? '',
$credentials['bundleId'] ?? '',
$options['sandbox'] ?? false
),
'fcm' => new FCM(\json_encode($credentials['serviceAccountJSON'])),
default => null
@@ -521,24 +539,25 @@ class Messaging extends Action
{
$credentials = $provider->getAttribute('credentials', []);
$options = $provider->getAttribute('options', []);
$apiKey = $credentials['apiKey'] ?? '';
return match ($provider->getAttribute('provider')) {
'mock' => new Mock('username', 'password'),
'smtp' => new SMTP(
$credentials['host'],
$credentials['port'],
$credentials['username'],
$credentials['password'],
$options['encryption'],
$options['autoTLS'],
$options['mailer'],
$credentials['host'] ?? '',
$credentials['port'] ?? 25,
$credentials['username'] ?? '',
$credentials['password'] ?? '',
$options['encryption'] ?? '',
$options['autoTLS'] ?? false,
$options['mailer'] ?? '',
),
'mailgun' => new Mailgun(
$credentials['apiKey'],
$credentials['domain'],
$credentials['isEuRegion']
$apiKey,
$credentials['domain'] ?? '',
$credentials['isEuRegion'] ?? false
),
'sendgrid' => new Sendgrid($credentials['apiKey']),
'sendgrid' => new Sendgrid($apiKey),
default => null
};
}
+125 -79
View File
@@ -8,6 +8,7 @@ use Appwrite\Permission;
use Appwrite\Role;
use Exception;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Authorization;
@@ -16,11 +17,11 @@ use Utopia\Database\Exception\Restricted;
use Utopia\Database\Exception\Structure;
use Utopia\Database\Helpers\ID;
use Utopia\Logger\Log;
use Utopia\Logger\Log\Breadcrumb;
use Utopia\Migration\Destinations\Appwrite as DestinationsAppwrite;
use Utopia\Migration\Destination;
use Utopia\Migration\Destinations\Appwrite as DestinationAppwrite;
use Utopia\Migration\Exception as MigrationException;
use Utopia\Migration\Source;
use Utopia\Migration\Sources\Appwrite;
use Utopia\Migration\Sources\Appwrite as SourceAppwrite;
use Utopia\Migration\Sources\Firebase;
use Utopia\Migration\Sources\NHost;
use Utopia\Migration\Sources\Supabase;
@@ -30,8 +31,11 @@ use Utopia\Queue\Message;
class Migrations extends Action
{
private ?Database $dbForProject = null;
private ?Database $dbForConsole = null;
protected Database $dbForProject;
protected Database $dbForConsole;
protected Document $project;
public static function getName(): string
{
@@ -53,11 +57,6 @@ class Migrations extends Action
}
/**
* @param Message $message
* @param Database $dbForProject
* @param Database $dbForConsole
* @param Log $log
* @return void
* @throws Exception
*/
public function action(Message $message, Database $dbForProject, Database $dbForConsole, Log $log): void
@@ -78,6 +77,7 @@ class Migrations extends Action
$this->dbForProject = $dbForProject;
$this->dbForConsole = $dbForConsole;
$this->project = $project;
/**
* Handle Event execution.
@@ -89,17 +89,17 @@ class Migrations extends Action
$log->addTag('migrationId', $migration->getId());
$log->addTag('projectId', $project->getId());
$this->processMigration($project, $migration, $log);
$this->processMigration($migration, $log);
}
/**
* @param string $source
* @param array $credentials
* @return Source
* @throws Exception
*/
protected function processSource(string $source, array $credentials): Source
protected function processSource(Document $migration): Source
{
$source = $migration->getAttribute('source');
$credentials = $migration->getAttribute('credentials');
return match ($source) {
Firebase::getName() => new Firebase(
json_decode($credentials['serviceAccount'], true),
@@ -122,11 +122,35 @@ class Migrations extends Action
$credentials['password'],
$credentials['port'],
),
Appwrite::getName() => new Appwrite($credentials['projectId'], str_starts_with($credentials['endpoint'], 'http://localhost/v1') ? 'http://appwrite/v1' : $credentials['endpoint'], $credentials['apiKey']),
SourceAppwrite::getName() => new SourceAppwrite(
$credentials['projectId'],
$credentials['endpoint'],
$credentials['apiKey'],
),
default => throw new \Exception('Invalid source type'),
};
}
/**
* @throws Exception
*/
protected function processDestination(Document $migration): Destination
{
$destination = $migration->getAttribute('destination');
$credentials = $migration->getAttribute('credentials');
return match ($destination) {
DestinationAppwrite::getName() => new DestinationAppwrite(
$credentials['projectId'],
$credentials['endpoint'],
$credentials['apiKey'],
$this->dbForProject,
Config::getParam('collections', [])['databases']['collections'],
),
default => throw new \Exception('Invalid destination type'),
};
}
/**
* @throws Authorization
* @throws Structure
@@ -167,8 +191,6 @@ class Migrations extends Action
}
/**
* @param Document $apiKey
* @return void
* @throws \Utopia\Database\Exception
* @throws Authorization
* @throws Conflict
@@ -181,8 +203,6 @@ class Migrations extends Action
}
/**
* @param Document $project
* @return Document
* @throws Authorization
* @throws Structure
* @throws \Utopia\Database\Exception
@@ -233,99 +253,116 @@ class Migrations extends Action
}
/**
* @param Document $project
* @param Document $migration
* @param Log $log
* @return void
* @throws Authorization
* @throws Conflict
* @throws Restricted
* @throws Structure
* @throws \Utopia\Database\Exception
* @throws Exception
*/
protected function processMigration(Document $project, Document $migration, Log $log): void
protected function processMigration(Document $migration, Log $log): void
{
/**
* @var Document $migrationDocument
* @var Transfer $transfer
*/
$migrationDocument = null;
$transfer = null;
$project = $this->project;
$projectDocument = $this->dbForConsole->getDocument('projects', $project->getId());
$tempAPIKey = $this->generateAPIKey($projectDocument);
$transfer = $source = $destination = null;
try {
$migrationDocument = $this->dbForProject->getDocument('migrations', $migration->getId());
$migrationDocument->setAttribute('stage', 'processing');
$migrationDocument->setAttribute('status', 'processing');
$log->addBreadcrumb(new Breadcrumb("debug", "migration", "Migration hit stage 'processing'", \microtime(true)));
$this->updateMigrationDocument($migrationDocument, $projectDocument);
$migration = $this->dbForProject->getDocument('migrations', $migration->getId());
$log->addTag('type', $migrationDocument->getAttribute('source'));
if (
$migration->getAttribute('source') === SourceAppwrite::getName() ||
$migration->getAttribute('destination') === DestinationAppwrite::getName()
) {
$credentials = $migration->getAttribute('credentials', []);
$source = $this->processSource($migrationDocument->getAttribute('source'), $migrationDocument->getAttribute('credentials'));
$credentials['projectId'] = $credentials['projectId'] ?? $projectDocument->getId();
$credentials['endpoint'] = $credentials['endpoint'] ?? 'http://appwrite/v1';
$credentials['apiKey'] = $credentials['apiKey'] ?? $tempAPIKey['secret'];
$migration->setAttribute('credentials', $credentials);
}
$migration->setAttribute('stage', 'processing');
$migration->setAttribute('status', 'processing');
$this->updateMigrationDocument($migration, $projectDocument);
$log->addTag('type', $migration->getAttribute('source'));
$source = $this->processSource($migration);
$destination = $this->processDestination($migration);
$source->report();
$destination = new DestinationsAppwrite(
$projectDocument->getId(),
'http://appwrite/v1',
$tempAPIKey['secret'],
);
$transfer = new Transfer(
$source,
$destination
);
/** Start Transfer */
$migrationDocument->setAttribute('stage', 'migrating');
$log->addBreadcrumb(new Breadcrumb("debug", "migration", "Migration hit stage 'migrating'", \microtime(true)));
$this->updateMigrationDocument($migrationDocument, $projectDocument);
$transfer->run($migrationDocument->getAttribute('resources'), function () use ($migrationDocument, $transfer, $projectDocument) {
$migrationDocument->setAttribute('resourceData', json_encode($transfer->getCache()));
$migrationDocument->setAttribute('statusCounters', json_encode($transfer->getStatusCounters()));
$migration->setAttribute('stage', 'migrating');
$this->updateMigrationDocument($migration, $projectDocument);
$this->updateMigrationDocument($migrationDocument, $projectDocument);
});
$transfer->run(
$migration->getAttribute('resources'),
function () use ($migration, $transfer, $projectDocument) {
$migration->setAttribute('resourceData', json_encode($transfer->getCache()));
$migration->setAttribute('statusCounters', json_encode($transfer->getStatusCounters()));
$this->updateMigrationDocument($migration, $projectDocument);
},
$migration->getAttribute('resourceId'),
$migration->getAttribute('resourceType')
);
$destination->shutDown();
$source->shutDown();
$sourceErrors = $source->getErrors();
$destinationErrors = $destination->getErrors();
if (!empty($sourceErrors) || !empty($destinationErrors)) {
$migrationDocument->setAttribute('status', 'failed');
$migrationDocument->setAttribute('stage', 'finished');
$log->addBreadcrumb(new Breadcrumb("debug", "migration", "Migration hit stage 'finished' and failed", \microtime(true)));
if (! empty($sourceErrors) || ! empty($destinationErrors)) {
$migration->setAttribute('status', 'failed');
$migration->setAttribute('stage', 'finished');
$errorMessages = [];
foreach ($sourceErrors as $error) {
/** @var MigrationException $error */
$errorMessages[] = "Error occurred while fetching '{$error->getResourceGroup()}:{$error->getResourceId()}' from source with message: '{$error->getMessage()}'";
/** @var $sourceErrors $error */
$message = "Error occurred while fetching '{$error->getResourceName()}:{$error->getResourceId()}' from source with message: '{$error->getMessage()}'";
if ($error->getPrevious()) {
$message .= " Message: ".$error->getPrevious()->getMessage() . " File: ".$error->getPrevious()->getFile() . " Line: ".$error->getPrevious()->getLine();
}
$errorMessages[] = $message;
}
foreach ($destinationErrors as $error) {
$message = "Error occurred while pushing '{$error->getResourceName()}:{$error->getResourceId()}' to destination with message: '{$error->getMessage()}'";
if ($error->getPrevious()) {
$message .= " Message: ".$error->getPrevious()->getMessage() . " File: ".$error->getPrevious()->getFile() . " Line: ".$error->getPrevious()->getLine();
}
/** @var MigrationException $error */
$errorMessages[] = "Error occurred while pushing '{$error->getResourceGroup()}:{$error->getResourceId()}' to destination with message: '{$error->getMessage()}'";
$errorMessages[] = $message;
}
$migrationDocument->setAttribute('errors', $errorMessages);
$migration->setAttribute('errors', $errorMessages);
$log->addExtra('migrationErrors', json_encode($errorMessages));
$this->updateMigrationDocument($migrationDocument, $projectDocument);
$this->updateMigrationDocument($migration, $projectDocument);
return;
}
$migrationDocument->setAttribute('status', 'completed');
$migrationDocument->setAttribute('stage', 'finished');
$log->addBreadcrumb(new Breadcrumb("debug", "migration", "Migration hit stage 'finished' and succeeded", \microtime(true)));
$migration->setAttribute('status', 'completed');
$migration->setAttribute('stage', 'finished');
} catch (\Throwable $th) {
Console::error($th->getMessage());
Console::error($th->getTraceAsString());
if ($migrationDocument) {
Console::error($th->getMessage());
Console::error($th->getTraceAsString());
$migrationDocument->setAttribute('status', 'failed');
$migrationDocument->setAttribute('stage', 'finished');
$migrationDocument->setAttribute('errors', [$th->getMessage()]);
if (! $migration->isEmpty()) {
$migration->setAttribute('status', 'failed');
$migration->setAttribute('stage', 'finished');
$migration->setAttribute('errors', [$th->getMessage()]);
return;
}
@@ -337,26 +374,35 @@ class Migrations extends Action
$errorMessages = [];
foreach ($sourceErrors as $error) {
/** @var MigrationException $error */
$errorMessages[] = "Error occurred while fetching '{$error->getResourceGroup()}:{$error->getResourceId()}' from source with message '{$error->getMessage()}'";
$errorMessages[] = "Error occurred while fetching '{$error->getResourceName()}:{$error->getResourceId()}' from source with message '{$error->getMessage()}'";
}
foreach ($destinationErrors as $error) {
/** @var MigrationException $error */
$errorMessages[] = "Error occurred while pushing '{$error->getResourceGroup()}:{$error->getResourceId()}' to destination with message '{$error->getMessage()}'";
$errorMessages[] = "Error occurred while pushing '{$error->getResourceName()}:{$error->getResourceId()}' to destination with message '{$error->getMessage()}'";
}
$migrationDocument->setAttribute('errors', $errorMessages);
$migration->setAttribute('errors', $errorMessages);
$log->addTag('migrationErrors', json_encode($errorMessages));
}
} finally {
if ($tempAPIKey) {
if (! $tempAPIKey->isEmpty()) {
$this->removeAPIKey($tempAPIKey);
}
if ($migrationDocument) {
$this->updateMigrationDocument($migrationDocument, $projectDocument);
if ($migrationDocument->getAttribute('status', '') == 'failed') {
throw new Exception("Migration failed");
}
$this->updateMigrationDocument($migration, $projectDocument);
if ($migration->getAttribute('status', '') === 'failed') {
Console::error('Migration('.$migration->getInternalId().':'.$migration->getId().') failed, Project('.$this->project->getInternalId().':'.$this->project->getId().')');
$destination->error();
$source->error();
throw new Exception('Migration failed');
}
if ($migration->getAttribute('status', '') === 'completed') {
$destination->success();
$source->success();
}
}
}
+5 -18
View File
@@ -163,7 +163,6 @@ class UsageDump extends Action
$id = \md5("{$time}_{$period}_{$key}");
$value = 0;
$diskValue = 0;
$previousValue = 0;
try {
$previousValue = ($dbForProject->getDocument('stats', $id))->getAttribute('value', 0);
@@ -179,8 +178,7 @@ class UsageDump extends Action
$collectionInternalId = $data[1];
try {
$value = $dbForProject->getSizeOfCollection('database_'.$databaseInternalId.'_collection_'.$collectionInternalId);
$diskValue = $dbForProject->getSizeOfCollectionOnDisk('database_'.$databaseInternalId.'_collection_'.$collectionInternalId);
$value = $dbForProject->getSizeOfCollection('database_' . $databaseInternalId . '_collection_' . $collectionInternalId);
} catch (\Exception $e) {
// Collection not found
if ($e->getMessage() !== 'Collection not found') {
@@ -190,25 +188,21 @@ class UsageDump extends Action
// Compare with previous value
$diff = $value - $previousValue;
$diskDiff = $diskValue - $previousValue;
if ($diff === 0 && $diskDiff === 0) {
if ($diff === 0) {
break;
}
// Update Collection
$updateMetric($dbForProject, $diff, $key, $period, $time);
$updateMetric($dbForProject, $diskDiff, $key . '_disk', $period, $time);
// Update Database
$databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE);
$updateMetric($dbForProject, $diff, $databaseKey, $period, $time);
$updateMetric($dbForProject, $diskDiff, $databaseKey . '_disk', $period, $time);
// Update Project
$projectKey = METRIC_DATABASES_STORAGE;
$updateMetric($dbForProject, $diff, $projectKey, $period, $time);
$updateMetric($dbForProject, $diskDiff, $projectKey . '_disk', $period, $time);
break;
// Database Level
case METRIC_DATABASE_LEVEL_STORAGE:
@@ -227,8 +221,7 @@ class UsageDump extends Action
foreach ($collections as $collection) {
try {
$value += $dbForProject->getSizeOfCollection('database_'.$databaseInternalId.'_collection_'.$collection->getInternalId());
$diskValue += $dbForProject->getSizeOfCollectionOnDisk('database_'.$databaseInternalId.'_collection_'.$collection->getInternalId());
$value += $dbForProject->getSizeOfCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId());
} catch (\Exception $e) {
// Collection not found
if ($e->getMessage() !== 'Collection not found') {
@@ -238,21 +231,18 @@ class UsageDump extends Action
}
$diff = $value - $previousValue;
$diskDiff = $diskValue - $previousValue;
if ($diff === 0 && $diskDiff === 0) {
if ($diff === 0) {
break;
}
// Update Database
$databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE);
$updateMetric($dbForProject, $diff, $databaseKey, $period, $time);
$updateMetric($dbForProject, $diskDiff, $databaseKey . '_disk', $period, $time);
// Update Project
$projectKey = METRIC_DATABASES_STORAGE;
$updateMetric($dbForProject, $diff, $projectKey, $period, $time);
$updateMetric($dbForProject, $diskDiff, $projectKey . '_disk', $period, $time);
break;
// Project Level
case METRIC_PROJECT_LEVEL_STORAGE:
@@ -266,8 +256,7 @@ class UsageDump extends Action
foreach ($collections as $collection) {
try {
$value += $dbForProject->getSizeOfCollection('database_'.$database->getInternalId().'_collection_'.$collection->getInternalId());
$diskValue += $dbForProject->getSizeOfCollectionOnDisk('database_'.$database->getInternalId().'_collection_'.$collection->getInternalId());
$value += $dbForProject->getSizeOfCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
} catch (\Exception $e) {
// Collection not found
if ($e->getMessage() !== 'Collection not found') {
@@ -278,12 +267,10 @@ class UsageDump extends Action
}
$diff = $value - $previousValue;
$diskDiff = $diskValue - $previousValue;
// Update Project
$projectKey = METRIC_DATABASES_STORAGE;
$updateMetric($dbForProject, $diff, $projectKey, $period, $time);
$updateMetric($dbForProject, $diskDiff, $projectKey . '_disk', $period, $time);
break;
}
}
@@ -549,6 +549,7 @@ class OpenAPI3 extends Format
switch ($rule['type']) {
case 'string':
case 'datetime':
case 'payload':
$type = 'string';
break;
+23 -28
View File
@@ -286,13 +286,26 @@ class Swagger2 extends Format
$validator = $validator->getValidator();
}
$validatorClass = (!empty($validator)) ? \get_class($validator) : '';
if ($validatorClass === 'Utopia\Validator\AnyOf') {
$validator = $param['validator']->getValidators()[0];
$validatorClass = \get_class($validator);
$class = !empty($validator)
? \get_class($validator)
: '';
$base = !empty($class)
? \get_parent_class($class)
: '';
switch ($base) {
case 'Appwrite\Utopia\Database\Validator\Queries\Base':
$class = $base;
break;
}
switch ($validatorClass) {
if ($class === 'Utopia\Validator\AnyOf') {
$validator = $param['validator']->getValidators()[0];
$class = \get_class($validator);
}
switch ($class) {
case 'Utopia\Validator\Text':
case 'Utopia\Database\Validator\UID':
$node['type'] = $validator->getType();
@@ -348,29 +361,7 @@ class Swagger2 extends Format
$consumes = ['multipart/form-data'];
$node['type'] = 'payload';
break;
case 'Appwrite\Utopia\Database\Validator\Queries\Attributes':
case 'Appwrite\Utopia\Database\Validator\Queries\Buckets':
case 'Appwrite\Utopia\Database\Validator\Queries\Collections':
case 'Appwrite\Utopia\Database\Validator\Queries\Databases':
case 'Appwrite\Utopia\Database\Validator\Queries\Deployments':
case 'Appwrite\Utopia\Database\Validator\Queries\Executions':
case 'Appwrite\Utopia\Database\Validator\Queries\Files':
case 'Appwrite\Utopia\Database\Validator\Queries\Functions':
case 'Appwrite\Utopia\Database\Validator\Queries\Identities':
case 'Appwrite\Utopia\Database\Validator\Queries\Indexes':
case 'Appwrite\Utopia\Database\Validator\Queries\Installations':
case 'Appwrite\Utopia\Database\Validator\Queries\Memberships':
case 'Appwrite\Utopia\Database\Validator\Queries\Messages':
case 'Appwrite\Utopia\Database\Validator\Queries\Migrations':
case 'Appwrite\Utopia\Database\Validator\Queries\Projects':
case 'Appwrite\Utopia\Database\Validator\Queries\Providers':
case 'Appwrite\Utopia\Database\Validator\Queries\Rules':
case 'Appwrite\Utopia\Database\Validator\Queries\Subscribers':
case 'Appwrite\Utopia\Database\Validator\Queries\Targets':
case 'Appwrite\Utopia\Database\Validator\Queries\Teams':
case 'Appwrite\Utopia\Database\Validator\Queries\Topics':
case 'Appwrite\Utopia\Database\Validator\Queries\Users':
case 'Appwrite\Utopia\Database\Validator\Queries\Variables':
case 'Appwrite\Utopia\Database\Validator\Queries\Base':
case 'Utopia\Database\Validator\Queries':
case 'Utopia\Database\Validator\Queries\Document':
case 'Utopia\Database\Validator\Queries\Documents':
@@ -585,6 +576,10 @@ class Swagger2 extends Format
$type = 'boolean';
break;
case 'payload':
$type = 'payload';
break;
default:
$type = 'object';
$rule['type'] = ($rule['type']) ?: 'none';
@@ -66,7 +66,7 @@ class Base extends Queries
new Limit(),
new Offset(),
new Cursor(),
new Filter($attributes),
new Filter($attributes, APP_DATABASE_QUERY_MAX_VALUES),
new Order($attributes),
];
@@ -8,6 +8,7 @@ class Migrations extends Base
'status',
'stage',
'source',
'destination',
'resources',
'statusCounters',
'resourceData',
+5 -1
View File
@@ -122,7 +122,11 @@ class Request extends UtopiaRequest
*/
public function getHeaders(): array
{
$headers = $this->generateHeaders();
try {
$headers = $this->generateHeaders();
} catch (\Throwable) {
$headers = [];
}
if (empty($this->swoole->cookie)) {
return $headers;
+5
View File
@@ -625,6 +625,11 @@ class Response extends SwooleResponse
}
}
if (!$data->isSet($key) && !$rule['required']) { // set output key null if data key is not set and required is false
$output[$key] = null;
continue;
}
if ($rule['array']) {
if (!is_array($data[$key])) {
throw new Exception($key . ' must be an array of type ' . $rule['type']);
+1
View File
@@ -14,6 +14,7 @@ abstract class Model
public const TYPE_DATETIME = 'datetime';
public const TYPE_DATETIME_EXAMPLE = '2020-10-15T06:38:00.000+00:00';
public const TYPE_RELATIONSHIP = 'relationship';
public const TYPE_PAYLOAD = 'payload';
/**
* @var bool
@@ -47,7 +47,18 @@ class Attribute extends Model
'required' => false,
'example' => false,
])
;
->addRule('$createdAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Attribute creation date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$updatedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Attribute update date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
]);
}
public array $conditions = [];
@@ -84,7 +84,6 @@ class Execution extends Model
'type' => self::TYPE_STRING,
'description' => 'HTTP response body. This will return empty unless execution is created as synchronous.',
'default' => '',
'example' => 'Developers are awesome.',
])
->addRule('responseHeaders', [
'type' => Response::MODEL_HEADERS,
+12 -5
View File
@@ -49,13 +49,22 @@ class Index extends Model
'array' => true,
'required' => false,
])
;
->addRule('$createdAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Index creation date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
->addRule('$updatedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Index update date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
]);
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
@@ -64,8 +73,6 @@ class Index extends Model
/**
* Get Collection
*
* @return string
*/
public function getType(): string
{
@@ -18,7 +18,7 @@ class Migration extends Model
])
->addRule('$createdAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Variable creation date in ISO 8601 format.',
'description' => 'Migration creation date in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
@@ -46,9 +46,15 @@ class Migration extends Model
'default' => '',
'example' => 'Appwrite',
])
->addRule('destination', [
'type' => self::TYPE_STRING,
'description' => 'A string containing the type of destination of the migration.',
'default' => 'Appwrite',
'example' => 'Appwrite',
])
->addRule('resources', [
'type' => self::TYPE_STRING,
'description' => 'Resources to migration.',
'description' => 'Resources to migrate.',
'default' => [],
'example' => ['user'],
'array' => true
@@ -234,6 +234,18 @@ class Project extends Model
'default' => '',
'example' => 'tls',
])
->addRule('pingCount', [
'type' => self::TYPE_INTEGER,
'description' => 'Number of times the ping was received for this project.',
'default' => 0,
'example' => 1,
])
->addRule('pingedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Last ping datetime in ISO 8601 format.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
;
$services = Config::getParam('services', []);
@@ -32,7 +32,7 @@ class Target extends Model
'type' => self::TYPE_STRING,
'description' => 'Target Name.',
'default' => '',
'example' => 'Aegon apple token',
'example' => 'Apple iPhone 12',
])
->addRule('userId', [
'type' => self::TYPE_STRING,
@@ -58,6 +58,12 @@ class Target extends Model
'description' => 'The target identifier.',
'default' => '',
'example' => 'token',
])
->addRule('expired', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Is the target expired.',
'default' => false,
'example' => false,
]);
}
+1 -1
View File
@@ -133,7 +133,7 @@ class HTTPTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
// looks like recent change in the validator
$this->assertTrue(empty($response['body']['schemaValidationMessages']));
$this->assertEmpty($response['body']['schemaValidationMessages'], 'Schema validation failed for ' . $file . ': ' . json_encode($response['body']['schemaValidationMessages'], JSON_PRETTY_PRINT));
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace Tests\E2E\General;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideClient;
class PingTest extends Scope
{
use ProjectCustom;
use SideClient;
public function testPing()
{
/**
* Test for SUCCESS
*/
// Without user session
$response = $this->client->call(Client::METHOD_GET, '/ping', [
'x-appwrite-project' => $this->getProject()['$id'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('Pong!', $response['body']);
// With user session
$response = $this->client->call(Client::METHOD_GET, '/ping', array_merge([
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('Pong!', $response['body']);
// With API key
$response = $this->client->call(Client::METHOD_GET, '/ping', [
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('Pong!', $response['body']);
/**
* Test for FAILURE
*/
// Fake project ID
$response = $this->client->call(Client::METHOD_GET, '/ping', \array_merge([
'x-appwrite-project' => 'fake-project-id',
], $this->getHeaders()));
$this->assertEquals(404, $response['headers']['status-code']);
$this->assertNotContains('Pong!', $response['body']);
}
}
+4 -7
View File
@@ -883,9 +883,6 @@ class UsageTest extends Scope
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$code = realpath(__DIR__ . '/../../resources/functions') . "/php/code.tar.gz";
$this->packageCode('php');
$response = $this->client->call(
Client::METHOD_POST,
'/functions/' . $functionId . '/deployments',
@@ -895,8 +892,8 @@ class UsageTest extends Scope
], $this->getHeaders()),
[
'entrypoint' => 'index.php',
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)),
'activate' => true
'code' => $this->packageFunction('php'),
'activate' => true,
]
);
@@ -934,7 +931,7 @@ class UsageTest extends Scope
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'async' => false,
'async' => 'false',
]
);
@@ -958,7 +955,7 @@ class UsageTest extends Scope
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'async' => false,
'async' => 'false',
]
);
@@ -2695,4 +2695,45 @@ class AccountCustomClientTest extends Scope
return $data;
}
public function testCreatePushTarget(): void
{
$response = $this->client->call(Client::METHOD_POST, '/account/targets/push', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()), [
'targetId' => ID::unique(),
'identifier' => 'test-identifier',
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals('test-identifier', $response['body']['identifier']);
}
public function testUpdatePushTarget(): void
{
$response = $this->client->call(Client::METHOD_POST, '/account/targets/push', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'targetId' => ID::unique(),
'identifier' => 'test-identifier-2',
]);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals('test-identifier-2', $response['body']['identifier']);
$response = $this->client->call(Client::METHOD_PUT, '/account/targets/'. $response['body']['$id'] .'/push', \array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'identifier' => 'test-identifier-updated',
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('test-identifier-updated', $response['body']['identifier']);
$this->assertEquals(false, $response['body']['expired']);
}
}
+17 -2
View File
@@ -1744,6 +1744,21 @@ trait DatabasesBase
$this->assertEquals(400, $documents['headers']['status-code']);
/**
* Test null value for cursor
*/
$documents = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
'{"method":"cursorAfter","values":[null]}',
],
]);
$this->assertEquals(400, $documents['headers']['status-code']);
return [];
}
@@ -2096,7 +2111,7 @@ trait DatabasesBase
*/
$conditions = [];
for ($i = 0; $i < 101; $i++) {
for ($i = 0; $i < APP_DATABASE_QUERY_MAX_VALUES + 1; $i++) {
$conditions[] = $i;
}
@@ -2109,7 +2124,7 @@ trait DatabasesBase
],
]);
$this->assertEquals(400, $documents['headers']['status-code']);
$this->assertEquals('Invalid query: Query on attribute has greater than 100 values: releaseYear', $documents['body']['message']);
$this->assertEquals('Invalid query: Query on attribute has greater than '.APP_DATABASE_QUERY_MAX_VALUES.' values: releaseYear', $documents['body']['message']);
$value = '';
+154 -181
View File
@@ -2,234 +2,207 @@
namespace Tests\E2E\Services\Functions;
use Appwrite\Tests\Async;
use CURLFile;
use Tests\E2E\Client;
use Utopia\CLI\Console;
trait FunctionsBase
{
use Async;
protected string $stdout = '';
protected string $stderr = '';
protected function packageCode($folder)
protected function setupFunction(mixed $params): string
{
Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr);
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), $params);
$this->assertEquals($function['headers']['status-code'], 201, 'Setup function failed with status code: ' . $function['headers']['status-code'] . ' and response: ' . json_encode($function['body'], JSON_PRETTY_PRINT));
$functionId = $function['body']['$id'];
return $functionId;
}
protected function awaitDeploymentIsBuilt($functionId, $deploymentId, $checkForSuccess = true): void
protected function setupDeployment(string $functionId, mixed $params): string
{
while (true) {
$deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, [
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]), $params);
$this->assertEquals($deployment['headers']['status-code'], 202, 'Setup deployment failed with status code: ' . $deployment['headers']['status-code'] . ' and response: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT));
$deploymentId = $deployment['body']['$id'] ?? '';
$this->assertEventually(function () use ($functionId, $deploymentId) {
$deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
]));
$this->assertEquals('ready', $deployment['body']['status'], 'Deployment status is not ready, deployment: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT));
}, 50000, 500);
if (
$deployment['headers']['status-code'] >= 400
|| \in_array($deployment['body']['status'], ['ready', 'failed'])
) {
break;
}
\sleep(1);
}
if ($checkForSuccess) {
$this->assertEquals(200, $deployment['headers']['status-code']);
$this->assertEquals('ready', $deployment['body']['status'], \json_encode($deployment['body']));
}
return $deploymentId;
}
// /**
// * @depends testCreateTeam
// */
// public function testGetTeam($data):array
// {
// $id = $data['teamUid'] ?? '';
protected function cleanupFunction(string $functionId): void
{
$function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]));
// /**
// * Test for SUCCESS
// */
// $response = $this->client->call(Client::METHOD_GET, '/teams/'.$id, array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()));
$this->assertEquals($function['headers']['status-code'], 204);
}
// $this->assertEquals(200, $response['headers']['status-code']);
// $this->assertNotEmpty($response['body']['$id']);
// $this->assertEquals('Arsenal', $response['body']['name']);
// $this->assertGreaterThan(-1, $response['body']['total']);
// $this->assertIsInt($response['body']['total']);
// $this->assertIsInt($response['body']['dateCreated']);
protected function createFunction(mixed $params): mixed
{
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), $params);
// /**
// * Test for FAILURE
// */
return $function;
}
// return [];
// }
protected function createVariable(string $functionId, mixed $params): mixed
{
$variable = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), $params);
// /**
// * @depends testCreateTeam
// */
// public function testListTeams($data):array
// {
// /**
// * Test for SUCCESS
// */
// $response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()));
return $variable;
}
// $this->assertEquals(200, $response['headers']['status-code']);
// $this->assertGreaterThan(0, $response['body']['total']);
// $this->assertIsInt($response['body']['total']);
// $this->assertCount(3, $response['body']['teams']);
protected function getFunction(string $functionId): mixed
{
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
// $response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()), [
// 'limit' => 2,
// ]);
return $function;
}
// $this->assertEquals(200, $response['headers']['status-code']);
// $this->assertGreaterThan(0, $response['body']['total']);
// $this->assertIsInt($response['body']['total']);
// $this->assertCount(2, $response['body']['teams']);
protected function getDeployment(string $functionId, string $deploymentId): mixed
{
$deployment = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
// $response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()), [
// 'offset' => 1,
// ]);
return $deployment;
}
// $this->assertEquals(200, $response['headers']['status-code']);
// $this->assertGreaterThan(0, $response['body']['total']);
// $this->assertIsInt($response['body']['total']);
// $this->assertCount(2, $response['body']['teams']);
protected function getExecution(string $functionId, $executionId): mixed
{
$execution = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
// $response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()), [
// 'search' => 'Manchester',
// ]);
return $execution;
}
// $this->assertEquals(200, $response['headers']['status-code']);
// $this->assertGreaterThan(0, $response['body']['total']);
// $this->assertIsInt($response['body']['total']);
// $this->assertCount(1, $response['body']['teams']);
// $this->assertEquals('Manchester United', $response['body']['teams'][0]['name']);
protected function listFunctions(mixed $params = []): mixed
{
$functions = $this->client->call(Client::METHOD_GET, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), $params);
// $response = $this->client->call(Client::METHOD_GET, '/teams', array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()), [
// 'search' => 'United',
// ]);
return $functions;
}
// $this->assertEquals(200, $response['headers']['status-code']);
// $this->assertGreaterThan(0, $response['body']['total']);
// $this->assertIsInt($response['body']['total']);
// $this->assertCount(1, $response['body']['teams']);
// $this->assertEquals('Manchester United', $response['body']['teams'][0]['name']);
protected function listDeployments(string $functionId, $params = []): mixed
{
$deployments = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), $params);
// /**
// * Test for FAILURE
// */
return $deployments;
}
// return [];
// }
protected function listExecutions(string $functionId, mixed $params = []): mixed
{
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), $params);
// public function testUpdateTeam():array
// {
// /**
// * Test for SUCCESS
// */
// $response = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()), [
// 'name' => 'Demo'
// ]);
return $executions;
}
// $this->assertEquals(201, $response['headers']['status-code']);
// $this->assertNotEmpty($response['body']['$id']);
// $this->assertEquals('Demo', $response['body']['name']);
// $this->assertGreaterThan(-1, $response['body']['total']);
// $this->assertIsInt($response['body']['total']);
// $this->assertIsInt($response['body']['dateCreated']);
protected function packageFunction(string $function): CURLFile
{
$folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function";
$tarPath = "$folderPath/code.tar.gz";
// $response = $this->client->call(Client::METHOD_PUT, '/teams/'.$response['body']['$id'], array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()), [
// 'name' => 'Demo New'
// ]);
Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr);
// $this->assertEquals(200, $response['headers']['status-code']);
// $this->assertNotEmpty($response['body']['$id']);
// $this->assertEquals('Demo New', $response['body']['name']);
// $this->assertGreaterThan(-1, $response['body']['total']);
// $this->assertIsInt($response['body']['total']);
// $this->assertIsInt($response['body']['dateCreated']);
if (filesize($tarPath) > 1024 * 1024 * 5) {
throw new \Exception('Code package is too large. Use the chunked upload method instead.');
}
// /**
// * Test for FAILURE
// */
// $response = $this->client->call(Client::METHOD_PUT, '/teams/'.$response['body']['$id'], array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()), [
// ]);
return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath));
}
// $this->assertEquals(400, $response['headers']['status-code']);
protected function createDeployment(string $functionId, mixed $params = []): mixed
{
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), $params);
// return [];
// }
return $deployment;
}
// public function testDeleteTeam():array
// {
// /**
// * Test for SUCCESS
// */
// $response = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()), [
// 'name' => 'Demo'
// ]);
protected function getFunctionUsage(string $functionId, mixed $params): mixed
{
$usage = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/usage', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), $params);
// $teamUid = $response['body']['$id'];
return $usage;
}
// $this->assertEquals(201, $response['headers']['status-code']);
// $this->assertNotEmpty($response['body']['$id']);
// $this->assertEquals('Demo', $response['body']['name']);
// $this->assertGreaterThan(-1, $response['body']['total']);
// $this->assertIsInt($response['body']['total']);
// $this->assertIsInt($response['body']['dateCreated']);
protected function getTemplate(string $templateId)
{
$template = $this->client->call(Client::METHOD_GET, '/functions/templates/' . $templateId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
// $response = $this->client->call(Client::METHOD_DELETE, '/teams/'.$teamUid, array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()));
return $template;
}
// $this->assertEquals(204, $response['headers']['status-code']);
// $this->assertEmpty($response['body']);
protected function createExecution(string $functionId, mixed $params = []): mixed
{
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), $params);
// /**
// * Test for FAILURE
// */
// $response = $this->client->call(Client::METHOD_GET, '/teams/'.$teamUid, array_merge([
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()));
return $execution;
}
// $this->assertEquals(404, $response['headers']['status-code']);
protected function deleteFunction(string $functionId): mixed
{
$function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
// return [];
// }
return $function;
}
}
@@ -13,13 +13,11 @@ class FunctionsConsoleClientTest extends Scope
{
use ProjectCustom;
use SideConsole;
use FunctionsBase;
public function testCreateFunction(): array
{
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
$function = $this->createFunction([
'functionId' => ID::unique(),
'name' => 'Test',
'execute' => [Role::user($this->getUser()['$id'])->toString()],
@@ -35,10 +33,9 @@ class FunctionsConsoleClientTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
$response = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
$functionId = $function['body']['$id'];
$function2 = $this->createFunction([
'functionId' => ID::unique(),
'name' => 'Test Failure',
'execute' => ['some-random-string'],
@@ -46,73 +43,59 @@ class FunctionsConsoleClientTest extends Scope
'entrypoint' => 'index.php',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$this->assertEquals(400, $function2['headers']['status-code']);
return [
'functionId' => $function['body']['$id']
'functionId' => $functionId,
];
}
/**
* @depends testCreateFunction
*/
public function testGetCollectionUsage(array $data)
public function testFunctionUsage(array $data)
{
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/usage', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()), [
'range' => '232h'
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_GET, '/functions/randomFunctionId/usage', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()), [
'range' => '24h'
]);
$this->assertEquals(404, $response['headers']['status-code']);
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/usage', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()), [
$usage = $this->getFunctionUsage($data['functionId'], [
'range' => '24h'
]);
$this->assertEquals(200, $usage['headers']['status-code']);
$this->assertEquals(19, count($usage['body']));
$this->assertEquals('24h', $usage['body']['range']);
$this->assertIsNumeric($usage['body']['deploymentsTotal']);
$this->assertIsNumeric($usage['body']['deploymentsStorageTotal']);
$this->assertIsNumeric($usage['body']['buildsTotal']);
$this->assertIsNumeric($usage['body']['buildsStorageTotal']);
$this->assertIsNumeric($usage['body']['buildsTimeTotal']);
$this->assertIsNumeric($usage['body']['buildsMbSecondsTotal']);
$this->assertIsNumeric($usage['body']['executionsTotal']);
$this->assertIsNumeric($usage['body']['executionsTimeTotal']);
$this->assertIsNumeric($usage['body']['executionsMbSecondsTotal']);
$this->assertIsArray($usage['body']['deployments']);
$this->assertIsArray($usage['body']['deploymentsStorage']);
$this->assertIsArray($usage['body']['builds']);
$this->assertIsArray($usage['body']['buildsTime']);
$this->assertIsArray($usage['body']['buildsStorage']);
$this->assertIsArray($usage['body']['buildsTime']);
$this->assertIsArray($usage['body']['buildsMbSeconds']);
$this->assertIsArray($usage['body']['executions']);
$this->assertIsArray($usage['body']['executionsTime']);
$this->assertIsArray($usage['body']['executionsMbSeconds']);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(19, count($response['body']));
$this->assertEquals('24h', $response['body']['range']);
$this->assertIsNumeric($response['body']['deploymentsTotal']);
$this->assertIsNumeric($response['body']['deploymentsStorageTotal']);
$this->assertIsNumeric($response['body']['buildsTotal']);
$this->assertIsNumeric($response['body']['buildsStorageTotal']);
$this->assertIsNumeric($response['body']['buildsTimeTotal']);
$this->assertIsNumeric($response['body']['buildsMbSecondsTotal']);
$this->assertIsNumeric($response['body']['executionsTotal']);
$this->assertIsNumeric($response['body']['executionsTimeTotal']);
$this->assertIsNumeric($response['body']['executionsMbSecondsTotal']);
$this->assertIsArray($response['body']['deployments']);
$this->assertIsArray($response['body']['deploymentsStorage']);
$this->assertIsArray($response['body']['builds']);
$this->assertIsArray($response['body']['buildsTime']);
$this->assertIsArray($response['body']['buildsStorage']);
$this->assertIsArray($response['body']['buildsTime']);
$this->assertIsArray($response['body']['buildsMbSeconds']);
$this->assertIsArray($response['body']['executions']);
$this->assertIsArray($response['body']['executionsTime']);
$this->assertIsArray($response['body']['executionsMbSeconds']);
/**
* Test for FAILURE
*/
$usage = $this->getFunctionUsage($data['functionId'], [
'range' => '232h'
]);
$this->assertEquals(400, $usage['headers']['status-code']);
$usage = $this->getFunctionUsage('randomFunctionId', [
'range' => '24h'
]);
$this->assertEquals(404, $usage['headers']['status-code']);
}
/**
@@ -123,31 +106,53 @@ class FunctionsConsoleClientTest extends Scope
/**
* Test for SUCCESS
*/
$variable = $this->createVariable(
$data['functionId'],
[
'key' => 'APP_TEST',
'value' => 'TESTINGVALUE'
]
);
$response = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'APP_TEST',
'value' => 'TESTINGVALUE'
]);
$this->assertEquals(201, $variable['headers']['status-code']);
$this->assertEquals(201, $response['headers']['status-code']);
$variableId = $response['body']['$id'];
$variableId = $variable['body']['$id'];
/**
* Test for FAILURE
*/
// Test for duplicate key
$variable = $this->createVariable(
$data['functionId'],
[
'key' => 'APP_TEST',
'value' => 'ANOTHERTESTINGVALUE'
]
);
$response = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'APP_TEST',
'value' => 'ANOTHER_TESTINGVALUE'
]);
$this->assertEquals(409, $variable['headers']['status-code']);
$this->assertEquals(409, $response['headers']['status-code']);
// Test for invalid key
$variable = $this->createVariable(
$data['functionId'],
[
'key' => str_repeat("A", 256),
'value' => 'TESTINGVALUE'
]
);
$this->assertEquals(400, $variable['headers']['status-code']);
// Test for invalid value
$variable = $this->createVariable(
$data['functionId'],
[
'key' => 'LONGKEY',
'value' => str_repeat("#", 8193),
]
);
$this->assertEquals(400, $variable['headers']['status-code']);
return array_merge(
$data,
@@ -155,28 +160,6 @@ class FunctionsConsoleClientTest extends Scope
'variableId' => $variableId
]
);
$longKey = str_repeat("A", 256);
$response = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => $longKey,
'value' => 'TESTINGVALUE'
]);
$this->assertEquals(400, $response['headers']['status-code']);
$longValue = str_repeat("#", 8193);
$response = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'key' => 'LONGKEY',
'value' => $longValue
]);
$this->assertEquals(400, $response['headers']['status-code']);
}
/**
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,214 @@
<?php
namespace Tests\E2E\Services\Functions;
use Appwrite\ID;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Utopia\Database\Helpers\Role;
class FunctionsScheduleTest extends Scope
{
use FunctionsBase;
use ProjectCustom;
use SideServer;
public function testCreateScheduledExecution()
{
/**
* Test for SUCCESS
*/
$functionId = $this->setupFunction([
'functionId' => ID::unique(),
'name' => 'Test',
'execute' => [Role::user($this->getUser()['$id'])->toString()],
'runtime' => 'php-8.0',
'entrypoint' => 'index.php',
'events' => [
'users.*.create',
'users.*.delete',
],
'schedule' => '* * * * *', // Execute every 60 seconds
'timeout' => 10,
]);
$this->setupDeployment($functionId, [
'entrypoint' => 'index.php',
'code' => $this->packageFunction('php'),
'activate' => true
]);
// Wait for scheduled execution
\sleep(60);
$this->assertEventually(function () use ($functionId) {
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $executions['headers']['status-code']);
$this->assertCount(1, $executions['body']['executions']);
$asyncExecution = $executions['body']['executions'][0];
$this->assertEquals('schedule', $asyncExecution['trigger']);
$this->assertEquals('completed', $asyncExecution['status']);
$this->assertEquals(200, $asyncExecution['responseStatusCode']);
$this->assertEquals('', $asyncExecution['responseBody']);
$this->assertNotEmpty($asyncExecution['logs']);
$this->assertNotEmpty($asyncExecution['errors']);
$this->assertGreaterThan(0, $asyncExecution['duration']);
}, 60000, 500);
$this->cleanupFunction($functionId);
}
public function testCreateScheduledAtExecution(): void
{
/**
* Test for SUCCESS
*/
$functionId = $this->setupFunction([
'functionId' => ID::unique(),
'name' => 'Test',
'execute' => [Role::user($this->getUser()['$id'])->toString()],
'runtime' => 'php-8.0',
'entrypoint' => 'index.php',
'timeout' => 10,
'logging' => true,
]);
$this->setupDeployment($functionId, [
'entrypoint' => 'index.php',
'code' => $this->packageFunction('php'),
'activate' => true
]);
// Schedule execution for the future
\date_default_timezone_set('UTC');
$futureTime = (new \DateTime())->add(new \DateInterval('PT2M')); // 2 minute in the future
$futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0);
$execution = $this->client->call(
Client::METHOD_POST,
'/functions/' . $functionId . '/executions',
[
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'origin' => 'http://localhost',
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $this->getUser()['session'],
],
[
'async' => true,
'scheduledAt' => $futureTime->format(\DateTime::ATOM),
'path' => '/custom-path',
'method' => 'PATCH',
'body' => 'custom-body',
'headers' => [
'x-custom-header' => 'custom-value'
]
]
);
$executionId = $execution['body']['$id'];
$this->assertEquals(202, $execution['headers']['status-code']);
$this->assertEquals('scheduled', $execution['body']['status']);
$this->assertEquals('PATCH', $execution['body']['requestMethod']);
$this->assertEquals('/custom-path', $execution['body']['requestPath']);
$this->assertCount(0, $execution['body']['requestHeaders']);
\sleep(120);
$this->assertEventually(function () use ($functionId, $executionId) {
$execution = $this->getExecution($functionId, $executionId);
$this->assertEquals(200, $execution['headers']['status-code']);
$this->assertEquals(200, $execution['body']['responseStatusCode']);
$this->assertEquals('completed', $execution['body']['status']);
$this->assertEquals('/custom-path', $execution['body']['requestPath']);
$this->assertEquals('PATCH', $execution['body']['requestMethod']);
$this->assertStringContainsString('body-is-custom-body', $execution['body']['logs']);
$this->assertStringContainsString('custom-header-is-custom-value', $execution['body']['logs']);
$this->assertStringContainsString('method-is-patch', $execution['body']['logs']);
$this->assertStringContainsString('path-is-/custom-path', $execution['body']['logs']);
$this->assertStringContainsString('user-is-' . $this->getUser()['$id'], $execution['body']['logs']);
$this->assertStringContainsString('jwt-is-valid', $execution['body']['logs']);
$this->assertGreaterThan(0, $execution['body']['duration']);
}, 10000, 500);
/* Test for FAILURE */
// Schedule synchronous execution
$execution = $this->createExecution($functionId, [
'async' => 'false',
'scheduledAt' => $futureTime->format(\DateTime::ATOM),
]);
$this->assertEquals(400, $execution['headers']['status-code']);
// Execution with seconds precision
$execution = $this->createExecution($functionId, [
'async' => true,
'scheduledAt' => (new \DateTime("2100-12-08 16:12:02"))->format(\DateTime::ATOM)
]);
$this->assertEquals(400, $execution['headers']['status-code']);
// Execution with milliseconds precision
$execution = $this->createExecution($functionId, [
'async' => true,
'scheduledAt' => (new \DateTime("2100-12-08 16:12:02.255"))->format(\DateTime::ATOM)
]);
$this->assertEquals(400, $execution['headers']['status-code']);
// Execution too soon
$execution = $this->createExecution($functionId, [
'async' => true,
'scheduledAt' => (new \DateTime())->add(new \DateInterval('PT1S'))->format(\DateTime::ATOM)
]);
$this->assertEquals(400, $execution['headers']['status-code']);
$this->cleanupFunction($functionId, $executionId);
}
public function testDeleteScheduledExecution()
{
$functionId = $this->setupFunction([
'functionId' => ID::unique(),
'name' => 'Test',
'execute' => [Role::user($this->getUser()['$id'])->toString()],
'runtime' => 'php-8.0',
'entrypoint' => 'index.php',
'timeout' => 10,
'logging' => true,
]);
$this->setupDeployment($functionId, [
'entrypoint' => 'index.php',
'code' => $this->packageFunction('php'),
'activate' => true
]);
$futureTime = (new \DateTime())->add(new \DateInterval('PT10H'));
$futureTime->setTime($futureTime->format('H'), $futureTime->format('i'), 0, 0);
$execution = $this->createExecution($functionId, [
'async' => true,
'scheduledAt' => $futureTime->format('Y-m-d H:i:s'),
]);
$this->assertEquals(202, $execution['headers']['status-code']);
$executionId = $execution['body']['$id'] ?? '';
$execution = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(204, $execution['headers']['status-code']);
$this->cleanupFunction($functionId);
}
}
+12 -2
View File
@@ -2,6 +2,7 @@
namespace Tests\E2E\Services\GraphQL;
use CURLFile;
use Utopia\CLI\Console;
trait Base
@@ -2496,8 +2497,17 @@ trait Base
protected string $stdout = '';
protected string $stderr = '';
protected function packageCode($folder): void
protected function packageFunction(string $function): CURLFile
{
Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr);
$folderPath = realpath(__DIR__ . '/../../../resources/functions') . "/$function";
$tarPath = "$folderPath/code.tar.gz";
Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr);
if (filesize($tarPath) > 1024 * 1024 * 5) {
throw new \Exception('Code package is too large. Use the chunked upload method instead.');
}
return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath));
}
}
@@ -2,7 +2,6 @@
namespace Tests\E2E\Services\GraphQL;
use CURLFile;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
@@ -83,10 +82,6 @@ class FunctionsClientTest extends Scope
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$CREATE_DEPLOYMENT);
$folder = 'php';
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$gqlPayload = [
'operations' => \json_encode([
'query' => $query,
@@ -99,7 +94,7 @@ class FunctionsClientTest extends Scope
'map' => \json_encode([
'code' => ["variables.code"]
]),
'code' => new CURLFile($code, 'application/gzip', 'code.tar.gz'),
'code' => $this->packageFunction('php')
];
$deployment = $this->client->call(Client::METHOD_POST, '/graphql', [
@@ -2,7 +2,6 @@
namespace Tests\E2E\Services\GraphQL;
use CURLFile;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
@@ -82,10 +81,6 @@ class FunctionsServerTest extends Scope
$projectId = $this->getProject()['$id'];
$query = $this->getQuery(self::$CREATE_DEPLOYMENT);
$folder = 'php';
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$gqlPayload = [
'operations' => \json_encode([
'query' => $query,
@@ -98,7 +93,7 @@ class FunctionsServerTest extends Scope
'map' => \json_encode([
'code' => ["variables.code"]
]),
'code' => new CURLFile($code, 'application/gzip', 'code.tar.gz'),
'code' => $this->packageFunction('php'),
];
$deployment = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
@@ -455,7 +455,7 @@ class HealthCustomServerTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('/CN=www.google.com', $response['body']['name']);
$this->assertEquals('www.google.com', $response['body']['subjectSN']);
$this->assertStringContainsString('Google Trust Services', $response['body']['issuerOrganisation']);
$this->assertContains($response['body']['issuerOrganisation'], ['Let\'s Encrypt', 'Google Trust Services']);
$this->assertIsInt($response['body']['validFrom']);
$this->assertIsInt($response['body']['validTo']);
@@ -467,7 +467,7 @@ class HealthCustomServerTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('/CN=appwrite.io', $response['body']['name']);
$this->assertEquals('appwrite.io', $response['body']['subjectSN']);
$this->assertEquals("Let's Encrypt", $response['body']['issuerOrganisation']);
$this->assertContains($response['body']['issuerOrganisation'], ['Let\'s Encrypt', 'Google Trust Services']);
$this->assertIsInt($response['body']['validFrom']);
$this->assertIsInt($response['body']['validTo']);
@@ -101,7 +101,7 @@ class ProjectsConsoleClientTest extends Scope
'region' => 'default'
]);
$this->assertEquals(400, $response['headers']['status-code']);
$this->assertEquals(401, $response['headers']['status-code']);
return [
'projectId' => $projectId,
@@ -546,7 +546,7 @@ class ProjectsConsoleClientTest extends Scope
'name' => '',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$this->assertEquals(401, $response['headers']['status-code']);
return ['projectId' => $projectId];
}
@@ -3793,4 +3793,272 @@ class ProjectsConsoleClientTest extends Scope
return $data;
}
/**
* @depends testCreateProject
*/
public function testCreateProjectVariable(array $data)
{
/**
* Test for SUCCESS
*/
$variable = $this->client->call(Client::METHOD_POST, '/project/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()), [
'key' => 'APP_TEST',
'value' => 'TESTINGVALUE'
]);
$this->assertEquals(201, $variable['headers']['status-code']);
$variableId = $variable['body']['$id'];
/**
* Test for FAILURE
*/
// Test for duplicate key
$variable = $this->client->call(Client::METHOD_POST, '/project/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()), [
'key' => 'APP_TEST',
'value' => 'ANOTHERTESTINGVALUE'
]);
$this->assertEquals(409, $variable['headers']['status-code']);
// Test for invalid key
$variable = $this->client->call(Client::METHOD_POST, '/project/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()), [
'key' => str_repeat("A", 256),
'value' => 'TESTINGVALUE'
]);
$this->assertEquals(400, $variable['headers']['status-code']);
// Test for invalid value
$variable = $this->client->call(Client::METHOD_POST, '/project/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()), [
'key' => 'LONGKEY',
'value' => str_repeat("#", 8193),
]);
$this->assertEquals(400, $variable['headers']['status-code']);
return array_merge(
$data,
[
'variableId' => $variableId,
]
);
}
/**
* @depends testCreateProjectVariable
*/
public function testListVariables(array $data)
{
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/project/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['variables']);
$this->assertEquals(1, $response['body']['total']);
$this->assertEquals("APP_TEST", $response['body']['variables'][0]['key']);
$this->assertEquals("TESTINGVALUE", $response['body']['variables'][0]['value']);
return $data;
}
/**
* @depends testListVariables
*/
public function testGetVariable(array $data)
{
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/project/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals("APP_TEST", $response['body']['key']);
$this->assertEquals("TESTINGVALUE", $response['body']['value']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/project/variables/NON_EXISTING_VARIABLE', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()));
$this->assertEquals(404, $response['headers']['status-code']);
return $data;
}
/**
* @depends testGetVariable
*/
public function testUpdateVariable(array $data)
{
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_PUT, '/project/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()), [
'key' => 'APP_TEST_UPDATE',
'value' => 'TESTINGVALUEUPDATED'
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals("APP_TEST_UPDATE", $response['body']['key']);
$this->assertEquals("TESTINGVALUEUPDATED", $response['body']['value']);
$variable = $this->client->call(Client::METHOD_GET, '/project/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()));
$this->assertEquals(200, $variable['headers']['status-code']);
$this->assertEquals("APP_TEST_UPDATE", $variable['body']['key']);
$this->assertEquals("TESTINGVALUEUPDATED", $variable['body']['value']);
$response = $this->client->call(Client::METHOD_GET, '/project/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(1, $response['body']['variables']);
$this->assertEquals("APP_TEST_UPDATE", $response['body']['variables'][0]['key']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_PUT, '/project/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()));
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_PUT, '/project/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()), [
'value' => 'TESTINGVALUEUPDATED_2'
]);
$this->assertEquals(400, $response['headers']['status-code']);
$longKey = str_repeat("A", 256);
$response = $this->client->call(Client::METHOD_PUT, '/project/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()), [
'key' => $longKey,
'value' => 'TESTINGVALUEUPDATED'
]);
$this->assertEquals(400, $response['headers']['status-code']);
$longValue = str_repeat("#", 8193);
$response = $this->client->call(Client::METHOD_PUT, '/project/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()), [
'key' => 'APP_TEST_UPDATE',
'value' => $longValue
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_PUT, '/project/variables/NON_EXISTING_VARIABLE', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()), [
'key' => 'APP_TEST_UPDATE',
'value' => 'TESTINGVALUEUPDATED'
]);
$this->assertEquals(404, $response['headers']['status-code']);
return $data;
}
/**
* @depends testUpdateVariable
*/
public function testDeleteVariable(array $data)
{
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_DELETE, '/project/variables/' . $data['variableId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()));
$this->assertEquals(204, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_GET, '/project/variables', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertCount(0, $response['body']['variables']);
$this->assertEquals(0, $response['body']['total']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_DELETE, '/project/variables/NON_EXISTING_VARIABLE', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $data['projectId'],
'x-appwrite-mode' => 'admin',
], $this->getHeaders()));
$this->assertEquals(404, $response['headers']['status-code']);
return $data;
}
}
+41 -26
View File
@@ -7,25 +7,34 @@ use WebSocket\ConnectionException;
trait RealtimeBase
{
private function getWebsocket($channels = [], $headers = [], $projectId = null): WebSocketClient
{
private function getWebsocket(
array $channels = [],
array $headers = [],
string $projectId = null
): WebSocketClient {
if (is_null($projectId)) {
$projectId = $this->getProject()['$id'];
}
$headers = array_merge([
'Origin' => 'appwrite.test'
], $headers);
$headers = array_merge(
[
"Origin" => "appwrite.test",
],
$headers
);
$query = [
'project' => $projectId,
'channels' => $channels
"project" => $projectId,
"channels" => $channels,
];
return new WebSocketClient('ws://appwrite-traefik/v1/realtime?' . http_build_query($query), [
'headers' => $headers,
'timeout' => 30,
]);
return new WebSocketClient(
"ws://appwrite-traefik/v1/realtime?" . http_build_query($query),
[
"headers" => $headers,
"timeout" => 30,
]
);
}
public function testConnection(): void
@@ -33,7 +42,7 @@ trait RealtimeBase
/**
* Test for SUCCESS
*/
$client = $this->getWebsocket(['documents']);
$client = $this->getWebsocket(["documents"]);
$this->assertNotEmpty($client->receive());
$client->close();
}
@@ -43,11 +52,11 @@ trait RealtimeBase
$client = $this->getWebsocket();
$payload = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $payload);
$this->assertArrayHasKey('data', $payload);
$this->assertEquals('error', $payload['type']);
$this->assertEquals(1008, $payload['data']['code']);
$this->assertEquals('Missing channels', $payload['data']['message']);
$this->assertArrayHasKey("type", $payload);
$this->assertArrayHasKey("data", $payload);
$this->assertEquals("error", $payload["type"]);
$this->assertEquals(1008, $payload["data"]["code"]);
$this->assertEquals("Missing channels", $payload["data"]["message"]);
\usleep(250000); // 250ms
$this->expectException(ConnectionException::class); // Check if server disconnnected client
$client->close();
@@ -55,18 +64,24 @@ trait RealtimeBase
public function testConnectionFailureUnknownProject(): void
{
$client = new WebSocketClient('ws://appwrite-traefik/v1/realtime?project=123', [
'headers' => [
'Origin' => 'appwrite.test'
$client = new WebSocketClient(
"ws://appwrite-traefik/v1/realtime?project=123",
[
"headers" => [
"Origin" => "appwrite.test",
],
]
]);
);
$payload = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $payload);
$this->assertArrayHasKey('data', $payload);
$this->assertEquals('error', $payload['type']);
$this->assertEquals(1008, $payload['data']['code']);
$this->assertEquals('Missing or unknown project ID', $payload['data']['message']);
$this->assertArrayHasKey("type", $payload);
$this->assertArrayHasKey("data", $payload);
$this->assertEquals("error", $payload["type"]);
$this->assertEquals(1008, $payload["data"]["code"]);
$this->assertEquals(
"Missing or unknown project ID",
$payload["data"]["message"]
);
\usleep(250000); // 250ms
$this->expectException(ConnectionException::class); // Check if server disconnnected client
$client->close();
@@ -2,7 +2,6 @@
namespace Tests\E2E\Services\Realtime;
use CURLFile;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
@@ -19,7 +18,7 @@ class RealtimeConsoleClientTest extends Scope
use ProjectCustom;
use SideConsole;
public function testManualAuthentication()
public function testManualAuthentication(): void
{
$user = $this->getUser();
$userId = $user['$id'] ?? '';
@@ -124,7 +123,7 @@ class RealtimeConsoleClientTest extends Scope
$client->close();
}
public function testAttributes()
public function testAttributes(): array
{
$user = $this->getUser();
$projectId = 'console';
@@ -184,6 +183,7 @@ class RealtimeConsoleClientTest extends Scope
'required' => true,
]);
$projectId = $this->getProject()['$id'];
$attributeKey = $name['body']['key'];
$this->assertEquals($name['headers']['status-code'], 202);
@@ -198,8 +198,9 @@ class RealtimeConsoleClientTest extends Scope
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(1, $response['data']['channels']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.create", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
@@ -218,8 +219,9 @@ class RealtimeConsoleClientTest extends Scope
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(1, $response['data']['channels']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.update", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
@@ -276,6 +278,8 @@ class RealtimeConsoleClientTest extends Scope
]);
$this->assertEquals($index['headers']['status-code'], 202);
$projectId = $this->getProject()['$id'];
$indexKey = $index['body']['key'];
$response = json_decode($client->receive(), true);
@@ -285,8 +289,9 @@ class RealtimeConsoleClientTest extends Scope
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(1, $response['data']['channels']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.create", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
@@ -303,8 +308,9 @@ class RealtimeConsoleClientTest extends Scope
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(1, $response['data']['channels']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.update", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
@@ -343,6 +349,8 @@ class RealtimeConsoleClientTest extends Scope
$this->assertContains('console', $response['data']['channels']);
$this->assertNotEmpty($response['data']['user']);
$projectId = $this->getProject()['$id'];
/**
* Test Delete Index
*/
@@ -353,6 +361,7 @@ class RealtimeConsoleClientTest extends Scope
], $this->getHeaders()));
$this->assertEquals($attribute['headers']['status-code'], 204);
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
@@ -360,8 +369,9 @@ class RealtimeConsoleClientTest extends Scope
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(1, $response['data']['channels']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.update", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
@@ -377,8 +387,9 @@ class RealtimeConsoleClientTest extends Scope
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(1, $response['data']['channels']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*.delete", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.indexes.*", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
@@ -416,10 +427,12 @@ class RealtimeConsoleClientTest extends Scope
$this->assertContains('console', $response['data']['channels']);
$this->assertNotEmpty($response['data']['user']);
$attributeKey = 'name';
$projectId = $this->getProject()['$id'];
/**
* Test Delete Attribute
*/
$attributeKey = 'name';
$attribute = $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['actorsId'] . '/attributes/' . $attributeKey, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -433,8 +446,9 @@ class RealtimeConsoleClientTest extends Scope
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(1, $response['data']['channels']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.update", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
@@ -450,8 +464,9 @@ class RealtimeConsoleClientTest extends Scope
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(1, $response['data']['channels']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*.delete", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}.attributes.*", $response['data']['events']);
$this->assertContains("databases.{$databaseId}.collections.{$actorsId}", $response['data']['events']);
@@ -463,6 +478,44 @@ class RealtimeConsoleClientTest extends Scope
$client->close();
}
public function testPing()
{
$client = $this->getWebsocket(['console'], [
'origin' => 'http://localhost',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
], 'console');
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertEquals('connected', $response['type']);
$pong = $this->client->call(Client::METHOD_GET, '/ping', [
'origin' => 'http://localhost',
'x-appwrite-project' => $this->getProject()['$id'],
]);
$this->assertEquals(200, $pong['headers']['status-code']);
$this->assertEquals('Pong!', $pong['body']);
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$this->getProject()['$id']}", $response['data']['channels']);
$this->assertContains("projects.{$this->getProject()['$id']}.ping", $response['data']['events']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertArrayHasKey('pingCount', $response['data']['payload']);
$this->assertArrayHasKey('pingedAt', $response['data']['payload']);
$this->assertEquals(1, $response['data']['payload']['pingCount']);
$client->close();
}
public function testCreateDeployment()
{
$response1 = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
@@ -505,22 +558,16 @@ class RealtimeConsoleClientTest extends Scope
/**
* Test Create Deployment
*/
$folder = 'php';
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$projectId = $this->getProject()['$id'];
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-project' => $projectId,
], $this->getHeaders()), [
'entrypoint' => 'index.php',
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)),
'code' => $this->packageFunction('php'),
'activate' => true
]);
$deploymentId = $deployment['body']['$id'] ?? '';
$this->assertEquals(202, $deployment['headers']['status-code']);
$response = json_decode($client->receive(), true);
@@ -530,8 +577,9 @@ class RealtimeConsoleClientTest extends Scope
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(1, $response['data']['channels']);
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
// $this->assertContains("functions.{$functionId}.deployments.{$deploymentId}.create", $response['data']['events']); TODO @christyjacob4 : enable test once we allow functions.* events
$this->assertNotEmpty($response['data']['payload']);
@@ -7,7 +7,7 @@ use Tests\E2E\Client;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideClient;
use Utopia\CLI\Console;
use Tests\E2E\Services\Functions\FunctionsBase;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
@@ -15,6 +15,7 @@ use WebSocket\ConnectionException;
class RealtimeCustomClientTest extends Scope
{
use FunctionsBase;
use RealtimeBase;
use ProjectCustom;
use SideClient;
@@ -1271,20 +1272,13 @@ class RealtimeCustomClientTest extends Scope
$this->assertEquals($function['headers']['status-code'], 201);
$this->assertNotEmpty($function['body']['$id']);
$folder = 'timeout';
$stderr = '';
$stdout = '';
$code = realpath(__DIR__ . '/../../../resources/functions') . "/{$folder}/code.tar.gz";
Console::execute('cd ' . realpath(__DIR__ . "/../../../resources/functions") . "/{$folder} && tar --exclude code.tar.gz -czf code.tar.gz .", '', $stdout, $stderr);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
]), [
'entrypoint' => 'index.php',
'code' => new CURLFile($code, 'application/x-gzip', basename($code)),
'code' => $this->packageFunction('timeout'),
'activate' => true
]);
@@ -1340,8 +1334,9 @@ class RealtimeCustomClientTest extends Scope
$this->assertEquals('event', $response['type']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(4, $response['data']['channels']);
$this->assertCount(5, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$this->getProject()['$id']}", $response['data']['channels']);
$this->assertContains('executions', $response['data']['channels']);
$this->assertContains("executions.{$executionId}", $response['data']['channels']);
$this->assertContains("functions.{$functionId}", $response['data']['channels']);
@@ -1362,8 +1357,9 @@ class RealtimeCustomClientTest extends Scope
$this->assertEquals('event', $responseUpdate['type']);
$this->assertNotEmpty($responseUpdate['data']);
$this->assertArrayHasKey('timestamp', $responseUpdate['data']);
$this->assertCount(4, $responseUpdate['data']['channels']);
$this->assertCount(5, $responseUpdate['data']['channels']);
$this->assertContains('console', $responseUpdate['data']['channels']);
$this->assertContains("projects.{$this->getProject()['$id']}", $response['data']['channels']);
$this->assertContains('executions', $responseUpdate['data']['channels']);
$this->assertContains("executions.{$executionId}", $responseUpdate['data']['channels']);
$this->assertContains("functions.{$functionId}", $responseUpdate['data']['channels']);
+11 -12
View File
@@ -161,7 +161,7 @@ trait TeamsBaseClient
$this->assertNotEmpty($response['body']['userEmail']);
$this->assertNotEmpty($response['body']['teamId']);
$this->assertNotEmpty($response['body']['teamName']);
$this->assertCount(2, $response['body']['roles']);
$this->assertCount(1, $response['body']['roles']);
$this->assertEquals(false, (new DatetimeValidator())->isValid($response['body']['joined'])); // is null in DB
$this->assertEquals(false, $response['body']['confirm']);
@@ -203,7 +203,7 @@ trait TeamsBaseClient
], $this->getHeaders()), [
'email' => $email,
'name' => $name,
'roles' => ['admin', 'editor'],
'roles' => ['developer'],
'url' => 'http://localhost:5000/join-us#title'
]);
@@ -214,7 +214,7 @@ trait TeamsBaseClient
$this->assertEquals($email, $response['body']['userEmail']);
$this->assertNotEmpty($response['body']['teamId']);
$this->assertNotEmpty($response['body']['teamName']);
$this->assertCount(2, $response['body']['roles']);
$this->assertCount(1, $response['body']['roles']);
$this->assertEquals(false, (new DatetimeValidator())->isValid($response['body']['joined'])); // is null in DB
$this->assertEquals(false, $response['body']['confirm']);
@@ -255,7 +255,7 @@ trait TeamsBaseClient
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'userId' => 'abcdefdg',
'roles' => ['admin', 'editor'],
'roles' => ['developer'],
'url' => 'http://localhost:5000/join-us#title'
]);
@@ -270,7 +270,7 @@ trait TeamsBaseClient
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'userId' => $userId,
'roles' => ['admin', 'editor'],
'roles' => ['developer'],
'url' => 'http://localhost:5000/join-us#title'
]);
@@ -281,7 +281,7 @@ trait TeamsBaseClient
$this->assertEquals($secondEmail, $response['body']['userEmail']);
$this->assertNotEmpty($response['body']['teamId']);
$this->assertNotEmpty($response['body']['teamName']);
$this->assertCount(2, $response['body']['roles']);
$this->assertCount(1, $response['body']['roles']);
$this->assertEquals(false, (new DateTimeValidator())->isValid($response['body']['joined'])); // is null in DB
$this->assertEquals(false, $response['body']['confirm']);
@@ -301,7 +301,7 @@ trait TeamsBaseClient
], $this->getHeaders()), [
'email' => $email,
'name' => 'Friend User',
'roles' => ['admin', 'editor'],
'roles' => ['developer'],
'url' => 'http://localhost:5000/join-us#title'
]);
@@ -313,7 +313,7 @@ trait TeamsBaseClient
], $this->getHeaders()), [
'email' => 'dasdkaskdjaskdjasjkd',
'name' => $name,
'roles' => ['admin', 'editor'],
'roles' => ['developer'],
'url' => 'http://localhost:5000/join-us#title'
]);
@@ -337,7 +337,7 @@ trait TeamsBaseClient
], $this->getHeaders()), [
'email' => $email,
'name' => $name,
'roles' => ['admin', 'editor'],
'roles' => ['developer'],
'url' => 'http://example.com/join-us#title' // bad url
]);
@@ -413,7 +413,7 @@ trait TeamsBaseClient
$this->assertNotEmpty($response['body']['$id']);
$this->assertNotEmpty($response['body']['userId']);
$this->assertNotEmpty($response['body']['teamId']);
$this->assertCount(2, $response['body']['roles']);
$this->assertCount(1, $response['body']['roles']);
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['joined']));
$this->assertEquals(true, $response['body']['confirm']);
$session = $response['cookies']['a_session_' . $this->getProject()['$id']];
@@ -571,7 +571,7 @@ trait TeamsBaseClient
/**
* Test for SUCCESS
*/
$roles = ['admin', 'editor', 'uncle'];
$roles = ['editor', 'uncle'];
$response = $this->client->call(Client::METHOD_PATCH, '/teams/' . $teamUid . '/memberships/' . $membershipUid, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
@@ -587,7 +587,6 @@ trait TeamsBaseClient
$this->assertCount(count($roles), $response['body']['roles']);
$this->assertEquals($roles[0], $response['body']['roles'][0]);
$this->assertEquals($roles[1], $response['body']['roles'][1]);
$this->assertEquals($roles[2], $response['body']['roles'][2]);
/**
* Test for unknown team

Some files were not shown because too many files have changed in this diff Show More