Compare commits

..
Author SHA1 Message Date
Chirag Aggarwal 187ba0a575 chore: update all examples to new endpoint 2025-04-24 14:46:03 +00:00
Chirag Aggarwal 68c85a52fb Merge branch '1.6.x' into fix-headers 2025-04-24 14:42:45 +00:00
Christy JacobandGitHub 8cb4d2894b Merge pull request #9685 from appwrite/chore-docs-endpoint
core: introduce endpoint.docs in specs
2025-04-24 18:14:01 +04:00
Chirag Aggarwal 75efa28125 core: introduce endpoint.docs in specs 2025-04-24 11:15:23 +00:00
Christy JacobandGitHub 7952eafdf0 Merge pull request #9681 from appwrite/revert-9648-feat-add-configurable-function-size
Revert "Add configurable function and build size"
2025-04-23 19:49:43 +04:00
Christy JacobandGitHub de9b0e6eee Revert "Add configurable function and build size" 2025-04-23 21:18:04 +05:30
Christy JacobandGitHub aeffeb8cc8 Merge pull request #9679 from appwrite/fix-oauth-redirects
fix: ensure all 4xx errors in OAuth redirect lead to the failure URL
2025-04-23 19:23:23 +04:00
Christy JacobandGitHub a11cbc3799 Merge pull request #9680 from appwrite/non-critical-events
feat: allow non-critical events to ignore exceptions when enqueuing the event
2025-04-23 19:18:19 +04:00
Fabian Gruber b0eb6434b8 fix: nullable group field in SDK\Method 2025-04-23 17:14:19 +02:00
Fabian Gruber fe28af292b feat: allow non-critical events to ignore exceptions when enqueuing the message 2025-04-23 16:48:09 +02:00
Christy Jacob 6b4ecbcd23 fix: ensure all 4xx errors in OAuth redirect lead to the failure URL 2025-04-23 18:39:55 +04:00
Chirag Aggarwal dab6701f79 chore: update sdk generator 2025-04-23 13:22:47 +00:00
Chirag Aggarwal 51763a6e84 chore: update platforms 2025-04-22 13:37:02 +00:00
Chirag Aggarwal 60c336bfa1 Merge branch '1.6.x' into fix-headers 2025-04-22 13:30:46 +00:00
Christy JacobandGitHub f58c8c5370 Merge pull request #9655 from appwrite/update-sdks
chore: update sdks
2025-04-22 17:18:41 +04:00
Christy JacobandGitHub 4228bc9b63 Merge pull request #9574 from appwrite/chore-sensitive-check
chore: abstract token secret hiding to response model
2025-04-22 17:18:11 +04:00
Christy JacobandGitHub ed8c39f649 Merge pull request #8933 from appwrite/feat-multi-region-docs
feat: update API endpoint in the code examples
2025-04-22 17:16:51 +04:00
Christy Jacob c0bd4401c5 feat: regenerate specs 2025-04-22 17:02:33 +04:00
Christy Jacob cd647e0c18 Merge branch '1.6.x' of github.com:appwrite/appwrite into feat-multi-region-docs 2025-04-22 14:02:20 +04:00
Matej BačoandGitHub 8906d94e8f Merge pull request #9648 from appwrite/feat-add-configurable-function-size
Add configurable function and build size
2025-04-22 10:51:29 +02:00
loks0n c9a7902e52 chore: update platforms 2025-04-17 19:45:32 +01:00
Chirag Aggarwal 73a10a1dca chore: include head requests 2025-04-17 17:55:33 +00:00
Chirag Aggarwal 7bd3c334ed fix: remove content-type header from get request specs 2025-04-17 17:43:13 +00:00
Christy JacobandGitHub 68e0083566 Merge pull request #9596 from appwrite/feat-group
feat: sdk group attribute
2025-04-17 18:26:01 +04:00
Christy JacobandGitHub f4e8d72593 Merge branch '1.6.x' into feat-group 2025-04-17 18:25:47 +04:00
Jake BarnbyandGitHub dd3f92856a Merge pull request #9657 from ArnabChatterjee20k/fix/error-order-attr-null
Fix/throwing 400 for null order attributes
2025-04-17 09:49:44 +00:00
arnab e6e28f32ff added comments for test case 2025-04-17 14:59:37 +05:30
arnab fb6ec704a1 test case added 2025-04-17 14:44:59 +05:30
Chirag Aggarwal 0f8e1db044 chore: update specs 2025-04-17 07:04:41 +00:00
ArnabChatterjee20kandGitHub 772ca7d36c Merge branch '1.6.x' into fix/error-order-attr-null 2025-04-17 10:25:00 +05:30
Jake BarnbyandGitHub 189f2e05e0 Merge pull request #9651 from appwrite/pla-2776
chore: fix error logging for CLI tasks
2025-04-17 04:53:49 +00:00
ArnabChatterjee20k b2f7ed48dd Simplify constructor usage by inlining message; limit try/catch to DB operations 2025-04-17 10:16:26 +05:30
arnab ecd77cb2e3 added order exception in projects and vcs 2025-04-16 17:53:39 +05:30
arnab 8d3070416a added order exception in rest of the controllers 2025-04-16 17:29:36 +05:30
arnab 6e422ebe3c added teams and users order exception 2025-04-16 17:16:39 +05:30
arnab 3c602e84e6 added order exception in teams 2025-04-16 17:09:53 +05:30
arnab 1cacb87c65 added order query exception handling in the storage 2025-04-16 17:09:13 +05:30
arnab 8e4bd363d2 added catching of order exception in database controllers 2025-04-16 17:03:14 +05:30
arnab b7c2220648 added exception and errors 2025-04-16 17:02:43 +05:30
ArnabChatterjee20k 85de3d27d8 updated database version 2025-04-16 15:22:38 +05:30
Chirag AggarwalandGitHub c7ab1dda9f Merge branch '1.6.x' into pla-2776 2025-04-16 12:58:13 +05:30
Chirag Aggarwal c1f62ce0bd Merge branch '1.6.x' into feat-group 2025-04-16 04:49:07 +00:00
Jake BarnbyandGitHub 74d946e42a Merge pull request #9583 from appwrite/feat-usage-dump-multi-tenant-batch
Feat: multi tenant dual writing
2025-04-16 04:14:09 +00:00
Damodar LohaniandGitHub 523a8b6ff6 Merge branch '1.6.x' into feat-usage-dump-multi-tenant-batch 2025-04-16 08:49:17 +05:45
Jake BarnbyandGitHub 06c87a28c9 Merge pull request #9656 from appwrite/fix-keys
Fix keys
2025-04-16 01:26:40 +00:00
Jake Barnby b9e8c1d8bc Skip server console test 2025-04-16 13:07:11 +12:00
Jake Barnby 53660f3100 Add more cases 2025-04-16 12:30:22 +12:00
Jake Barnby bea723ff2e Add test 2025-04-16 12:04:19 +12:00
Jake Barnby ba58a372d1 Throw on key not found 2025-04-16 10:50:53 +12:00
Chirag Aggarwal 59e43b1035 chore: update sdks 2025-04-15 20:55:11 +00:00
Christy Jacob 304f67c88f fix: usage test assertion 2025-04-15 18:18:40 +04:00
Christy JacobandGitHub 8e2571539c Merge pull request #9653 from appwrite/update-usage-test
fix: usage test assertion
2025-04-15 17:46:46 +04:00
Christy JacobandGitHub 3e0323dae8 Merge branch '1.6.x' into update-usage-test 2025-04-15 17:43:10 +04:00
Christy Jacob d0181787b9 fix: usage test assertion 2025-04-15 17:38:34 +04:00
Chirag Aggarwal f08ea92df4 chore: update cli 2025-04-15 11:08:54 +00:00
c3120f4aaa chore: add error tag
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2025-04-15 10:30:29 +05:30
Damodar LohaniandGitHub 969c58fda9 Merge branch '1.6.x' into feat-usage-dump-multi-tenant-batch 2025-04-15 10:44:47 +05:45
Damodar Lohani 49814c2a7e bring back stats usage dump 2025-04-15 04:58:16 +00:00
Damodar Lohani f9141ca564 fix param types 2025-04-15 04:48:19 +00:00
Damodar Lohani 4edec1494a fix remove comments 2025-04-15 04:45:56 +00:00
Jake BarnbyandGitHub 0b60898ad5 Merge pull request #9643 from appwrite/feat-bulk-callbacks
Update database
2025-04-15 04:28:18 +00:00
Damodar LohaniandGitHub 82ce35029a Merge pull request #9585 from appwrite/fix-merge-stats-usage-and-dump
Fix: merge the working of StatsUsage and StatsUsageDump
2025-04-15 09:50:43 +05:45
Damodar Lohani 93f86e57ea use clone 2025-04-15 03:15:24 +00:00
a46c5d1029 Update src/Appwrite/Platform/Workers/StatsUsage.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2025-04-15 08:54:04 +05:45
Chirag Aggarwal 88b229b13e chore: fix error logging for CLI tasks 2025-04-14 21:35:26 +00:00
Chirag Aggarwal 3ed87a8ab9 chore: update endpoint descriptions to follow CRUD naming 2025-04-14 18:40:48 +00:00
Khushboo Verma 6e86527eb6 Keep build size in MBs 2025-04-14 14:33:46 +00:00
5d9bdd4cc7 Update app/controllers/api/functions.php
Co-authored-by: Matej Bačo <matejbacocom@gmail.com>
2025-04-14 19:53:39 +05:30
Khushboo Verma 6852ee1f0f Read buildSize from plan 2025-04-14 14:07:23 +00:00
Khushboo Verma 81c3d96629 Consistent naming 2025-04-14 14:00:44 +00:00
Khushboo Verma 1acbff316e Use 1000 instead of 1024 2025-04-14 13:46:52 +00:00
Khushboo Verma f02707627f Refactor 2025-04-14 12:16:50 +00:00
Khushboo Verma 0df1396d52 Add plan to worker.php 2025-04-14 11:54:40 +00:00
Khushboo Verma c4509f5ce7 Add plan to builds worker 2025-04-14 10:31:08 +00:00
Khushboo Verma 3f0857b36d Add plan to create deployment 2025-04-14 10:28:29 +00:00
Jake BarnbyandGitHub e3f64c3234 Merge pull request #9646 from ArnabChatterjee20k/update-migrations-version
chore: updated the migration version to 8.6
2025-04-14 10:14:18 +00:00
ArnabChatterjee20kandGitHub 2c36fab1c6 Merge branch '1.6.x' into update-migrations-version 2025-04-14 15:21:30 +05:30
ArnabChatterjee20k f8d92c04ad updated the migration version to 8.6 2025-04-14 15:09:40 +05:30
Jake Barnby 950cc420a2 Update database 2025-04-14 19:40:51 +12:00
Jake Barnby cbb943cc88 Merge remote-tracking branch 'origin/1.6.x' into feat-bulk-callbacks 2025-04-14 19:40:13 +12:00
Jake Barnby 862664dd62 Format 2025-04-14 19:05:42 +12:00
Jake Barnby 498f9e40c8 Update worker bulk method usage 2025-04-14 18:50:33 +12:00
Jake BarnbyandGitHub dc40fd7ddc Merge pull request #9644 from appwrite/fix-tasks
Fix tasks
2025-04-14 06:49:22 +00:00
Jake Barnby a6281992b1 Fix maintenance 2025-04-14 18:26:13 +12:00
Jake Barnby 3be50d2ab5 Fix syntax 2025-04-14 18:26:07 +12:00
Jake Barnby 2a2bf7f154 Fix maintenance 2025-04-14 18:23:35 +12:00
Jake Barnby 9a8ba13dbf Fix syntax 2025-04-14 18:18:10 +12:00
Jake Barnby ecdea8b487 Update database 2025-04-14 18:07:12 +12:00
Damodar LohaniandGitHub 5f6e65c1aa Merge branch 'feat-usage-dump-multi-tenant-batch' into fix-merge-stats-usage-and-dump 2025-04-13 09:47:45 +05:45
Damodar LohaniandGitHub 932f34df92 Merge branch '1.6.x' into feat-usage-dump-multi-tenant-batch 2025-04-13 09:47:30 +05:45
Damodar Lohani fd5a900758 update database 2025-04-13 03:46:59 +00:00
Damodar LohaniandGitHub fb7223bd1b Merge branch 'feat-usage-dump-multi-tenant-batch' into fix-merge-stats-usage-and-dump 2025-04-13 09:30:51 +05:45
Chirag Aggarwal b1685a5ca1 chore: update users auth group to sessions 2025-04-12 13:38:00 +00:00
Chirag Aggarwal c3656fd4e7 Merge branch '1.6.x' into feat-group 2025-04-12 06:50:20 +00:00
Chirag Aggarwal b3c9e6181f chore: update groups 2025-04-12 06:50:02 +00:00
Chirag Aggarwal 04733e25c8 chore: make group nullable and remove it from endpoints where its not required 2025-04-12 06:41:57 +00:00
Steven NguyenandGitHub 67084374fb Merge pull request #9637 from appwrite/update-console
chore: update console
2025-04-11 20:22:47 -07:00
Steven NguyenandGitHub 4d9521edc6 Merge pull request #9633 from appwrite/fix-migrations-destination
Fix migrations
2025-04-11 11:18:52 -07:00
Chirag Aggarwal 1dd0b419c5 chore: update console 2025-04-11 15:32:03 +00:00
Christy JacobandGitHub 636b0775fd Merge pull request #9636 from appwrite/revert-9632-revert-8667-multi-region-support
Revert "Revert "Multi region support""
2025-04-11 19:19:37 +04:00
Shimon NewmanandGitHub c4a506445d Revert "Revert "Multi region support"" 2025-04-11 17:52:19 +03:00
Steven Nguyen 8d046f021c fix: update upgrade migration to create missing attributes
Between version 1.6.0 and 1.6.1, the migrations collection was updated
to include a destination attribute and the databases collection was
updated to include an originalId attribute. However, the upgrade
migration was not updated to create these attributes.

This PR updates the migration to create the missing attributes.
2025-04-10 16:21:12 -07:00
Christy JacobandGitHub 2d0885ed70 Merge pull request #9632 from appwrite/revert-8667-multi-region-support
Revert "Multi region support"
2025-04-10 20:55:05 +04:00
Christy JacobandGitHub ffe153630d Revert "Multi region support" 2025-04-10 22:19:23 +05:30
Christy JacobandGitHub 6cd9fc72d9 Merge pull request #8667 from appwrite/multi-region-support
Multi region support
2025-04-10 20:05:21 +04:00
shimon 312d1029c0 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-04-10 18:36:05 +03:00
shimon fb802c7fe4 addressing comments 2025-04-10 18:35:45 +03:00
Christy JacobandGitHub 942775c352 Merge pull request #9631 from appwrite/chore-bump-console-5.2.56
Bump console to version 5.2.56
2025-04-10 19:01:06 +04:00
shimon f5852a52e2 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-04-10 17:20:23 +03:00
shimon ff89da513f addressing comments 2025-04-10 17:20:05 +03:00
Steven Nguyen 49ab765ee8 Bump console to version 5.2.56 2025-04-09 17:02:24 -07:00
Christy JacobandGitHub 2495101dfd Merge pull request #9624 from appwrite/feat-cache-telemetry
feat: cache telemetry
2025-04-10 00:31:06 +04:00
shimon 07cdd66296 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-04-09 10:02:40 +03:00
Jake BarnbyandGitHub e1b4917acf Merge pull request #9628 from appwrite/chore-update-migrations
Update migrations lib
2025-04-09 05:45:43 +00:00
Jake Barnby 796a1b5bd2 Update migrations lib 2025-04-09 17:26:41 +12:00
Christy JacobandGitHub 881525f349 Merge pull request #9623 from appwrite/feat-figma-oauth
feat: add figma oauth provider
2025-04-08 16:57:37 +04:00
Christy JacobandGitHub 8086d6eef0 Merge pull request #9625 from appwrite/update-composer
chore: update dependencies
2025-04-08 16:28:51 +04:00
Chirag Aggarwal badbcaf1a8 chore: update dependencies 2025-04-08 11:33:38 +00:00
loks0n e6d9c5c5fd chore: fmt 2025-04-08 12:24:05 +01:00
loks0n 86c26f2dc7 feat: cache telemetry 2025-04-08 12:12:28 +01:00
Chirag Aggarwal 2c453d5fd9 fix: request issues and permissions 2025-04-08 08:51:04 +00:00
Chirag Aggarwal 3bd8405deb feat: add figma oauth provider 2025-04-08 07:56:35 +00:00
shimon fed98ffdfe Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	docker-compose.yml
2025-04-07 13:06:54 +03:00
Damodar LohaniandGitHub 57bc76edc7 Merge branch '1.6.x' into feat-usage-dump-multi-tenant-batch 2025-04-07 09:21:32 +05:45
shimon 31c326fe87 sync with 1.6.x 2025-04-03 11:31:45 +03:00
shimon 0dc319d244 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-04-03 11:30:14 +03:00
Damodar Lohani ae0cdaff63 Merge remote-tracking branch 'origin/feat-usage-dump-multi-tenant-batch' into fix-merge-stats-usage-and-dump 2025-04-01 04:38:40 +00:00
Chirag Aggarwal 2a7038bb8e chore: remove logging 2025-03-31 09:33:37 +00:00
Chirag Aggarwal 2c434924de chore: update openapi specs 2025-03-31 09:32:47 +00:00
Chirag Aggarwal d9b7a018ea chore: update specs 2025-03-31 06:23:31 +00:00
Chirag Aggarwal abcaf6cd73 feat: sdk group attribute 2025-03-31 05:48:17 +00:00
Damodar Lohani 81ee68e219 Merge remote-tracking branch 'origin/1.6.x' into feat-usage-dump-multi-tenant-batch 2025-03-31 04:07:10 +00:00
Damodar Lohani e14ae7ea4a fix 2025-03-31 03:59:37 +00:00
Damodar Lohani ec21effd2c Fix type error 2025-03-31 03:55:06 +00:00
Damodar Lohani 7399362c20 Merge remote-tracking branch 'origin/1.6.x' into fix-merge-stats-usage-and-dump 2025-03-31 03:45:46 +00:00
Damodar Lohani d5ad714f5a fix missing queue name 2025-03-30 06:09:03 +00:00
Damodar Lohani 1389bab3d1 remove db storage check 2025-03-30 05:56:14 +00:00
Damodar Lohani 100189c0fb Merge remote-tracking branch 'origin/feat-usage-dump-multi-tenant-batch' into fix-merge-stats-usage-and-dump 2025-03-30 05:55:28 +00:00
Damodar Lohani d36a09eec6 remove database storage triggers 2025-03-30 05:54:48 +00:00
Damodar Lohani f4d01f4c37 fix: remove stats usage dump worker from docker 2025-03-27 08:36:52 +00:00
Damodar Lohani cfb1f4fcdb fix: remove stats usage dump 2025-03-27 08:29:12 +00:00
Damodar Lohani 05067302d2 Fix: merge the working of StatsUsage and StatsUsageDump 2025-03-27 08:25:48 +00:00
Damodar Lohani 1a54bd58a6 check empty project id 2025-03-27 07:01:44 +00:00
Damodar Lohani c443a2f999 Fix dual writing 2025-03-27 05:50:01 +00:00
Damodar Lohani 9f537a5bee Feat: multi tenant dual writing 2025-03-27 04:59:34 +00:00
Chirag Aggarwal 8b1e97bae4 chore: remove console import 2025-03-26 14:38:31 +00:00
Chirag Aggarwal 7b8f25696c chore: added showSensitve wrapper instead of param 2025-03-26 14:37:35 +00:00
shimon 35cd2b5295 adding custom domain validation against _APP_CUSTOM_DOMAIN_DENY_LIST 2025-03-26 09:39:53 +02:00
shimon 989fa2b8a0 adding custom domain validation against _APP_CUSTOM_DOMAIN_DENY_LIST 2025-03-26 09:25:49 +02:00
shimon cb2d0dc592 adding custom domain validation against _APP_CUSTOM_DOMAIN_DENY_LIST 2025-03-26 09:07:38 +02:00
shimon a9d1f6668f adding custom domain validation against _APP_CUSTOM_DOMAIN_DENY_LIST 2025-03-26 09:05:36 +02:00
shimon 8447732614 adding region query to deleteProjectsByTeam 2025-03-25 21:11:41 +02:00
shimon 9f446da0a5 adding region query to deleteProjectsByTeam 2025-03-25 20:28:29 +02:00
shimon 62effa7bc4 adding region query to deleteProjectsByTeam 2025-03-25 20:23:08 +02:00
shimon dedcdc2e0e adding region query to deleteProjectsByTeam 2025-03-25 19:13:11 +02:00
shimon 854256e8a6 debug 2025-03-25 10:47:05 +02:00
shimon d72f69f792 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.lock
2025-03-25 10:46:41 +02:00
Chirag AggarwalandGitHub 7fdde1a518 Update Response.php 2025-03-25 14:09:47 +05:30
shimon 16aca35bd4 debug 2025-03-24 20:33:51 +02:00
Chirag Aggarwal e93010c5fe chore: add showSensitive attribute 2025-03-24 14:26:56 +00:00
Chirag Aggarwal d677b8ed88 fix: webhook tests 2025-03-24 14:06:36 +00:00
Chirag Aggarwal 3399308ee3 Merge branch '1.6.x' into chore-sensitive-check 2025-03-24 12:28:54 +00:00
shimon 56d78b1712 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	app/worker.php
2025-03-24 10:56:47 +02:00
shimon e4c4ffdb3c pull resources changes 2025-03-24 10:54:18 +02:00
shimon ab1683070b debug 2025-03-23 19:59:51 +02:00
shimon 9f3d15bd1c Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-03-23 19:58:34 +02:00
shimon a7199ff980 debug 2025-03-23 10:44:00 +02:00
shimon 434fcdc508 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-03-21 19:08:53 +02:00
shimon ed7c2fa451 debug 2025-03-21 19:08:37 +02:00
shimon 69bdf50c85 debug 2025-03-20 18:27:23 +02:00
shimon ea02639767 debug 2025-03-20 18:15:51 +02:00
shimon bd19da2e7a debug 2025-03-20 17:12:59 +02:00
shimon 6c0ce5cc41 debug 2025-03-20 15:05:48 +02:00
shimon 403e5773a2 cleanup 2025-03-20 14:26:11 +02:00
shimon 68e8b7ca2b Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-03-20 10:47:07 +02:00
shimon 438d5185a9 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-03-19 19:47:44 +02:00
shimon d3cabcd5ac composer 2025-03-19 11:15:25 +02:00
shimon 84133ef631 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.lock
2025-03-19 11:13:58 +02:00
shimon 2be59a94da update 2025-03-19 11:13:30 +02:00
shimon fca2e95fdd debug 2025-03-18 19:29:40 +02:00
shimon 5683c310db debug 2025-03-18 18:36:46 +02:00
shimon ab790f6c02 debug 2025-03-16 15:24:29 +02:00
shimon ff618a0565 debug 2025-03-16 14:42:22 +02:00
shimon b3db296f13 sync with main 2025-03-16 14:05:03 +02:00
shimon e019e426f1 debug 2025-03-16 13:47:54 +02:00
shimon adacec4ad1 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	app/init.php
#	composer.lock
2025-03-16 10:55:24 +02:00
shimon a60d916c51 debug 2025-03-11 19:18:05 +02:00
shimon d522b1a04c debug 2025-03-11 18:56:24 +02:00
shimon 4b740dce86 sync with 1.6.x 2025-03-11 18:23:35 +02:00
shimon 6a05bd4e0d Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	app/init.php
2025-03-11 18:19:44 +02:00
shimon 0570567b2e sync with 1.6.x 2025-03-09 21:24:24 +02:00
shimon 3a29bcb41b Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.lock
2025-03-09 21:23:33 +02:00
shimon 1b3fe3046d sync with 1.6.x 2025-03-06 13:12:03 +02:00
shimon 7c91157dc8 sync with 1.6.x 2025-03-06 12:10:10 +02:00
shimon 584e4c0d75 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-03-06 12:03:10 +02:00
shimon f0477f8940 sync with 1.6.x 2025-03-05 17:30:59 +02:00
shimon 3bc81c82ec Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	src/Appwrite/Platform/Workers/Usage.php
2025-03-05 17:29:57 +02:00
shimon 12eb3b4eea debug 2025-03-03 10:47:28 +02:00
shimon adb451234b debug 2025-03-02 22:27:13 +02:00
shimon 52a73856bc debug 2025-03-02 21:19:45 +02:00
shimon 921b9154eb debug 2025-03-02 20:56:28 +02:00
shimon 5ed1ca617f cleanup 2025-02-27 11:02:27 +02:00
shimon 12f274223f sync against 1.6.x 2025-02-27 10:31:47 +02:00
shimon 368b61493f Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.lock
2025-02-27 10:29:06 +02:00
shimon 458df23797 debug cert 2025-02-26 17:41:44 +02:00
shimon 369caae87d debug cert 2025-02-26 17:40:06 +02:00
shimon f48791fba6 debug cert 2025-02-26 15:53:04 +02:00
shimon e2b9c64f91 debug cert 2025-02-25 18:01:30 +02:00
shimon 69e9a1bd8c debug cert 2025-02-25 17:42:36 +02:00
shimon 1f606a591d debug cert 2025-02-24 14:13:18 +02:00
shimon e972ccedd6 debug 2025-02-20 10:26:39 +02:00
shimon e4369e5e85 debug 2025-02-20 09:48:17 +02:00
shimon 45b2d58aa5 debug 2025-02-19 23:28:09 +02:00
shimon be1c12147f debug 2025-02-19 22:59:04 +02:00
shimon 4157e89ab7 debug 2025-02-19 22:33:13 +02:00
shimon c151d2aaca debug 2025-02-19 18:18:39 +02:00
shimon baa40e4007 debug 2025-02-19 18:15:26 +02:00
shimon 197e32f128 debug 2025-02-19 13:52:14 +02:00
shimon 39e5c5e734 debug 2025-02-19 10:11:47 +02:00
shimon a8a8df602f sync against 1.6.x 2025-02-17 10:30:53 +02:00
shimon d641177618 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-02-16 20:13:11 +02:00
shimon 463bc4aac3 sync against 1.6.x 2025-02-15 06:28:19 +02:00
shimon e597ee4493 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	app/http.php
2025-02-15 06:24:48 +02:00
shimon 00478e8533 _APP_DOMAIN_FUNCTIONS_FALLBACK 2025-02-14 11:08:43 +02:00
Chirag AggarwalandGitHub 9aad6721cd Merge branch '1.6.x' into chore-sensitive-check 2025-02-14 09:04:56 +05:30
Chirag Aggarwal ec5fd1d798 chore: remove showsensitive 2025-02-11 09:06:11 +00:00
Chirag Aggarwal f723948907 Merge branch '1.6.x' into chore-sensitive-check 2025-02-11 08:29:06 +00:00
shimon 2106022507 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.json
#	composer.lock
2025-02-09 10:06:56 +02:00
Matej Bačo e992ed11c1 Fix unwanted redirects in functions 2025-02-06 11:08:28 +01:00
shimon 50400c4b43 _APP_DOMAIN_FUNCTIONS_FALLBACK 2025-02-05 13:14:03 +02:00
shimon 3cfb013f1e _APP_DOMAIN_FUNCTIONS_FALLBACK 2025-02-05 12:49:04 +02:00
shimon be27912a6e _APP_DOMAIN_FUNCTIONS_FALLBACK 2025-02-05 11:35:19 +02:00
shimon b73b450a2e updated against 1.6.x 2025-02-04 21:36:06 +02:00
shimon 4dc7206e04 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.json
#	composer.lock
2025-02-04 21:12:11 +02:00
shimon b18d3bc678 payload debug 2025-01-24 18:36:32 +02:00
shimon 0cc5088e8e payload debug 2025-01-24 18:03:24 +02:00
shimon 2c4f5d3daa payload debug 2025-01-24 17:52:07 +02:00
shimon 1675683e76 payload debug 2025-01-24 17:38:29 +02:00
shimon 78dcfd2770 payload debug 2025-01-24 17:25:57 +02:00
shimon 87de071f53 payload debug 2025-01-24 16:53:26 +02:00
shimon 39585d0438 payload debug 2025-01-24 16:46:02 +02:00
shimon 270dfd685d payload debug 2025-01-24 16:39:16 +02:00
shimon de29879793 payload debug 2025-01-24 16:22:11 +02:00
shimon a70e323a15 payload debug 2025-01-24 09:41:53 +02:00
shimon ff1bfb61f1 payload debug 2025-01-24 09:06:10 +02:00
shimon 54c8a890d3 removed comments 2025-01-23 09:23:19 +02:00
shimon 74b06cdd96 debug platforms 2025-01-23 08:51:13 +02:00
shimon 5113c9e50a debug platforms 2025-01-23 07:53:03 +02:00
shimon 0052487d49 debug platforms 2025-01-23 07:47:23 +02:00
shimon 9841f6ae87 debug platforms 2025-01-23 07:40:44 +02:00
shimon 9b58ae7ff5 debug platforms 2025-01-23 07:23:35 +02:00
shimon 59c41478ca debug platforms 2025-01-22 21:14:41 +02:00
shimon 37087c37ca debug platforms 2025-01-22 20:44:18 +02:00
shimon 066008293d debug 2025-01-22 18:49:57 +02:00
shimon b9acae5a25 debug 2025-01-21 09:43:34 +02:00
shimon d0452e5d3b debug 2025-01-20 19:26:14 +02:00
shimon f94f6642ea debug 2025-01-20 17:45:42 +02:00
shimon 01dc8fa396 debug 2025-01-20 16:16:38 +02:00
shimon 430c883a88 debug 2025-01-20 15:29:33 +02:00
shimon dbec8c01a9 debug 2025-01-20 11:47:47 +02:00
shimon 0c37530974 debug 2025-01-20 11:34:25 +02:00
shimon 5fa4a1232f debug 2025-01-20 11:32:54 +02:00
shimon 645fbd4f1c debug 2025-01-20 10:33:06 +02:00
shimon b2f920d983 debug 2025-01-20 10:03:54 +02:00
shimon 3c152b8349 debug 2025-01-20 09:25:15 +02:00
shimon bad427071e debug 2025-01-19 22:41:00 +02:00
shimon 419d18bb1c debug 2025-01-19 22:25:03 +02:00
shimon 6e1675c574 debug 2025-01-19 22:10:52 +02:00
shimon 561af1d386 debug 2025-01-19 21:03:48 +02:00
shimon 286f7e6b86 added attr installations collection 2025-01-19 11:22:22 +02:00
shimon fe18afcfca Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-01-19 10:33:47 +02:00
shimon 348199712a composer.lock 2025-01-16 15:10:07 +02:00
shimon 1ae7d730ba Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support 2025-01-16 15:04:52 +02:00
shimon 246c334127 debug region validation 2025-01-13 21:05:19 +02:00
ChiragAgg5k 11867bef50 chore: fix webhooks 2025-01-13 21:46:42 +05:30
Chirag Aggarwal 8b0465f129 fix: webhooks 2025-01-13 15:04:33 +00:00
Chirag Aggarwal 99f736aa52 chore: add sensitive to fn logs n errors 2025-01-11 13:46:44 +00:00
Chirag AggarwalandGitHub 48fd3b371e Merge branch '1.6.x' into chore-sensitive-check 2025-01-10 23:51:26 +05:30
Chirag Aggarwal acea8812da chore: add senstive attribute to session 2025-01-10 18:21:04 +00:00
Chirag Aggarwal 13597d7c54 chore: abstract token secret hiding to response model 2025-01-10 13:47:38 +00:00
shimon f4d2583865 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support 2025-01-08 15:54:46 +02:00
shimon 18f17ff9fb remove abuse from deletes worker 2025-01-08 15:54:31 +02:00
shimon 33a8da45bd rollback project usage changes 2024-12-25 12:34:02 +02:00
shimon a7d09118e4 composer 2024-12-24 18:53:28 +02:00
shimon 0e0009dd27 composer 2024-12-24 18:52:33 +02:00
shimon 3159e5de0c project usage updates 2024-12-24 18:22:17 +02:00
shimon e4bdbdd9ba Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.lock
2024-12-24 18:18:44 +02:00
shimon e154407fc6 Merge remote-tracking branch 'origin/multi-region-support' into multi-region-support 2024-12-14 20:23:32 +02:00
shimon 06b69360f4 sync with 1.6.x 2024-12-14 20:23:07 +02:00
shimon aa2a45f142 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	app/controllers/api/functions.php
#	app/controllers/api/messaging.php
#	docker-compose.yml
2024-12-14 20:18:06 +02:00
Jake Barnby 54d1ef257d Remove logs 2024-12-12 21:01:26 +13:00
Jake Barnby 39e4a04625 Fix default region 2024-12-12 20:31:50 +13:00
Jake Barnby 9a89cc1332 Remove logs 2024-12-12 19:06:35 +13:00
Jake Barnby f4637e1b7d Create audit/abuse for V2 2024-12-12 18:55:25 +13:00
Jake Barnby 8f390a70c7 Fix self-hosted region check default 2024-12-12 18:54:58 +13:00
Jake Barnby c9e6bfbdb3 Update database 2024-12-09 16:48:41 +13:00
shimon 8e0bd8d18d composer 2024-12-08 15:47:29 +02:00
shimon d0be46be74 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.json
#	composer.lock
2024-12-08 15:46:49 +02:00
Jake Barnby dd6cde6ae5 Log trace 2024-12-05 21:30:26 +13:00
Jake Barnby 714c2f30d7 Update database 2024-12-05 17:50:24 +13:00
shimon 316b739a27 composer 2024-12-03 19:43:39 +02:00
shimon b04a85491b debug 2024-12-03 17:36:19 +02:00
shimon 635dbdb5cb debug 2024-12-03 16:38:44 +02:00
shimon d06e41f81e debug 2024-12-03 15:00:45 +02:00
shimon 1a6f23c3a1 debug 2024-12-02 10:13:10 +02:00
shimon ba510a5641 debug 2024-12-01 20:07:40 +02:00
shimon cb93b9e1e7 debug 2024-12-01 19:25:43 +02:00
shimon 61d030b2c6 sync with 1.6.x 2024-11-29 15:24:52 +02:00
shimon 1d49d8284c Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support 2024-11-29 15:02:56 +02:00
shimon 65474df642 debug 2024-11-29 13:30:04 +02:00
shimon ac27c77426 debug 2024-11-29 13:14:20 +02:00
shimon 5e3e0b6683 debug 2024-11-26 12:43:31 +02:00
shimon 9468b08eb2 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support 2024-11-26 12:31:22 +02:00
shimon d2bf15b20b debug 2024-11-26 11:50:34 +02:00
shimon b1e5a5b987 Merge branch 'multi-region-support' of https://github.com/appwrite/appwrite into multi-region-support 2024-11-26 11:11:51 +02:00
Shimon NewmanandGitHub 9ca420a0e6 Merge pull request #9027 from appwrite/fix-no-pool-access
Fix no pool access
2024-11-26 11:08:45 +02:00
shimon 6f8c38dfa0 debug 2024-11-26 09:26:14 +02:00
Jake Barnby ddb86127b7 Fix lint 2024-11-26 18:29:10 +13:00
Jake Barnby ea0a5a186c Merge remote-tracking branch 'origin/multi-region-support' into fix-no-pool-access 2024-11-26 16:21:48 +13:00
Jake Barnby 928590b175 Only create databases for current region 2024-11-26 16:20:11 +13:00
shimon 7656ba11ff debug 2024-11-25 12:31:06 +02:00
shimon 7ae7ccbe49 debug 2024-11-25 12:02:00 +02:00
shimon 6d376df00a updates 2024-11-25 09:44:57 +02:00
Jake Barnby 0e03842173 Move pool access inside not V2 block 2024-11-25 17:20:27 +13:00
shimon aa8af5a213 env var 2024-11-24 23:09:22 +02:00
shimon f1a3cd13ff env var 2024-11-24 22:52:20 +02:00
shimon 00989fcdde env var 2024-11-24 20:40:06 +02:00
shimon c677374a11 env var 2024-11-24 19:39:44 +02:00
shimon 3f0a8fbe21 env var 2024-11-24 19:03:46 +02:00
shimon 7d5ae2f1c3 env var 2024-11-24 19:01:24 +02:00
shimon 0f8ad539ad composer.lock 2024-11-24 10:32:27 +02:00
shimon 9839a4ef36 sync with 1.6.x 2024-11-24 10:31:21 +02:00
shimon e91c762953 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support 2024-11-24 10:30:54 +02:00
shimon 3f6368f046 adding app region to console project 2024-11-21 23:35:45 +02:00
shimon d802639b0e Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	.github/workflows/tests.yml
#	app/controllers/api/projects.php
#	app/init.php
#	composer.lock
#	docker-compose.yml
#	src/Appwrite/Platform/Workers/Deletes.php
2024-11-21 22:11:35 +02:00
shimon 3d333633e4 Merge branch 'debug-whitelist-console' of https://github.com/appwrite/appwrite into multi-region-support 2024-11-21 22:00:17 +02:00
Christy Jacob 900f64bf8d feat: hack for multi region 2024-11-12 18:00:38 +01:00
loks0n dad8f597ca debug: whitelist console 2024-11-12 16:05:51 +01:00
Luke B. SilverandGitHub 90f18c57ac Merge pull request #8962 from appwrite/feat-fix-web-routes
feat fix web routes
2024-11-12 12:42:30 +01:00
loks0n 6e7dd26f75 feat: fix web routes 2024-11-12 12:37:15 +01:00
Christy JacobandGitHub 9151abc951 Merge pull request #8797 from appwrite/feat-migration
Feat migration
2024-11-12 10:27:24 +01:00
Jake Barnby 714f6b62ac Merge remote-tracking branch 'origin/feat-custom-cf-hostnames' into feat-migration 2024-11-12 21:45:04 +13:00
Jake Barnby 316693798e Merge remote-tracking branch 'origin/1.6.x' into feat-custom-cf-hostnames 2024-11-12 21:44:50 +13:00
Jake Barnby c7f92fbb02 Merge remote-tracking branch 'origin/feat-custom-cf-hostnames' into feat-migration 2024-11-12 21:43:52 +13:00
Jake Barnby e715f9e90c Remove redundant V1 tests 2024-11-12 20:52:25 +13:00
Jake Barnby f6ea6d2fc5 Fix matrix 2024-11-12 19:15:33 +13:00
Jake Barnby e626c2ad45 Run in all table modes in parallel 2024-11-12 19:14:46 +13:00
Jake Barnby 3b19ef6b1e Remove V2 tests 2024-11-12 15:00:23 +13:00
Jake Barnby c1a3b29a97 Temp test all shared tables versions 2024-11-12 14:37:03 +13:00
Jake Barnby aa2385ffb9 Merge branch 'revert-revert-8946-chore-update-database' into feat-migration
# Conflicts:
#	composer.json
#	composer.lock
2024-11-12 13:36:05 +13:00
Jake Barnby 8b99517dd0 Merge remote-tracking branch 'origin/1.6.x' into feat-migration 2024-11-12 13:34:54 +13:00
Jake Barnby 33037a3ae5 Update database 2024-11-12 13:34:40 +13:00
Jake Barnby b0852aeca3 Update database 2024-11-11 23:15:00 +13:00
Jake Barnby 09da4378c6 Merge remote-tracking branch 'origin/1.6.x' into feat-migration 2024-11-11 23:13:35 +13:00
Jake Barnby a85b43654c Reapply "Update database"
This reverts commit b1a1ad618a.
2024-11-11 15:22:57 +13:00
shimon ad8693c018 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	Dockerfile
2024-11-10 19:39:27 +02:00
shimon a0280e3c43 adding app region to console project 2024-11-10 12:26:34 +02:00
Christy Jacob 76216c3bf6 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-09 16:01:16 +00:00
loks0n c8c4576bbc Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-09 13:07:15 +01:00
loks0n dac8766eb4 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-08 22:02:31 +01:00
loks0n f6de256220 chore: composer update 2024-11-08 21:50:33 +01:00
loks0n 2f9b44930f Revert "Merge pull request #8946 from appwrite/chore-update-database"
This reverts commit c0bd1664be, reversing
changes made to b42dd86451.
2024-11-08 21:48:27 +01:00
loks0n 627df0d11f chore: remove redundant compression 2024-11-08 20:24:55 +01:00
loks0n fcd323baa4 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-08 20:24:35 +01:00
loks0n 0009b73b06 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-08 16:53:22 +01:00
shimon b5efbdbb0f updates 2024-11-08 14:43:30 +02:00
shimon a6e9d58f12 Merge branch 'feat-migration' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	app/cli.php
#	app/controllers/api/projects.php
2024-11-08 14:12:48 +02:00
shimon b496bcb6df updates 2024-11-08 14:06:02 +02:00
Jake Barnby 030e26b223 Merge remote-tracking branch 'origin/1.6.x' into feat-custom-cf-hostnames 2024-11-08 23:33:10 +13:00
Jake Barnby 9e69e41e02 Update lock 2024-11-08 17:04:29 +13:00
Jake Barnby 773fa6ffd6 Update lock 2024-11-08 16:34:07 +13:00
Jake Barnby 21bff77c50 Merge remote-tracking branch 'origin/feat-custom-cf-hostnames' into feat-migration
# Conflicts:
#	composer.json
#	composer.lock
2024-11-08 16:32:55 +13:00
loks0n 472f167b4a Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-07 20:31:41 +01:00
shimon f86bf6fdfd updates 2024-11-07 13:59:53 +02:00
shimon f3315c69a0 projects.php 2024-11-07 13:19:52 +02:00
shimon 76701b0cca composer 2024-11-07 13:03:45 +02:00
shimon 2cc73151d1 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.json
#	composer.lock
2024-11-07 13:02:45 +02:00
shimon 39e237dfb4 composer 2024-11-07 12:45:18 +02:00
shimon c624d94c45 composer 2024-11-07 12:44:51 +02:00
shimon b22188251d composer 2024-11-07 12:41:00 +02:00
shimon 68d017f452 composer 2024-11-07 12:36:53 +02:00
shimon 2ed854893d composer 2024-11-07 12:32:48 +02:00
shimon eb083a7e10 composer 2024-11-07 12:22:18 +02:00
shimon 2076a429c6 composer 2024-11-07 12:00:44 +02:00
shimon 59766b6ce6 composer 2024-11-07 12:00:26 +02:00
shimon 36bff12939 project creation fix 2024-11-07 11:56:09 +02:00
Fabian Gruber b4c986839e Merge branch '1.6.x' into feat-custom-cf-hostnames 2024-11-06 12:40:56 +01:00
Christy Jacob 4d81a901cb feat: update API endpoint in specs 2024-11-06 12:22:31 +01:00
Christy Jacob c11888c906 feat: update API endpoint in specs 2024-11-06 12:16:15 +01:00
Christy Jacob e6fb296666 feat: update API endpoint in the code examples 2024-11-06 12:07:14 +01:00
Damodar LohaniandGitHub d425c9f071 Merge pull request #8930 from appwrite/pathc-update-utopia-storage
upgrade utopia storage
2024-11-06 16:01:56 +05:45
Damodar Lohani e219ee2d84 upgrade utopia storage 2024-11-06 10:02:11 +00:00
Jake Barnby e66d0216c6 Merge remote-tracking branch 'origin/1.6.x' into feat-custom-cf-hostnames 2024-11-06 17:02:06 +13:00
Steven NguyenandGitHub e3e20d2923 Merge pull request #8922 from appwrite/lohanidamodar-patch-4
Improve compression param checks
2024-11-05 17:35:39 -08:00
loks0n e0c503e249 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-05 19:47:29 +01:00
shimon c3a8db0ea4 Rollback cross regions connections 2024-11-05 17:14:23 +02:00
Jake Barnby 228cabab3a Merge remote-tracking branch 'origin/1.6.x' into feat-custom-cf-hostnames 2024-11-05 23:38:24 +13:00
shimon 2b3e7f67a4 Rollback cross regions connections 2024-11-05 12:21:16 +02:00
shimon c2d5e8ec11 Rollback cross regions connections 2024-11-05 12:03:57 +02:00
Damodar Lohani 028c5366c2 make compression nullable and assign default in action
- also update the `update bucket` endpoint
2024-11-05 08:12:58 +00:00
Damodar LohaniandGitHub cca0c3ae45 Make compression param whitelist strict 2024-11-05 12:59:11 +05:45
Jake Barnby 0b85a64dab Merge remote-tracking branch 'origin/1.6.x' into feat-custom-cf-hostnames 2024-11-04 22:44:25 +13:00
shimon 4eafce998d Rollback cross regions connections 2024-11-03 14:58:09 +02:00
shimon 4963d98331 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.lock
2024-11-03 14:45:04 +02:00
Jake Barnby 1633acbdc3 Merge remote-tracking branch 'origin/feat-custom-cf-hostnames' into feat-migration
# Conflicts:
#	composer.json
#	composer.lock
2024-11-01 16:16:06 +13:00
shimon 9e07579a52 Rollback cross regions connections 2024-10-31 21:38:27 +02:00
shimon 16a2d61874 Rollback cross regions connections 2024-10-31 17:43:49 +02:00
Jake BarnbyandGitHub 75e8635563 Merge pull request #8904 from appwrite/1.6.x
Merge 1.6.x into feat-custom-cf-hostnames
2024-10-31 22:12:38 +13:00
Jake Barnby 83e94629dc Merge remote-tracking branch 'origin/feat-custom-cf-hostnames' into feat-migration 2024-10-31 21:49:49 +13:00
shimon b00b609621 sync against 1.6.x 2024-10-31 10:22:08 +02:00
shimon cdbd72ec3d sync against 1.6.x 2024-10-31 10:06:52 +02:00
shimon a6104ad8cd Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	app/worker.php
#	composer.json
#	composer.lock
#	src/Appwrite/Platform/Workers/Functions.php
2024-10-29 18:00:54 +02:00
Fabian Gruber 6e3160c502 Merge branch '1.6.x' into feat-custom-cf-hostnames 2024-10-29 16:15:54 +01:00
Fabian Gruber 104ec3188f Merge branch '1.6.x' into feat-custom-cf-hostnames 2024-10-29 15:36:38 +01:00
shimon 4e37699e25 debug 2024-10-29 11:15:55 +02:00
shimon 118252d3af injecting poolForQueue to schedulers 2024-10-28 12:04:58 +02:00
shimon 509f06c2f0 injecting poolForQueue to schedulers 2024-10-28 11:41:59 +02:00
shimon 12458af84a injecting poolForQueue to schedulers 2024-10-28 11:34:02 +02:00
shimon 66bfc5748b injecting poolForQueue to schedulers 2024-10-28 11:15:41 +02:00
shimon cf32ba0351 debug 2024-10-27 18:10:30 +02:00
Fabian Gruber b47a4703c4 Merge branch '1.6.x' into feat-custom-cf-hostnames 2024-10-25 13:39:12 +02:00
Fabian Gruber 4ceff430f9 Merge branch '1.6.x' into feat-custom-cf-hostnames 2024-10-24 12:49:26 +02:00
Christy JacobandGitHub 5603516d88 Merge branch '1.6.x' into feat-custom-cf-hostnames 2024-10-23 23:56:43 +04:00
Jake Barnby 013d95ebf3 Update audit and abuse 2024-10-23 18:42:00 +13:00
Jake Barnby eca8d116c2 Fix attributes 2024-10-23 18:10:41 +13:00
Jake Barnby 8210808db0 Improve logic 2024-10-23 18:00:06 +13:00
Jake Barnby 4a003f2826 Force create audit/abuse metadata documents for shared tables v1 2024-10-23 17:41:44 +13:00
Christy Jacob 54f39494c0 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-10-22 19:51:03 +00:00
Christy Jacob ae4edf119f chore: linter 2024-10-22 19:49:10 +00:00
shimon 7be7b3389c debug 2024-10-22 21:31:32 +03:00
shimon 54555cb3cd updates 2024-10-22 19:39:57 +03:00
Fabian GruberandChristy Jacob a56293e018 chore: replace 'Expires' with 'Cache-Control: private' header to avoid CDN caching 2024-10-22 12:18:58 +00:00
shimon f8b2878943 sync with feat-migration 2024-10-22 11:09:27 +03:00
Jake Barnby a0c58317bd Merge remote-tracking branch 'origin/1.6.x' into feat-migration
# Conflicts:
#	composer.json
#	composer.lock
2024-10-22 15:13:45 +13:00
shimon e687c64ca0 sync with feat-migration 2024-10-21 18:58:11 +03:00
shimon 3ff5472543 sync with feat-migration 2024-10-21 18:57:11 +03:00
Jake Barnby d979c0c914 Create metadata document from outside if duplicate on shared tables V1 2024-10-21 22:43:47 +13:00
shimon a41b767400 debug 2024-10-21 10:32:53 +03:00
shimon ba0a495d3c debug 2024-10-21 10:01:32 +03:00
shimon 6435fa84af debug 2024-10-21 10:00:35 +03:00
shimon 8743330ad8 debug 2024-10-21 09:59:05 +03:00
shimon a37aa5551a debug 2024-10-20 17:59:39 +03:00
shimon b2521cf8a3 debug 2024-10-20 17:36:08 +03:00
shimon c853b6593a debug 2024-10-20 17:22:37 +03:00
shimon d980711c30 debug 2024-10-20 16:32:19 +03:00
shimon 7ab55e0694 debug 2024-10-20 16:21:39 +03:00
shimon db244937ff debug 2024-10-20 16:10:14 +03:00
shimon bcb593e6df debug 2024-10-20 15:56:43 +03:00
shimon 9bfab69062 debug 2024-10-20 15:33:10 +03:00
shimon 5451a9cd23 sync against feat-migration 2024-10-20 10:56:44 +03:00
Christy Jacob 5ea83b8827 chore: update cert generation 2024-10-18 23:45:42 +00:00
Christy Jacob 6fe7d92fea chore: update cert generation 2024-10-18 22:32:03 +00:00
Christy Jacob 28b02a4ba8 chore: fix bug 2024-10-18 21:18:20 +00:00
Christy Jacob f4d456fa4c chore: updat certs logic 2024-10-18 20:52:29 +00:00
Fabian Gruber 687252b15b fix adding custom hostname & error logging 2024-10-18 22:21:53 +02:00
Fabian Gruber f21b1c87a6 fix missing ssl object 2024-10-18 22:07:36 +02:00
Fabian Gruber ccbde6c470 fix zone id 2024-10-18 21:44:36 +02:00
Torsten Dittmann f189c41421 temp: custom cloudflare hostnames 2024-10-18 21:39:16 +02:00
Jake BarnbyandGitHub 5fcb990b1c Merge branch '1.6.x' into feat-migration 2024-10-15 21:17:15 +13:00
Jake Barnby 30bd25d134 Update lock 2024-10-15 20:19:19 +13:00
Jake Barnby 36d2b84301 Update lock 2024-10-15 18:09:54 +13:00
shimon a80b921927 debug 2024-10-14 16:21:23 +03:00
shimon 1373d62c52 debug 2024-10-14 16:18:01 +03:00
shimon d0481482ae debug 2024-10-14 10:36:52 +03:00
Jake Barnby 78b9be2e78 Lint 2024-10-14 14:55:18 +13:00
Jake Barnby a3b7cf84d5 Allow shared tables V1 and V2 2024-10-14 14:50:56 +13:00
Jake Barnby add6a60af4 Remove redundant files 2024-10-14 14:33:53 +13:00
Jake Barnby 4543c182b5 Merge remote-tracking branch 'origin/1.6.x' into feat-migration
# Conflicts:
#	Dockerfile
#	composer.json
#	composer.lock
#	src/Appwrite/Platform/Workers/Deletes.php
2024-10-14 14:29:04 +13:00
shimon 051d6e580c debug 2024-10-13 18:15:11 +03:00
shimon 36e4c8c399 debug 2024-10-10 12:53:45 +03:00
shimon 7921bd62d9 debug 2024-10-10 12:11:35 +03:00
shimon 74ecc20751 debug 2024-10-10 12:07:53 +03:00
shimon d477de256a update file preview cache save flow 2024-10-10 11:40:13 +03:00
shimon cffbc77d05 update file preview cache save flow 2024-10-10 11:05:54 +03:00
shimon 16fe23244b builds metric reduce 2024-10-09 20:13:28 +03:00
shimon 7d7b2c7544 debug 2024-10-08 21:51:43 +03:00
shimon d2c66894fd debug 2024-10-07 17:56:08 +03:00
shimon 1cd3e69161 debug 2024-10-07 11:42:14 +03:00
shimon 5850caff35 debug 2024-10-07 11:25:52 +03:00
shimon 0cce55565a updates 2024-10-06 20:16:00 +03:00
shimon 6b429c9da9 updates 2024-10-06 18:59:22 +03:00
shimon 8f6151a575 updates 2024-10-06 18:56:39 +03:00
shimon 3f19fac0b4 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	app/init.php
#	app/realtime.php
#	composer.json
#	src/Appwrite/Platform/Tasks/ScheduleBase.php
#	src/Appwrite/Platform/Tasks/ScheduleExecutions.php
#	src/Appwrite/Platform/Tasks/ScheduleFunctions.php
#	src/Appwrite/Platform/Tasks/ScheduleMessages.php
#	src/Appwrite/Platform/Workers/Deletes.php
#	src/Appwrite/Platform/Workers/Migrations.php
2024-10-04 16:26:47 +03:00
shimon 08e5ed6a8a debug 2024-09-29 17:37:56 +03:00
shimon e4d554689f debug 2024-09-29 17:35:27 +03:00
shimon 19912c87c9 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2024-09-29 17:04:34 +03:00
shimon 0de1e64958 debug 2024-09-24 12:41:14 +03:00
shimon aa0a795ec6 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2024-09-22 09:49:50 +03:00
shimon d1b062a418 Scheduled task fix 2024-09-19 20:03:15 +03:00
shimon 812d8a34cf updates 2024-09-18 13:45:09 +03:00
shimon ec02d881c9 updates 2024-09-18 11:52:59 +03:00
shimon b706571a21 _APP_OPTIONS_ROUTER_PROTECTION 2024-09-18 10:55:30 +03:00
shimon 134f90a464 fix 2024-09-17 23:32:36 +03:00
shimon eb553fa96c debug 2024-09-17 21:09:16 +03:00
shimon fd8b7eb6fc update 2024-09-17 15:35:48 +03:00
shimon 501411f5b5 1.6.x 2024-09-15 17:42:46 +02:00
shimon bee844fcdb sync against 1.6x 2024-09-15 17:40:55 +02:00
Jake Barnby d18a67e6d8 Update packages 2024-09-12 23:06:47 +12:00
Jake Barnby 25d44b5b4c Update generator 2024-09-12 23:04:44 +12:00
Jake Barnby ee7de0cb41 Update database 2024-09-12 23:02:12 +12:00
Jake Barnby 28642b9f04 Shared tables array 2024-09-12 17:06:16 +12:00
Jake Barnby c18308c024 Update database 2024-08-27 17:43:29 +12:00
Jake Barnby 1eafbf2ee2 Update database 2024-08-19 21:21:20 +12:00
Jake Barnby b98bcdd4a1 Update database 2024-08-19 18:27:45 +12:00
Jake Barnby 4c8cd5b6c6 Update audit, abuse, database 2024-08-19 14:52:15 +12:00
Jake Barnby 5b37a91b46 Merge remote-tracking branch 'origin/1.5.x' into feat-migration 2024-08-13 22:11:18 +12:00
Jake Barnby 52647cef23 Merge remote-tracking branch 'origin/feat-single-shared-metadata-docs' into feat-migration
# Conflicts:
#	composer.json
#	composer.lock
2024-08-13 22:10:57 +12:00
Jake Barnby 11ec31aefb Update database 2024-08-13 21:49:25 +12:00
Jake Barnby 73ca1efbeb Catch duplicate db on project create 2024-08-13 21:49:14 +12:00
Jake Barnby 340aa48d93 Catch duplicate on console db create 2024-08-13 21:14:52 +12:00
Jake Barnby 8ff72b341e Don't set tenant for project collections creation 2024-08-12 23:46:34 +12:00
Jake Barnby a23402075e Update database 2024-08-12 17:45:43 +12:00
Jake Barnby 6b86ed0117 Merge remote-tracking branch 'origin/1.5.x' into feat-migration
# Conflicts:
#	composer.lock
2024-08-07 22:10:04 +12:00
Jake Barnby e2a45cec8e Merge branch '1.5.x' into feat-migration
# Conflicts:
#	composer.json
#	composer.lock
2024-08-05 21:13:42 +12:00
Jake Barnby ec2eb2bfa3 Revert to utopia database instead of extension 2024-07-02 19:10:02 +12:00
Jake Barnby 10adfc5124 Add mirror override 2024-07-02 14:55:13 +12:00
Jake Barnby 5f7aae0e60 Add missing import 2024-06-21 13:43:01 +12:00
Jake Barnby 4952608b8d Merge remote-tracking branch 'origin/refactor-usage-sn' into feat-migration 2024-06-21 13:39:51 +12:00
Jake Barnby 42ebefef92 Merge remote-tracking branch 'origin/refactor-usage-sn' into feat-migration 2024-06-19 19:32:30 +12:00
Jake Barnby 03d33601b0 Override find 2024-06-14 13:08:30 +12:00
17478 changed files with 193599 additions and 242680 deletions
-11
View File
@@ -1,11 +0,0 @@
reviews:
path_filters:
- "!app/config/specs/**"
- "!docs/examples/**"
- "!docs/references/**"
- "!docs/sdks/**"
auto_review:
base_branches:
- main
- 1.6.x
- 1.7.x
+12 -16
View File
@@ -15,17 +15,15 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io
_APP_EMAIL_SECURITY=security@appwrite.io
_APP_EMAIL_CERTIFICATES=certificates@appwrite.io
_APP_SYSTEM_RESPONSE_FORMAT=
_APP_CUSTOM_DOMAIN_DENY_LIST=
_APP_OPTIONS_ABUSE=disabled
_APP_OPTIONS_ROUTER_PROTECTION=disabled
_APP_OPTIONS_FORCE_HTTPS=disabled
_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled
_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled
_APP_OPENSSL_KEY_V1=your-secret-key
_APP_DOMAIN=traefik
_APP_DOMAIN_FUNCTIONS=functions.localhost
_APP_DOMAIN_SITES=sites.localhost
_APP_DOMAIN_TARGET_CNAME=test.appwrite.io
_APP_DOMAIN_TARGET_A=127.0.0.1
_APP_DOMAIN_TARGET_AAAA=::1
_APP_DOMAIN_TARGET=localhost
_APP_RULES_FORMAT=md5
_APP_REDIS_HOST=redis
_APP_REDIS_PORT=6379
@@ -72,19 +70,17 @@ _APP_SMS_FROM=+123456789
_APP_SMS_PROJECTS_DENY_LIST=
_APP_STORAGE_LIMIT=30000000
_APP_STORAGE_PREVIEW_LIMIT=20000000
_APP_COMPUTE_SIZE_LIMIT=30000000
_APP_FUNCTIONS_SIZE_LIMIT=30000000
_APP_FUNCTIONS_TIMEOUT=900
_APP_SITES_TIMEOUT=30
_APP_COMPUTE_BUILD_TIMEOUT=900
_APP_COMPUTE_CPUS=8
_APP_COMPUTE_MEMORY=8192
_APP_COMPUTE_INACTIVE_THRESHOLD=600
_APP_COMPUTE_MAINTENANCE_INTERVAL=600
_APP_COMPUTE_RUNTIMES_NETWORK=runtimes
_APP_FUNCTIONS_BUILD_TIMEOUT=900
_APP_FUNCTIONS_CPUS=8
_APP_FUNCTIONS_MEMORY=8192
_APP_FUNCTIONS_INACTIVE_THRESHOLD=600
_APP_FUNCTIONS_MAINTENANCE_INTERVAL=600
_APP_FUNCTIONS_RUNTIMES_NETWORK=runtimes
_APP_EXECUTOR_SECRET=your-secret-key
_APP_EXECUTOR_HOST=http://exc1/v1
_APP_EXECUTOR_HOST=http://proxy/v1
_APP_FUNCTIONS_RUNTIMES=php-8.0,node-18.0,python-3.9,ruby-3.1
_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.29
_APP_MAINTENANCE_INTERVAL=86400
_APP_MAINTENANCE_DELAY=
_APP_MAINTENANCE_RETENTION_CACHE=2592000
@@ -93,7 +89,7 @@ _APP_MAINTENANCE_RETENTION_ABUSE=86400
_APP_MAINTENANCE_RETENTION_AUDIT=1209600
_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE=15778800
_APP_USAGE_AGGREGATION_INTERVAL=30
_APP_STATS_RESOURCES_INTERVAL=30
_APP_STATS_RESOURCES_INTERVAL=3600
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
_APP_USAGE_STATS=enabled
-120
View File
@@ -1,120 +0,0 @@
name: Benchmark
concurrency:
group: '${{ github.workflow }}-${{ github.ref }}'
cancel-in-progress: true
env:
IMAGE: appwrite-dev
CACHE_KEY: 'appwrite-dev-${{ github.event.pull_request.head.sha }}'
'on':
- pull_request
jobs:
setup:
name: Setup & Build Appwrite Image
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Appwrite
uses: docker/build-push-action@v6
with:
context: .
push: false
tags: '${{ env.IMAGE }}'
load: true
cache-from: type=gha
cache-to: 'type=gha,mode=max'
outputs: 'type=docker,dest=/tmp/${{ env.IMAGE }}.tar'
build-args: |
DEBUG=false
TESTING=true
VERSION=dev
- name: Cache Docker Image
uses: actions/cache@v4
with:
key: '${{ env.CACHE_KEY }}'
path: '/tmp/${{ env.IMAGE }}.tar'
benchmarking:
name: Benchmark
runs-on: ubuntu-latest
needs: setup
permissions:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Load Cache
uses: actions/cache@v4
with:
key: '${{ env.CACHE_KEY }}'
path: '/tmp/${{ env.IMAGE }}.tar'
fail-on-cache-miss: true
- name: Load and Start Appwrite
run: |
sed -i 's/traefik/localhost/g' .env
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose up -d
sleep 10
- name: Install Oha
run: |
echo "deb [signed-by=/usr/share/keyrings/azlux-archive-keyring.gpg] http://packages.azlux.fr/debian/ stable main" | sudo tee /etc/apt/sources.list.d/azlux.list
sudo wget -O /usr/share/keyrings/azlux-archive-keyring.gpg https://azlux.fr/repo.gpg
sudo apt update
sudo apt install oha
- name: Benchmark PR
run: 'oha -z 180s http://localhost/v1/health/version -j > benchmark.json'
- name: Cleaning
run: docker compose down -v
- name: Installing latest version
run: |
rm docker-compose.yml
rm .env
curl https://appwrite.io/install/compose -o docker-compose.yml
curl https://appwrite.io/install/env -o .env
sed -i 's/_APP_OPTIONS_ABUSE=enabled/_APP_OPTIONS_ABUSE=disabled/g' .env
docker compose up -d
sleep 10
- name: Benchmark Latest
run: oha -z 180s http://localhost/v1/health/version -j > benchmark-latest.json
- name: Prepare comment
run: |
echo '## :sparkles: Benchmark results' > benchmark.txt
echo ' ' >> benchmark.txt
echo "- Requests per second: $(jq -r '.summary.requestsPerSec|tonumber?|floor|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json)" >> benchmark.txt
echo "- Requests with 200 status code: $(jq -r '.statusCodeDistribution."200"|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json)" >> benchmark.txt
echo "- P99 latency: $(jq -r '.latencyPercentiles.p99' benchmark.json )" >> benchmark.txt
echo " " >> benchmark.txt
echo " " >> benchmark.txt
echo "## :zap: Benchmark Comparison" >> benchmark.txt
echo " " >> benchmark.txt
echo "| Metric | This PR | Latest version | " >> benchmark.txt
echo "| --- | --- | --- | " >> benchmark.txt
echo "| RPS | $(jq -r '.summary.requestsPerSec|tonumber?|floor|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json) | $(jq -r '.summary.requestsPerSec|tonumber|floor|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark-latest.json) | " >> benchmark.txt
echo "| 200 | $(jq -r '.statusCodeDistribution."200"|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json) | $(jq -r '.statusCodeDistribution."200"|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark-latest.json) | " >> benchmark.txt
echo "| P99 | $(jq -r '.latencyPercentiles.p99' benchmark.json ) | $(jq -r '.latencyPercentiles.p99' benchmark-latest.json ) | " >> benchmark.txt
- name: Save results
uses: actions/upload-artifact@v4
if: '${{ !cancelled() }}'
with:
name: benchmark.json
path: benchmark.json
retention-days: 7
- name: Find Comment
if: github.event.pull_request.head.repo.full_name == github.repository
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: '${{ github.event.pull_request.number }}'
comment-author: 'github-actions[bot]'
body-includes: Benchmark results
- name: Comment on PR
if: github.event.pull_request.head.repo.full_name == github.repository
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: '${{ steps.fc.outputs.comment-id }}'
issue-number: '${{ github.event.pull_request.number }}'
body-path: benchmark.txt
edit-mode: replace
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Cleanup
run: |
+1 -1
View File
@@ -34,7 +34,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 2
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 2
submodules: recursive
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
-16
View File
@@ -1,16 +0,0 @@
name: "Static code analysis"
on: [pull_request]
jobs:
lint:
name: CodeQL
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Run CodeQL
run: |
docker run --rm -v $PWD:/app composer:2.6 sh -c \
"composer install --profile --ignore-platform-reqs && composer check"
+70 -102
View File
@@ -90,9 +90,6 @@ jobs:
docker compose up -d
sleep 10
- name: Logs
run: docker compose logs appwrite
- name: Doctor
run: docker compose exec -T appwrite doctor
@@ -122,14 +119,6 @@ jobs:
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose up -d
sleep 10
- name: Wait for Open Runtimes
timeout-minutes: 3
run: |
while ! docker compose logs openruntimes-executor | grep -q "Executor is ready."; do
echo "Waiting for Executor to come online"
sleep 1
done
- name: Run General Tests
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/General --debug
@@ -153,8 +142,6 @@ jobs:
Locale,
Projects,
Realtime,
Sites,
Proxy,
Storage,
Teams,
Users,
@@ -180,14 +167,6 @@ jobs:
docker compose up -d
sleep 30
- name: Wait for Open Runtimes
timeout-minutes: 3
run: |
while ! docker compose logs openruntimes-executor | grep -q "Executor is ready."; do
echo "Waiting for Executor to come online"
sleep 1
done
- name: Run ${{ matrix.service }} tests with Project table mode
run: |
echo "Using project tables"
@@ -197,7 +176,7 @@ jobs:
docker compose exec -T \
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude=devKeys
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug
e2e_shared_mode_test:
name: E2E Shared Mode Service Test
@@ -220,16 +199,13 @@ jobs:
Locale,
Projects,
Realtime,
Sites,
Proxy,
Storage,
Teams,
Users,
Webhooks,
VCS,
Messaging,
Migrations,
Tokens
Migrations
]
tables-mode: [
'Shared V1',
@@ -253,14 +229,6 @@ jobs:
docker compose up -d
sleep 30
- name: Wait for Open Runtimes
timeout-minutes: 3
run: |
while ! docker compose logs openruntimes-executor | grep -q "Executor is ready."; do
echo "Waiting for Executor to come online"
sleep 1
done
- name: Run ${{ matrix.service }} tests with ${{ matrix.tables-mode }} table mode
run: |
if [ "${{ matrix.tables-mode }}" == "Shared V1" ]; then
@@ -272,90 +240,90 @@ jobs:
export _APP_DATABASE_SHARED_TABLES=database_db_main
export _APP_DATABASE_SHARED_TABLES_V1=
fi
docker compose exec -T \
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude=devKeys
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug
e2e_dev_keys:
name: E2E Service Test (Dev Keys)
benchmarking:
name: Benchmark
runs-on: ubuntu-latest
needs: setup
strategy:
fail-fast: false
permissions:
pull-requests: write
steps:
- name: checkout
- name: Checkout repository
uses: actions/checkout@v4
- name: Load Cache
uses: actions/cache@v4
with:
key: ${{ env.CACHE_KEY }}
path: /tmp/${{ env.IMAGE }}.tar
fail-on-cache-miss: true
- name: Load and Start Appwrite
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env
sed -i 's/traefik/localhost/g' .env
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose up -d
sleep 30
- name: Run Projects tests with dev keys in ${{ matrix.tables-mode }} table mode
sleep 10
- name: Install Oha
run: |
echo "Using project tables"
export _APP_DATABASE_SHARED_TABLES=
export _APP_DATABASE_SHARED_TABLES_V1=
docker compose exec -T \
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys
e2e_dev_keys_shared_mode:
name: E2E Shared Mode Service Test (Dev Keys)
runs-on: ubuntu-latest
needs: [ setup, check_database_changes ]
if: needs.check_database_changes.outputs.database_changed == 'true'
strategy:
fail-fast: false
matrix:
tables-mode: [
'Shared V1',
'Shared V2',
]
steps:
- name: checkout
uses: actions/checkout@v4
- name: Load Cache
uses: actions/cache@v4
echo "deb [signed-by=/usr/share/keyrings/azlux-archive-keyring.gpg] http://packages.azlux.fr/debian/ stable main" | sudo tee /etc/apt/sources.list.d/azlux.list
sudo wget -O /usr/share/keyrings/azlux-archive-keyring.gpg https://azlux.fr/repo.gpg
sudo apt update
sudo apt install oha
- name: Benchmark PR
run: oha -z 180s http://localhost/v1/health/version -j > benchmark.json
- name: Cleaning
run: docker compose down -v
- name: Installing latest version
run: |
rm docker-compose.yml
rm .env
curl https://appwrite.io/install/compose -o docker-compose.yml
curl https://appwrite.io/install/env -o .env
sed -i 's/_APP_OPTIONS_ABUSE=enabled/_APP_OPTIONS_ABUSE=disabled/g' .env
docker compose up -d
sleep 10
- name: Benchmark Latest
run: oha -z 180s http://localhost/v1/health/version -j > benchmark-latest.json
- name: Prepare comment
run: |
echo '## :sparkles: Benchmark results' > benchmark.txt
echo ' ' >> benchmark.txt
echo "- Requests per second: $(jq -r '.summary.requestsPerSec|tonumber?|floor|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json)" >> benchmark.txt
echo "- Requests with 200 status code: $(jq -r '.statusCodeDistribution."200"|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json)" >> benchmark.txt
echo "- P99 latency: $(jq -r '.latencyPercentiles.p99' benchmark.json )" >> benchmark.txt
echo " " >> benchmark.txt
echo " " >> benchmark.txt
echo "## :zap: Benchmark Comparison" >> benchmark.txt
echo " " >> benchmark.txt
echo "| Metric | This PR | Latest version | " >> benchmark.txt
echo "| --- | --- | --- | " >> benchmark.txt
echo "| RPS | $(jq -r '.summary.requestsPerSec|tonumber?|floor|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json) | $(jq -r '.summary.requestsPerSec|tonumber|floor|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark-latest.json) | " >> benchmark.txt
echo "| 200 | $(jq -r '.statusCodeDistribution."200"|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json) | $(jq -r '.statusCodeDistribution."200"|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark-latest.json) | " >> benchmark.txt
echo "| P99 | $(jq -r '.latencyPercentiles.p99' benchmark.json ) | $(jq -r '.latencyPercentiles.p99' benchmark-latest.json ) | " >> benchmark.txt
- name: Save results
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
key: ${{ env.CACHE_KEY }}
path: /tmp/${{ env.IMAGE }}.tar
fail-on-cache-miss: true
- name: Load and Start Appwrite
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env
docker compose up -d
sleep 30
- name: Run Projects tests with dev keys in ${{ matrix.tables-mode }} table mode
run: |
if [ "${{ matrix.tables-mode }}" == "Shared V1" ]; then
echo "Using shared tables V1"
export _APP_DATABASE_SHARED_TABLES=database_db_main
export _APP_DATABASE_SHARED_TABLES_V1=database_db_main
elif [ "${{ matrix.tables-mode }}" == "Shared V2" ]; then
echo "Using shared tables V2"
export _APP_DATABASE_SHARED_TABLES=database_db_main
export _APP_DATABASE_SHARED_TABLES_V1=
fi
docker compose exec -T \
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys
name: benchmark.json
path: benchmark.json
retention-days: 7
- name: Find Comment
if: github.event.pull_request.head.repo.full_name == github.repository
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: Benchmark results
- name: Comment on PR
if: github.event.pull_request.head.repo.full_name == github.repository
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body-path: benchmark.txt
edit-mode: replace
-1
View File
@@ -3,7 +3,6 @@
/node_modules/
/tests/resources/storage/
/tests/resources/functions/**/code.tar.gz
/tests/resources/sites/**/code.tar.gz
/app/sdks/*
/.idea/
!/.idea/workspace.xml
-22
View File
@@ -163,28 +163,6 @@ Other containes should be named the same as their service, for example `redis` s
- [Encryption](https://medium.com/searchencrypt/what-is-encryption-how-does-it-work-e8f20e340537#:~:text=Encryption%20is%20a%20process%20that,%2C%20or%20decrypt%2C%20the%20information.)
- [Hashing](https://searchsqlserver.techtarget.com/definition/hashing#:~:text=Hashing%20is%20the%20transformation%20of,it%20using%20the%20original%20value.)
## Modules
As Appwrite grows, we noticed approach of having all service endpoints in `app/controllers/api/[service].php` is not maintainable. Not only it creates massive files, it also doesnt contain all product's features such as workers or tasks. While there might still be some occurances of those controller files, we avoid it in all new development, and gradually migrate existing controllers to **HTTP modules**.
### HTTP Endpoints
Every endpoint file follows below structure, making it consistent with HTTP REST endpoint path:
```
src/Appwrite/Platform/Modules/[service]/Http/[resource]/[action].php
```
Tips and tricks:
1. If endpoint doesn't have resource, use service name as resource name too
> Example: `Modules/Sites/Http/Sites/Get.php`
2. If there are multiple resources, use then all in folder structure
> Example: `Modules/Sites/Http/Deployments/Builds/Create.php`
3. Action can only be `Get`, `Create`, `Update`, `Delete` or `XList`
## Architecture
Appwrite's current structure is a combination of both [Monolithic](https://en.wikipedia.org/wiki/Monolithic_application) and [Microservice](https://en.wikipedia.org/wiki/Microservices) architectures.
-4
View File
@@ -44,14 +44,12 @@ COPY ./dev /usr/src/code/dev
# Set Volumes
RUN mkdir -p /storage/uploads && \
mkdir -p /storage/imports && \
mkdir -p /storage/cache && \
mkdir -p /storage/config && \
mkdir -p /storage/certificates && \
mkdir -p /storage/functions && \
mkdir -p /storage/debug && \
chown -Rf www-data.www-data /storage/uploads && chmod -Rf 0755 /storage/uploads && \
chown -Rf www-data.www-data /storage/imports && chmod -Rf 0755 /storage/imports && \
chown -Rf www-data.www-data /storage/cache && chmod -Rf 0755 /storage/cache && \
chown -Rf www-data.www-data /storage/config && chmod -Rf 0755 /storage/config && \
chown -Rf www-data.www-data /storage/certificates && chmod -Rf 0755 /storage/certificates && \
@@ -70,7 +68,6 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/sdks && \
chmod +x /usr/local/bin/specs && \
chmod +x /usr/local/bin/ssl && \
chmod +x /usr/local/bin/screenshot && \
chmod +x /usr/local/bin/test && \
chmod +x /usr/local/bin/upgrade && \
chmod +x /usr/local/bin/vars && \
@@ -89,7 +86,6 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/worker-migrations && \
chmod +x /usr/local/bin/worker-webhooks && \
chmod +x /usr/local/bin/worker-stats-usage && \
chmod +x /usr/local/bin/worker-stats-usage-dump && \
chmod +x /usr/local/bin/stats-resources && \
chmod +x /usr/local/bin/worker-stats-resources
+26 -8
View File
@@ -26,8 +26,8 @@ use Utopia\Queue\Publisher;
use Utopia\Registry\Registry;
use Utopia\System\System;
// overwriting runtimes to be architecture agnostic for CLI
Config::setParam('runtimes', (new Runtimes('v5'))->getAll(supported: false));
// Overwriting runtimes to be architecture agnostic for CLI
Config::setParam('runtimes', (new Runtimes('v4'))->getAll(supported: false));
// require controllers after overwriting runtimes
require_once __DIR__ . '/controllers/general.php';
@@ -219,6 +219,13 @@ CLI::setResource('queueForCertificates', function (Publisher $publisher) {
}, ['publisher']);
CLI::setResource('logError', function (Registry $register) {
return function (Throwable $error, string $namespace, string $action) use ($register) {
Console::error('[Error] Timestamp: ' . date('c', time()));
Console::error('[Error] Type: ' . get_class($error));
Console::error('[Error] Message: ' . $error->getMessage());
Console::error('[Error] File: ' . $error->getFile());
Console::error('[Error] Line: ' . $error->getLine());
Console::error('[Error] Trace: ' . $error->getTraceAsString());
$logger = $register->get('logger');
if ($logger) {
@@ -237,6 +244,7 @@ CLI::setResource('logError', function (Registry $register) {
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('detailedTrace', $error->getTrace());
$log->setAction($action);
@@ -250,24 +258,34 @@ CLI::setResource('logError', function (Registry $register) {
Console::error('Error pushing log: ' . $th->getMessage());
}
}
Console::warning("Failed: {$error->getMessage()}");
Console::warning($error->getTraceAsString());
};
}, ['register']);
CLI::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
$platform = new Appwrite();
$platform->init(Service::TYPE_TASK);
$args = $platform->getEnv('argv');
if (!isset($args[0])) {
Console::error('Missing task name');
Console::exit(1);
}
\array_shift($args);
$taskName = $args[0];
$platform->init(Service::TYPE_TASK);
$cli = $platform->getCli();
$cli
->error()
->inject('error')
->action(function (Throwable $error) {
Console::error($error->getMessage());
->inject('logError')
->action(function (Throwable $error, callable $logError) use ($taskName) {
call_user_func_array($logError, [
$error,
'Task',
$taskName,
]);
});
$cli->run();
-7
View File
@@ -1417,13 +1417,6 @@ return [
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_roles'),
'type' => Database::INDEX_KEY,
'attributes' => ['roles'],
'lengths' => [128],
'orders' => [],
],
],
],
+13 -273
View File
@@ -287,17 +287,6 @@ return [
'array' => false,
'filters' => ['subQueryKeys'],
],
[
'$id' => ID::custom('devKeys'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['subQueryDevKeys'],
],
[
'$id' => ID::custom('search'),
'type' => Database::VAR_STRING,
@@ -700,125 +689,6 @@ return [
],
],
'devKeys' => [
'$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('devKeys'),
'name' => 'Dev keys',
'attributes' => [
[
'$id' => ID::custom('projectInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('projectId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => 0,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('name'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('secret'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 512, // var_dump of \bin2hex(\random_bytes(128)) => string(256) doubling for encryption
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => ['encrypt'],
],
[
'$id' => ID::custom('expire'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('accessedAt'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('sdks'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => true,
'filters' => [],
],
[
'$id' => ID::custom('search'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => ID::custom('_key_project'),
'type' => Database::INDEX_KEY,
'attributes' => ['projectInternalId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_accessedAt',
'type' => Database::INDEX_KEY,
'attributes' => ['accessedAt'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_search'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [],
'orders' => [],
],
],
],
'webhooks' => [
'$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('webhooks'),
@@ -1143,10 +1013,10 @@ return [
'filters' => [],
],
[
'$id' => ID::custom('type'), // 'api', 'redirect', 'deployment' (site or function)
'$id' => ID::custom('resourceType'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 32,
'size' => 100,
'signed' => true,
'required' => true,
'default' => null,
@@ -1154,32 +1024,10 @@ return [
'filters' => [],
],
[
'$id' => ID::custom('trigger'), // 'manual', 'deployment', '' (empty)
'$id' => ID::custom('resourceInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 32,
'signed' => true,
'required' => false,
'default' => '',
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('redirectUrl'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 2048,
'signed' => true,
'required' => false,
'default' => '',
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('redirectStatusCode'),
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
@@ -1187,68 +1035,13 @@ return [
'filters' => [],
],
[
'$id' => ID::custom('deploymentResourceType'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 32,
'signed' => true,
'required' => false,
'default' => '',
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('deploymentId'),
'$id' => ID::custom('resourceId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => '',
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('deploymentInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => '',
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('deploymentResourceId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => '',
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('deploymentResourceInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => '',
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('deploymentVcsProviderBranch'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => '',
'default' => null,
'array' => false,
'filters' => [],
],
@@ -1274,17 +1067,6 @@ return [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('search'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('owner'),
'type' => Database::VAR_STRING,
@@ -1309,13 +1091,6 @@ return [
],
],
'indexes' => [
[
'$id' => ID::custom('_key_search'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['search'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_domain'),
'type' => Database::INDEX_UNIQUE,
@@ -1338,59 +1113,24 @@ return [
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_type',
'$id' => '_key_resourceInternalId',
'type' => Database::INDEX_KEY,
'attributes' => ['type'],
'lengths' => [32],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_trigger',
'type' => Database::INDEX_KEY,
'attributes' => ['trigger'],
'lengths' => [32],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_deploymentResourceType',
'type' => Database::INDEX_KEY,
'attributes' => ['deploymentResourceType'],
'lengths' => [32],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_deploymentResourceId',
'type' => Database::INDEX_KEY,
'attributes' => ['deploymentResourceId'],
'attributes' => ['resourceInternalId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_deploymentResourceInternalId',
'$id' => '_key_resourceId',
'type' => Database::INDEX_KEY,
'attributes' => ['deploymentResourceInternalId'],
'attributes' => ['resourceId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_deploymentId',
'$id' => '_key_resourceType',
'type' => Database::INDEX_KEY,
'attributes' => ['deploymentId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_deploymentInternalId',
'type' => Database::INDEX_KEY,
'attributes' => ['deploymentInternalId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => '_key_deploymentVcsProviderBranch',
'type' => Database::INDEX_KEY,
'attributes' => ['deploymentVcsProviderBranch'],
'lengths' => [Database::LENGTH_KEY],
'attributes' => ['resourceType'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
File diff suppressed because it is too large Load Diff
+7 -65
View File
@@ -375,13 +375,6 @@ return [
'code' => 409,
],
/** Console */
Exception::RESOURCE_ALREADY_EXISTS => [
'name' => Exception::RESOURCE_ALREADY_EXISTS,
'description' => 'Resource with the requested ID already exists. Please choose a different ID and try again.',
'code' => 409,
],
/** Membership */
Exception::MEMBERSHIP_NOT_FOUND => [
'name' => Exception::MEMBERSHIP_NOT_FOUND,
@@ -488,18 +481,6 @@ return [
'code' => 403,
],
/** Tokens */
Exception::TOKEN_NOT_FOUND => [
'name' => Exception::TOKEN_NOT_FOUND,
'description' => 'The requested file token could not be found.',
'code' => 404,
],
Exception::TOKEN_EXPIRED => [
'name' => Exception::TOKEN_EXPIRED,
'description' => 'The requested file token has expired.',
'code' => 401,
],
/** VCS */
Exception::INSTALLATION_NOT_FOUND => [
'name' => Exception::INSTALLATION_NOT_FOUND,
@@ -539,7 +520,7 @@ return [
'code' => 404,
],
Exception::FUNCTION_ENTRYPOINT_MISSING => [
'name' => Exception::FUNCTION_ENTRYPOINT_MISSING,
'name' => Exception::FUNCTION_RUNTIME_UNSUPPORTED,
'description' => 'Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".',
'code' => 404,
],
@@ -553,28 +534,6 @@ return [
'description' => 'Function Template with the requested ID could not be found.',
'code' => 404,
],
Exception::FUNCTION_RUNTIME_NOT_DETECTED => [
'name' => Exception::FUNCTION_RUNTIME_NOT_DETECTED,
'description' => 'Function runtime could not be detected.',
'code' => 400,
],
Exception::FUNCTION_EXECUTE_PERMISSION_MISSING => [
'name' => Exception::FUNCTION_EXECUTE_PERMISSION_MISSING,
'description' => 'To execute function using domain, execute permissions must include "any" or "guests".',
'code' => 401,
],
/** Sites */
Exception::SITE_NOT_FOUND => [
'name' => Exception::SITE_NOT_FOUND,
'description' => 'Site with the requested ID could not be found.',
'code' => 404,
],
Exception::SITE_TEMPLATE_NOT_FOUND => [
'name' => Exception::SITE_TEMPLATE_NOT_FOUND,
'description' => 'Site Template with the requested ID could not be found.',
'code' => 404,
],
/** Builds */
Exception::BUILD_NOT_FOUND => [
@@ -597,16 +556,6 @@ return [
'description' => 'Build with the requested ID is already completed and cannot be canceled.',
'code' => 400,
],
Exception::BUILD_CANCELED => [
'name' => Exception::BUILD_CANCELED,
'description' => 'Build with the requested ID has been canceled.',
'code' => 400,
],
Exception::BUILD_FAILED => [
'name' => Exception::BUILD_FAILED,
'description' => 'Build with the requested ID failed. Please check the logs for more information.',
'code' => 400,
],
/** Deployments */
Exception::DEPLOYMENT_NOT_FOUND => [
@@ -628,13 +577,6 @@ return [
'code' => 400,
],
/** Logs */
Exception::LOG_NOT_FOUND => [
'name' => Exception::LOG_NOT_FOUND,
'description' => 'Log with the requested ID could not be found.',
'code' => 404,
],
/** Databases */
Exception::DATABASE_NOT_FOUND => [
'name' => Exception::DATABASE_NOT_FOUND,
@@ -651,6 +593,11 @@ return [
'description' => 'Database timed out. Try adjusting your queries or adding an index.',
'code' => 408
],
Exception::DATABASE_QUERY_ORDER_NULL => [
'name' => Exception::DATABASE_QUERY_ORDER_NULL,
'description' => 'The order attribute had a null value. Cursor pagination requires all documents order attribute values are non-null.',
'code' => 400,
],
/** Collections */
Exception::COLLECTION_NOT_FOUND => [
@@ -859,7 +806,7 @@ return [
Exception::RULE_VERIFICATION_FAILED => [
'name' => Exception::RULE_VERIFICATION_FAILED,
'description' => 'Domain verification failed. Please check if your DNS records are correct and try again.',
'code' => 400,
'code' => 401,
'publish' => true
],
Exception::PROJECT_SMTP_CONFIG_INVALID => [
@@ -902,11 +849,6 @@ return [
'description' => 'Variable with the same ID already exists in this project. Try again with a different ID.',
'code' => 409,
],
Exception::VARIABLE_CANNOT_UNSET_SECRET => [
'name' => Exception::VARIABLE_CANNOT_UNSET_SECRET,
'description' => 'Secret variables cannot be marked as non-secret. Please re-create the variable if this is your intention.',
'code' => 400,
],
Exception::GRAPHQL_NO_QUERY => [
'name' => Exception::GRAPHQL_NO_QUERY,
'description' => 'Param "query" is not optional.',
-28
View File
@@ -217,34 +217,6 @@ return [
],
]
],
'sites' => [
'$model' => Response::MODEL_SITE,
'$resource' => true,
'$description' => 'This event triggers on any sites event.',
'deployments' => [
'$model' => Response::MODEL_DEPLOYMENT,
'$resource' => true,
'$description' => 'This event triggers on any deployments event.',
'create' => [
'$description' => 'This event triggers when a deployment is created.',
],
'delete' => [
'$description' => 'This event triggers when a deployment is deleted.'
],
'update' => [
'$description' => 'This event triggers when a deployment is updated.'
],
],
'create' => [
'$description' => 'This event triggers when a site is created.'
],
'delete' => [
'$description' => 'This event triggers when a site is deleted.',
],
'update' => [
'$description' => 'This event triggers when a site is updated.',
]
],
'functions' => [
'$model' => Response::MODEL_FUNCTION,
'$resource' => true,
-295
View File
@@ -1,295 +0,0 @@
<?php
/**
* List of Appwrite Sites supported frameworks
*/
use Utopia\Config\Config;
$templateRuntimes = Config::getParam('template-runtimes');
function getVersions(array $versions, string $prefix)
{
return array_map(function ($version) use ($prefix) {
return $prefix . '-' . $version;
}, $versions);
}
return [
'analog' => [
'key' => 'analog',
'name' => 'Analog',
'screenshotSleep' => 3000,
'buildRuntime' => 'node-22',
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
'bundleCommand' => 'bash /usr/local/server/helpers/analog/bundle.sh',
'envCommand' => 'source /usr/local/server/helpers/analog/env.sh',
'adapters' => [
'ssr' => [
'key' => 'ssr',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './dist/analog',
'startCommand' => 'bash helpers/analog/server.sh',
],
'static' => [
'key' => 'static',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './dist/analog/public',
'startCommand' => 'bash helpers/server.sh',
'fallbackFile' => 'index.html'
]
]
],
'angular' => [
'key' => 'angular',
'name' => 'Angular',
'screenshotSleep' => 3000,
'buildRuntime' => 'node-22',
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
'bundleCommand' => 'bash /usr/local/server/helpers/angular/bundle.sh',
'envCommand' => 'source /usr/local/server/helpers/angular/env.sh',
'adapters' => [
'ssr' => [
'key' => 'ssr',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './dist/angular',
'startCommand' => 'bash helpers/angular/server.sh',
],
'static' => [
'key' => 'static',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './dist/angular/browser',
'startCommand' => 'bash helpers/server.sh',
'fallbackFile' => 'index.csr.html'
]
]
],
'nextjs' => [
'key' => 'nextjs',
'name' => 'Next.js',
'screenshotSleep' => 3000,
'buildRuntime' => 'node-22',
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
'bundleCommand' => 'bash /usr/local/server/helpers/next-js/bundle.sh',
'envCommand' => 'source /usr/local/server/helpers/next-js/env.sh',
'adapters' => [
'ssr' => [
'key' => 'ssr',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './.next',
'startCommand' => 'bash helpers/next-js/server.sh',
],
'static' => [
'key' => 'static',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './out',
'startCommand' => 'bash helpers/server.sh',
]
]
],
'react' => [
'key' => 'react',
'name' => 'React',
'screenshotSleep' => 3000,
'buildRuntime' => 'node-22',
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
'adapters' => [
'static' => [
'key' => 'static',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './dist',
'startCommand' => 'bash helpers/server.sh',
'fallbackFile' => 'index.html'
]
]
],
'nuxt' => [
'key' => 'nuxt',
'name' => 'Nuxt',
'screenshotSleep' => 3000,
'buildRuntime' => 'node-22',
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
'bundleCommand' => 'bash /usr/local/server/helpers/nuxt/bundle.sh',
'envCommand' => 'source /usr/local/server/helpers/nuxt/env.sh',
'adapters' => [
'ssr' => [
'key' => 'ssr',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './.output',
'startCommand' => 'bash helpers/nuxt/server.sh',
],
'static' => [
'key' => 'static',
'buildCommand' => 'npm run generate',
'installCommand' => 'npm install',
'outputDirectory' => './output/public',
'startCommand' => 'bash helpers/server.sh',
]
]
],
'vue' => [
'key' => 'vue',
'name' => 'Vue.js',
'screenshotSleep' => 5000,
'buildRuntime' => 'node-22',
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
'adapters' => [
'static' => [
'key' => 'static',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './dist',
'startCommand' => 'bash helpers/server.sh',
'fallbackFile' => 'index.html'
]
]
],
'sveltekit' => [
'key' => 'sveltekit',
'name' => 'SvelteKit',
'screenshotSleep' => 3000,
'buildRuntime' => 'node-22',
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
'bundleCommand' => 'bash /usr/local/server/helpers/sveltekit/bundle.sh',
'envCommand' => 'source /usr/local/server/helpers/sveltekit/env.sh',
'adapters' => [
'ssr' => [
'key' => 'ssr',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './build',
'startCommand' => 'bash helpers/sveltekit/server.sh',
],
'static' => [
'key' => 'static',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './build',
'startCommand' => 'bash helpers/server.sh',
]
]
],
'astro' => [
'key' => 'astro',
'name' => 'Astro',
'screenshotSleep' => 3000,
'buildRuntime' => 'node-22',
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
'bundleCommand' => 'bash /usr/local/server/helpers/astro/bundle.sh',
'envCommand' => 'source /usr/local/server/helpers/astro/env.sh',
'adapters' => [
'ssr' => [
'key' => 'ssr',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './dist',
'startCommand' => 'bash helpers/astro/server.sh',
],
'static' => [
'key' => 'static',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './dist',
'startCommand' => 'bash helpers/server.sh',
]
]
],
'remix' => [
'key' => 'remix',
'name' => 'Remix',
'screenshotSleep' => 3000,
'buildRuntime' => 'node-22',
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
'bundleCommand' => 'bash /usr/local/server/helpers/remix/bundle.sh',
'envCommand' => 'source /usr/local/server/helpers/remix/env.sh',
'adapters' => [
'ssr' => [
'key' => 'ssr',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './build',
'startCommand' => 'bash helpers/remix/server.sh',
],
'static' => [
'key' => 'static',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './build/client',
'startCommand' => 'bash helpers/server.sh',
]
]
],
'lynx' => [
'key' => 'lynx',
'name' => 'Lynx',
'screenshotSleep' => 5000,
'buildRuntime' => 'node-22',
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
'adapters' => [
'static' => [
'key' => 'static',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './dist',
'startCommand' => 'bash helpers/server.sh',
'fallbackFile' => 'index.html'
]
]
],
'flutter' => [
'key' => 'flutter',
'name' => 'Flutter',
'screenshotSleep' => 5000,
'buildRuntime' => 'flutter-3.29',
'runtimes' => getVersions($templateRuntimes['FLUTTER']['versions'], 'flutter'),
'adapters' => [
'static' => [
'key' => 'static',
'buildCommand' => 'flutter build web',
'installCommand' => '',
'outputDirectory' => './build/web',
'startCommand' => 'bash helpers/server.sh',
],
],
],
'vite' => [
'key' => 'vite',
'name' => 'Vite',
'screenshotSleep' => 3000,
'buildRuntime' => 'node-22',
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
'adapters' => [
'static' => [
'key' => 'static',
'buildCommand' => 'npm run build',
'installCommand' => 'npm install',
'outputDirectory' => './dist',
'startCommand' => 'bash helpers/server.sh',
],
]
],
'other' => [
'key' => 'other',
'name' => 'Other',
'screenshotSleep' => 3000,
'buildRuntime' => 'node-22',
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
'adapters' => [
'static' => [
'key' => 'static',
'buildCommand' => '',
'installCommand' => '',
'outputDirectory' => './',
'startCommand' => 'bash helpers/server.sh',
],
]
],
];
@@ -1,8 +1,39 @@
<?php
use Utopia\Config\Config;
$templateRuntimes = Config::getParam('template-runtimes');
const TEMPLATE_RUNTIMES = [
'NODE' => [
'name' => 'node',
'versions' => ['22', '21.0', '20.0', '19.0', '18.0', '16.0', '14.5']
],
'PYTHON' => [
'name' => 'python',
'versions' => ['3.12', '3.11', '3.10', '3.9', '3.8']
],
'DART' => [
'name' => 'dart',
'versions' => ['3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16']
],
'GO' => [
'name' => 'go',
'versions' => ['1.23']
],
'PHP' => [
'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.1', '1.0']
],
'RUBY' => [
'name' => 'ruby',
'versions' => ['3.3', '3.2', '3.1', '3.0']
],
];
function getRuntimes($runtime, $commands, $entrypoint, $providerRootDirectory, $versionsDenyList = [])
{
@@ -23,7 +54,6 @@ return [
'icon' => 'icon-lightning-bolt',
'id' => 'starter',
'name' => 'Starter function',
'score' => 5,
'tagline' =>
'A simple function to get started. Edit this function to explore endless possibilities with Appwrite Functions.',
'permissions' => ['any'],
@@ -32,24 +62,24 @@ return [
'timeout' => 15,
'useCases' => ['starter'],
'runtimes' => [
...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/starter'),
...getRuntimes(TEMPLATE_RUNTIMES['NODE'], 'npm install', 'src/main.js', 'node/starter'),
...getRuntimes(
$templateRuntimes['PYTHON'],
TEMPLATE_RUNTIMES['PYTHON'],
'pip install -r requirements.txt',
'src/main.py',
'python/starter'
),
...getRuntimes($templateRuntimes['DART'], 'dart pub get', 'lib/main.dart', 'dart/starter'),
...getRuntimes($templateRuntimes['GO'], '', 'main.go', 'go/starter'),
...getRuntimes(TEMPLATE_RUNTIMES['DART'], 'dart pub get', 'lib/main.dart', 'dart/starter'),
...getRuntimes(TEMPLATE_RUNTIMES['GO'], '', 'main.go', 'go/starter'),
...getRuntimes(
$templateRuntimes['PHP'],
TEMPLATE_RUNTIMES['PHP'],
'composer install',
'src/index.php',
'php/starter'
),
...getRuntimes($templateRuntimes['DENO'], 'deno cache src/main.ts', 'src/main.ts', 'deno/starter'),
...getRuntimes($templateRuntimes['BUN'], 'bun install', 'src/main.ts', 'bun/starter'),
...getRuntimes($templateRuntimes['RUBY'], 'bundle install', 'lib/main.rb', 'ruby/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'),
],
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/starter">file</a>.',
'vcsProvider' => 'github',
@@ -63,7 +93,6 @@ return [
'icon' => 'icon-upstash',
'id' => 'query-upstash-vector',
'name' => 'Query Upstash Vector',
'score' => 4,
'tagline' => 'Vector database that stores text embeddings and context retrieval for LLMs',
'permissions' => ['any'],
'events' => [],
@@ -72,7 +101,7 @@ return [
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/query-upstash-vector'
@@ -108,7 +137,6 @@ return [
'icon' => 'icon-redis',
'id' => 'query-redis-labs',
'name' => 'Query Redis Labs',
'score' => 4,
'tagline' => 'Key-value database with advanced caching capabilities.',
'permissions' => ['any'],
'events' => [],
@@ -117,7 +145,7 @@ return [
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/query-redis-labs'
@@ -152,7 +180,6 @@ return [
'icon' => 'icon-neo4j',
'id' => 'query-neo4j-auradb',
'name' => 'Query Neo4j AuraDB',
'score' => 4,
'tagline' => 'Graph database with focus on relations between data.',
'permissions' => ['any'],
'events' => [],
@@ -161,7 +188,7 @@ return [
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/query-neo4j-auradb'
@@ -204,7 +231,6 @@ return [
'icon' => 'icon-mongodb',
'id' => 'query-mongo-atlas',
'name' => 'Query MongoDB Atlas',
'score' => 4,
'tagline' =>
'Realtime NoSQL document database with geospecial, graph, search, and vector suport.',
'permissions' => ['any'],
@@ -214,7 +240,7 @@ return [
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/query-mongo-atlas'
@@ -242,7 +268,6 @@ return [
'icon' => 'icon-neon',
'id' => 'query-neon-postgres',
'name' => 'Query Neon Postgres',
'score' => 4,
'tagline' =>
'Reliable SQL database with replication, point-in-time recovery, and pgvector support.',
'permissions' => ['any'],
@@ -252,7 +277,7 @@ return [
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/query-neon-postgres'
@@ -311,7 +336,6 @@ return [
'icon' => 'icon-open-ai',
'id' => 'prompt-chatgpt',
'name' => 'Prompt ChatGPT',
'score' => 7,
'tagline' => 'Ask questions and let OpenAI GPT-3.5-turbo answer.',
'permissions' => ['any'],
'events' => [],
@@ -320,25 +344,25 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/prompt-chatgpt'
),
...getRuntimes(
$templateRuntimes['PYTHON'],
TEMPLATE_RUNTIMES['PYTHON'],
'pip install -r requirements.txt',
'src/main.py',
'python/prompt_chatgpt'
),
...getRuntimes(
$templateRuntimes['PHP'],
TEMPLATE_RUNTIMES['PHP'],
'composer install',
'src/index.php',
'php/prompt-chatgpt'
),
...getRuntimes(
$templateRuntimes['DART'],
TEMPLATE_RUNTIMES['DART'],
'dart pub get',
'lib/main.dart',
'dart/prompt_chatgpt'
@@ -373,7 +397,6 @@ return [
'icon' => 'icon-discord',
'id' => 'discord-command-bot',
'name' => 'Discord Command Bot',
'score' => 6,
'tagline' => 'Simple command using Discord Interactions.',
'permissions' => ['any'],
'events' => [],
@@ -382,19 +405,19 @@ return [
'useCases' => ['messaging'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install && npm run setup',
'src/main.js',
'node/discord-command-bot'
),
...getRuntimes(
$templateRuntimes['PYTHON'],
TEMPLATE_RUNTIMES['PYTHON'],
'pip install -r requirements.txt && python src/setup.py',
'src/main.py',
'python/discord_command_bot'
),
...getRuntimes(
$templateRuntimes['GO'],
TEMPLATE_RUNTIMES['GO'],
'',
'main.go',
'go/discord-command-bot'
@@ -437,7 +460,6 @@ return [
'icon' => 'icon-perspective-api',
'id' => 'analyze-with-perspectiveapi',
'name' => 'Analyze with PerspectiveAPI',
'score' => 5,
'tagline' => 'Automate moderation by getting toxicity of messages.',
'permissions' => ['any'],
'events' => [],
@@ -446,7 +468,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/analyze-with-perspectiveapi'
@@ -473,7 +495,6 @@ return [
'icon' => 'icon-pangea',
'id' => 'censor-with-redact',
'name' => 'Censor with Redact',
'score' => 5,
'tagline' =>
'Censor sensitive information from a provided text string using Redact API by Pangea.',
'permissions' => ['any'],
@@ -483,19 +504,19 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/censor-with-redact'
),
...getRuntimes(
$templateRuntimes['PYTHON'],
TEMPLATE_RUNTIMES['PYTHON'],
'pip install -r requirements.txt',
'src/main.py',
'python/censor_with_redact'
),
...getRuntimes(
$templateRuntimes['DART'],
TEMPLATE_RUNTIMES['DART'],
'dart pub get',
'lib/main.dart',
'dart/censor_with_redact'
@@ -522,7 +543,6 @@ return [
'icon' => 'icon-document',
'id' => 'generate-pdf',
'name' => 'Generate PDF',
'score' => 7,
'tagline' => 'Document containing sample invoice in PDF format.',
'permissions' => ['any'],
'events' => [],
@@ -530,7 +550,7 @@ return [
'timeout' => 15,
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes($templateRuntimes['NODE'], 'npm install', 'src/main.js', 'node/generate-pdf')
...getRuntimes(TEMPLATE_RUNTIMES['NODE'], 'npm install', 'src/main.js', 'node/generate-pdf')
],
'instructions' => 'For documentation and instructions check out <a target="_blank" rel="noopener noreferrer" class="link" href="https://github.com/appwrite/templates/tree/main/node/generate-pdf">file</a>.',
'vcsProvider' => 'github',
@@ -544,7 +564,6 @@ return [
'icon' => 'icon-github',
'id' => 'github-issue-bot',
'name' => 'GitHub issue bot',
'score' => 4,
'tagline' =>
'Automate the process of responding to newly opened issues in a GitHub repository.',
'permissions' => ['any'],
@@ -554,7 +573,7 @@ return [
'useCases' => ['dev-tools'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/github-issue-bot'
@@ -589,7 +608,6 @@ return [
'icon' => 'icon-bookmark',
'id' => 'url-shortener',
'name' => 'URL shortener',
'score' => 3,
'tagline' => 'Generate URL with short ID and redirect to the original URL when visited.',
'permissions' => ['any'],
'events' => [],
@@ -598,7 +616,7 @@ return [
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/url-shortener'
@@ -641,7 +659,6 @@ return [
'icon' => 'icon-algolia',
'id' => 'sync-with-algolia',
'name' => 'Sync with Algolia',
'score' => 4,
'tagline' => 'Intuitive search bar for any data in Appwrite Databases.',
'permissions' => ['any'],
'events' => [],
@@ -650,19 +667,19 @@ return [
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/sync-with-algolia'
),
...getRuntimes(
$templateRuntimes['PYTHON'],
TEMPLATE_RUNTIMES['PYTHON'],
'pip install -r requirements.txt',
'src/main.py',
'python/sync_with_algolia'
),
...getRuntimes(
$templateRuntimes['PHP'],
TEMPLATE_RUNTIMES['PHP'],
'composer install',
'src/index.php',
'php/sync-with-algolia'
@@ -723,7 +740,6 @@ return [
'icon' => 'icon-meilisearch',
'id' => 'sync-with-meilisearch',
'name' => 'Sync with Meilisearch',
'score' => 4,
'tagline' => 'Intuitive search bar for any data in Appwrite Databases.',
'permissions' => ['any'],
'events' => [],
@@ -732,31 +748,31 @@ return [
'useCases' => ['databases'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/sync-with-meilisearch'
),
...getRuntimes(
$templateRuntimes['PYTHON'],
TEMPLATE_RUNTIMES['PYTHON'],
'pip install -r requirements.txt',
'src/main.py',
'python/sync-with-meilisearch'
),
...getRuntimes(
$templateRuntimes['PHP'],
TEMPLATE_RUNTIMES['PHP'],
'composer install',
'src/index.php',
'php/sync-with-meilisearch'
),
...getRuntimes(
$templateRuntimes['BUN'],
TEMPLATE_RUNTIMES['BUN'],
'bun install',
'src/main.ts',
'bun/sync-with-meilisearch'
),
...getRuntimes(
$templateRuntimes['RUBY'],
TEMPLATE_RUNTIMES['RUBY'],
'bundle install',
'lib/main.rb',
'ruby/sync-with-meilisearch'
@@ -817,7 +833,6 @@ return [
'icon' => 'icon-vonage',
'id' => 'whatsapp-with-vonage',
'name' => 'WhatsApp with Vonage',
'score' => 6,
'tagline' => 'Simple bot to answer WhatsApp messages.',
'permissions' => ['any'],
'events' => [],
@@ -826,37 +841,37 @@ return [
'useCases' => ['messaging'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/whatsapp-with-vonage'
),
...getRuntimes(
$templateRuntimes['PYTHON'],
TEMPLATE_RUNTIMES['PYTHON'],
'pip install -r requirements.txt',
'src/main.py',
'python/whatsapp_with_vonage'
),
...getRuntimes(
$templateRuntimes['DART'],
TEMPLATE_RUNTIMES['DART'],
'dart pub get',
'lib/main.dart',
'dart/whatsapp-with-vonage'
),
...getRuntimes(
$templateRuntimes['PHP'],
TEMPLATE_RUNTIMES['PHP'],
'composer install',
'src/index.php',
'php/whatsapp-with-vonage'
),
...getRuntimes(
$templateRuntimes['BUN'],
TEMPLATE_RUNTIMES['BUN'],
'bun install',
'src/main.ts',
'bun/whatsapp-with-vonage'
),
...getRuntimes(
$templateRuntimes['RUBY'],
TEMPLATE_RUNTIMES['RUBY'],
'bundle install',
'lib/main.rb',
'ruby/whatsapp-with-vonage'
@@ -904,7 +919,6 @@ return [
'icon' => 'icon-bell',
'id' => 'push-notification-with-fcm',
'name' => 'Push notification with FCM',
'score' => 4,
'tagline' => 'Send push notifications to your users using Firebase Cloud Messaging (FCM).',
'permissions' => ['any'],
'events' => [],
@@ -913,7 +927,7 @@ return [
'useCases' => ['messaging'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/push-notification-with-fcm'
@@ -961,7 +975,6 @@ return [
'icon' => 'icon-mail',
'id' => 'email-contact-form',
'name' => 'Email contact form',
'score' => 7,
'tagline' => 'Sends an email with the contents of a HTML form.',
'permissions' => ['any'],
'events' => [],
@@ -970,19 +983,19 @@ return [
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/email-contact-form'
),
...getRuntimes(
$templateRuntimes['PYTHON'],
TEMPLATE_RUNTIMES['PYTHON'],
'pip install -r requirements.txt',
'src/main.py',
'python/email_contact_form'
),
...getRuntimes(
$templateRuntimes['PHP'],
TEMPLATE_RUNTIMES['PHP'],
'composer install',
'src/index.php',
'php/email-contact-form'
@@ -1045,7 +1058,6 @@ return [
'icon' => 'icon-stripe',
'id' => 'subscriptions-with-stripe',
'name' => 'Subscriptions with Stripe',
'score' => 6,
'tagline' => 'Receive recurring card payments and grant subscribers extra permissions.',
'permissions' => ['any'],
'events' => [],
@@ -1054,7 +1066,7 @@ return [
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/subscriptions-with-stripe'
@@ -1087,7 +1099,6 @@ return [
'icon' => 'icon-stripe',
'id' => 'payments-with-stripe',
'name' => 'Payments with Stripe',
'score' => 8,
'tagline' => 'Receive card payments and store paid orders.',
'permissions' => ['any'],
'events' => [],
@@ -1096,7 +1107,7 @@ return [
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/payments-with-stripe'
@@ -1145,7 +1156,6 @@ return [
'icon' => 'icon-chat',
'id' => 'text-generation-with-huggingface',
'name' => 'Text generation',
'score' => 5,
'tagline' => 'Generate text using the Hugging Face inference API.',
'permissions' => ['any'],
'events' => [],
@@ -1154,7 +1164,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/text-generation-with-huggingface'
@@ -1180,7 +1190,6 @@ return [
'icon' => 'icon-translate',
'id' => 'language-translation-with-huggingface',
'name' => 'Language translation',
'score' => 5,
'tagline' => 'Translate text using the Hugging Face inference API.',
'permissions' => ['any'],
'events' => [],
@@ -1189,7 +1198,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/language-translation-with-huggingface'
@@ -1215,7 +1224,6 @@ return [
'icon' => 'icon-eye',
'id' => 'image-classification-with-huggingface',
'name' => 'Image classification',
'score' => 5,
'tagline' => 'Classify images using the Hugging Face inference API.',
'permissions' => ['any'],
'events' => ['buckets.*.files.*.create'],
@@ -1224,7 +1232,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install && npm run setup',
'src/main.js',
'node/image-classification-with-huggingface'
@@ -1274,7 +1282,6 @@ return [
'icon' => 'icon-eye',
'id' => 'object-detection-with-huggingface',
'name' => 'Object detection',
'score' => 5,
'tagline' => 'Detect objects in images using the Hugging Face inference API.',
'permissions' => ['any'],
'events' => ['buckets.*.files.*.create'],
@@ -1283,7 +1290,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install && npm run setup',
'src/main.js',
'node/object-detection-with-huggingface'
@@ -1333,7 +1340,6 @@ return [
'icon' => 'icon-text',
'id' => 'speech-recognition-with-huggingface',
'name' => 'Speech recognition',
'score' => 5,
'tagline' => 'Transcribe audio to text using the Hugging Face inference API.',
'permissions' => ['any'],
'events' => ['buckets.*.files.*.create'],
@@ -1342,7 +1348,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install && npm run setup',
'src/main.js',
'node/speech-recognition-with-huggingface'
@@ -1392,7 +1398,6 @@ return [
'icon' => 'icon-chat',
'id' => 'text-to-speech-with-huggingface',
'name' => 'Text to speech',
'score' => 5,
'tagline' => 'Convert text to speech using the Hugging Face inference API.',
'permissions' => ['any'],
'events' => ['databases.*.collections.*.documents.*.create'],
@@ -1401,7 +1406,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install && npm run setup',
'src/main.js',
'node/text-to-speech-with-huggingface'
@@ -1451,7 +1456,6 @@ return [
'icon' => 'icon-chip',
'id' => 'generate-with-replicate',
'name' => 'Generate with Replicate',
'score' => 5,
'tagline' => "Generate text, audio and images using Replicate's API.",
'permissions' => ['any'],
'events' => [],
@@ -1460,7 +1464,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/generate-with-replicate'
@@ -1487,7 +1491,6 @@ return [
'icon' => 'icon-chip',
'id' => 'generate-with-together-ai',
'name' => 'Generate with Together AI',
'score' => 5,
'tagline' => "Generate text and images using Together AI's API.",
'permissions' => ['any'],
'events' => [],
@@ -1496,7 +1499,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/generate-with-together-ai'
@@ -1530,7 +1533,6 @@ return [
'icon' => 'icon-chip',
'id' => 'chat-with-perplexity-ai',
'name' => 'Chat with Perplexity AI',
'score' => 5,
'tagline' => 'Create a chatbot using the Perplexity AI API.',
'permissions' => ['any'],
'events' => [],
@@ -1539,7 +1541,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/chat-with-perplexity-ai'
@@ -1572,7 +1574,6 @@ return [
'icon' => 'icon-chip',
'id' => 'generate-with-replicate',
'name' => 'Generate with Replicate',
'score' => 5,
'tagline' => "Generate text, audio and images using Replicate's API.",
'permissions' => ['any'],
'events' => [],
@@ -1581,7 +1582,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/generate-with-replicate'
@@ -1608,7 +1609,6 @@ return [
'icon' => 'icon-document-search',
'id' => 'sync-with-pinecone',
'name' => 'Sync with Pinecone',
'score' => 4,
'tagline' => "Sync your Appwrite database with Pinecone's vector database.",
'permissions' => ['any'],
'events' => [],
@@ -1617,7 +1617,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/sync-with-pinecone'
@@ -1672,7 +1672,6 @@ return [
'icon' => 'icon-chip',
'id' => 'rag-with-langchain',
'name' => 'RAG with LangChain',
'score' => 6,
'tagline' => 'Generate text using a LangChain RAG model',
'permissions' => ['any'],
'events' => [],
@@ -1681,7 +1680,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/rag-with-langchain'
@@ -1736,7 +1735,6 @@ return [
'icon' => 'icon-chat',
'id' => 'speak-with-elevenlabs',
'name' => 'Speak with ElevenLabs',
'score' => 5,
'tagline' => 'Convert text to speech using the ElevenLabs API.',
'permissions' => ['any'],
'cron' => '',
@@ -1745,7 +1743,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/speak-with-elevenlabs'
@@ -1792,7 +1790,6 @@ return [
'icon' => 'icon-chip',
'id' => 'speak-with-lmnt',
'name' => 'Speak with LMNT',
'score' => 5,
'tagline' => 'Convert text to speech using the LMNT API.',
'permissions' => ['any'],
'cron' => '',
@@ -1801,7 +1798,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/speak-with-lmnt'
@@ -1834,7 +1831,6 @@ return [
'icon' => 'icon-chip',
'id' => 'chat-with-anyscale',
'name' => 'Chat with AnyScale',
'score' => 5,
'tagline' => 'Create a chatbot using the AnyScale API.',
'permissions' => ['any'],
'cron' => '',
@@ -1843,7 +1839,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/chat-with-anyscale'
@@ -1876,7 +1872,6 @@ return [
'icon' => 'icon-music-note',
'id' => 'music-generation-with-huggingface',
'name' => 'Music generation',
'score' => 4,
'tagline' => 'Generate music from a text prompt using the Hugging Face inference API.',
'permissions' => ['any'],
'events' => [],
@@ -1885,7 +1880,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install && npm run setup',
'src/main.js',
'node/music-generation-with-huggingface'
@@ -1919,7 +1914,6 @@ return [
'icon' => 'icon-chip',
'id' => 'generate-with-fal-ai',
'name' => 'Generate with fal.ai',
'score' => 5,
'tagline' => "Generate images using fal.ai's API.",
'permissions' => ['any'],
'events' => [],
@@ -1928,7 +1922,7 @@ return [
'useCases' => ['ai'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/generate-with-fal-ai'
@@ -1955,7 +1949,6 @@ return [
'icon' => 'icon-currency-dollar',
'id' => 'subscriptions-with-lemon-squeezy',
'name' => 'Subscriptions with Lemon Squeezy',
'score' => 6,
'tagline' => 'Receive recurring card payments and grant subscribers extra permissions.',
'permissions' => ['any'],
'events' => [],
@@ -1964,7 +1957,7 @@ return [
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/subscriptions-with-lemon-squeezy'
@@ -2011,7 +2004,6 @@ return [
'icon' => 'icon-currency-dollar',
'id' => 'payments-with-lemon-squeezy',
'name' => 'Payments with Lemon Squeezy',
'score' => 6,
'tagline' => 'Receive card payments and store paid orders.',
'permissions' => ['any'],
'events' => [],
@@ -2020,7 +2012,7 @@ return [
'useCases' => ['utilities'],
'runtimes' => [
...getRuntimes(
$templateRuntimes['NODE'],
TEMPLATE_RUNTIMES['NODE'],
'npm install',
'src/main.js',
'node/payments-with-lemon-squeezy'
+10
View File
@@ -142,6 +142,16 @@ return [
'beta' => false,
'mock' => false,
],
'figma' => [
'name' => 'Figma',
'developers' => 'https://www.figma.com/developers/api#oauth2',
'icon' => 'icon-figma',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'github' => [
'name' => 'GitHub',
'developers' => 'https://developer.github.com/',
+16 -16
View File
@@ -11,7 +11,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '17.0.1',
'version' => '17.0.2',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -59,7 +59,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '15.0.0',
'version' => '15.0.1',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -77,7 +77,7 @@ return [
[
'key' => 'apple',
'name' => 'Apple',
'version' => '9.0.0',
'version' => '9.0.1',
'url' => 'https://github.com/appwrite/sdk-for-apple',
'package' => 'https://github.com/appwrite/sdk-for-apple',
'enabled' => true,
@@ -112,7 +112,7 @@ return [
[
'key' => 'android',
'name' => 'Android',
'version' => '6.1.0',
'version' => '7.0.1',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
'enabled' => true,
@@ -134,7 +134,7 @@ return [
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.7.1',
'version' => '0.7.3',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
@@ -217,7 +217,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '6.2.2',
'version' => '6.2.3',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -245,7 +245,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '15.0.1',
'version' => '16.0.0',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -263,7 +263,7 @@ return [
[
'key' => 'deno',
'name' => 'Deno',
'version' => '12.2.0',
'version' => '14.0.0',
'url' => 'https://github.com/appwrite/sdk-for-deno',
'package' => 'https://deno.land/x/appwrite',
'enabled' => true,
@@ -281,7 +281,7 @@ return [
[
'key' => 'php',
'name' => 'PHP',
'version' => '12.2.0',
'version' => '14.0.0',
'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true,
@@ -299,7 +299,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '9.0.2',
'version' => '10.0.0',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -317,7 +317,7 @@ return [
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '12.2.0',
'version' => '15.0.0',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@@ -335,7 +335,7 @@ return [
[
'key' => 'go',
'name' => 'Go',
'version' => '0.3.0',
'version' => '0.5.0',
'url' => 'https://github.com/appwrite/sdk-for-go',
'package' => 'https://github.com/appwrite/sdk-for-go',
'enabled' => true,
@@ -353,7 +353,7 @@ return [
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.11.0',
'version' => '0.12.0',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@@ -371,7 +371,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '14.0.0',
'version' => '15.0.0',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@@ -389,7 +389,7 @@ return [
[
'key' => 'kotlin',
'name' => 'Kotlin',
'version' => '6.2.0',
'version' => '8.0.0',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,
@@ -411,7 +411,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '8.0.0',
'version' => '9.0.0',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,
-7
View File
@@ -26,7 +26,6 @@ $member = [
'subscribers.write',
'subscribers.read',
'assistant.read',
'rules.read'
];
$admins = [
@@ -59,10 +58,6 @@ $admins = [
'health.read',
'functions.read',
'functions.write',
'sites.read',
'sites.write',
'log.read',
'log.write',
'execution.read',
'execution.write',
'rules.read',
@@ -81,8 +76,6 @@ $admins = [
'topics.read',
'subscribers.write',
'subscribers.read',
'tokens.read',
'tokens.write',
];
return [
+1 -1
View File
@@ -6,4 +6,4 @@
use Appwrite\Runtimes\Runtimes;
return (new Runtimes('v5'))->getAll();
return (new Runtimes('v4'))->getAll();
@@ -1,6 +1,6 @@
<?php
use Appwrite\Platform\Modules\Compute\Specification;
use Appwrite\Functions\Specification;
return [
Specification::S_05VCPU_512MB => [
-18
View File
@@ -64,18 +64,6 @@ return [ // List of publicly visible scopes
'functions.write' => [
'description' => 'Access to create, update, and delete your project\'s functions and code deployments',
],
'sites.read' => [
'description' => 'Access to read your project\'s sites and deployments',
],
'sites.write' => [
'description' => 'Access to create, update, and delete your project\'s sites and deployments',
],
'log.read' => [
'description' => 'Access to read your site\'s logs',
],
'log.write' => [
'description' => 'Access to update, and delete your site\'s logs',
],
'execution.read' => [
'description' => 'Access to read your project\'s execution logs',
],
@@ -142,10 +130,4 @@ return [ // List of publicly visible scopes
'assistant.read' => [
'description' => 'Access to read the Assistant service',
],
'tokens.read' => [
'description' => 'Access to read your project\'s tokens',
],
'tokens.write' => [
'description' => 'Access to create, update, and delete your project\'s tokens',
],
];
+1 -14
View File
@@ -173,25 +173,12 @@ return [
'optional' => false,
'icon' => '',
],
'sites' => [
'key' => 'sites',
'name' => 'Sites',
'subtitle' => 'The Sites Service allows you view, create and manage your web applications.',
'description' => '/docs/services/sites.md',
'controller' => '', // Uses modules
'sdk' => true,
'docs' => true,
'docsUrl' => 'https://appwrite.io/docs/sites',
'tests' => false,
'optional' => true,
'icon' => '/images/services/sites.png',
],
'functions' => [
'key' => 'functions',
'name' => 'Functions',
'subtitle' => 'The Functions Service allows you view, create and manage your Cloud Functions.',
'description' => '/docs/services/functions.md',
'controller' => '', // Uses modules
'controller' => 'api/functions.php',
'sdk' => true,
'docs' => true,
'docsUrl' => 'https://appwrite.io/docs/functions',
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+137 -37
View File
@@ -1,7 +1,7 @@
{
"openapi": "3.0.0",
"info": {
"version": "1.6.1",
"version": "1.6.2",
"title": "Appwrite",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
@@ -18,6 +18,9 @@
"servers": [
{
"url": "https:\/\/cloud.appwrite.io\/v1"
},
{
"url": "https:\/\/<REGION>.cloud.appwrite.io\/v1"
}
],
"paths": {
@@ -43,6 +46,7 @@
},
"x-appwrite": {
"method": "get",
"group": "account",
"weight": 9,
"cookies": false,
"type": "",
@@ -91,6 +95,7 @@
},
"x-appwrite": {
"method": "create",
"group": "account",
"weight": 8,
"cookies": false,
"type": "",
@@ -175,6 +180,7 @@
},
"x-appwrite": {
"method": "updateEmail",
"group": "account",
"weight": 34,
"cookies": false,
"type": "",
@@ -250,6 +256,7 @@
},
"x-appwrite": {
"method": "listIdentities",
"group": "identities",
"weight": 57,
"cookies": false,
"type": "",
@@ -308,6 +315,7 @@
},
"x-appwrite": {
"method": "deleteIdentity",
"group": "identities",
"weight": 58,
"cookies": false,
"type": "",
@@ -370,6 +378,7 @@
},
"x-appwrite": {
"method": "createJWT",
"group": "tokens",
"weight": 29,
"cookies": false,
"type": "",
@@ -418,6 +427,7 @@
},
"x-appwrite": {
"method": "listLogs",
"group": "logs",
"weight": 31,
"cookies": false,
"type": "",
@@ -483,6 +493,7 @@
},
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 44,
"cookies": false,
"type": "",
@@ -552,6 +563,7 @@
},
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 46,
"cookies": false,
"type": "",
@@ -597,7 +609,7 @@
]
},
"put": {
"summary": "Verify authenticator",
"summary": "Update authenticator (confirmation)",
"operationId": "accountUpdateMfaAuthenticator",
"tags": [
"account"
@@ -617,6 +629,7 @@
},
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 47,
"cookies": false,
"type": "",
@@ -694,6 +707,7 @@
},
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 51,
"cookies": false,
"type": "",
@@ -761,6 +775,7 @@
},
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 52,
"cookies": false,
"type": "",
@@ -814,7 +829,7 @@
}
},
"put": {
"summary": "Create MFA challenge (confirmation)",
"summary": "Update MFA challenge (confirmation)",
"operationId": "accountUpdateMfaChallenge",
"tags": [
"account"
@@ -834,6 +849,7 @@
},
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 53,
"cookies": false,
"type": "",
@@ -909,6 +925,7 @@
},
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 45,
"cookies": false,
"type": "",
@@ -939,7 +956,7 @@
},
"\/account\/mfa\/recovery-codes": {
"get": {
"summary": "Get MFA recovery codes",
"summary": "List MFA recovery codes",
"operationId": "accountGetMfaRecoveryCodes",
"tags": [
"account"
@@ -959,6 +976,7 @@
},
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 50,
"cookies": false,
"type": "",
@@ -1007,6 +1025,7 @@
},
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 48,
"cookies": false,
"type": "",
@@ -1035,7 +1054,7 @@
]
},
"patch": {
"summary": "Regenerate MFA recovery codes",
"summary": "Update MFA recovery codes (regenerate)",
"operationId": "accountUpdateMfaRecoveryCodes",
"tags": [
"account"
@@ -1055,6 +1074,7 @@
},
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 49,
"cookies": false,
"type": "",
@@ -1105,6 +1125,7 @@
},
"x-appwrite": {
"method": "updateName",
"group": "account",
"weight": 32,
"cookies": false,
"type": "",
@@ -1174,6 +1195,7 @@
},
"x-appwrite": {
"method": "updatePassword",
"group": "account",
"weight": 33,
"cookies": false,
"type": "",
@@ -1248,6 +1270,7 @@
},
"x-appwrite": {
"method": "updatePhone",
"group": "account",
"weight": 35,
"cookies": false,
"type": "",
@@ -1323,6 +1346,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"group": "account",
"weight": 30,
"cookies": false,
"type": "",
@@ -1371,6 +1395,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"group": "account",
"weight": 36,
"cookies": false,
"type": "",
@@ -1440,6 +1465,7 @@
},
"x-appwrite": {
"method": "createRecovery",
"group": "recovery",
"weight": 38,
"cookies": false,
"type": "",
@@ -1496,7 +1522,7 @@
}
},
"put": {
"summary": "Create password recovery (confirmation)",
"summary": "Update password recovery (confirmation)",
"operationId": "accountUpdateRecovery",
"tags": [
"account"
@@ -1516,6 +1542,7 @@
},
"x-appwrite": {
"method": "updateRecovery",
"group": "recovery",
"weight": 39,
"cookies": false,
"type": "",
@@ -1597,6 +1624,7 @@
},
"x-appwrite": {
"method": "listSessions",
"group": "sessions",
"weight": 11,
"cookies": false,
"type": "",
@@ -1638,6 +1666,7 @@
},
"x-appwrite": {
"method": "deleteSessions",
"group": "sessions",
"weight": 12,
"cookies": false,
"type": "",
@@ -1688,6 +1717,7 @@
},
"x-appwrite": {
"method": "createAnonymousSession",
"group": "sessions",
"weight": 17,
"cookies": false,
"type": "",
@@ -1736,6 +1766,7 @@
},
"x-appwrite": {
"method": "createEmailPasswordSession",
"group": "sessions",
"weight": 16,
"cookies": false,
"type": "",
@@ -1809,6 +1840,7 @@
},
"x-appwrite": {
"method": "updateMagicURLSession",
"group": "sessions",
"weight": 26,
"cookies": false,
"type": "",
@@ -1875,6 +1907,7 @@
},
"x-appwrite": {
"method": "createOAuth2Session",
"group": "sessions",
"weight": 19,
"cookies": false,
"type": "webAuth",
@@ -1902,7 +1935,7 @@
"parameters": [
{
"name": "provider",
"description": "OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoho, zoom.",
"description": "OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoho, zoom.",
"required": true,
"schema": {
"type": "string",
@@ -1922,6 +1955,7 @@
"dropbox",
"etsy",
"facebook",
"figma",
"github",
"gitlab",
"google",
@@ -2015,6 +2049,7 @@
},
"x-appwrite": {
"method": "updatePhoneSession",
"group": "sessions",
"weight": 27,
"cookies": false,
"type": "",
@@ -2088,6 +2123,7 @@
},
"x-appwrite": {
"method": "createSession",
"group": "sessions",
"weight": 18,
"cookies": false,
"type": "",
@@ -2161,6 +2197,7 @@
},
"x-appwrite": {
"method": "getSession",
"group": "sessions",
"weight": 13,
"cookies": false,
"type": "",
@@ -2221,6 +2258,7 @@
},
"x-appwrite": {
"method": "updateSession",
"group": "sessions",
"weight": 15,
"cookies": false,
"type": "",
@@ -2274,6 +2312,7 @@
},
"x-appwrite": {
"method": "deleteSession",
"group": "sessions",
"weight": 14,
"cookies": false,
"type": "",
@@ -2336,6 +2375,7 @@
},
"x-appwrite": {
"method": "updateStatus",
"group": "account",
"weight": 37,
"cookies": false,
"type": "",
@@ -2386,6 +2426,7 @@
},
"x-appwrite": {
"method": "createPushTarget",
"group": "pushTargets",
"weight": 54,
"cookies": false,
"type": "",
@@ -2464,6 +2505,7 @@
},
"x-appwrite": {
"method": "updatePushTarget",
"group": "pushTargets",
"weight": 55,
"cookies": false,
"type": "",
@@ -2534,6 +2576,7 @@
},
"x-appwrite": {
"method": "deletePushTarget",
"group": "pushTargets",
"weight": 56,
"cookies": false,
"type": "",
@@ -2594,6 +2637,7 @@
},
"x-appwrite": {
"method": "createEmailToken",
"group": "tokens",
"weight": 25,
"cookies": false,
"type": "",
@@ -2672,6 +2716,7 @@
},
"x-appwrite": {
"method": "createMagicURLToken",
"group": "tokens",
"weight": 24,
"cookies": false,
"type": "",
@@ -2751,6 +2796,7 @@
},
"x-appwrite": {
"method": "createOAuth2Token",
"group": "tokens",
"weight": 23,
"cookies": false,
"type": "webAuth",
@@ -2778,7 +2824,7 @@
"parameters": [
{
"name": "provider",
"description": "OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoho, zoom.",
"description": "OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoho, zoom.",
"required": true,
"schema": {
"type": "string",
@@ -2798,6 +2844,7 @@
"dropbox",
"etsy",
"facebook",
"figma",
"github",
"gitlab",
"google",
@@ -2891,6 +2938,7 @@
},
"x-appwrite": {
"method": "createPhoneToken",
"group": "tokens",
"weight": 28,
"cookies": false,
"type": "",
@@ -2967,6 +3015,7 @@
},
"x-appwrite": {
"method": "createVerification",
"group": "verification",
"weight": 40,
"cookies": false,
"type": "",
@@ -3014,7 +3063,7 @@
}
},
"put": {
"summary": "Create email verification (confirmation)",
"summary": "Update email verification (confirmation)",
"operationId": "accountUpdateVerification",
"tags": [
"account"
@@ -3034,6 +3083,7 @@
},
"x-appwrite": {
"method": "updateVerification",
"group": "verification",
"weight": 41,
"cookies": false,
"type": "",
@@ -3109,6 +3159,7 @@
},
"x-appwrite": {
"method": "createPhoneVerification",
"group": "verification",
"weight": 42,
"cookies": false,
"type": "",
@@ -3160,6 +3211,7 @@
},
"x-appwrite": {
"method": "updatePhoneVerification",
"group": "verification",
"weight": 43,
"cookies": false,
"type": "",
@@ -3228,6 +3280,7 @@
},
"x-appwrite": {
"method": "getBrowser",
"group": null,
"weight": 60,
"cookies": false,
"type": "location",
@@ -3353,6 +3406,7 @@
},
"x-appwrite": {
"method": "getCreditCard",
"group": null,
"weight": 59,
"cookies": false,
"type": "location",
@@ -3484,6 +3538,7 @@
},
"x-appwrite": {
"method": "getFavicon",
"group": null,
"weight": 63,
"cookies": false,
"type": "location",
@@ -3541,6 +3596,7 @@
},
"x-appwrite": {
"method": "getFlag",
"group": null,
"weight": 61,
"cookies": false,
"type": "location",
@@ -4028,6 +4084,7 @@
},
"x-appwrite": {
"method": "getImage",
"group": null,
"weight": 62,
"cookies": false,
"type": "location",
@@ -4109,6 +4166,7 @@
},
"x-appwrite": {
"method": "getInitials",
"group": null,
"weight": 65,
"cookies": false,
"type": "location",
@@ -4200,6 +4258,7 @@
},
"x-appwrite": {
"method": "getQR",
"group": null,
"weight": 64,
"cookies": false,
"type": "location",
@@ -4298,6 +4357,7 @@
},
"x-appwrite": {
"method": "listDocuments",
"group": "documents",
"weight": 109,
"cookies": false,
"type": "",
@@ -4382,6 +4442,7 @@
},
"x-appwrite": {
"method": "createDocument",
"group": "documents",
"weight": 108,
"cookies": false,
"type": "",
@@ -4488,6 +4549,7 @@
},
"x-appwrite": {
"method": "getDocument",
"group": "documents",
"weight": 110,
"cookies": false,
"type": "",
@@ -4582,6 +4644,7 @@
},
"x-appwrite": {
"method": "updateDocument",
"group": "documents",
"weight": 112,
"cookies": false,
"type": "",
@@ -4680,6 +4743,7 @@
},
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 113,
"cookies": false,
"type": "",
@@ -4763,7 +4827,8 @@
},
"x-appwrite": {
"method": "listExecutions",
"weight": 306,
"group": "executions",
"weight": 305,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4848,7 +4913,8 @@
},
"x-appwrite": {
"method": "createExecution",
"weight": 305,
"group": "executions",
"weight": 304,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4962,7 +5028,8 @@
},
"x-appwrite": {
"method": "getExecution",
"weight": 307,
"group": "executions",
"weight": 306,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5035,7 +5102,8 @@
},
"x-appwrite": {
"method": "query",
"weight": 331,
"group": "graphql",
"weight": 330,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5086,7 +5154,8 @@
},
"x-appwrite": {
"method": "mutation",
"weight": 330,
"group": "graphql",
"weight": 329,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5137,6 +5206,7 @@
},
"x-appwrite": {
"method": "get",
"group": null,
"weight": 117,
"cookies": false,
"type": "",
@@ -5188,6 +5258,7 @@
},
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 118,
"cookies": false,
"type": "",
@@ -5239,6 +5310,7 @@
},
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 122,
"cookies": false,
"type": "",
@@ -5290,6 +5362,7 @@
},
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 119,
"cookies": false,
"type": "",
@@ -5341,6 +5414,7 @@
},
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 120,
"cookies": false,
"type": "",
@@ -5392,6 +5466,7 @@
},
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 121,
"cookies": false,
"type": "",
@@ -5443,6 +5518,7 @@
},
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 123,
"cookies": false,
"type": "",
@@ -5494,6 +5570,7 @@
},
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 124,
"cookies": false,
"type": "",
@@ -5545,7 +5622,8 @@
},
"x-appwrite": {
"method": "createSubscriber",
"weight": 376,
"group": "subscribers",
"weight": 375,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5627,7 +5705,8 @@
},
"x-appwrite": {
"method": "deleteSubscriber",
"weight": 380,
"group": "subscribers",
"weight": 379,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5701,7 +5780,8 @@
},
"x-appwrite": {
"method": "listFiles",
"weight": 208,
"group": "files",
"weight": 207,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5786,7 +5866,8 @@
},
"x-appwrite": {
"method": "createFile",
"weight": 207,
"group": "files",
"weight": 206,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -5883,7 +5964,8 @@
},
"x-appwrite": {
"method": "getFile",
"weight": 209,
"group": "files",
"weight": 208,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5954,7 +6036,8 @@
},
"x-appwrite": {
"method": "updateFile",
"weight": 214,
"group": "files",
"weight": 213,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6042,7 +6125,8 @@
},
"x-appwrite": {
"method": "deleteFile",
"weight": 215,
"group": "files",
"weight": 214,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6108,7 +6192,8 @@
},
"x-appwrite": {
"method": "getFileDownload",
"weight": 211,
"group": "files",
"weight": 210,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6174,7 +6259,8 @@
},
"x-appwrite": {
"method": "getFilePreview",
"weight": 210,
"group": "files",
"weight": 209,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6390,7 +6476,8 @@
},
"x-appwrite": {
"method": "getFileView",
"weight": 212,
"group": "files",
"weight": 211,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6463,7 +6550,8 @@
},
"x-appwrite": {
"method": "list",
"weight": 219,
"group": "teams",
"weight": 218,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6538,7 +6626,8 @@
},
"x-appwrite": {
"method": "create",
"weight": 218,
"group": "teams",
"weight": 217,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6622,7 +6711,8 @@
},
"x-appwrite": {
"method": "get",
"weight": 220,
"group": "teams",
"weight": 219,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6683,7 +6773,8 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 222,
"group": "teams",
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6756,7 +6847,8 @@
},
"x-appwrite": {
"method": "delete",
"weight": 224,
"group": "teams",
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6819,7 +6911,8 @@
},
"x-appwrite": {
"method": "listMemberships",
"weight": 226,
"group": "memberships",
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6904,7 +6997,8 @@
},
"x-appwrite": {
"method": "createMembership",
"weight": 225,
"group": "memberships",
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7014,7 +7108,8 @@
},
"x-appwrite": {
"method": "getMembership",
"weight": 227,
"group": "memberships",
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7085,7 +7180,8 @@
},
"x-appwrite": {
"method": "updateMembership",
"weight": 228,
"group": "memberships",
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7171,7 +7267,8 @@
},
"x-appwrite": {
"method": "deleteMembership",
"weight": 230,
"group": "memberships",
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7244,7 +7341,8 @@
},
"x-appwrite": {
"method": "updateMembershipStatus",
"weight": 229,
"group": "memberships",
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7341,7 +7439,8 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 221,
"group": "teams",
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7401,7 +7500,8 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 223,
"group": "teams",
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-43
View File
@@ -1,43 +0,0 @@
<?php
// TODO: Remove, replace with runtimes.php directly
// Used in function templates and site frameworks
return [
'NODE' => [
'name' => 'node',
'versions' => ['22', '21.0', '20.0', '19.0', '18.0', '16.0', '14.5']
],
'PYTHON' => [
'name' => 'python',
'versions' => ['3.12', '3.11', '3.10', '3.9', '3.8']
],
'DART' => [
'name' => 'dart',
'versions' => ['3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16']
],
'GO' => [
'name' => 'go',
'versions' => ['1.23']
],
'PHP' => [
'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.1', '1.0']
],
'RUBY' => [
'name' => 'ruby',
'versions' => ['3.3', '3.2', '3.1', '3.0']
],
'FLUTTER' => [
'name' => 'flutter',
'versions' => ['3.24']
],
];
File diff suppressed because it is too large Load Diff
+18 -133
View File
@@ -45,16 +45,7 @@ return [
],
[
'name' => '_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS',
'description' => 'Deprecated since 1.7.0. Allows you to force HTTPS connection to function domains. This feature redirects any HTTP call to HTTPS and adds the \'Strict-Transport-Security\' header to all HTTP responses. By default, set to \'enabled\'. To disable, set to \'disabled\'. This feature will work only when your ports are set to default 80 and 443.',
'introduction' => '',
'default' => 'disabled',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_OPTIONS_ROUTER_FORCE_HTTPS',
'description' => 'Allows you to force HTTPS connection to function and site domains. This feature redirects any HTTP call to HTTPS and adds the \'Strict-Transport-Security\' header to all HTTP responses. By default, set to \'enabled\'. To disable, set to \'disabled\'. This feature will work only when your ports are set to default 80 and 443.',
'description' => 'Allows you to force HTTPS connection to function domains. This feature redirects any HTTP call to HTTPS and adds the \'Strict-Transport-Security\' header to all HTTP responses. By default, set to \'enabled\'. To disable, set to \'disabled\'. This feature will work only when your ports are set to default 80 and 443.',
'introduction' => '',
'default' => 'disabled',
'required' => false,
@@ -88,6 +79,15 @@ return [
'question' => 'Enter your Appwrite hostname',
'filter' => ''
],
[
'name' => '_APP_CUSTOM_DOMAIN_DENY_LIST',
'description' => 'List of reserved or prohibited domains when configuring custom domains.',
'introduction' => '',
'default' => 'example.com,test.com,app.example.com',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DOMAIN_FUNCTIONS',
'description' => 'A domain to use for function preview URLs. Setting to empty turns off function preview URLs.',
@@ -99,40 +99,13 @@ return [
],
[
'name' => '_APP_DOMAIN_TARGET',
'description' => 'Deprecated since 1.7.0. A DNS A record hostname to serve as a CNAME target for your Appwrite custom domains. You can use the same value as used for the Appwrite \'_APP_DOMAIN\' variable. The default value is \'localhost\'.',
'description' => 'A DNS A record hostname to serve as a CNAME target for your Appwrite custom domains. You can use the same value as used for the Appwrite \'_APP_DOMAIN\' variable. The default value is \'localhost\'.',
'introduction' => '',
'default' => 'localhost',
'required' => true,
'question' => 'Enter a DNS A record hostname to serve as a CNAME for your custom domains.' . PHP_EOL . 'You can use the same value as used for the Appwrite hostname.',
'filter' => 'domainTarget'
],
[
'name' => '_APP_DOMAIN_TARGET_CNAME',
'description' => 'A domain that can be used as DNS CNAME record to point to instance of Appwrite server.',
'introduction' => '',
'default' => 'localhost',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DOMAIN_TARGET_AAAA',
'description' => 'An IPv6 that can be used as DNS AAAA record to point to instance of Appwrite server.',
'introduction' => '',
'default' => '::1',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DOMAIN_TARGET_A',
'description' => 'An IPV4 that can be used as DNS A record to point to instance of Appwrite server.',
'introduction' => '',
'default' => '127.0.0.1',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_CONSOLE_WHITELIST_ROOT',
'description' => 'This option allows you to disable the creation of new users on the Appwrite console. When enabled only 1 user will be able to use the registration form. New users can be added by inviting them to your project. By default this option is enabled.',
@@ -782,22 +755,13 @@ return [
'variables' => [
[
'name' => '_APP_FUNCTIONS_SIZE_LIMIT',
'description' => 'Deprecated since 1.7.0. The maximum size of a function in bytes. The default value is 30MB.',
'description' => 'The maximum size of a function in bytes. The default value is 30MB.',
'introduction' => '0.13.0',
'default' => '30000000',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_COMPUTE_SIZE_LIMIT',
'description' => 'The maximum size of a function and site deployments in bytes. The default value is 30MB.',
'introduction' => '1.7.0',
'default' => '30000000',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_FUNCTIONS_BUILD_SIZE_LIMIT',
'description' => 'The maximum size of a built deployment in bytes. The default value is 2,000,000,000 (2GB), and the maximum value is 4,294,967,295 (4.2GB).',
@@ -818,22 +782,13 @@ return [
],
[
'name' => '_APP_FUNCTIONS_BUILD_TIMEOUT',
'description' => 'Deprecated since 1.7.0. The maximum number of seconds allowed as a timeout value when building a new function. The default value is 900 seconds.',
'description' => 'The maximum number of seconds allowed as a timeout value when building a new function. The default value is 900 seconds.',
'introduction' => '0.13.0',
'default' => '900',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_COMPUTE_BUILD_TIMEOUT',
'description' => 'The maximum number of seconds allowed as a timeout value when building a new function or site. The default value is 900 seconds.',
'introduction' => '1.7.0',
'default' => '900',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_FUNCTIONS_CONTAINERS',
'description' => 'Deprecated since 1.2.0. Runtimes now timeout by inactivity using \'_APP_FUNCTIONS_INACTIVE_THRESHOLD\'.',
@@ -845,40 +800,22 @@ return [
],
[
'name' => '_APP_FUNCTIONS_CPUS',
'description' => 'Deprecated since 1.7.0. The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it\'s empty, CPU limit will be disabled.',
'description' => 'The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it\'s empty, CPU limit will be disabled.',
'introduction' => '0.7.0',
'default' => '0',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_COMPUTE_CPUS',
'description' => 'The maximum number of CPU core a single cloud function or a site is allowed to use. Please note that setting a value higher than available cores might result in an error. The default value is empty. When it\'s empty, CPU limit will be disabled.',
'introduction' => '1.7.0',
'default' => '0',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_FUNCTIONS_MEMORY',
'description' => 'Deprecated since 1.7.0. The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty, memory limit will be disabled.',
'description' => 'The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty, memory limit will be disabled.',
'introduction' => '0.7.0',
'default' => '0',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_COMPUTE_MEMORY',
'description' => 'The maximum amount of memory a single function or site is allowed to use in megabytes. The default value is empty. When it\'s empty, memory limit will be disabled.',
'introduction' => '1.7.0',
'default' => '0',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_FUNCTIONS_MEMORY_SWAP',
'description' => 'Deprecated since 1.2.0. High use of swap memory is not recommended to preserve harddrive health.',
@@ -936,22 +873,13 @@ return [
],
[
'name' => '_APP_FUNCTIONS_INACTIVE_THRESHOLD',
'description' => 'Deprecated since 1.7.0. The minimum time a function must be inactive before it can be shut down and cleaned up. This feature is intended to clean up unused containers. Containers may remain active for longer than the interval before being shut down, as Appwrite only cleans up unused containers every hour. If no value is provided, the default is 60 seconds.',
'description' => 'The minimum time a function must be inactive before it can be shut down and cleaned up. This feature is intended to clean up unused containers. Containers may remain active for longer than the interval before being shut down, as Appwrite only cleans up unused containers every hour. If no value is provided, the default is 60 seconds.',
'introduction' => '0.13.0',
'default' => '60',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_COMPUTE_INACTIVE_THRESHOLD',
'description' => 'The minimum time a function or site must be inactive before it can be shut down and cleaned up. This feature is intended to clean up unused containers. Containers may remain active for longer than the interval before being shut down, as Appwrite only cleans up unused containers every hour. If no value is provided, the default is 60 seconds.',
'introduction' => '1.7.0',
'default' => '60',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => 'DOCKERHUB_PULL_USERNAME',
'description' => 'Deprecated with 1.2.0, use \'_APP_DOCKER_HUB_USERNAME\' instead.',
@@ -990,22 +918,13 @@ return [
],
[
'name' => '_APP_FUNCTIONS_RUNTIMES_NETWORK',
'description' => 'Deprecated since 1.7.0. The docker network used for communication between the executor and runtimes.',
'description' => 'The docker network used for communication between the executor and runtimes.',
'introduction' => '1.2.0',
'default' => 'runtimes',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_COMPUTE_RUNTIMES_NETWORK',
'description' => 'The docker network used for communication between the executor and runtimes for sites and functions.',
'introduction' => '1.7.0',
'default' => 'runtimes',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DOCKER_HUB_USERNAME',
'description' => 'The username for hub.docker.com. This variable is used to pull images from hub.docker.com.',
@@ -1026,7 +945,7 @@ return [
],
[
'name' => '_APP_FUNCTIONS_MAINTENANCE_INTERVAL',
'description' => 'Deprecated since 1.7.0. Interval value containing the number of seconds that the executor should wait before checking for inactive runtimes. The default value is 3600 seconds (1 hour).',
'description' => 'Interval value containing the number of seconds that the executor should wait before checking for inactive runtimes. The default value is 3600 seconds (1 hour).',
'introduction' => '1.4.0',
'default' => '3600',
'required' => false,
@@ -1034,42 +953,8 @@ return [
'question' => '',
'filter' => ''
],
[
'name' => '_APP_COMPUTE_MAINTENANCE_INTERVAL',
'description' => 'Interval value containing the number of seconds that the executor should wait before checking for inactive runtimes of functions and sites. The default value is 3600 seconds (1 hour).',
'introduction' => '1.7.0',
'default' => '3600',
'required' => false,
'overwrite' => true,
'question' => '',
'filter' => ''
],
],
],
[
'category' => 'Sites',
'description' => '',
'variables' => [
[
'name' => '_APP_SITES_TIMEOUT',
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new site. The default value is 900 seconds. This is the global limit, timeout for individual functions are configured in the sites\'s settings or in appwrite.json.',
'introduction' => '1.7.0',
'default' => '900',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_SITES_RUNTIMES',
'description' => "This option allows you to enable or disable runtime environments for Sites. Disable unused runtimes to save disk space.\n\nTo enable cloud site runtimes, pass a list of enabled environments separated by a comma.\n\nCurrently, supported environments are: " . \implode(', ', \array_keys(Config::getParam('runtimes'))),
'introduction' => '1.7.0',
'default' => 'static-1,node-22,flutter-3.29',
'required' => false,
'question' => '',
'filter' => ''
],
]
],
[
'category' => 'VCS (Version Control System)',
'description' => '',
+87 -105
View File
@@ -43,6 +43,7 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -155,9 +156,6 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails) {
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
/** @var Utopia\Database\Document $user */
$userFromRequest = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
@@ -273,7 +271,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('expire', $expire)
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $sessionSecret) : '')
->setAttribute('secret', Auth::encodeSession($user->getId(), $sessionSecret))
;
$response->dynamic($session, Response::MODEL_SESSION);
@@ -289,6 +287,7 @@ App::post('/v1/account')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'create',
description: '/docs/references/account/create.md',
auth: [],
@@ -432,6 +431,7 @@ App::get('/v1/account')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'get',
description: '/docs/references/account/get.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -461,6 +461,7 @@ App::delete('/v1/account')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'delete',
description: '/docs/references/account/delete.md',
auth: [AuthType::ADMIN],
@@ -513,6 +514,7 @@ App::get('/v1/account/sessions')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'listSessions',
description: '/docs/references/account/list-sessions.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -530,9 +532,6 @@ App::get('/v1/account/sessions')
->inject('project')
->action(function (Response $response, Document $user, Locale $locale, Document $project) {
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$sessions = $user->getAttribute('sessions', []);
$current = Auth::sessionVerify($sessions, Auth::$secret);
@@ -542,7 +541,7 @@ App::get('/v1/account/sessions')
$session->setAttribute('countryName', $countryName);
$session->setAttribute('current', ($current == $session->getId()) ? true : false);
$session->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $session->getAttribute('secret', '') : '');
$session->setAttribute('secret', $session->getAttribute('secret', ''));
$sessions[$key] = $session;
}
@@ -562,6 +561,7 @@ App::delete('/v1/account/sessions')
->label('audits.resource', 'user/{user.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'deleteSessions',
description: '/docs/references/account/delete-sessions.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -631,6 +631,7 @@ App::get('/v1/account/sessions/:sessionId')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'getSession',
description: '/docs/references/account/get-session.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -649,10 +650,6 @@ App::get('/v1/account/sessions/:sessionId')
->inject('project')
->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Document $project) {
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$sessions = $user->getAttribute('sessions', []);
$sessionId = ($sessionId === 'current')
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
@@ -665,7 +662,7 @@ App::get('/v1/account/sessions/:sessionId')
$session
->setAttribute('current', ($session->getAttribute('secret') == Auth::hash(Auth::$secret)))
->setAttribute('countryName', $countryName)
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $session->getAttribute('secret', '') : '')
->setAttribute('secret', $session->getAttribute('secret', ''))
;
return $response->dynamic($session, Response::MODEL_SESSION);
@@ -684,6 +681,7 @@ App::delete('/v1/account/sessions/:sessionId')
->label('audits.resource', 'user/{user.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'deleteSession',
description: '/docs/references/account/delete-session.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -772,6 +770,7 @@ App::patch('/v1/account/sessions/:sessionId')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'updateSession',
description: '/docs/references/account/update-session.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -856,6 +855,7 @@ App::post('/v1/account/sessions/email')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'createEmailPasswordSession',
description: '/docs/references/account/create-session-email-password.md',
auth: [],
@@ -897,10 +897,6 @@ App::post('/v1/account/sessions/email')
throw new Exception(Exception::USER_BLOCKED); // User is in status blocked
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$user->setAttributes($profile->getArrayCopy());
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]);
@@ -966,7 +962,7 @@ App::post('/v1/account/sessions/email')
$session
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '')
->setAttribute('secret', Auth::encodeSession($user->getId(), $secret))
;
$queueForEvents
@@ -996,6 +992,7 @@ App::post('/v1/account/sessions/anonymous')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'createAnonymousSession',
description: '/docs/references/account/create-session-anonymous.md',
auth: [],
@@ -1019,9 +1016,6 @@ App::post('/v1/account/sessions/anonymous')
->inject('queueForEvents')
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) {
$protocol = $request->getProtocol();
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if ('console' === $project->getId()) {
throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED, 'Failed to create anonymous user');
@@ -1123,7 +1117,7 @@ App::post('/v1/account/sessions/anonymous')
$session
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '')
->setAttribute('secret', Auth::encodeSession($user->getId(), $secret))
;
$response->dynamic($session, Response::MODEL_SESSION);
@@ -1139,6 +1133,7 @@ App::post('/v1/account/sessions/token')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'createSession',
description: '/docs/references/account/create-session.md',
auth: [],
@@ -1172,6 +1167,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
->label('scope', 'sessions.write')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'createOAuth2Session',
description: '/docs/references/account/create-session-oauth2.md',
type: MethodType::WEBAUTH,
@@ -1188,8 +1184,8 @@ App::get('/v1/account/sessions/oauth2/:provider')
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('oAuthProviders'), fn ($node) => (!$node['mock'])))) . '.')
->param('success', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s 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', 'devKey'])
->param('failure', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s 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', 'devKey'])
->param('success', '', fn ($clients) => new Host($clients), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s 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'])
->param('failure', '', fn ($clients) => new Host($clients), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s 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'])
->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
->inject('request')
->inject('response')
@@ -1243,7 +1239,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
});
App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->desc('OAuth2 callback')
->desc('Get OAuth2 callback')
->groups(['account'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('scope', 'public')
@@ -1273,7 +1269,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
});
App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->desc('OAuth2 callback')
->desc('Create OAuth2 callback')
->groups(['account'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('scope', 'public')
@@ -1304,7 +1300,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
});
App::get('/v1/account/sessions/oauth2/:provider/redirect')
->desc('OAuth2 redirect')
->desc('Get OAuth2 redirect')
->groups(['api', 'account', 'session'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('event', 'users.[userId].sessions.[sessionId].create')
@@ -1449,7 +1445,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Query::notEqual('userInternalId', $user->getInternalId()),
]);
if (!$identityWithMatchingEmail->isEmpty()) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
$failureRedirect(Exception::USER_ALREADY_EXISTS);
}
$userWithMatchingEmail = $dbForProject->find('users', [
@@ -1457,7 +1453,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Query::notEqual('$id', $userId),
]);
if (!empty($userWithMatchingEmail)) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
$failureRedirect(Exception::USER_ALREADY_EXISTS);
}
$sessionUpgrade = true;
@@ -1486,7 +1482,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
if ($user === false || $user->isEmpty()) { // No user logged in or with OAuth2 provider ID, create new one or connect with account with same email
if (empty($email)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
$failureRedirect(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
}
/**
@@ -1529,7 +1525,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Query::equal('providerEmail', [$email]),
]);
if (!$identityWithMatchingEmail->isEmpty()) {
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
}
try {
@@ -1601,7 +1597,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Query::notEqual('userInternalId', $user->getInternalId()),
]);
if (!empty($identitiesWithMatchingEmail)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
}
$dbForProject->createDocument('identities', new Document([
@@ -1769,6 +1765,7 @@ App::get('/v1/account/tokens/oauth2/:provider')
->label('scope', 'sessions.write')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createOAuth2Token',
description: '/docs/references/account/create-token-oauth2.md',
auth: [],
@@ -1784,8 +1781,8 @@ App::get('/v1/account/tokens/oauth2/:provider')
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('oAuthProviders'), fn ($node) => (!$node['mock'])))) . '.')
->param('success', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s 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', 'devKey'])
->param('failure', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s 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', 'devKey'])
->param('success', '', fn ($clients) => new Host($clients), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s 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'])
->param('failure', '', fn ($clients) => new Host($clients), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s 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'])
->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
->inject('request')
->inject('response')
@@ -1849,6 +1846,7 @@ App::post('/v1/account/tokens/magic-url')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createMagicURLToken',
description: '/docs/references/account/create-token-magic-url.md',
auth: [],
@@ -1864,7 +1862,7 @@ App::post('/v1/account/tokens/magic-url')
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('email', '', new Email(), 'User email.')
->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the magic URL login. 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', 'devKey'])
->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the magic URL login. 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'])
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
->inject('request')
->inject('response')
@@ -1884,9 +1882,6 @@ App::post('/v1/account/tokens/magic-url')
$phrase = Phrase::generate();
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$result = $dbForProject->findOne('users', [Query::equal('email', [$email])]);
if (!$result->isEmpty()) {
@@ -2068,17 +2063,11 @@ App::post('/v1/account/tokens/magic-url')
->setRecipient($email)
->trigger();
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $tokenSecret);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$token->setAttribute('secret', '');
}
if (!empty($phrase)) {
$token->setAttribute('phrase', $phrase);
}
@@ -2098,6 +2087,7 @@ App::post('/v1/account/tokens/email')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createEmailToken',
description: '/docs/references/account/create-token-email.md',
auth: [],
@@ -2131,10 +2121,6 @@ App::post('/v1/account/tokens/email')
$phrase = Phrase::generate();
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$result = $dbForProject->findOne('users', [Query::equal('email', [$email])]);
if (!$result->isEmpty()) {
$user->setAttributes($result->getArrayCopy());
@@ -2303,17 +2289,11 @@ App::post('/v1/account/tokens/email')
->setRecipient($email)
->trigger();
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $tokenSecret);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$token->setAttribute('secret', '');
}
if (!empty($phrase)) {
$token->setAttribute('phrase', $phrase);
}
@@ -2333,6 +2313,7 @@ App::put('/v1/account/sessions/magic-url')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'updateMagicURLSession',
description: '/docs/references/account/create-session.md',
auth: [],
@@ -2370,6 +2351,7 @@ App::put('/v1/account/sessions/phone')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'updatePhoneSession',
description: '/docs/references/account/create-session.md',
auth: [],
@@ -2408,6 +2390,7 @@ App::post('/v1/account/tokens/phone')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createPhoneToken',
description: '/docs/references/account/create-token-phone.md',
auth: [],
@@ -2439,10 +2422,6 @@ App::post('/v1/account/tokens/phone')
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$result = $dbForProject->findOne('users', [Query::equal('phone', [$phone])]);
if (!$result->isEmpty()) {
$user->setAttributes($result->getArrayCopy());
@@ -2594,14 +2573,13 @@ App::post('/v1/account/tokens/phone')
}
}
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $secret);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '');
// Encode secret for clients
$token->setAttribute('secret', Auth::encodeSession($user->getId(), $secret));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -2616,6 +2594,7 @@ App::post('/v1/account/jwts')
->label('auth.type', 'jwt')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createJWT',
description: '/docs/references/account/create-jwt.md',
auth: [],
@@ -2664,6 +2643,7 @@ App::get('/v1/account/prefs')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'getPrefs',
description: '/docs/references/account/get-prefs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2690,6 +2670,7 @@ App::get('/v1/account/logs')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'logs',
name: 'listLogs',
description: '/docs/references/account/list-logs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2766,6 +2747,7 @@ App::patch('/v1/account/name')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updateName',
description: '/docs/references/account/update-name.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2804,6 +2786,7 @@ App::patch('/v1/account/password')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updatePassword',
description: '/docs/references/account/update-password.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2877,6 +2860,7 @@ App::patch('/v1/account/email')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updateEmail',
description: '/docs/references/account/update-email.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2973,6 +2957,7 @@ App::patch('/v1/account/phone')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updatePhone',
description: '/docs/references/account/update-phone.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3058,6 +3043,7 @@ App::patch('/v1/account/prefs')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updatePrefs',
description: '/docs/references/account/update-prefs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3095,6 +3081,7 @@ App::patch('/v1/account/status')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updateStatus',
description: '/docs/references/account/update-status.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3145,6 +3132,7 @@ App::post('/v1/account/recovery')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'recovery',
name: 'createRecovery',
description: '/docs/references/account/create-recovery.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3159,7 +3147,7 @@ App::post('/v1/account/recovery')
->label('abuse-limit', 10)
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
->param('email', '', new Email(), 'User email.')
->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients', 'devKey'])
->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients'])
->inject('request')
->inject('response')
->inject('user')
@@ -3174,11 +3162,6 @@ App::post('/v1/account/recovery')
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
}
$url = htmlentities($url);
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$email = \strtolower($email);
$profile = $dbForProject->findOne('users', [
@@ -3302,19 +3285,13 @@ App::post('/v1/account/recovery')
->setSubject($subject)
->trigger();
// Set to unhashed secret for events and server responses
$recovery->setAttribute('secret', $secret);
$queueForEvents
->setParam('userId', $profile->getId())
->setParam('tokenId', $recovery->getId())
->setUser($profile)
->setPayload($response->output($recovery, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$recovery->setAttribute('secret', '');
}
->setPayload(Response::showSensitive(fn () => $response->output($recovery, Response::MODEL_TOKEN)), sensitive: ['secret']);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -3322,7 +3299,7 @@ App::post('/v1/account/recovery')
});
App::put('/v1/account/recovery')
->desc('Create password recovery (confirmation)')
->desc('Update password recovery (confirmation)')
->groups(['api', 'account'])
->label('scope', 'sessions.write')
->label('event', 'users.[userId].recovery.[tokenId].update')
@@ -3331,6 +3308,7 @@ App::put('/v1/account/recovery')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'recovery',
name: 'updateRecovery',
description: '/docs/references/account/update-recovery.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3407,7 +3385,7 @@ App::put('/v1/account/recovery')
$queueForEvents
->setParam('userId', $profile->getId())
->setParam('tokenId', $recoveryDocument->getId())
;
->setPayload(Response::showSensitive(fn () => $response->output($recoveryDocument, Response::MODEL_TOKEN)), sensitive: ['secret']);
$response->dynamic($recoveryDocument, Response::MODEL_TOKEN);
});
@@ -3421,6 +3399,7 @@ App::post('/v1/account/verification')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'createVerification',
description: '/docs/references/account/create-email-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3434,7 +3413,7 @@ App::post('/v1/account/verification')
))
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{userId}')
->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients', 'devKey']) // TODO add built-in confirm page
->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add built-in confirm page
->inject('request')
->inject('response')
->inject('project')
@@ -3454,9 +3433,6 @@ App::post('/v1/account/verification')
throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED);
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION);
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
@@ -3565,18 +3541,12 @@ App::post('/v1/account/verification')
->setName($user->getAttribute('name') ?? '')
->trigger();
// Set to unhashed secret for events and server responses
$verification->setAttribute('secret', $verificationSecret);
$queueForEvents
->setParam('userId', $user->getId())
->setParam('tokenId', $verification->getId())
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$verification->setAttribute('secret', '');
}
->setPayload(Response::showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -3584,7 +3554,7 @@ App::post('/v1/account/verification')
});
App::put('/v1/account/verification')
->desc('Create email verification (confirmation)')
->desc('Update email verification (confirmation)')
->groups(['api', 'account'])
->label('scope', 'public')
->label('event', 'users.[userId].verification.[tokenId].update')
@@ -3592,6 +3562,7 @@ App::put('/v1/account/verification')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'updateVerification',
description: '/docs/references/account/update-email-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3643,7 +3614,8 @@ App::put('/v1/account/verification')
$queueForEvents
->setParam('userId', $userId)
->setParam('tokenId', $verification->getId());
->setParam('tokenId', $verification->getId())
->setPayload(Response::showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
$response->dynamic($verification, Response::MODEL_TOKEN);
});
@@ -3658,6 +3630,7 @@ App::post('/v1/account/verification/phone')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'createPhoneVerification',
description: '/docs/references/account/create-phone-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3696,10 +3669,6 @@ App::post('/v1/account/verification/phone')
throw new Exception(Exception::USER_PHONE_ALREADY_VERIFIED);
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$secret = null;
$sendSMS = true;
$mockNumbers = $project->getAttribute('auths', [])['mockNumbers'] ?? [];
@@ -3788,7 +3757,6 @@ App::post('/v1/account/verification/phone')
}
}
// Set to unhashed secret for events and server responses
$verification->setAttribute('secret', $secret);
$queueForEvents
@@ -3796,11 +3764,6 @@ App::post('/v1/account/verification/phone')
->setParam('tokenId', $verification->getId())
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$verification->setAttribute('secret', '');
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($verification, Response::MODEL_TOKEN);
@@ -3815,6 +3778,7 @@ App::put('/v1/account/verification/phone')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'updatePhoneVerification',
description: '/docs/references/account/update-phone-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3880,6 +3844,7 @@ App::patch('/v1/account/mfa')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMFA',
description: '/docs/references/account/update-mfa.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3933,6 +3898,7 @@ App::get('/v1/account/mfa/factors')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'listMfaFactors',
description: '/docs/references/account/list-mfa-factors.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3973,6 +3939,7 @@ App::post('/v1/account/mfa/authenticators/:type')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'createMfaAuthenticator',
description: '/docs/references/account/create-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4040,7 +4007,7 @@ App::post('/v1/account/mfa/authenticators/:type')
});
App::put('/v1/account/mfa/authenticators/:type')
->desc('Verify authenticator')
->desc('Update authenticator (confirmation)')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
@@ -4049,6 +4016,7 @@ App::put('/v1/account/mfa/authenticators/:type')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMfaAuthenticator',
description: '/docs/references/account/update-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4118,6 +4086,7 @@ App::post('/v1/account/mfa/recovery-codes')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'createMfaRecoveryCodes',
description: '/docs/references/account/create-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4155,7 +4124,7 @@ App::post('/v1/account/mfa/recovery-codes')
});
App::patch('/v1/account/mfa/recovery-codes')
->desc('Regenerate MFA recovery codes')
->desc('Update MFA recovery codes (regenerate)')
->groups(['api', 'account', 'mfaProtected'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
@@ -4164,6 +4133,7 @@ App::patch('/v1/account/mfa/recovery-codes')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMfaRecoveryCodes',
description: '/docs/references/account/update-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4200,11 +4170,12 @@ App::patch('/v1/account/mfa/recovery-codes')
});
App::get('/v1/account/mfa/recovery-codes')
->desc('Get MFA recovery codes')
->desc('List MFA recovery codes')
->groups(['api', 'account', 'mfaProtected'])
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'getMfaRecoveryCodes',
description: '/docs/references/account/get-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4243,6 +4214,7 @@ App::delete('/v1/account/mfa/authenticators/:type')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'deleteMfaAuthenticator',
description: '/docs/references/account/delete-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4288,6 +4260,7 @@ App::post('/v1/account/mfa/challenge')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'createMfaChallenge',
description: '/docs/references/account/create-mfa-challenge.md',
auth: [],
@@ -4498,7 +4471,7 @@ App::post('/v1/account/mfa/challenge')
});
App::put('/v1/account/mfa/challenge')
->desc('Create MFA challenge (confirmation)')
->desc('Update MFA challenge (confirmation)')
->groups(['api', 'account', 'mfa'])
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[sessionId].create')
@@ -4507,6 +4480,7 @@ App::put('/v1/account/mfa/challenge')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMfaChallenge',
description: '/docs/references/account/update-mfa-challenge.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4600,6 +4574,7 @@ App::post('/v1/account/targets/push')
->label('event', 'users.[userId].targets.[targetId].create')
->label('sdk', new Method(
namespace: 'account',
group: 'pushTargets',
name: 'createPushTarget',
description: '/docs/references/account/create-push-target.md',
auth: [AuthType::SESSION],
@@ -4680,6 +4655,7 @@ App::put('/v1/account/targets/:targetId/push')
->label('event', 'users.[userId].targets.[targetId].update')
->label('sdk', new Method(
namespace: 'account',
group: 'pushTargets',
name: 'updatePushTarget',
description: '/docs/references/account/update-push-target.md',
auth: [AuthType::SESSION],
@@ -4744,6 +4720,7 @@ App::delete('/v1/account/targets/:targetId/push')
->label('event', 'users.[userId].targets.[targetId].delete')
->label('sdk', new Method(
namespace: 'account',
group: 'pushTargets',
name: 'deletePushTarget',
description: '/docs/references/account/delete-push-target.md',
auth: [AuthType::SESSION],
@@ -4794,6 +4771,7 @@ App::get('/v1/account/identities')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'identities',
name: 'listIdentities',
description: '/docs/references/account/list-identities.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4845,8 +4823,11 @@ App::get('/v1/account/identities')
}
$filterQueries = Query::groupByType($queries)['filters'];
$results = $dbForProject->find('identities', $queries);
try {
$results = $dbForProject->find('identities', $queries);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
@@ -4865,6 +4846,7 @@ App::delete('/v1/account/identities/:identityId')
->label('audits.userId', '{user.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'identities',
name: 'deleteIdentity',
description: '/docs/references/account/delete-identity.md',
auth: [AuthType::SESSION, AuthType::JWT],
+8 -2
View File
@@ -171,6 +171,7 @@ App::get('/v1/avatars/credit-cards/:code')
->label('cache.resource', 'avatar/credit-card')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getCreditCard',
description: '/docs/references/avatars/get-credit-card.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -198,6 +199,7 @@ App::get('/v1/avatars/browsers/:code')
->label('cache.resource', 'avatar/browser')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getBrowser',
description: '/docs/references/avatars/get-browser.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -225,6 +227,7 @@ App::get('/v1/avatars/flags/:code')
->label('cache.resource', 'avatar/flag')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getFlag',
description: '/docs/references/avatars/get-flag.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -252,6 +255,7 @@ App::get('/v1/avatars/image')
->label('cache.resource', 'avatar/image')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getImage',
description: '/docs/references/avatars/get-image.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -322,6 +326,7 @@ App::get('/v1/avatars/favicon')
->label('cache.resource', 'avatar/favicon')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getFavicon',
description: '/docs/references/avatars/get-favicon.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -472,6 +477,7 @@ App::get('/v1/avatars/qr')
->label('scope', 'avatars.read')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getQR',
description: '/docs/references/avatars/get-qr.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -496,7 +502,6 @@ App::get('/v1/avatars/qr')
'addQuietzone' => true,
'quietzoneSize' => $margin,
'outputType' => QRCode::OUTPUT_IMAGICK,
'scale' => 15,
]);
$qrcode = new QRCode($options);
@@ -511,7 +516,7 @@ App::get('/v1/avatars/qr')
$response
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->setContentType('image/png')
->send($image->output('png', 90));
->send($image->output('png', 9));
});
App::get('/v1/avatars/initials')
@@ -521,6 +526,7 @@ App::get('/v1/avatars/initials')
->label('cache.resource', 'avatar/initials')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getInitials',
description: '/docs/references/avatars/get-initials.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
+10 -27
View File
@@ -8,9 +8,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Database\Document;
use Utopia\Domains\Domain;
use Utopia\System\System;
use Utopia\Validator\IP;
use Utopia\Validator\Text;
App::init()
@@ -29,6 +27,7 @@ App::get('/v1/console/variables')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'console',
group: 'console',
name: 'variables',
description: '/docs/references/console/variables.md',
auth: [AuthType::ADMIN],
@@ -42,21 +41,10 @@ App::get('/v1/console/variables')
))
->inject('response')
->action(function (Response $response) {
$validator = new Domain(System::getEnv('_APP_DOMAIN'));
$isDomainValid = !empty(System::getEnv('_APP_DOMAIN', '')) && $validator->isKnown() && !$validator->isTest();
$validator = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME'));
$isCNAMEValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')) && $validator->isKnown() && !$validator->isTest();
$validator = new IP(IP::V4);
$isAValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_A', '')) && ($validator->isValid(System::getEnv('_APP_DOMAIN_TARGET_A')));
$validator = new IP(IP::V6);
$isAAAAValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_AAAA', '')) && $validator->isValid(System::getEnv('_APP_DOMAIN_TARGET_AAAA'));
$isDomainEnabled = $isDomainValid && (
$isAAAAValid || $isAValid || $isCNAMEValid
);
$isDomainEnabled = !empty(System::getEnv('_APP_DOMAIN', ''))
&& !empty(System::getEnv('_APP_DOMAIN_TARGET', ''))
&& System::getEnv('_APP_DOMAIN', '') !== 'localhost'
&& System::getEnv('_APP_DOMAIN_TARGET', '') !== 'localhost';
$isVcsEnabled = !empty(System::getEnv('_APP_VCS_GITHUB_APP_NAME', ''))
&& !empty(System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY', ''))
@@ -67,30 +55,25 @@ App::get('/v1/console/variables')
$isAssistantEnabled = !empty(System::getEnv('_APP_ASSISTANT_OPENAI_API_KEY', ''));
$variables = new Document([
'_APP_DOMAIN_TARGET_CNAME' => System::getEnv('_APP_DOMAIN_TARGET_CNAME'),
'_APP_DOMAIN_TARGET_AAAA' => System::getEnv('_APP_DOMAIN_TARGET_AAAA'),
'_APP_DOMAIN_TARGET_A' => System::getEnv('_APP_DOMAIN_TARGET_A'),
'_APP_DOMAIN_TARGET' => System::getEnv('_APP_DOMAIN_TARGET'),
'_APP_STORAGE_LIMIT' => +System::getEnv('_APP_STORAGE_LIMIT'),
'_APP_COMPUTE_SIZE_LIMIT' => +System::getEnv('_APP_COMPUTE_SIZE_LIMIT'),
'_APP_FUNCTIONS_SIZE_LIMIT' => +System::getEnv('_APP_FUNCTIONS_SIZE_LIMIT'),
'_APP_USAGE_STATS' => System::getEnv('_APP_USAGE_STATS'),
'_APP_VCS_ENABLED' => $isVcsEnabled,
'_APP_DOMAIN_ENABLED' => $isDomainEnabled,
'_APP_ASSISTANT_ENABLED' => $isAssistantEnabled,
'_APP_DOMAIN_SITES' => System::getEnv('_APP_DOMAIN_SITES'),
'_APP_DOMAIN_FUNCTIONS' => System::getEnv('_APP_DOMAIN_FUNCTIONS'),
'_APP_OPTIONS_FORCE_HTTPS' => System::getEnv('_APP_OPTIONS_FORCE_HTTPS'),
'_APP_DOMAINS_NAMESERVERS' => System::getEnv('_APP_DOMAINS_NAMESERVERS'),
'_APP_ASSISTANT_ENABLED' => $isAssistantEnabled
]);
$response->dynamic($variables, Response::MODEL_CONSOLE_VARIABLES);
});
App::post('/v1/console/assistant')
->desc('Ask query')
->desc('Create assistant query')
->groups(['api', 'assistant'])
->label('scope', 'assistant.read')
->label('sdk', new Method(
namespace: 'assistant',
group: 'console',
name: 'chat',
description: '/docs/references/assistant/chat.md',
auth: [AuthType::ADMIN],
+88 -23
View File
@@ -31,6 +31,7 @@ use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Index as IndexException;
use Utopia\Database\Exception\Limit as LimitException;
use Utopia\Database\Exception\NotFound as NotFoundException;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Exception\Restricted as RestrictedException;
use Utopia\Database\Exception\Structure as StructureException;
@@ -466,6 +467,7 @@ App::post('/v1/databases')
->label('audits.resource', 'database/{response.$id}')
->label('sdk', new Method(
namespace: 'databases',
group: 'databases',
name: 'create',
description: '/docs/references/databases/create.md',
auth: [AuthType::KEY],
@@ -534,7 +536,6 @@ App::post('/v1/databases')
}
$queueForEvents->setParam('databaseId', $database->getId());
$queueForStatsUsage->addMetric(str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_STORAGE), 1); // per database
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -548,6 +549,7 @@ App::get('/v1/databases')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'databases',
name: 'list',
description: '/docs/references/databases/list.md',
auth: [AuthType::KEY],
@@ -597,9 +599,15 @@ App::get('/v1/databases')
$filterQueries = Query::groupByType($queries)['filters'];
try {
$databases = $dbForProject->find('databases', $queries);
$total = $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'databases' => $dbForProject->find('databases', $queries),
'total' => $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT),
'databases' => $databases,
'total' => $total,
]), Response::MODEL_DATABASE_LIST);
});
@@ -610,6 +618,7 @@ App::get('/v1/databases/:databaseId')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'databases',
name: 'get',
description: '/docs/references/databases/get.md',
auth: [AuthType::KEY],
@@ -642,6 +651,7 @@ App::get('/v1/databases/:databaseId/logs')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'logs',
name: 'listLogs',
description: '/docs/references/databases/get-logs.md',
auth: [AuthType::ADMIN],
@@ -745,6 +755,7 @@ App::put('/v1/databases/:databaseId')
->label('audits.resource', 'database/{response.$id}')
->label('sdk', new Method(
namespace: 'databases',
group: 'databases',
name: 'update',
description: '/docs/references/databases/update.md',
auth: [AuthType::KEY],
@@ -790,6 +801,7 @@ App::delete('/v1/databases/:databaseId')
->label('audits.resource', 'database/{request.databaseId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'databases',
name: 'delete',
description: '/docs/references/databases/delete.md',
auth: [AuthType::KEY],
@@ -830,9 +842,6 @@ App::delete('/v1/databases/:databaseId')
->setParam('databaseId', $database->getId())
->setPayload($response->output($database, Response::MODEL_DATABASE));
$queueForStatsUsage
->addMetric(METRIC_DATABASES_STORAGE, 1); // Global, deletion forces full recalculation
$response->noContent();
});
@@ -846,6 +855,7 @@ App::post('/v1/databases/:databaseId/collections')
->label('audits.resource', 'database/{request.databaseId}/collection/{response.$id}')
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'createCollection',
description: '/docs/references/databases/create-collection.md',
auth: [AuthType::KEY],
@@ -917,6 +927,7 @@ App::get('/v1/databases/:databaseId/collections')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'listCollections',
description: '/docs/references/databases/list-collections.md',
auth: [AuthType::KEY],
@@ -975,9 +986,15 @@ App::get('/v1/databases/:databaseId/collections')
$filterQueries = Query::groupByType($queries)['filters'];
try {
$collections = $dbForProject->find('database_' . $database->getInternalId(), $queries);
$total = $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'collections' => $dbForProject->find('database_' . $database->getInternalId(), $queries),
'total' => $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT),
'collections' => $collections,
'total' => $total,
]), Response::MODEL_COLLECTION_LIST);
});
@@ -989,6 +1006,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'getCollection',
description: '/docs/references/databases/get-collection.md',
auth: [AuthType::KEY],
@@ -1030,6 +1048,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'listCollectionLogs',
description: '/docs/references/databases/get-collection-logs.md',
auth: [AuthType::ADMIN],
@@ -1142,6 +1161,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'updateCollection',
description: '/docs/references/databases/update-collection.md',
auth: [AuthType::KEY],
@@ -1216,6 +1236,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'deleteCollection',
description: '/docs/references/databases/delete-collection.md',
auth: [AuthType::KEY],
@@ -1279,6 +1300,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createStringAttribute',
description: '/docs/references/databases/create-string-attribute.md',
auth: [AuthType::KEY],
@@ -1341,6 +1363,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email'
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createEmailAttribute',
description: '/docs/references/databases/create-email-attribute.md',
auth: [AuthType::KEY],
@@ -1389,6 +1412,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createEnumAttribute',
description: '/docs/references/databases/create-attribute-enum.md',
auth: [AuthType::KEY],
@@ -1442,6 +1466,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createIpAttribute',
description: '/docs/references/databases/create-ip-attribute.md',
auth: [AuthType::KEY],
@@ -1490,6 +1515,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createUrlAttribute',
description: '/docs/references/databases/create-url-attribute.md',
auth: [AuthType::KEY],
@@ -1538,6 +1564,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createIntegerAttribute',
description: '/docs/references/databases/create-integer-attribute.md',
auth: [AuthType::KEY],
@@ -1615,6 +1642,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float'
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createFloatAttribute',
description: '/docs/references/databases/create-float-attribute.md',
auth: [AuthType::KEY],
@@ -1690,6 +1718,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createBooleanAttribute',
description: '/docs/references/databases/create-boolean-attribute.md',
auth: [AuthType::KEY],
@@ -1737,6 +1766,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createDatetimeAttribute',
description: '/docs/references/databases/create-datetime-attribute.md',
auth: [AuthType::KEY],
@@ -1787,6 +1817,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createRelationshipAttribute',
description: '/docs/references/databases/create-relationship-attribute.md',
auth: [AuthType::KEY],
@@ -1918,6 +1949,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'listAttributes',
description: '/docs/references/databases/list-attributes.md',
auth: [AuthType::KEY],
@@ -1986,9 +2018,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
}
$filters = Query::groupByType($queries)['filters'];
$attributes = $dbForProject->find('attributes', $queries);
$total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT);
try {
$attributes = $dbForProject->find('attributes', $queries);
$total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'attributes' => $attributes,
@@ -2004,6 +2039,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'getAttribute',
description: '/docs/references/databases/get-attribute.md',
auth: [AuthType::KEY],
@@ -2088,6 +2124,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateStringAttribute',
description: '/docs/references/databases/update-string-attribute.md',
auth: [AuthType::KEY],
@@ -2139,6 +2176,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateEmailAttribute',
description: '/docs/references/databases/update-email-attribute.md',
auth: [AuthType::KEY],
@@ -2188,6 +2226,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateEnumAttribute',
description: '/docs/references/databases/update-enum-attribute.md',
auth: [AuthType::KEY],
@@ -2239,6 +2278,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateIpAttribute',
description: '/docs/references/databases/update-ip-attribute.md',
auth: [AuthType::KEY],
@@ -2288,6 +2328,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateUrlAttribute',
description: '/docs/references/databases/update-url-attribute.md',
auth: [AuthType::KEY],
@@ -2337,6 +2378,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateIntegerAttribute',
description: '/docs/references/databases/update-integer-attribute.md',
auth: [AuthType::KEY],
@@ -2396,6 +2438,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateFloatAttribute',
description: '/docs/references/databases/update-float-attribute.md',
auth: [AuthType::KEY],
@@ -2455,6 +2498,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateBooleanAttribute',
description: '/docs/references/databases/update-boolean-attribute.md',
auth: [AuthType::KEY],
@@ -2503,6 +2547,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateDatetimeAttribute',
description: '/docs/references/databases/update-datetime-attribute.md',
auth: [AuthType::KEY],
@@ -2551,6 +2596,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateRelationshipAttribute',
description: '/docs/references/databases/update-relationship-attribute.md',
auth: [AuthType::KEY],
@@ -2616,6 +2662,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'deleteAttribute',
description: '/docs/references/databases/delete-attribute.md',
auth: [AuthType::KEY],
@@ -2732,9 +2779,6 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->setContext('database', $db)
->setPayload($response->output($attribute, $model));
$queueForStatsUsage
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$db->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
$response->noContent();
});
@@ -2749,6 +2793,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'createIndex',
description: '/docs/references/databases/create-index.md',
auth: [AuthType::KEY],
@@ -2919,6 +2964,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'indexes',
name: 'listIndexes',
description: '/docs/references/databases/list-indexes.md',
auth: [AuthType::KEY],
@@ -2987,9 +3033,16 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$total = $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT);
$indexes = $dbForProject->find('indexes', $queries);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'total' => $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT),
'indexes' => $dbForProject->find('indexes', $queries),
'total' => $total,
'indexes' => $indexes,
]), Response::MODEL_INDEX_LIST);
});
@@ -3001,6 +3054,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'indexes',
name: 'getIndex',
description: '/docs/references/databases/get-index.md',
auth: [AuthType::KEY],
@@ -3050,6 +3104,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'indexes',
name: 'deleteIndex',
description: '/docs/references/databases/delete-index.md',
auth: [AuthType::KEY],
@@ -3128,6 +3183,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
[
new Method(
namespace: 'databases',
group: 'documents',
name: 'createDocument',
description: '/docs/references/databases/create-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -3356,8 +3412,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations)
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); // per collection
$response->addHeader('X-Debug-Operations', $operations);
@@ -3390,6 +3445,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'listDocuments',
description: '/docs/references/databases/list-documents.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -3453,9 +3509,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
$cursor->setValue($cursorDocument);
}
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries);
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT);
try {
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries);
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$operations = 0;
@@ -3566,6 +3625,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'getDocument',
description: '/docs/references/databases/get-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -3677,6 +3737,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'logs',
name: 'listDocumentLogs',
description: '/docs/references/databases/get-document-logs.md',
auth: [AuthType::ADMIN],
@@ -3797,6 +3858,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'updateDocument',
description: '/docs/references/databases/update-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -4051,6 +4113,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'deleteDocument',
description: '/docs/references/databases/delete-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -4141,8 +4204,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1)
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1)
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); // per collection
$response->addHeader('X-Debug-Operations', 1);
@@ -4172,6 +4234,7 @@ App::get('/v1/databases/usage')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: null,
name: 'getUsage',
description: '/docs/references/databases/get-usage.md',
auth: [AuthType::ADMIN],
@@ -4267,6 +4330,7 @@ App::get('/v1/databases/:databaseId/usage')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: null,
name: 'getDatabaseUsage',
description: '/docs/references/databases/get-database-usage.md',
auth: [AuthType::ADMIN],
@@ -4368,6 +4432,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: null,
name: 'getCollectionUsage',
description: '/docs/references/databases/get-collection-usage.md',
auth: [AuthType::ADMIN],
File diff suppressed because it is too large Load Diff
+3
View File
@@ -44,6 +44,7 @@ App::get('/v1/graphql')
->label('scope', 'graphql')
->label('sdk', new Method(
namespace: 'graphql',
group: 'graphql',
name: 'get',
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
hide: true,
@@ -90,6 +91,7 @@ App::post('/v1/graphql/mutation')
->label('scope', 'graphql')
->label('sdk', new Method(
namespace: 'graphql',
group: 'graphql',
name: 'mutation',
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
description: '/docs/references/graphql/post.md',
@@ -140,6 +142,7 @@ App::post('/v1/graphql')
->label('scope', 'graphql')
->label('sdk', new Method(
namespace: 'graphql',
group: 'graphql',
name: 'query',
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
description: '/docs/references/graphql/post.md',
+24 -39
View File
@@ -32,6 +32,7 @@ App::get('/v1/health')
->label('scope', 'health.read')
->label('sdk', new Method(
namespace: 'health',
group: 'health',
name: 'get',
auth: [AuthType::KEY],
description: '/docs/references/health/get.md',
@@ -71,6 +72,7 @@ App::get('/v1/health/db')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getDB',
description: '/docs/references/health/get-db.md',
responses: [
@@ -131,6 +133,7 @@ App::get('/v1/health/cache')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getCache',
description: '/docs/references/health/get-cache.md',
responses: [
@@ -195,6 +198,7 @@ App::get('/v1/health/pubsub')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getPubSub',
description: '/docs/references/health/get-pubsub.md',
responses: [
@@ -259,6 +263,7 @@ App::get('/v1/health/time')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getTime',
description: '/docs/references/health/get-time.md',
responses: [
@@ -322,6 +327,7 @@ App::get('/v1/health/queue/webhooks')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueWebhooks',
description: '/docs/references/health/get-queue-webhooks.md',
responses: [
@@ -354,6 +360,7 @@ App::get('/v1/health/queue/logs')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueLogs',
description: '/docs/references/health/get-queue-logs.md',
responses: [
@@ -386,6 +393,7 @@ App::get('/v1/health/certificate')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getCertificate',
description: '/docs/references/health/get-certificate.md',
responses: [
@@ -442,6 +450,7 @@ App::get('/v1/health/queue/certificates')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueCertificates',
description: '/docs/references/health/get-queue-certificates.md',
responses: [
@@ -474,6 +483,7 @@ App::get('/v1/health/queue/builds')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueBuilds',
description: '/docs/references/health/get-queue-builds.md',
responses: [
@@ -506,6 +516,7 @@ App::get('/v1/health/queue/databases')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueDatabases',
description: '/docs/references/health/get-queue-databases.md',
responses: [
@@ -539,6 +550,7 @@ App::get('/v1/health/queue/deletes')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueDeletes',
description: '/docs/references/health/get-queue-deletes.md',
responses: [
@@ -571,6 +583,7 @@ App::get('/v1/health/queue/mails')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueMails',
description: '/docs/references/health/get-queue-mails.md',
responses: [
@@ -603,6 +616,7 @@ App::get('/v1/health/queue/messaging')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueMessaging',
description: '/docs/references/health/get-queue-messaging.md',
responses: [
@@ -635,6 +649,7 @@ App::get('/v1/health/queue/migrations')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueMigrations',
description: '/docs/references/health/get-queue-migrations.md',
responses: [
@@ -667,6 +682,7 @@ App::get('/v1/health/queue/functions')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueFunctions',
description: '/docs/references/health/get-queue-functions.md',
responses: [
@@ -699,6 +715,7 @@ App::get('/v1/health/queue/stats-resources')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueStatsResources',
description: '/docs/references/health/get-queue-stats-resources.md',
responses: [
@@ -731,6 +748,7 @@ App::get('/v1/health/queue/stats-usage')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueUsage',
description: '/docs/references/health/get-queue-stats-usage.md',
responses: [
@@ -756,38 +774,6 @@ App::get('/v1/health/queue/stats-usage')
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
});
App::get('/v1/health/queue/stats-usage-dump')
->desc('Get usage dump queue')
->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
name: 'getQueueStatsUsageDump',
description: '/docs/references/health/get-queue-stats-usage-dump.md',
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_HEALTH_QUEUE,
)
],
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::STATS_USAGE_DUMP_QUEUE_NAME));
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
}
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
});
App::get('/v1/health/storage/local')
->desc('Get local storage')
->groups(['api', 'health'])
@@ -795,6 +781,7 @@ App::get('/v1/health/storage/local')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'storage',
name: 'getStorageLocal',
description: '/docs/references/health/get-storage-local.md',
responses: [
@@ -844,6 +831,7 @@ App::get('/v1/health/storage')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'storage',
name: 'getStorage',
description: '/docs/references/health/get-storage.md',
responses: [
@@ -857,10 +845,9 @@ App::get('/v1/health/storage')
->inject('response')
->inject('deviceForFiles')
->inject('deviceForFunctions')
->inject('deviceForSites')
->inject('deviceForBuilds')
->action(function (Response $response, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForSites, Device $deviceForBuilds) {
$devices = [$deviceForFiles, $deviceForFunctions, $deviceForSites, $deviceForBuilds];
->action(function (Response $response, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds) {
$devices = [$deviceForFiles, $deviceForFunctions, $deviceForBuilds];
$checkStart = \microtime(true);
foreach ($devices as $device) {
@@ -892,6 +879,7 @@ App::get('/v1/health/anti-virus')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getAntivirus',
description: '/docs/references/health/get-storage-anti-virus.md',
responses: [
@@ -937,6 +925,7 @@ App::get('/v1/health/queue/failed/:name')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getFailedJobs',
description: '/docs/references/health/get-failed-queue-jobs.md',
responses: [
@@ -955,7 +944,6 @@ App::get('/v1/health/queue/failed/:name')
Event::FUNCTIONS_QUEUE_NAME,
Event::STATS_RESOURCES_QUEUE_NAME,
Event::STATS_USAGE_QUEUE_NAME,
Event::STATS_USAGE_DUMP_QUEUE_NAME,
Event::WEBHOOK_QUEUE_NAME,
Event::CERTIFICATES_QUEUE_NAME,
Event::BUILDS_QUEUE_NAME,
@@ -981,9 +969,6 @@ App::get('/v1/health/stats') // Currently only used internally
->desc('Get system stats')
->groups(['api', 'health'])
->label('scope', 'root')
// ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
// ->label('sdk.namespace', 'health')
// ->label('sdk.method', 'getStats')
->label('docs', false)
->inject('response')
->inject('register')
+8
View File
@@ -17,6 +17,7 @@ App::get('/v1/locale')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'get',
description: '/docs/references/locale/get-locale.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -80,6 +81,7 @@ App::get('/v1/locale/codes')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCodes',
description: '/docs/references/locale/list-locale-codes.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -105,6 +107,7 @@ App::get('/v1/locale/countries')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCountries',
description: '/docs/references/locale/list-countries.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -141,6 +144,7 @@ App::get('/v1/locale/countries/eu')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCountriesEU',
description: '/docs/references/locale/list-countries-eu.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -179,6 +183,7 @@ App::get('/v1/locale/countries/phones')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCountriesPhones',
description: '/docs/references/locale/list-countries-phones.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -216,6 +221,7 @@ App::get('/v1/locale/continents')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listContinents',
description: '/docs/references/locale/list-continents.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -251,6 +257,7 @@ App::get('/v1/locale/currencies')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCurrencies',
description: '/docs/references/locale/list-currencies.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -277,6 +284,7 @@ App::get('/v1/locale/languages')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listLanguages',
description: '/docs/references/locale/list-languages.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
+84 -14
View File
@@ -30,6 +30,7 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
@@ -63,6 +64,7 @@ App::post('/v1/messaging/providers/mailgun')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createMailgunProvider',
description: '/docs/references/messaging/create-mailgun-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -156,6 +158,7 @@ App::post('/v1/messaging/providers/sendgrid')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createSendgridProvider',
description: '/docs/references/messaging/create-sendgrid-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -237,6 +240,7 @@ App::post('/v1/messaging/providers/smtp')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createSmtpProvider',
description: '/docs/references/messaging/create-smtp-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -331,6 +335,7 @@ App::post('/v1/messaging/providers/msg91')
->label('event', 'providers.[providerId].create')
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createMsg91Provider',
description: '/docs/references/messaging/create-msg91-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -413,6 +418,7 @@ App::post('/v1/messaging/providers/telesign')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createTelesignProvider',
description: '/docs/references/messaging/create-telesign-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -496,6 +502,7 @@ App::post('/v1/messaging/providers/textmagic')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createTextmagicProvider',
description: '/docs/references/messaging/create-textmagic-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -579,6 +586,7 @@ App::post('/v1/messaging/providers/twilio')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createTwilioProvider',
description: '/docs/references/messaging/create-twilio-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -662,6 +670,7 @@ App::post('/v1/messaging/providers/vonage')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createVonageProvider',
description: '/docs/references/messaging/create-vonage-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -745,6 +754,7 @@ App::post('/v1/messaging/providers/fcm')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createFcmProvider',
description: '/docs/references/messaging/create-fcm-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -814,6 +824,7 @@ App::post('/v1/messaging/providers/apns')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createApnsProvider',
description: '/docs/references/messaging/create-apns-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -903,6 +914,7 @@ App::get('/v1/messaging/providers')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'listProviders',
description: '/docs/references/messaging/list-providers.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -951,10 +963,15 @@ App::get('/v1/messaging/providers')
$cursor->setValue($cursorDocument);
}
try {
$providers = $dbForProject->find('providers', $queries);
$total = $dbForProject->count('providers', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'providers' => $dbForProject->find('providers', $queries),
'total' => $dbForProject->count('providers', $queries, APP_LIMIT_COUNT),
'providers' => $providers,
'total' => $total,
]), Response::MODEL_PROVIDER_LIST);
});
@@ -965,6 +982,7 @@ App::get('/v1/messaging/providers/:providerId/logs')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'listProviderLogs',
description: '/docs/references/messaging/list-provider-logs.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1061,6 +1079,7 @@ App::get('/v1/messaging/providers/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'getProvider',
description: '/docs/references/messaging/get-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1094,6 +1113,7 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateMailgunProvider',
description: '/docs/references/messaging/update-mailgun-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1206,6 +1226,7 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateSendgridProvider',
description: '/docs/references/messaging/update-sendgrid-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1303,6 +1324,7 @@ App::patch('/v1/messaging/providers/smtp/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateSmtpProvider',
description: '/docs/references/messaging/update-smtp-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1431,6 +1453,7 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateMsg91Provider',
description: '/docs/references/messaging/update-msg91-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1517,6 +1540,7 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateTelesignProvider',
description: '/docs/references/messaging/update-telesign-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1605,6 +1629,7 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateTextmagicProvider',
description: '/docs/references/messaging/update-textmagic-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1693,6 +1718,7 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateTwilioProvider',
description: '/docs/references/messaging/update-twilio-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1781,6 +1807,7 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateVonageProvider',
description: '/docs/references/messaging/update-vonage-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1869,6 +1896,7 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateFcmProvider',
description: '/docs/references/messaging/update-fcm-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1944,6 +1972,7 @@ App::patch('/v1/messaging/providers/apns/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateApnsProvider',
description: '/docs/references/messaging/update-apns-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2045,6 +2074,7 @@ App::delete('/v1/messaging/providers/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'deleteProvider',
description: '/docs/references/messaging/delete-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2087,6 +2117,7 @@ App::post('/v1/messaging/topics')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'createTopic',
description: '/docs/references/messaging/create-topic.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2133,6 +2164,7 @@ App::get('/v1/messaging/topics')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'listTopics',
description: '/docs/references/messaging/list-topics.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2181,10 +2213,15 @@ App::get('/v1/messaging/topics')
$cursor->setValue($cursorDocument[0]);
}
try {
$topics = $dbForProject->find('topics', $queries);
$total = $dbForProject->count('topics', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'topics' => $dbForProject->find('topics', $queries),
'total' => $dbForProject->count('topics', $queries, APP_LIMIT_COUNT),
'topics' => $topics,
'total' => $total,
]), Response::MODEL_TOPIC_LIST);
});
@@ -2195,6 +2232,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'listTopicLogs',
description: '/docs/references/messaging/list-topic-logs.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2292,6 +2330,7 @@ App::get('/v1/messaging/topics/:topicId')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'getTopic',
description: '/docs/references/messaging/get-topic.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2326,6 +2365,7 @@ App::patch('/v1/messaging/topics/:topicId')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'updateTopic',
description: '/docs/references/messaging/update-topic.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2376,6 +2416,7 @@ App::delete('/v1/messaging/topics/:topicId')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'deleteTopic',
description: '/docs/references/messaging/delete-topic.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2423,6 +2464,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'createSubscriber',
description: '/docs/references/messaging/create-subscriber.md',
auth: [AuthType::JWT, AuthType::SESSION, AuthType::ADMIN, AuthType::KEY],
@@ -2522,6 +2564,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'listSubscribers',
description: '/docs/references/messaging/list-subscribers.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2579,8 +2622,11 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
$cursor->setValue($cursorDocument);
}
$subscribers = $dbForProject->find('subscribers', $queries);
try {
$subscribers = $dbForProject->find('subscribers', $queries);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) {
return function () use ($subscriber, $dbForProject) {
@@ -2607,6 +2653,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'listSubscriberLogs',
description: '/docs/references/messaging/list-subscriber-logs.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2704,6 +2751,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'getSubscriber',
description: '/docs/references/messaging/get-subscriber.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2752,6 +2800,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'deleteSubscriber',
description: '/docs/references/messaging/delete-subscriber.md',
auth: [AuthType::JWT, AuthType::SESSION, AuthType::ADMIN, AuthType::KEY],
@@ -2818,6 +2867,7 @@ App::post('/v1/messaging/messages/email')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'createEmail',
description: '/docs/references/messaging/create-email.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2976,6 +3026,7 @@ App::post('/v1/messaging/messages/sms')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'createSms',
description: '/docs/references/messaging/create-sms.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3098,6 +3149,7 @@ App::post('/v1/messaging/messages/push')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'createPush',
description: '/docs/references/messaging/create-push.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3312,6 +3364,7 @@ App::get('/v1/messaging/messages')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'listMessages',
description: '/docs/references/messaging/list-messages.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3360,10 +3413,15 @@ App::get('/v1/messaging/messages')
$cursor->setValue($cursorDocument);
}
try {
$messages = $dbForProject->find('messages', $queries);
$total = $dbForProject->count('messages', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'messages' => $dbForProject->find('messages', $queries),
'total' => $dbForProject->count('messages', $queries, APP_LIMIT_COUNT),
'messages' => $messages,
'total' => $total,
]), Response::MODEL_MESSAGE_LIST);
});
@@ -3374,6 +3432,7 @@ App::get('/v1/messaging/messages/:messageId/logs')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'logs',
name: 'listMessageLogs',
description: '/docs/references/messaging/list-message-logs.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3471,6 +3530,7 @@ App::get('/v1/messaging/messages/:messageId/targets')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'listTargets',
description: '/docs/references/messaging/list-message-targets.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3533,10 +3593,15 @@ App::get('/v1/messaging/messages/:messageId/targets')
$cursor->setValue($cursorDocument);
}
try {
$targets = $dbForProject->find('targets', $queries);
$total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'targets' => $dbForProject->find('targets', $queries),
'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT),
'targets' => $targets,
'total' => $total,
]), Response::MODEL_TARGET_LIST);
});
@@ -3547,6 +3612,7 @@ App::get('/v1/messaging/messages/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'getMessage',
description: '/docs/references/messaging/get-message.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3580,6 +3646,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'updateEmail',
description: '/docs/references/messaging/update-email.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3786,6 +3853,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'updateSms',
description: '/docs/references/messaging/update-sms.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3947,6 +4015,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'updatePush',
description: '/docs/references/messaging/update-push.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -4206,6 +4275,7 @@ App::delete('/v1/messaging/messages/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'delete',
description: '/docs/references/messaging/delete-message.md',
auth: [AuthType::ADMIN, AuthType::KEY],
+32 -148
View File
@@ -1,38 +1,27 @@
<?php
use Appwrite\Auth\Auth;
use Appwrite\Event\Event;
use Appwrite\Event\Migration;
use Appwrite\Extend\Exception;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Database\Validator\CompoundUID;
use Appwrite\Utopia\Database\Validator\Queries\Migrations;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
use Utopia\Migration\Resource;
use Utopia\Migration\Sources\Appwrite;
use Utopia\Migration\Sources\CSV;
use Utopia\Migration\Sources\Firebase;
use Utopia\Migration\Sources\NHost;
use Utopia\Migration\Sources\Supabase;
use Utopia\Migration\Transfer;
use Utopia\Storage\Compression\Algorithms\GZIP;
use Utopia\Storage\Compression\Algorithms\Zstd;
use Utopia\Storage\Compression\Compression;
use Utopia\Storage\Device;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Integer;
use Utopia\Validator\Text;
@@ -43,12 +32,13 @@ include_once __DIR__ . '/../shared/api.php';
App::post('/v1/migrations/appwrite')
->groups(['api', 'migrations'])
->desc('Migrate Appwrite data')
->desc('Create Appwrite migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createAppwriteMigration',
description: '/docs/references/migrations/migration-appwrite.md',
auth: [AuthType::ADMIN],
@@ -101,14 +91,16 @@ App::post('/v1/migrations/appwrite')
->dynamic($migration, Response::MODEL_MIGRATION);
});
App::post('/v1/migrations/firebase')
->groups(['api', 'migrations'])
->desc('Migrate Firebase data')
->desc('Create Firebase migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createFirebaseMigration',
description: '/docs/references/migrations/migration-firebase.md',
auth: [AuthType::ADMIN],
@@ -169,12 +161,13 @@ App::post('/v1/migrations/firebase')
App::post('/v1/migrations/supabase')
->groups(['api', 'migrations'])
->desc('Migrate Supabase data')
->desc('Create Supabase migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createSupabaseMigration',
description: '/docs/references/migrations/migration-supabase.md',
auth: [AuthType::ADMIN],
@@ -235,12 +228,13 @@ App::post('/v1/migrations/supabase')
App::post('/v1/migrations/nhost')
->groups(['api', 'migrations'])
->desc('Migrate NHost data')
->desc('Create NHost migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createNHostMigration',
description: '/docs/references/migrations/migration-nhost.md',
auth: [AuthType::ADMIN],
@@ -301,136 +295,13 @@ App::post('/v1/migrations/nhost')
->dynamic($migration, Response::MODEL_MIGRATION);
});
App::post('/v1/migrations/csv')
->groups(['api', 'migrations'])
->desc('Import documents from a CSV')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
name: 'createCsvMigration',
description: '/docs/references/migrations/migration-csv.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_ACCEPTED,
model: Response::MODEL_MIGRATION,
)
]
))
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.')
->param('resourceId', null, new CompoundUID(), 'Composite ID in the format {databaseId:collectionId}, identifying a collection within a database.')
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('deviceForFiles')
->inject('deviceForImports')
->inject('queueForEvents')
->inject('queueForMigrations')
->action(function (string $bucketId, string $fileId, string $resourceId, Response $response, Database $dbForProject, Document $project, Device $deviceForFiles, Device $deviceForImports, Event $queueForEvents, Migration $queueForMigrations) {
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
$path = $file->getAttribute('path', '');
if (!$deviceForFiles->exists($path)) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path);
}
// no encryption, compression on files above 20MB.
$hasEncryption = !empty($file->getAttribute('openSSLCipher'));
$compression = $file->getAttribute('algorithm', Compression::NONE);
$hasCompression = $compression !== Compression::NONE;
$migrationId = ID::unique();
$newPath = $deviceForImports->getPath('/' . $migrationId . '_' . $fileId . '.csv');
if ($hasEncryption || $hasCompression) {
$source = $deviceForFiles->read($path);
// 1. decrypt
if ($hasEncryption) {
$source = OpenSSL::decrypt(
$source,
$file->getAttribute('openSSLCipher'),
System::getEnv('_APP_OPENSSL_KEY_V' . $file->getAttribute('openSSLVersion')),
0,
hex2bin($file->getAttribute('openSSLIV')),
hex2bin($file->getAttribute('openSSLTag'))
);
}
// 2. decompress
if ($hasCompression) {
switch ($compression) {
case Compression::ZSTD:
$source = (new Zstd())->decompress($source);
break;
case Compression::GZIP:
$source = (new GZIP())->decompress($source);
break;
}
}
// manual write after decryption and/or decompression
if (! $deviceForImports->write($newPath, $source, 'text/csv')) {
throw new \Exception("Unable to copy file");
}
} elseif (! $deviceForFiles->transfer($path, $newPath, $deviceForImports)) {
throw new \Exception("Unable to copy file");
}
$fileSize = $deviceForImports->getFileSize($newPath);
$resources = Transfer::extractServices([Transfer::GROUP_DATABASES]);
$migration = $dbForProject->createDocument('migrations', new Document([
'$id' => $migrationId,
'status' => 'pending',
'stage' => 'init',
'source' => CSV::getName(),
'destination' => Appwrite::getName(),
'resources' => $resources,
'resourceId' => $resourceId,
'resourceType' => Resource::TYPE_DATABASE,
'statusCounters' => [],
'resourceData' => [],
'errors' => [],
'options' => [
'path' => $newPath,
'size' => $fileSize,
],
]));
$queueForEvents->setParam('migrationId', $migration->getId());
$queueForMigrations
->setMigration($migration)
->setProject($project)
->trigger();
$response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($migration, Response::MODEL_MIGRATION);
});
App::get('/v1/migrations')
->groups(['api', 'migrations'])
->desc('List migrations')
->label('scope', 'migrations.read')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'list',
description: '/docs/references/migrations/list-migrations.md',
auth: [AuthType::ADMIN],
@@ -482,10 +353,15 @@ App::get('/v1/migrations')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$migrations = $dbForProject->find('migrations', $queries);
$total = $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'migrations' => $dbForProject->find('migrations', $queries),
'total' => $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT),
'migrations' => $migrations,
'total' => $total,
]), Response::MODEL_MIGRATION_LIST);
});
@@ -495,6 +371,7 @@ App::get('/v1/migrations/:migrationId')
->label('scope', 'migrations.read')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'get',
description: '/docs/references/migrations/get-migration.md',
auth: [AuthType::ADMIN],
@@ -520,10 +397,11 @@ App::get('/v1/migrations/:migrationId')
App::get('/v1/migrations/appwrite/report')
->groups(['api', 'migrations'])
->desc('Generate a report on Appwrite data')
->desc('Get Appwrite migration report')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'getAppwriteReport',
description: '/docs/references/migrations/migration-appwrite-report.md',
auth: [AuthType::ADMIN],
@@ -543,6 +421,7 @@ App::get('/v1/migrations/appwrite/report')
->inject('project')
->inject('user')
->action(function (array $resources, string $endpoint, string $projectID, string $key, Response $response) {
$appwrite = new Appwrite($projectID, $endpoint, $key);
try {
@@ -567,10 +446,11 @@ App::get('/v1/migrations/appwrite/report')
App::get('/v1/migrations/firebase/report')
->groups(['api', 'migrations'])
->desc('Generate a report on Firebase data')
->desc('Get Firebase migration report')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'getFirebaseReport',
description: '/docs/references/migrations/migration-firebase-report.md',
auth: [AuthType::ADMIN],
@@ -619,10 +499,11 @@ App::get('/v1/migrations/firebase/report')
App::get('/v1/migrations/supabase/report')
->groups(['api', 'migrations'])
->desc('Generate a report on Supabase Data')
->desc('Get Supabase migration report')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'getSupabaseReport',
description: '/docs/references/migrations/migration-supabase-report.md',
auth: [AuthType::ADMIN],
@@ -667,10 +548,11 @@ App::get('/v1/migrations/supabase/report')
App::get('/v1/migrations/nhost/report')
->groups(['api', 'migrations'])
->desc('Generate a report on NHost Data')
->desc('Get NHost migration report')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'getNHostReport',
description: '/docs/references/migrations/migration-nhost-report.md',
auth: [AuthType::ADMIN],
@@ -715,13 +597,14 @@ App::get('/v1/migrations/nhost/report')
App::patch('/v1/migrations/:migrationId')
->groups(['api', 'migrations'])
->desc('Retry migration')
->desc('Update retry migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].retry')
->label('audits.event', 'migration.retry')
->label('audits.resource', 'migrations/{request.migrationId}')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'retry',
description: '/docs/references/migrations/retry-migration.md',
auth: [AuthType::ADMIN],
@@ -772,6 +655,7 @@ App::delete('/v1/migrations/:migrationId')
->label('audits.resource', 'migrations/{request.migrationId}')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'delete',
description: '/docs/references/migrations/delete-migration.md',
auth: [AuthType::ADMIN],
+15 -18
View File
@@ -17,7 +17,6 @@ use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DateTimeValidator;
use Utopia\Database\Validator\UID;
use Utopia\Validator\Boolean;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
@@ -27,6 +26,7 @@ App::get('/v1/project/usage')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'getUsage',
description: '/docs/references/project/get-usage.md',
auth: [AuthType::ADMIN],
@@ -149,7 +149,7 @@ App::get('/v1/project/usage')
$executionsBreakdown = array_map(function ($function) use ($dbForProject) {
$id = $function->getId();
$name = $function->getAttribute('name');
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS);
$metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS);
$value = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
@@ -165,7 +165,7 @@ App::get('/v1/project/usage')
$executionsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
$id = $function->getId();
$name = $function->getAttribute('name');
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS);
$metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS);
$value = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
@@ -181,7 +181,7 @@ App::get('/v1/project/usage')
$buildsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
$id = $function->getId();
$name = $function->getAttribute('name');
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS);
$metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_MB_SECONDS);
$value = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
@@ -230,13 +230,13 @@ App::get('/v1/project/usage')
$functionsStorageBreakdown = array_map(function ($function) use ($dbForProject) {
$id = $function->getId();
$name = $function->getAttribute('name');
$deploymentMetric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE);
$deploymentMetric = str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $function->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE);
$deploymentValue = $dbForProject->findOne('stats', [
Query::equal('metric', [$deploymentMetric]),
Query::equal('period', ['inf'])
]);
$buildMetric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE);
$buildMetric = str_replace(['{functionInternalId}'], [$function->getInternalId()], METRIC_FUNCTION_ID_BUILDS_STORAGE);
$buildValue = $dbForProject->findOne('stats', [
Query::equal('metric', [$buildMetric]),
Query::equal('period', ['inf'])
@@ -254,7 +254,7 @@ App::get('/v1/project/usage')
$executionsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
$id = $function->getId();
$name = $function->getAttribute('name');
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS);
$metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS);
$value = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
@@ -270,7 +270,7 @@ App::get('/v1/project/usage')
$buildsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
$id = $function->getId();
$name = $function->getAttribute('name');
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS);
$metric = str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_MB_SECONDS);
$value = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
@@ -388,6 +388,7 @@ App::post('/v1/project/variables')
->label('audits.event', 'variable.create')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'createVariable',
description: '/docs/references/project/create-variable.md',
auth: [AuthType::ADMIN],
@@ -400,12 +401,11 @@ App::post('/v1/project/variables')
))
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
->param('secret', true, new Boolean(), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
->inject('project')
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->action(function (string $key, string $value, bool $secret, Document $project, Response $response, Database $dbForProject, Database $dbForPlatform) {
->action(function (string $key, string $value, Document $project, Response $response, Database $dbForProject, Database $dbForPlatform) {
$variableId = ID::unique();
$variable = new Document([
@@ -420,7 +420,6 @@ App::post('/v1/project/variables')
'resourceType' => 'project',
'key' => $key,
'value' => $value,
'secret' => $secret,
'search' => implode(' ', [$variableId, $key, 'project']),
]);
@@ -449,6 +448,7 @@ App::get('/v1/project/variables')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'listVariables',
description: '/docs/references/project/list-variables.md',
auth: [AuthType::ADMIN],
@@ -479,6 +479,7 @@ App::get('/v1/project/variables/:variableId')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'getVariable',
description: '/docs/references/project/get-variable.md',
auth: [AuthType::ADMIN],
@@ -508,6 +509,7 @@ App::put('/v1/project/variables/:variableId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'updateVariable',
description: '/docs/references/project/update-variable.md',
auth: [AuthType::ADMIN],
@@ -521,25 +523,19 @@ App::put('/v1/project/variables/:variableId')
->param('variableId', '', new UID(), 'Variable unique ID.', false)
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true)
->param('secret', null, new Boolean(), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
->inject('project')
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->action(function (string $variableId, string $key, ?string $value, ?bool $secret, Document $project, Response $response, Database $dbForProject, Database $dbForPlatform) {
->action(function (string $variableId, string $key, ?string $value, Document $project, Response $response, Database $dbForProject, Database $dbForPlatform) {
$variable = $dbForProject->getDocument('variables', $variableId);
if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceType') !== 'project') {
throw new Exception(Exception::VARIABLE_NOT_FOUND);
}
if ($variable->getAttribute('secret') === true && $secret === false) {
throw new Exception(Exception::VARIABLE_CANNOT_UNSET_SECRET);
}
$variable
->setAttribute('key', $key)
->setAttribute('value', $value ?? $variable->getAttribute('value'))
->setAttribute('secret', $secret ?? $variable->getAttribute('secret'))
->setAttribute('search', implode(' ', [$variableId, $key, 'project']));
try {
@@ -565,6 +561,7 @@ App::delete('/v1/project/variables/:variableId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'deleteVariable',
description: '/docs/references/project/delete-variable.md',
auth: [AuthType::ADMIN],
+68 -8
View File
@@ -28,6 +28,7 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -68,6 +69,7 @@ App::post('/v1/projects')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'create',
description: '/docs/references/projects/create.md',
auth: [AuthType::ADMIN],
@@ -138,6 +140,14 @@ App::post('/v1/projects')
$databases = Config::getParam('pools-database', []);
if ($region !== 'default') {
$databaseKeys = System::getEnv('_APP_DATABASE_KEYS', '');
$keys = explode(',', $databaseKeys);
$databases = array_filter($keys, function ($value) use ($region) {
return str_contains($value, $region);
});
}
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
$index = \array_search($databaseOverride, $databases);
if ($index !== false) {
@@ -205,17 +215,17 @@ App::post('/v1/projects')
$dsn = new DSN('mysql://' . $dsn);
}
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
$dbForProject = new Database($adapter, $cache);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
$projectTables = !\in_array($dsn->getHost(), $sharedTables);
$sharedTablesV1 = \in_array($dsn->getHost(), $sharedTablesV1);
$sharedTablesV2 = !$projectTables && !$sharedTablesV1;
$sharedTables = $sharedTablesV1 || $sharedTablesV2;
if (!$sharedTablesV2) {
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
$dbForProject = new Database($adapter, $cache);
if ($sharedTables) {
$dbForProject
->setSharedTables(true)
@@ -297,6 +307,7 @@ App::get('/v1/projects')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'list',
description: '/docs/references/projects/list.md',
auth: [AuthType::ADMIN],
@@ -349,10 +360,15 @@ App::get('/v1/projects')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$projects = $dbForPlatform->find('projects', $queries);
$total = $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'projects' => $dbForPlatform->find('projects', $queries),
'total' => $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT),
'projects' => $projects,
'total' => $total,
]), Response::MODEL_PROJECT_LIST);
});
@@ -362,6 +378,7 @@ App::get('/v1/projects/:projectId')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'get',
description: '/docs/references/projects/get.md',
auth: [AuthType::ADMIN],
@@ -394,6 +411,7 @@ App::patch('/v1/projects/:projectId')
->label('audits.resource', 'project/{request.projectId}')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'update',
description: '/docs/references/projects/update.md',
auth: [AuthType::ADMIN],
@@ -447,6 +465,7 @@ App::patch('/v1/projects/:projectId/team')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateTeam',
description: '/docs/references/projects/update-team.md',
auth: [AuthType::ADMIN],
@@ -521,6 +540,7 @@ App::patch('/v1/projects/:projectId/service')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateServiceStatus',
description: '/docs/references/projects/update-service-status.md',
auth: [AuthType::ADMIN],
@@ -558,6 +578,7 @@ App::patch('/v1/projects/:projectId/service/all')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateServiceStatusAll',
description: '/docs/references/projects/update-service-status-all.md',
auth: [AuthType::ADMIN],
@@ -598,6 +619,7 @@ App::patch('/v1/projects/:projectId/api')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateApiStatus',
description: '/docs/references/projects/update-api-status.md',
auth: [AuthType::ADMIN],
@@ -635,6 +657,7 @@ App::patch('/v1/projects/:projectId/api/all')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateApiStatusAll',
description: '/docs/references/projects/update-api-status-all.md',
auth: [AuthType::ADMIN],
@@ -675,6 +698,7 @@ App::patch('/v1/projects/:projectId/oauth2')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateOAuth2',
description: '/docs/references/projects/update-oauth2.md',
auth: [AuthType::ADMIN],
@@ -725,6 +749,7 @@ App::patch('/v1/projects/:projectId/auth/session-alerts')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateSessionAlerts',
description: '/docs/references/projects/update-session-alerts.md',
auth: [AuthType::ADMIN],
@@ -762,6 +787,7 @@ App::patch('/v1/projects/:projectId/auth/memberships-privacy')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateMembershipsPrivacy',
description: '/docs/references/projects/update-memberships-privacy.md',
auth: [AuthType::ADMIN],
@@ -803,6 +829,7 @@ App::patch('/v1/projects/:projectId/auth/limit')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthLimit',
description: '/docs/references/projects/update-auth-limit.md',
auth: [AuthType::ADMIN],
@@ -840,6 +867,7 @@ App::patch('/v1/projects/:projectId/auth/duration')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthDuration',
description: '/docs/references/projects/update-auth-duration.md',
auth: [AuthType::ADMIN],
@@ -877,6 +905,7 @@ App::patch('/v1/projects/:projectId/auth/:method')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthStatus',
description: '/docs/references/projects/update-auth-status.md',
auth: [AuthType::ADMIN],
@@ -917,6 +946,7 @@ App::patch('/v1/projects/:projectId/auth/password-history')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthPasswordHistory',
description: '/docs/references/projects/update-auth-password-history.md',
auth: [AuthType::ADMIN],
@@ -954,6 +984,7 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthPasswordDictionary',
description: '/docs/references/projects/update-auth-password-dictionary.md',
auth: [AuthType::ADMIN],
@@ -986,11 +1017,12 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
});
App::patch('/v1/projects/:projectId/auth/personal-data')
->desc('Enable or disable checking user passwords for similarity with their personal data.')
->desc('Update personal data check')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updatePersonalDataCheck',
description: '/docs/references/projects/update-personal-data-check.md',
auth: [AuthType::ADMIN],
@@ -1028,6 +1060,7 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthSessionsLimit',
description: '/docs/references/projects/update-auth-sessions-limit.md',
auth: [AuthType::ADMIN],
@@ -1065,6 +1098,7 @@ App::patch('/v1/projects/:projectId/auth/mock-numbers')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateMockNumbers',
description: '/docs/references/projects/update-mock-numbers.md',
auth: [AuthType::ADMIN],
@@ -1112,6 +1146,7 @@ App::delete('/v1/projects/:projectId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'delete',
description: '/docs/references/projects/delete.md',
auth: [AuthType::ADMIN],
@@ -1155,6 +1190,7 @@ App::post('/v1/projects/:projectId/webhooks')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'createWebhook',
description: '/docs/references/projects/create-webhook.md',
auth: [AuthType::ADMIN],
@@ -1219,6 +1255,7 @@ App::get('/v1/projects/:projectId/webhooks')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'listWebhooks',
description: '/docs/references/projects/list-webhooks.md',
auth: [AuthType::ADMIN],
@@ -1257,6 +1294,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'getWebhook',
description: '/docs/references/projects/get-webhook.md',
auth: [AuthType::ADMIN],
@@ -1297,6 +1335,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'updateWebhook',
description: '/docs/references/projects/update-webhook.md',
auth: [AuthType::ADMIN],
@@ -1362,6 +1401,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'updateWebhookSignature',
description: '/docs/references/projects/update-webhook-signature.md',
auth: [AuthType::ADMIN],
@@ -1407,6 +1447,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'deleteWebhook',
description: '/docs/references/projects/delete-webhook.md',
auth: [AuthType::ADMIN],
@@ -1454,6 +1495,7 @@ App::post('/v1/projects/:projectId/keys')
->label('scope', 'keys.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'createKey',
description: '/docs/references/projects/create-key.md',
auth: [AuthType::ADMIN],
@@ -1510,6 +1552,7 @@ App::get('/v1/projects/:projectId/keys')
->label('scope', 'keys.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'listKeys',
description: '/docs/references/projects/list-keys.md',
auth: [AuthType::ADMIN],
@@ -1548,6 +1591,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
->label('scope', 'keys.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'getKey',
description: '/docs/references/projects/get-key.md',
auth: [AuthType::ADMIN],
@@ -1588,6 +1632,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
->label('scope', 'keys.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'updateKey',
description: '/docs/references/projects/update-key.md',
auth: [AuthType::ADMIN],
@@ -1640,6 +1685,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
->label('scope', 'keys.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'deleteKey',
description: '/docs/references/projects/delete-key.md',
auth: [AuthType::ADMIN],
@@ -1687,6 +1733,7 @@ App::post('/v1/projects/:projectId/jwts')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'createJWT',
description: '/docs/references/projects/create-jwt.md',
auth: [AuthType::ADMIN],
@@ -1730,6 +1777,7 @@ App::post('/v1/projects/:projectId/platforms')
->label('scope', 'platforms.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'createPlatform',
description: '/docs/references/projects/create-platform.md',
auth: [AuthType::ADMIN],
@@ -1786,6 +1834,7 @@ App::get('/v1/projects/:projectId/platforms')
->label('scope', 'platforms.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'listPlatforms',
description: '/docs/references/projects/list-platforms.md',
auth: [AuthType::ADMIN],
@@ -1824,6 +1873,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
->label('scope', 'platforms.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'getPlatform',
description: '/docs/references/projects/get-platform.md',
auth: [AuthType::ADMIN],
@@ -1864,6 +1914,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
->label('scope', 'platforms.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'updatePlatform',
description: '/docs/references/projects/update-platform.md',
auth: [AuthType::ADMIN],
@@ -1919,6 +1970,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
->label('scope', 'platforms.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'deletePlatform',
description: '/docs/references/projects/delete-platform.md',
auth: [AuthType::ADMIN],
@@ -1966,6 +2018,7 @@ App::patch('/v1/projects/:projectId/smtp')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'updateSmtp',
description: '/docs/references/projects/update-smtp.md',
auth: [AuthType::ADMIN],
@@ -2062,6 +2115,7 @@ App::post('/v1/projects/:projectId/smtp/tests')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'createSmtpTest',
description: '/docs/references/projects/create-smtp-test.md',
auth: [AuthType::ADMIN],
@@ -2128,6 +2182,7 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'getSmsTemplate',
description: '/docs/references/projects/get-sms-template.md',
auth: [AuthType::ADMIN],
@@ -2175,6 +2230,7 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'getEmailTemplate',
description: '/docs/references/projects/get-email-template.md',
auth: [AuthType::ADMIN],
@@ -2233,6 +2289,7 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'updateSmsTemplate',
description: '/docs/references/projects/update-sms-template.md',
auth: [AuthType::ADMIN],
@@ -2279,6 +2336,7 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'updateEmailTemplate',
description: '/docs/references/projects/update-email-template.md',
auth: [AuthType::ADMIN],
@@ -2335,6 +2393,7 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'deleteSmsTemplate',
description: '/docs/references/projects/delete-sms-template.md',
auth: [AuthType::ADMIN],
@@ -2380,11 +2439,12 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
});
App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
->desc('Reset custom email template')
->desc('Delete custom email template')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'deleteEmailTemplate',
description: '/docs/references/projects/delete-email-template.md',
auth: [AuthType::ADMIN],
+166 -25
View File
@@ -4,7 +4,7 @@ use Appwrite\Event\Certificate;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\DNS;
use Appwrite\Network\Validator\CNAME;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
@@ -15,15 +15,169 @@ use Utopia\App;
use Utopia\Database\Database;
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;
use Utopia\System\System;
use Utopia\Validator\AnyOf;
use Utopia\Validator\IP;
use Utopia\Validator\Domain as ValidatorDomain;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
App::post('/v1/proxy/rules')
->groups(['api', 'proxy'])
->desc('Create rule')
->label('scope', 'rules.write')
->label('event', 'rules.[ruleId].create')
->label('audits.event', 'rule.create')
->label('audits.resource', 'rule/{response.$id}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'createRule',
description: '/docs/references/proxy/create-rule.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_PROXY_RULE,
)
]
))
->param('domain', null, new ValidatorDomain(), 'Domain name.')
->param('resourceType', null, new WhiteList(['api', 'function']), 'Action definition for the rule. Possible values are "api", "function"')
->param('resourceId', '', new UID(), 'ID of resource for the action type. If resourceType is "api", leave empty. If resourceType is "function", provide ID of the function.', true)
->inject('response')
->inject('project')
->inject('queueForCertificates')
->inject('queueForEvents')
->inject('dbForPlatform')
->inject('dbForProject')
->action(function (string $domain, string $resourceType, string $resourceId, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject) {
$mainDomain = System::getEnv('_APP_DOMAIN', '');
if ($domain === $mainDomain) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.');
}
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS');
$denyListDomains = System::getEnv('_APP_CUSTOM_DOMAIN_DENY_LIST');
if (!empty($denyListDomains)) {
$functionsDomain .= ',' . $denyListDomains;
}
$deniedDomains = array_map('trim', explode(',', $functionsDomain));
foreach ($deniedDomains as $deniedDomain) {
if (str_ends_with($domain, $deniedDomain)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or its subdomain to a specific resource. Please use a different domain.');
}
}
if ($domain === 'localhost' || $domain === APP_HOSTNAME_INTERNAL) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please pick another one.');
}
// TODO: @christyjacob remove once we migrate the rules in 1.7.x
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
$document = $dbForPlatform->getDocument('rules', md5($domain));
} else {
$document = $dbForPlatform->findOne('rules', [
Query::equal('domain', [$domain]),
]);
}
if (!$document->isEmpty()) {
if ($document->getAttribute('projectId') === $project->getId()) {
$resourceType = $document->getAttribute('resourceType');
$resourceId = $document->getAttribute('resourceId');
$message = "Domain already assigned to '{$resourceType}' service";
if (!empty($resourceId)) {
$message .= " with ID '{$resourceId}'";
}
$message .= '.';
} else {
$message = 'Domain already assigned to different project.';
}
throw new Exception(Exception::RULE_ALREADY_EXISTS, $message);
}
$resourceInternalId = '';
if ($resourceType == 'function') {
if (empty($resourceId)) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$function = $dbForProject->getDocument('functions', $resourceId);
if ($function->isEmpty()) {
throw new Exception(Exception::RULE_RESOURCE_NOT_FOUND);
}
$resourceInternalId = $function->getInternalId();
}
try {
$domain = new Domain($domain);
} catch (\Throwable) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.');
}
// TODO: @christyjacob remove once we migrate the rules in 1.7.x
$ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain->get()) : ID::unique();
$rule = new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
'projectInternalId' => $project->getInternalId(),
'domain' => $domain->get(),
'resourceType' => $resourceType,
'resourceId' => $resourceId,
'resourceInternalId' => $resourceInternalId,
'certificateId' => '',
'owner' => '',
'region' => $project->getAttribute('region')
]);
$status = 'created';
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS');
if (!empty($functionsDomain) && \str_ends_with($domain->get(), $functionsDomain)) {
$status = 'verified';
}
if ($status === 'created') {
$target = new Domain(System::getEnv('_APP_DOMAIN_TARGET', ''));
$validator = new CNAME($target->get()); // Verify Domain with DNS records
if ($validator->isValid($domain->get())) {
$status = 'verifying';
$queueForCertificates
->setDomain(new Document([
'domain' => $rule->getAttribute('domain')
]))
->trigger();
}
}
$rule->setAttribute('status', $status);
$rule = $dbForPlatform->createDocument('rules', $rule);
$queueForEvents->setParam('ruleId', $rule->getId());
$rule->setAttribute('logs', '');
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($rule, Response::MODEL_PROXY_RULE);
});
App::get('/v1/proxy/rules')
->groups(['api', 'proxy'])
@@ -31,6 +185,7 @@ App::get('/v1/proxy/rules')
->label('scope', 'rules.read')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'listRules',
description: '/docs/references/proxy/list-rules.md',
auth: [AuthType::ADMIN],
@@ -105,6 +260,7 @@ App::get('/v1/proxy/rules/:ruleId')
->label('scope', 'rules.read')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'getRule',
description: '/docs/references/proxy/get-rule.md',
auth: [AuthType::ADMIN],
@@ -142,6 +298,7 @@ App::delete('/v1/proxy/rules/:ruleId')
->label('audits.resource', 'rule/{request.ruleId}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'deleteRule',
description: '/docs/references/proxy/delete-rule.md',
auth: [AuthType::ADMIN],
@@ -186,6 +343,7 @@ App::patch('/v1/proxy/rules/:ruleId/verification')
->label('audits.resource', 'rule/{response.$id}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'updateRuleVerification',
description: '/docs/references/proxy/update-rule-verification.md',
auth: [AuthType::ADMIN],
@@ -210,27 +368,17 @@ App::patch('/v1/proxy/rules/:ruleId/verification')
throw new Exception(Exception::RULE_NOT_FOUND);
}
$validators = [];
$targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', ''));
if (!$targetCNAME->isKnown() || $targetCNAME->isTest()) {
$validators[] = new DNS($targetCNAME->get(), DNS::RECORD_CNAME);
}
if ((new IP(IP::V4))->isValid(System::getEnv('_APP_DOMAIN_TARGET_A', ''))) {
$validators[] = new DNS(System::getEnv('_APP_DOMAIN_TARGET_A', ''), DNS::RECORD_A);
}
if ((new IP(IP::V6))->isValid(System::getEnv('_APP_DOMAIN_TARGET_AAAA', ''))) {
$validators[] = new DNS(System::getEnv('_APP_DOMAIN_TARGET_AAAA', ''), DNS::RECORD_AAAA);
}
$target = new Domain(System::getEnv('_APP_DOMAIN_TARGET', ''));
if (empty($validators)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'At least one of domain targets environment variable must be configured.');
if (!$target->isKnown() || $target->isTest()) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Domain target must be configured as environment variable.');
}
if ($rule->getAttribute('verification') === true) {
return $response->dynamic($rule, Response::MODEL_PROXY_RULE);
}
$validator = new AnyOf($validators, AnyOf::TYPE_STRING);
$validator = new CNAME($target->get()); // Verify Domain with DNS records
$domain = new Domain($rule->getAttribute('domain', ''));
$validationStart = \microtime(true);
@@ -238,14 +386,7 @@ App::patch('/v1/proxy/rules/:ruleId/verification')
$log->addExtra('dnsTiming', \strval(\microtime(true) - $validationStart));
$log->addTag('dnsDomain', $domain->get());
$errors = [];
foreach ($validators as $validator) {
if (!empty($validator->getLogs())) {
$errors[] = $validator->getLogs();
}
}
$error = \implode("\n", $errors);
$error = $validator->getLogs();
$log->addExtra('dnsResponse', \is_array($error) ? \json_encode($error) : \strval($error));
throw new Exception(Exception::RULE_VERIFICATION_FAILED);
+29 -16
View File
@@ -24,6 +24,7 @@ use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\NotFound as NotFoundException;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -62,6 +63,7 @@ App::post('/v1/storage/buckets')
->label('audits.resource', 'bucket/{response.$id}')
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'createBucket',
description: '/docs/references/storage/create-bucket.md',
auth: [AuthType::KEY],
@@ -164,6 +166,7 @@ App::get('/v1/storage/buckets')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'listBuckets',
description: '/docs/references/storage/list-buckets.md',
auth: [AuthType::KEY],
@@ -216,10 +219,15 @@ App::get('/v1/storage/buckets')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$buckets = $dbForProject->find('buckets', $queries);
$total = $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'buckets' => $dbForProject->find('buckets', $queries),
'total' => $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT),
'buckets' => $buckets,
'total' => $total,
]), Response::MODEL_BUCKET_LIST);
});
@@ -230,6 +238,7 @@ App::get('/v1/storage/buckets/:bucketId')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'getBucket',
description: '/docs/references/storage/get-bucket.md',
auth: [AuthType::KEY],
@@ -264,6 +273,7 @@ App::put('/v1/storage/buckets/:bucketId')
->label('audits.resource', 'bucket/{response.$id}')
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'updateBucket',
description: '/docs/references/storage/update-bucket.md',
auth: [AuthType::KEY],
@@ -334,6 +344,7 @@ App::delete('/v1/storage/buckets/:bucketId')
->label('audits.resource', 'bucket/{request.bucketId}')
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'deleteBucket',
description: '/docs/references/storage/delete-bucket.md',
auth: [AuthType::KEY],
@@ -387,6 +398,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'createFile',
description: '/docs/references/storage/create-file.md',
type: MethodType::UPLOAD,
@@ -756,6 +768,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'listFiles',
description: '/docs/references/storage/list-files.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -837,6 +850,8 @@ App::get('/v1/storage/buckets/:bucketId/files')
}
} catch (NotFoundException) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
@@ -853,6 +868,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'getFile',
description: '/docs/references/storage/get-file.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -909,6 +925,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->label('cache.resource', 'file/{request.fileId}')
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'getFilePreview',
description: '/docs/references/storage/get-file-preview.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -936,10 +953,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true)
->inject('response')
->inject('dbForProject')
->inject('resourceToken')
->inject('deviceForFiles')
->inject('deviceForLocal')
->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Response $response, Database $dbForProject, Document $resourceToken, Device $deviceForFiles, Device $deviceForLocal) {
->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Response $response, Database $dbForProject, Device $deviceForFiles, Device $deviceForLocal) {
if (!\extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
@@ -954,24 +970,19 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getInternalId();
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid && !$isToken) {
if (!$fileSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($fileSecurity && !$valid && !$isToken) {
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
} else {
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
}
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getInternalId()) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
@@ -1097,6 +1108,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'getFileDownload',
description: '/docs/references/storage/get-file-download.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1245,6 +1257,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'getFileView',
description: '/docs/references/storage/get-file-view.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1402,9 +1415,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->groups(['api', 'storage'])
->label('scope', 'public')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location')
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.')
->param('jwt', '', new Text(2048, 0), 'JSON Web Token to validate', true)
@@ -1565,6 +1575,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'updateFile',
description: '/docs/references/storage/update-file.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1679,6 +1690,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'deleteFile',
description: '/docs/references/storage/delete-file.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1771,7 +1783,6 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
$response->noContent();
});
/** Storage usage */
App::get('/v1/storage/usage')
->desc('Get storage usage stats')
->groups(['api', 'storage'])
@@ -1779,6 +1790,7 @@ App::get('/v1/storage/usage')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: null,
name: 'getUsage',
description: '/docs/references/storage/get-usage.md',
auth: [AuthType::ADMIN],
@@ -1865,6 +1877,7 @@ App::get('/v1/storage/:bucketId/usage')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: null,
name: 'getBucketUsage',
description: '/docs/references/storage/get-bucket-usage.md',
auth: [AuthType::ADMIN],
+35 -15
View File
@@ -33,6 +33,7 @@ use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Authorization as AuthorizationException;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -51,7 +52,6 @@ use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Host;
use Utopia\Validator\Text;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList;
App::post('/v1/teams')
@@ -63,6 +63,7 @@ App::post('/v1/teams')
->label('audits.resource', 'team/{response.$id}')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'create',
description: '/docs/references/teams/create-team.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -153,6 +154,7 @@ App::get('/v1/teams')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'list',
description: '/docs/references/teams/list-teams.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -205,9 +207,12 @@ App::get('/v1/teams')
}
$filterQueries = Query::groupByType($queries)['filters'];
$results = $dbForProject->find('teams', $queries);
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
try {
$results = $dbForProject->find('teams', $queries);
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'teams' => $results,
@@ -221,6 +226,7 @@ App::get('/v1/teams/:teamId')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'get',
description: '/docs/references/teams/get-team.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -251,6 +257,7 @@ App::get('/v1/teams/:teamId/prefs')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'getPrefs',
description: '/docs/references/teams/get-team-prefs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -286,6 +293,7 @@ App::put('/v1/teams/:teamId')
->label('audits.resource', 'team/{response.$id}')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'updateName',
description: '/docs/references/teams/update-team-name.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -333,6 +341,7 @@ App::put('/v1/teams/:teamId/prefs')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'updatePrefs',
description: '/docs/references/teams/update-team-prefs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -372,6 +381,7 @@ App::delete('/v1/teams/:teamId')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'delete',
description: '/docs/references/teams/delete-team.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -430,6 +440,7 @@ App::post('/v1/teams/:teamId/memberships')
->label('audits.userId', '{request.userId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'createMembership',
description: '/docs/references/teams/create-team-membership.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -456,7 +467,7 @@ App::post('/v1/teams/:teamId/memberships')
}
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, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), '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', 'devKey']) // TODO add our own built-in confirm page
->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) // TODO add our own built-in confirm page
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
->inject('response')
->inject('project')
@@ -797,6 +808,7 @@ App::get('/v1/teams/:teamId/memberships')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'listMemberships',
description: '/docs/references/teams/list-team-members.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -860,17 +872,20 @@ App::get('/v1/teams/:teamId/memberships')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$memberships = $dbForProject->find(
collection: 'memberships',
queries: $queries,
);
$total = $dbForProject->count(
collection: 'memberships',
queries: $filterQueries,
max: APP_LIMIT_COUNT
);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$memberships = $dbForProject->find(
collection: 'memberships',
queries: $queries,
);
$total = $dbForProject->count(
collection: 'memberships',
queries: $filterQueries,
max: APP_LIMIT_COUNT
);
$memberships = array_filter($memberships, fn (Document $membership) => !empty($membership->getAttribute('userId')));
@@ -935,6 +950,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'getMembership',
description: '/docs/references/teams/get-team-member.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1021,6 +1037,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'updateMembership',
description: '/docs/references/teams/update-team-membership.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1124,6 +1141,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->label('audits.userId', '{request.userId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'updateMembershipStatus',
description: '/docs/references/teams/update-team-membership-status.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -1281,6 +1299,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'deleteMembership',
description: '/docs/references/teams/delete-team-membership.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1351,6 +1370,7 @@ App::get('/v1/teams/:teamId/logs')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'logs',
name: 'listLogs',
description: '/docs/references/teams/get-team-logs.md',
auth: [AuthType::ADMIN],
+71 -28
View File
@@ -21,7 +21,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Identities;
use Appwrite\Utopia\Database\Validator\Queries\Memberships;
use Appwrite\Utopia\Database\Validator\Queries\Targets;
use Appwrite\Utopia\Database\Validator\Queries\Users;
use Appwrite\Utopia\Request;
@@ -34,6 +33,7 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -192,6 +192,7 @@ App::post('/v1/users')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'create',
description: '/docs/references/users/create-user.md',
auth: [AuthType::KEY],
@@ -226,6 +227,7 @@ App::post('/v1/users/bcrypt')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createBcryptUser',
description: '/docs/references/users/create-bcrypt-user.md',
auth: [AuthType::KEY],
@@ -260,6 +262,7 @@ App::post('/v1/users/md5')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createMD5User',
description: '/docs/references/users/create-md5-user.md',
auth: [AuthType::KEY],
@@ -294,6 +297,7 @@ App::post('/v1/users/argon2')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createArgon2User',
description: '/docs/references/users/create-argon2-user.md',
auth: [AuthType::KEY],
@@ -328,6 +332,7 @@ App::post('/v1/users/sha')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createSHAUser',
description: '/docs/references/users/create-sha-user.md',
auth: [AuthType::KEY],
@@ -369,6 +374,7 @@ App::post('/v1/users/phpass')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createPHPassUser',
description: '/docs/references/users/create-phpass-user.md',
auth: [AuthType::KEY],
@@ -403,6 +409,7 @@ App::post('/v1/users/scrypt')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createScryptUser',
description: '/docs/references/users/create-scrypt-user.md',
auth: [AuthType::KEY],
@@ -450,6 +457,7 @@ App::post('/v1/users/scrypt-modified')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createScryptModifiedUser',
description: '/docs/references/users/create-scrypt-modified-user.md',
auth: [AuthType::KEY],
@@ -488,6 +496,7 @@ App::post('/v1/users/:userId/targets')
->label('scope', 'targets.write')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'createTarget',
description: '/docs/references/users/create-target.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -579,6 +588,7 @@ App::get('/v1/users')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'list',
description: '/docs/references/users/list-users.md',
auth: [AuthType::KEY],
@@ -631,10 +641,15 @@ App::get('/v1/users')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$users = $dbForProject->find('users', $queries);
$total = $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'users' => $dbForProject->find('users', $queries),
'total' => $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT),
'users' => $users,
'total' => $total,
]), Response::MODEL_USER_LIST);
});
@@ -644,6 +659,7 @@ App::get('/v1/users/:userId')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'get',
description: '/docs/references/users/get-user.md',
auth: [AuthType::KEY],
@@ -674,6 +690,7 @@ App::get('/v1/users/:userId/prefs')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'getPrefs',
description: '/docs/references/users/get-user-prefs.md',
auth: [AuthType::KEY],
@@ -706,6 +723,7 @@ App::get('/v1/users/:userId/targets/:targetId')
->label('scope', 'targets.read')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'getTarget',
description: '/docs/references/users/get-user-target.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -743,6 +761,7 @@ App::get('/v1/users/:userId/sessions')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'listSessions',
description: '/docs/references/users/list-user-sessions.md',
auth: [AuthType::KEY],
@@ -789,6 +808,7 @@ App::get('/v1/users/:userId/memberships')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'memberships',
name: 'listMemberships',
description: '/docs/references/users/list-user-memberships.md',
auth: [AuthType::KEY],
@@ -800,11 +820,9 @@ App::get('/v1/users/:userId/memberships')
]
))
->param('userId', '', new UID(), 'User ID.')
->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Memberships::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $userId, array $queries, string $search, Response $response, Database $dbForProject) {
->action(function (string $userId, Response $response, Database $dbForProject) {
$user = $dbForProject->getDocument('users', $userId);
@@ -812,19 +830,6 @@ App::get('/v1/users/:userId/memberships')
throw new Exception(Exception::USER_NOT_FOUND);
}
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
if (!empty($search)) {
$queries[] = Query::search('search', $search);
}
// Set internal queries
$queries[] = Query::equal('userInternalId', [$user->getInternalId()]);
$memberships = array_map(function ($membership) use ($dbForProject, $user) {
$team = $dbForProject->getDocument('teams', $membership->getAttribute('teamId'));
@@ -834,7 +839,7 @@ App::get('/v1/users/:userId/memberships')
->setAttribute('userEmail', $user->getAttribute('email'));
return $membership;
}, $dbForProject->find('memberships', $queries));
}, $user->getAttribute('memberships', []));
$response->dynamic(new Document([
'memberships' => $memberships,
@@ -848,6 +853,7 @@ App::get('/v1/users/:userId/logs')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'logs',
name: 'listLogs',
description: '/docs/references/users/list-user-logs.md',
auth: [AuthType::KEY],
@@ -944,6 +950,7 @@ App::get('/v1/users/:userId/targets')
->label('scope', 'targets.read')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'listTargets',
description: '/docs/references/users/list-user-targets.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -996,10 +1003,15 @@ App::get('/v1/users/:userId/targets')
$cursor->setValue($cursorDocument);
}
try {
$targets = $dbForProject->find('targets', $queries);
$total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'targets' => $dbForProject->find('targets', $queries),
'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT),
'targets' => $targets,
'total' => $total,
]), Response::MODEL_TARGET_LIST);
});
@@ -1009,6 +1021,7 @@ App::get('/v1/users/identities')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'identities',
name: 'listIdentities',
description: '/docs/references/users/list-identities.md',
auth: [AuthType::KEY],
@@ -1061,10 +1074,15 @@ App::get('/v1/users/identities')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$identities = $dbForProject->find('identities', $queries);
$total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'identities' => $dbForProject->find('identities', $queries),
'total' => $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT),
'identities' => $identities,
'total' => $total,
]), Response::MODEL_IDENTITY_LIST);
});
@@ -1078,6 +1096,7 @@ App::patch('/v1/users/:userId/status')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateStatus',
description: '/docs/references/users/update-user-status.md',
auth: [AuthType::KEY],
@@ -1118,6 +1137,7 @@ App::put('/v1/users/:userId/labels')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateLabels',
description: '/docs/references/users/update-user-labels.md',
auth: [AuthType::KEY],
@@ -1160,6 +1180,7 @@ App::patch('/v1/users/:userId/verification/phone')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updatePhoneVerification',
description: '/docs/references/users/update-user-phone-verification.md',
auth: [AuthType::KEY],
@@ -1201,6 +1222,7 @@ App::patch('/v1/users/:userId/name')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateName',
description: '/docs/references/users/update-user-name.md',
auth: [AuthType::KEY],
@@ -1243,6 +1265,7 @@ App::patch('/v1/users/:userId/password')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updatePassword',
description: '/docs/references/users/update-user-password.md',
auth: [AuthType::KEY],
@@ -1325,6 +1348,7 @@ App::patch('/v1/users/:userId/email')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateEmail',
description: '/docs/references/users/update-user-email.md',
auth: [AuthType::KEY],
@@ -1424,6 +1448,7 @@ App::patch('/v1/users/:userId/phone')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updatePhone',
description: '/docs/references/users/update-user-phone.md',
auth: [AuthType::KEY],
@@ -1513,6 +1538,7 @@ App::patch('/v1/users/:userId/verification')
->label('audits.userId', '{request.userId}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateEmailVerification',
description: '/docs/references/users/update-user-email-verification.md',
auth: [AuthType::KEY],
@@ -1550,6 +1576,7 @@ App::patch('/v1/users/:userId/prefs')
->label('scope', 'users.write')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updatePrefs',
description: '/docs/references/users/update-user-prefs.md',
auth: [AuthType::KEY],
@@ -1590,6 +1617,7 @@ App::patch('/v1/users/:userId/targets/:targetId')
->label('scope', 'targets.write')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'updateTarget',
description: '/docs/references/users/update-target.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -1694,6 +1722,7 @@ App::patch('/v1/users/:userId/mfa')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateMfa',
description: '/docs/references/users/update-user-mfa.md',
auth: [AuthType::KEY],
@@ -1733,6 +1762,7 @@ App::get('/v1/users/:userId/mfa/factors')
->label('usage.metric', 'users.{scope}.requests.read')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'listMfaFactors',
description: '/docs/references/users/list-mfa-factors.md',
auth: [AuthType::KEY],
@@ -1771,6 +1801,7 @@ App::get('/v1/users/:userId/mfa/recovery-codes')
->label('usage.metric', 'users.{scope}.requests.read')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'getMfaRecoveryCodes',
description: '/docs/references/users/get-mfa-recovery-codes.md',
auth: [AuthType::KEY],
@@ -1815,6 +1846,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'createMfaRecoveryCodes',
description: '/docs/references/users/create-mfa-recovery-codes.md',
auth: [AuthType::KEY],
@@ -1856,7 +1888,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
});
App::put('/v1/users/:userId/mfa/recovery-codes')
->desc('Regenerate MFA recovery codes')
->desc('Update MFA recovery codes (regenerate)')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.mfa.recovery-codes')
->label('scope', 'users.write')
@@ -1866,6 +1898,7 @@ App::put('/v1/users/:userId/mfa/recovery-codes')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'updateMfaRecoveryCodes',
description: '/docs/references/users/update-mfa-recovery-codes.md',
auth: [AuthType::KEY],
@@ -1916,6 +1949,7 @@ App::delete('/v1/users/:userId/mfa/authenticators/:type')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'deleteMfaAuthenticator',
description: '/docs/references/users/delete-mfa-authenticator.md',
auth: [AuthType::KEY],
@@ -1963,6 +1997,7 @@ App::post('/v1/users/:userId/sessions')
->label('usage.metric', 'sessions.{scope}.requests.create')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'createSession',
description: '/docs/references/users/create-session.md',
auth: [AuthType::KEY],
@@ -2047,6 +2082,7 @@ App::post('/v1/users/:userId/tokens')
->label('audits.resource', 'user/{request.userId}')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'createToken',
description: '/docs/references/users/create-token.md',
auth: [AuthType::KEY],
@@ -2109,6 +2145,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->label('audits.resource', 'user/{request.userId}')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'deleteSession',
description: '/docs/references/users/delete-user-session.md',
auth: [AuthType::KEY],
@@ -2159,6 +2196,7 @@ App::delete('/v1/users/:userId/sessions')
->label('audits.resource', 'user/{user.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'deleteSessions',
description: '/docs/references/users/delete-user-sessions.md',
auth: [AuthType::KEY],
@@ -2208,6 +2246,7 @@ App::delete('/v1/users/:userId')
->label('audits.resource', 'user/{request.userId}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'delete',
description: '/docs/references/users/delete.md',
auth: [AuthType::KEY],
@@ -2257,6 +2296,7 @@ App::delete('/v1/users/:userId/targets/:targetId')
->label('scope', 'targets.write')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'deleteTarget',
description: '/docs/references/users/delete-target.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -2314,6 +2354,7 @@ App::delete('/v1/users/identities/:identityId')
->label('audits.resource', 'identity/{request.$identityId}')
->label('sdk', new Method(
namespace: 'users',
group: 'identities',
name: 'deleteIdentity',
description: '/docs/references/users/delete-identity.md',
auth: [AuthType::KEY],
@@ -2353,6 +2394,7 @@ App::post('/v1/users/:userId/jwts')
->label('scope', 'users.write')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'createJWT',
description: '/docs/references/users/create-user-jwt.md',
auth: [AuthType::KEY],
@@ -2408,6 +2450,7 @@ App::get('/v1/users/usage')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: null,
name: 'getUsage',
description: '/docs/references/users/get-usage.md',
auth: [AuthType::ADMIN],
+131 -376
View File
@@ -14,11 +14,12 @@ use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Appwrite\Vcs\Comment;
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\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -26,35 +27,22 @@ use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Detector\Detection\Framework\Astro;
use Utopia\Detector\Detection\Framework\Flutter;
use Utopia\Detector\Detection\Framework\NextJs;
use Utopia\Detector\Detection\Framework\Nuxt;
use Utopia\Detector\Detection\Framework\Remix;
use Utopia\Detector\Detection\Framework\SvelteKit;
use Utopia\Detector\Detection\Packager\NPM;
use Utopia\Detector\Detection\Packager\PNPM;
use Utopia\Detector\Detection\Packager\Yarn;
use Utopia\Detector\Detection\Runtime\Bun;
use Utopia\Detector\Detection\Runtime\CPP;
use Utopia\Detector\Detection\Runtime\Dart;
use Utopia\Detector\Detection\Runtime\Deno;
use Utopia\Detector\Detection\Runtime\Dotnet;
use Utopia\Detector\Detection\Runtime\Java;
use Utopia\Detector\Detection\Runtime\Node;
use Utopia\Detector\Detection\Runtime\PHP;
use Utopia\Detector\Detection\Runtime\Python;
use Utopia\Detector\Detection\Runtime\Ruby;
use Utopia\Detector\Detection\Runtime\Swift;
use Utopia\Detector\Detector\Framework;
use Utopia\Detector\Detector\Packager;
use Utopia\Detector\Detector\Runtime;
use Utopia\Detector\Detector\Strategy;
use Utopia\Detector\Adapter\Bun;
use Utopia\Detector\Adapter\CPP;
use Utopia\Detector\Adapter\Dart;
use Utopia\Detector\Adapter\Deno;
use Utopia\Detector\Adapter\Dotnet;
use Utopia\Detector\Adapter\Java;
use Utopia\Detector\Adapter\JavaScript;
use Utopia\Detector\Adapter\PHP;
use Utopia\Detector\Adapter\Python;
use Utopia\Detector\Adapter\Ruby;
use Utopia\Detector\Adapter\Swift;
use Utopia\Detector\Detector;
use Utopia\System\System;
use Utopia\Validator\Boolean;
use Utopia\Validator\Host;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
use Utopia\VCS\Adapter\Git\GitHub;
use Utopia\VCS\Exception\RepositoryNotFound;
@@ -62,30 +50,29 @@ use function Swoole\Coroutine\batch;
$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForPlatform, Build $queueForBuilds, callable $getProjectDB, Request $request) {
$errors = [];
foreach ($repositories as $repository) {
foreach ($repositories as $resource) {
try {
$resourceType = $repository->getAttribute('resourceType');
$resourceType = $resource->getAttribute('resourceType');
if ($resourceType !== "function" && $resourceType !== "site") {
if ($resourceType !== "function") {
continue;
}
$projectId = $repository->getAttribute('projectId');
$projectId = $resource->getAttribute('projectId');
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$dbForProject = $getProjectDB($project);
$resourceCollection = $resourceType === "function" ? 'functions' : 'sites';
$resourceId = $repository->getAttribute('resourceId');
$resource = Authorization::skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId));
$resourceInternalId = $resource->getInternalId();
$functionId = $resource->getAttribute('resourceId');
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
$functionInternalId = $function->getInternalId();
$deploymentId = ID::unique();
$repositoryId = $repository->getId();
$repositoryInternalId = $repository->getInternalId();
$providerRepositoryId = $repository->getAttribute('providerRepositoryId');
$installationId = $repository->getAttribute('installationId');
$installationInternalId = $repository->getAttribute('installationInternalId');
$productionBranch = $resource->getAttribute('providerBranch');
$repositoryId = $resource->getId();
$repositoryInternalId = $resource->getInternalId();
$providerRepositoryId = $resource->getAttribute('providerRepositoryId');
$installationId = $resource->getAttribute('installationId');
$installationInternalId = $resource->getAttribute('installationInternalId');
$productionBranch = $function->getAttribute('providerBranch');
$activate = false;
if ($providerBranch == $productionBranch && $external === false) {
@@ -109,7 +96,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$isAuthorized = !$external;
if (!$isAuthorized && !empty($providerPullRequestId)) {
if (\in_array($providerPullRequestId, $repository->getAttribute('providerPullRequestIds', []))) {
if (\in_array($providerPullRequestId, $resource->getAttribute('providerPullRequestIds', []))) {
$isAuthorized = true;
}
}
@@ -122,7 +109,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$latestCommentId = '';
if (!empty($providerPullRequestId) && $resource->getAttribute('providerSilentMode', false) === false) {
if (!empty($providerPullRequestId) && $function->getAttribute('providerSilentMode', false) === false) {
$latestComment = Authorization::skip(fn () => $dbForPlatform->findOne('vcsComments', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::equal('providerPullRequestId', [$providerPullRequestId]),
@@ -131,15 +118,14 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
if (!$latestComment->isEmpty()) {
$latestCommentId = $latestComment->getAttribute('providerCommentId', '');
$comment = new Comment();
$comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId));
$comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '');
$comment->addBuild($project, $function, $commentStatus, $deploymentId, $action);
$latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment()));
} else {
$comment = new Comment();
$comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '');
$comment->addBuild($project, $function, $commentStatus, $deploymentId, $action);
$latestCommentId = \strval($github->createComment($owner, $repositoryName, $providerPullRequestId, $comment->generateComment()));
if (!empty($latestCommentId)) {
@@ -176,19 +162,19 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$latestCommentId = $comment->getAttribute('providerCommentId', '');
$comment = new Comment();
$comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId));
$comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '');
$comment->addBuild($project, $function, $commentStatus, $deploymentId, $action);
$latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment()));
}
}
if (!$isAuthorized) {
$resourceName = $resource->getAttribute('name');
$functionName = $function->getAttribute('name');
$projectName = $project->getAttribute('name');
$name = "{$resourceName} ({$projectName})";
$name = "{$functionName} ({$projectName})";
$message = 'Authorization required for external contributor.';
$providerRepositoryId = $repository->getAttribute('providerRepositoryId');
$providerRepositoryId = $resource->getAttribute('providerRepositoryId');
try {
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
if (empty($repositoryName)) {
@@ -208,32 +194,18 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$providerRepositoryOwner = $pullRequestResponse['head']['repo']['name'];
}
$commands = [];
if (!empty($resource->getAttribute('installCommand', ''))) {
$commands[] = $resource->getAttribute('installCommand', '');
}
if (!empty($resource->getAttribute('buildCommand', ''))) {
$commands[] = $resource->getAttribute('buildCommand', '');
}
if (!empty($resource->getAttribute('commands', ''))) {
$commands[] = $resource->getAttribute('commands', '');
}
$deployment = Authorization::skip(fn () => $dbForProject->createDocument('deployments', new Document([
$deployment = $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'resourceId' => $resourceId,
'resourceInternalId' => $resourceInternalId,
'resourceType' => $resourceCollection,
'entrypoint' => $resource->getAttribute('entrypoint', ''),
'buildCommands' => \implode(' && ', $commands),
'buildOutput' => $resource->getAttribute('outputDirectory', ''),
'adapter' => $resource->getAttribute('adapter', ''),
'fallbackFile' => $resource->getAttribute('fallbackFile', ''),
'resourceId' => $functionId,
'resourceInternalId' => $functionInternalId,
'resourceType' => 'functions',
'entrypoint' => $function->getAttribute('entrypoint'),
'commands' => $function->getAttribute('commands'),
'type' => 'vcs',
'installationId' => $installationId,
'installationInternalId' => $installationInternalId,
@@ -247,120 +219,21 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
'providerCommitHash' => $providerCommitHash,
'providerCommitAuthorUrl' => $providerCommitAuthorUrl,
'providerCommitAuthor' => $providerCommitAuthor,
'providerCommitMessage' => mb_strimwidth($providerCommitMessage, 0, 255, '...'),
'providerCommitMessage' => $providerCommitMessage,
'providerCommitUrl' => $providerCommitUrl,
'providerCommentId' => \strval($latestCommentId),
'providerBranch' => $providerBranch,
'search' => implode(' ', [$deploymentId, $resource->getAttribute('entrypoint', '')]),
'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint')]),
'activate' => $activate,
])));
]));
$resource = $resource
->setAttribute('latestDeploymentId', $deployment->getId())
->setAttribute('latestDeploymentInternalId', $deployment->getInternalId())
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
Authorization::skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource));
if ($resource->getCollection() === 'sites') {
$projectId = $project->getId();
// Deployment preview
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
$domain = ID::unique() . "." . $sitesDomain;
$ruleId = md5($domain);
Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
'projectInternalId' => $project->getInternalId(),
'domain' => $domain,
'type' => 'deployment',
'trigger' => 'deployment',
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getInternalId(),
'deploymentResourceType' => 'site',
'deploymentResourceId' => $resourceId,
'deploymentResourceInternalId' => $resourceInternalId,
'deploymentVcsProviderBranch' => $providerBranch,
'status' => 'verified',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain]),
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
// VCS branch preview
if (!empty($providerBranch)) {
$domain = "branch-{$providerBranch}-{$resource->getId()}-{$project->getId()}.{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
'projectInternalId' => $project->getInternalId(),
'domain' => $domain,
'type' => 'deployment',
'trigger' => 'deployment',
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getInternalId(),
'deploymentResourceType' => 'site',
'deploymentResourceId' => $resourceId,
'deploymentResourceInternalId' => $resourceInternalId,
'deploymentVcsProviderBranch' => $providerBranch,
'status' => 'verified',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain]),
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
} catch (Duplicate $err) {
// Ignore, rule already exists; will be updated by builds worker
}
}
// VCS commit preview
if (!empty($providerCommitHash)) {
$domain = "commit-{$providerCommitHash}-{$resource->getId()}-{$project->getId()}.{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
'projectInternalId' => $project->getInternalId(),
'domain' => $domain,
'type' => 'deployment',
'trigger' => 'deployment',
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getInternalId(),
'deploymentResourceType' => 'site',
'deploymentResourceId' => $resourceId,
'deploymentResourceInternalId' => $resourceInternalId,
'deploymentVcsProviderBranch' => $providerBranch,
'status' => 'verified',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain]),
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
} catch (Duplicate $err) {
// Ignore, rule already exists; will be updated by builds worker
}
}
}
if (!empty($providerCommitHash) && $resource->getAttribute('providerSilentMode', false) === false) {
$resourceName = $resource->getAttribute('name');
if (!empty($providerCommitHash) && $function->getAttribute('providerSilentMode', false) === false) {
$functionName = $function->getAttribute('name');
$projectName = $project->getAttribute('name');
$name = "{$resourceName} ({$projectName})";
$name = "{$functionName} ({$projectName})";
$message = 'Starting...';
$providerRepositoryId = $repository->getAttribute('providerRepositoryId');
$providerRepositoryId = $resource->getAttribute('providerRepositoryId');
try {
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
if (empty($repositoryName)) {
@@ -371,17 +244,17 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
}
$owner = $github->getOwnerName($providerInstallationId);
$providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/$resourceCollection/$resourceType-$resourceId";
$providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/functions/function-$functionId";
$github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'pending', $message, $providerTargetUrl, $name);
}
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($resource)
->setResource($function)
->setDeployment($deployment)
->setProject($project); // set the project because it won't be set for git deployments
$queueForBuilds->trigger(); // must trigger here so that we create a build for each function/site
$queueForBuilds->trigger(); // must trigger here so that we create a build for each function
//TODO: Add event?
} catch (Throwable $e) {
@@ -397,12 +270,13 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
};
App::get('/v1/vcs/github/authorize')
->desc('Install GitHub app')
->desc('Create GitHub app installation')
->groups(['api', 'vcs'])
->label('scope', 'vcs.read')
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('sdk', new Method(
namespace: 'vcs',
group: 'installations',
name: 'createGitHubInstallation',
description: '/docs/references/vcs/create-github-installation.md',
auth: [AuthType::ADMIN],
@@ -446,7 +320,7 @@ App::get('/v1/vcs/github/authorize')
});
App::get('/v1/vcs/github/callback')
->desc('Capture installation and authorization from GitHub app')
->desc('Get installation and authorization from GitHub app')
->groups(['api', 'vcs'])
->label('scope', 'public')
->label('error', __DIR__ . '/../../views/general/error.phtml')
@@ -584,6 +458,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'getRepositoryContents',
description: '/docs/references/vcs/get-repository-contents.md',
auth: [AuthType::ADMIN],
@@ -644,35 +519,31 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
]), Response::MODEL_VCS_CONTENT_LIST);
});
App::post('/v1/vcs/github/installations/:installationId/detections')
->alias('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection')
->desc('Create repository detection')
App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection')
->desc('Create runtime settings detection')
->groups(['api', 'vcs'])
->label('scope', 'vcs.write')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'createRepositoryDetection',
description: '/docs/references/vcs/create-repository-detection.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_DETECTION_RUNTIME,
),
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_DETECTION_FRAMEWORK,
model: Response::MODEL_DETECTION,
)
]
))
->param('installationId', '', new Text(256), 'Installation Id')
->param('providerRepositoryId', '', new Text(256), 'Repository Id')
->param('type', '', new WhiteList(['runtime', 'framework']), 'Detector type. Must be one of the following: runtime, framework')
->param('providerRootDirectory', '', new Text(256, 0), 'Path to Root Directory', true)
->inject('gitHub')
->inject('response')
->inject('project')
->inject('dbForPlatform')
->action(function (string $installationId, string $providerRepositoryId, string $type, string $providerRootDirectory, GitHub $github, Response $response, Database $dbForPlatform) {
->action(function (string $installationId, string $providerRepositoryId, string $providerRootDirectory, GitHub $github, Response $response, Document $project, Database $dbForPlatform) {
$installation = $dbForPlatform->getDocument('installations', $installationId);
if ($installation->isEmpty()) {
@@ -698,108 +569,32 @@ App::post('/v1/vcs/github/installations/:installationId/detections')
$files = \array_column($files, 'name');
$languages = $github->listRepositoryLanguages($owner, $repositoryName);
$detector = new Packager($files);
$detector
->addOption(new Yarn())
->addOption(new PNPM())
->addOption(new NPM());
$detection = $detector->detect();
$detectorFactory = new Detector($files, $languages);
$packager = !\is_null($detection) ? $detection->getName() : 'npm';
$detectorFactory
->addDetector(new JavaScript())
->addDetector(new Bun())
->addDetector(new PHP())
->addDetector(new Python())
->addDetector(new Dart())
->addDetector(new Swift())
->addDetector(new Ruby())
->addDetector(new Java())
->addDetector(new CPP())
->addDetector(new Deno())
->addDetector(new Dotnet());
if ($type === 'framework') {
$output = new Document([
'framework' => '',
'installCommand' => '',
'buildCommand' => '',
'outputDirectory' => '',
]);
$runtime = $detectorFactory->detect();
$detector = new Framework($files, $packager);
$detector
->addOption(new Flutter())
->addOption(new Nuxt())
->addOption(new Astro())
->addOption(new SvelteKit())
->addOption(new NextJs())
->addOption(new Remix());
$runtimes = Config::getParam('runtimes');
$runtimeDetail = \array_reverse(\array_filter(\array_keys($runtimes), function ($key) use ($runtime, $runtimes) {
return $runtimes[$key]['key'] === $runtime;
}))[0] ?? '';
$framework = $detector->detect();
$detection = [];
$detection['runtime'] = $runtimeDetail;
if (!\is_null($framework)) {
$output->setAttribute('installCommand', $framework->getInstallCommand());
$output->setAttribute('buildCommand', $framework->getBuildCommand());
$output->setAttribute('outputDirectory', $framework->getOutputDirectory());
$framework = $framework->getName();
} else {
$framework = 'other';
$output->setAttribute('installCommand', '');
$output->setAttribute('buildCommand', '');
$output->setAttribute('outputDirectory', '');
}
$frameworks = Config::getParam('frameworks');
if (!\in_array($framework, \array_keys($frameworks), true)) {
$framework = 'other';
}
$output->setAttribute('framework', $framework);
} else {
$output = new Document([
'runtime' => '',
'commands' => '',
'entrypoint' => '',
]);
$strategies = [
new Strategy(Strategy::FILEMATCH),
new Strategy(Strategy::LANGUAGES),
new Strategy(Strategy::EXTENSION),
];
foreach ($strategies as $strategy) {
$detector = new Runtime($strategy === Strategy::LANGUAGES ? $languages : $files, $strategy, $packager);
$detector
->addOption(new Node())
->addOption(new Bun())
->addOption(new Deno())
->addOption(new PHP())
->addOption(new Python())
->addOption(new Dart())
->addOption(new Swift())
->addOption(new Ruby())
->addOption(new Java())
->addOption(new CPP())
->addOption(new Dotnet());
$runtime = $detector->detect();
if (!\is_null($runtime)) {
$output->setAttribute('commands', $runtime->getCommands());
$output->setAttribute('entrypoint', $runtime->getEntrypoint());
$runtime = $runtime->getName();
break;
}
}
if (!empty($runtime)) {
$runtimes = Config::getParam('runtimes');
$runtimeWithVersion = '';
foreach ($runtimes as $runtimeKey => $runtimeConfig) {
if ($runtimeConfig['key'] === $runtime) {
$runtimeWithVersion = $runtimeKey;
}
}
if (empty($runtimeWithVersion)) {
throw new Exception(Exception::FUNCTION_RUNTIME_NOT_DETECTED);
}
$output->setAttribute('runtime', $runtimeWithVersion);
} else {
throw new Exception(Exception::FUNCTION_RUNTIME_NOT_DETECTED);
}
}
$response->dynamic($output, $type === 'framework' ? Response::MODEL_DETECTION_FRAMEWORK : Response::MODEL_DETECTION_RUNTIME);
$response->dynamic(new Document($detection), Response::MODEL_DETECTION);
});
App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
@@ -808,27 +603,24 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'listRepositories',
description: '/docs/references/vcs/list-repositories.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST,
),
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST,
model: Response::MODEL_PROVIDER_REPOSITORY_LIST,
)
]
))
->param('installationId', '', new Text(256), 'Installation Id')
->param('type', '', new WhiteList(['runtime', 'framework']), 'Detector type. Must be one of the following: runtime, framework')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('gitHub')
->inject('response')
->inject('project')
->inject('dbForPlatform')
->action(function (string $installationId, string $type, string $search, GitHub $github, Response $response, Database $dbForPlatform) {
->action(function (string $installationId, string $search, GitHub $github, Response $response, Document $project, Database $dbForPlatform) {
if (empty($search)) {
$search = "";
}
@@ -858,86 +650,39 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
return $repo;
}, $repos);
$repos = batch(\array_map(function ($repo) use ($type, $github) {
return function () use ($repo, $type, $github) {
$files = $github->listRepositoryContents($repo['organization'], $repo['name'], '');
$files = \array_column($files, 'name');
$detector = new Packager($files);
$detector
->addOption(new Yarn())
->addOption(new PNPM())
->addOption(new NPM());
$detection = $detector->detect();
$packager = !\is_null($detection) ? $detection->getName() : 'npm';
if ($type === 'framework') {
$frameworkDetector = new Framework($files, $packager);
$frameworkDetector
->addOption(new Flutter())
->addOption(new Nuxt())
->addOption(new Astro())
->addOption(new SvelteKit())
->addOption(new NextJs())
->addOption(new Remix());
$detectedFramework = $frameworkDetector->detect();
if (!\is_null($detectedFramework)) {
$framework = $detectedFramework->getName();
} else {
$framework = 'other';
}
$frameworks = Config::getParam('frameworks');
if (!\in_array($framework, \array_keys($frameworks), true)) {
$framework = 'other';
}
$repo['framework'] = $framework;
} else {
$repos = batch(\array_map(function ($repo) use ($github) {
return function () use ($repo, $github) {
try {
$files = $github->listRepositoryContents($repo['organization'], $repo['name'], '');
$files = \array_column($files, 'name');
$languages = $github->listRepositoryLanguages($repo['organization'], $repo['name']);
$strategies = [
new Strategy(Strategy::FILEMATCH),
new Strategy(Strategy::LANGUAGES),
new Strategy(Strategy::EXTENSION),
];
$detectorFactory = new Detector($files, $languages);
foreach ($strategies as $strategy) {
$detector = new Runtime($strategy === Strategy::LANGUAGES ? $languages : $files, $strategy, $packager);
$detector
->addOption(new Node())
->addOption(new Bun())
->addOption(new Deno())
->addOption(new PHP())
->addOption(new Python())
->addOption(new Dart())
->addOption(new Swift())
->addOption(new Ruby())
->addOption(new Java())
->addOption(new CPP())
->addOption(new Dotnet());
$detectorFactory
->addDetector(new JavaScript())
->addDetector(new Bun())
->addDetector(new PHP())
->addDetector(new Python())
->addDetector(new Dart())
->addDetector(new Swift())
->addDetector(new Ruby())
->addDetector(new Java())
->addDetector(new CPP())
->addDetector(new Deno())
->addDetector(new Dotnet());
$runtime = $detector->detect();
$runtime = $detectorFactory->detect();
if (!\is_null($runtime)) {
$runtime = $runtime->getName();
break;
}
}
$runtimes = Config::getParam('runtimes');
$runtimeDetail = \array_reverse(\array_filter(\array_keys($runtimes), function ($key) use ($runtime, $runtimes) {
return $runtimes[$key]['key'] === $runtime;
}))[0] ?? '';
if (!empty($runtime)) {
$runtimes = Config::getParam('runtimes');
$runtimeWithVersion = '';
foreach ($runtimes as $runtimeKey => $runtimeConfig) {
if ($runtimeConfig['key'] === $runtime) {
$runtimeWithVersion = $runtimeKey;
}
}
$repo['runtime'] = $runtimeWithVersion ?? '';
}
$repo['runtime'] = $runtimeDetail;
} catch (Throwable $error) {
$repo['runtime'] = "";
Console::warning("Runtime not detected for " . $repo['organization'] . "/" . $repo['name']);
}
return $repo;
};
@@ -948,9 +693,9 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
}, $repos);
$response->dynamic(new Document([
$type === 'framework' ? 'frameworkProviderRepositories' : 'runtimeProviderRepositories' => $repos,
'providerRepositories' => $repos,
'total' => \count($repos),
]), ($type === 'framework') ? Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST : Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST);
]), Response::MODEL_PROVIDER_REPOSITORY_LIST);
});
App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
@@ -959,6 +704,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
->label('scope', 'vcs.write')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'createRepository',
description: '/docs/references/vcs/create-repository.md',
auth: [AuthType::ADMIN],
@@ -1071,6 +817,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'getRepository',
description: '/docs/references/vcs/get-repository.md',
auth: [AuthType::ADMIN],
@@ -1125,6 +872,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'listRepositoryBranches',
description: '/docs/references/vcs/list-repository-branches.md',
auth: [AuthType::ADMIN],
@@ -1217,7 +965,7 @@ App::post('/v1/vcs/github/events')
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
//find resourceId from relevant resources table
//find functionId from functions table
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::limit(100),
@@ -1229,7 +977,7 @@ App::post('/v1/vcs/github/events')
}
} elseif ($event == $github::EVENT_INSTALLATION) {
if ($parsedPayload["action"] == "deleted") {
// TODO: Use worker for this job instead (update function/site as well)
// TODO: Use worker for this job instead (update function as well)
$providerInstallationId = $parsedPayload["installationId"];
$installations = $dbForPlatform->find('installations', [
@@ -1318,6 +1066,7 @@ App::get('/v1/vcs/installations')
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'installations',
name: 'listInstallations',
description: '/docs/references/vcs/list-installations.md',
auth: [AuthType::ADMIN],
@@ -1373,9 +1122,12 @@ App::get('/v1/vcs/installations')
}
$filterQueries = Query::groupByType($queries)['filters'];
$results = $dbForPlatform->find('installations', $queries);
$total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT);
try {
$results = $dbForPlatform->find('installations', $queries);
$total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'installations' => $results,
@@ -1389,6 +1141,7 @@ App::get('/v1/vcs/installations/:installationId')
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'installations',
name: 'getInstallation',
description: '/docs/references/vcs/get-installation.md',
auth: [AuthType::ADMIN],
@@ -1423,6 +1176,7 @@ App::delete('/v1/vcs/installations/:installationId')
->label('scope', 'vcs.write')
->label('sdk', new Method(
namespace: 'vcs',
group: 'installations',
name: 'deleteInstallation',
description: '/docs/references/vcs/delete-installation.md',
auth: [AuthType::ADMIN],
@@ -1458,11 +1212,12 @@ App::delete('/v1/vcs/installations/:installationId')
});
App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositoryId')
->desc('Authorize external deployment')
->desc('Update external deployment (authorize)')
->groups(['api', 'vcs'])
->label('scope', 'vcs.write')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'updateExternalDeployments',
description: '/docs/references/vcs/update-external-deployments.md',
auth: [AuthType::ADMIN],
File diff suppressed because it is too large Load Diff
+35 -54
View File
@@ -157,15 +157,6 @@ $usageDatabaseListener = function (string $event, Document $document, StatsUsage
$queueForStatsUsage
->addMetric(METRIC_FUNCTIONS, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForStatsUsage
->addReduce($document);
}
break;
case $document->getCollection() === 'sites':
$queueForStatsUsage
->addMetric(METRIC_SITES, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForStatsUsage
->addReduce($document);
@@ -175,10 +166,8 @@ $usageDatabaseListener = function (string $event, Document $document, StatsUsage
$queueForStatsUsage
->addMetric(METRIC_DEPLOYMENTS, $value) // per project
->addMetric(METRIC_DEPLOYMENTS_STORAGE, $document->getAttribute('size') * $value) // per project
->addMetric(str_replace(['{resourceType}'], [$document->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_DEPLOYMENTS), $value) // per function
->addMetric(str_replace(['{resourceType}'], [$document->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_DEPLOYMENTS_STORAGE), $document->getAttribute('size') * $value)
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $value) // per function
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), $document->getAttribute('size') * $value);
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS), $value) // per function
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE), $document->getAttribute('size') * $value);
break;
default:
break;
@@ -249,34 +238,36 @@ App::init()
subject: 'keys'
);
if ($dbKey) {
$accessedAt = $dbKey->getAttribute('accessedAt', 0);
if (!$dbKey) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$dbKey->setAttribute('accessedAt', DateTime::now());
$accessedAt = $dbKey->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$dbKey->setAttribute('accessedAt', DateTime::now());
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdkValidator->isValid($sdk)) {
$sdks = $dbKey->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
$sdks[] = $sdk;
$dbKey->setAttribute('sdks', $sdks);
/** Update access time as well */
$dbKey->setAttribute('accessedAt', Datetime::now());
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdk !== 'UNKNOWN' && $sdkValidator->isValid($sdk)) {
$sdks = $dbKey->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
$sdks[] = $sdk;
$dbKey->setAttribute('sdks', $sdks);
/** Update access time as well */
$dbKey->setAttribute('accessedAt', Datetime::now());
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
}
$queueForAudits->setUser($user);
}
$queueForAudits->setUser($user);
}
} // Admin User Authentication
elseif (($project->getId() === 'console' && !$team->isEmpty() && !$user->isEmpty()) || ($project->getId() !== 'console' && !$user->isEmpty() && $mode === APP_MODE_ADMIN)) {
@@ -311,7 +302,7 @@ App::init()
// Update project last activity
if (!$project->isEmpty() && $project->getId() !== 'console') {
$accessedAt = $project->getAttribute('accessedAt', 0);
$accessedAt = $project->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
$project->setAttribute('accessedAt', DateTime::now());
Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
@@ -320,7 +311,7 @@ App::init()
// Update user last activity
if (!empty($user->getId())) {
$accessedAt = $user->getAttribute('accessedAt', 0);
$accessedAt = $user->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCESS)) > $accessedAt) {
$user->setAttribute('accessedAt', DateTime::now());
@@ -399,12 +390,10 @@ App::init()
->inject('queueForStatsUsage')
->inject('dbForProject')
->inject('timelimit')
->inject('resourceToken')
->inject('mode')
->inject('apiKey')
->inject('plan')
->inject('devKey')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey) use ($usageDatabaseListener, $eventDatabaseListener) {
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, string $mode, ?Key $apiKey, array $plan) use ($usageDatabaseListener, $eventDatabaseListener) {
$route = $utopia->getRoute();
@@ -471,7 +460,6 @@ App::init()
$enabled // Abuse is enabled
&& !$isAppUser // User is not API key
&& !$isPrivilegedUser // User is not an admin
&& $devKey->isEmpty() // request doesn't not contain development key
&& $abuse->check() // Route is rate-limited
) {
throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED);
@@ -548,17 +536,13 @@ App::init()
$data = $cache->load($key, $timestamp);
if (!empty($data) && !$cacheLog->isEmpty()) {
$parts = explode('/', $cacheLog->getAttribute('resourceType'));
$parts = explode('/', $cacheLog->getAttribute('resourceType', ''));
$type = $parts[0] ?? null;
if ($type === 'bucket' && (!$isImageTransformation || !$isDisabled)) {
$bucketId = $parts[1] ?? null;
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getInternalId();
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
@@ -566,23 +550,20 @@ App::init()
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid && !$isToken) {
if (!$fileSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$parts = explode('/', $cacheLog->getAttribute('resource'));
$fileId = $parts[1] ?? null;
if ($fileSecurity && !$valid && !$isToken) {
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
} else {
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
}
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getInternalId()) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
@@ -808,7 +789,7 @@ App::shutdown()
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
$signature = md5($data['payload']);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$accessedAt = $cacheLog->getAttribute('accessedAt', 0);
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
$now = DateTime::now();
if ($cacheLog->isEmpty()) {
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
+4 -53
View File
@@ -13,7 +13,6 @@ use Swoole\Table;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\CLI\Console;
use Utopia\Compression\Compression;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
@@ -29,8 +28,6 @@ use Utopia\Pools\Group;
use Utopia\Swoole\Files;
use Utopia\System\System;
Files::load(__DIR__.'/../public');
const DOMAIN_SYNC_TIMER = 30; // 30 seconds
$domains = new Table(1_000_000); // 1 million rows
@@ -252,7 +249,8 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
$audit->setup();
}
if ($dbForPlatform->getDocument('buckets', 'default')->isEmpty()) {
if ($dbForPlatform->getDocument('buckets', 'default')->isEmpty() &&
!$dbForPlatform->exists($dbForPlatform->getDatabase(), 'bucket_1')) {
Console::info(" └── Creating default bucket...");
$dbForPlatform->createDocument('buckets', new Document([
'$id' => ID::custom('default'),
@@ -304,54 +302,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
$dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
}
if (Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) {
Console::info(" └── Creating screenshots bucket...");
Authorization::skip(fn () => $dbForPlatform->createDocument('buckets', new Document([
'$id' => ID::custom('screenshots'),
'$collection' => ID::custom('buckets'),
'name' => 'Screenshots',
'maximumFileSize' => 5000000, // ~5MB
'allowedFileExtensions' => [ 'png' ],
'enabled' => true,
'compression' => Compression::GZIP,
'encryption' => false,
'antivirus' => false,
'fileSecurity' => true,
'$permissions' => [],
'search' => 'buckets Screenshots',
])));
$bucket = Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
Console::info(" └── Creating files collection for screenshots bucket...");
$files = $collections['buckets']['files'] ?? [];
if (empty($files)) {
throw new Exception('Files collection is not configured.');
}
$attributes = array_map(fn ($attr) => new Document([
'$id' => ID::custom($attr['$id']),
'type' => $attr['type'],
'size' => $attr['size'],
'required' => $attr['required'],
'signed' => $attr['signed'],
'array' => $attr['array'],
'filters' => $attr['filters'],
'default' => $attr['default'] ?? null,
'format' => $attr['format'] ?? ''
]), $files['attributes']);
$indexes = array_map(fn ($index) => new Document([
'$id' => ID::custom($index['$id']),
'type' => $index['type'],
'attributes' => $index['attributes'],
'lengths' => $index['lengths'],
'orders' => $index['orders'],
]), $files['indexes']);
Authorization::skip(fn () => $dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes));
}
});
$projectCollections = $collections['projects'];
@@ -564,6 +514,7 @@ $http->on('Task', function () use ($register, $domains) {
if ($lastSyncUpdate != null) {
$queries[] = Query::greaterThanEqual('$updatedAt', $lastSyncUpdate);
}
$queries[] = Query::equal('resourceType', ['function']);
$results = [];
try {
$results = Authorization::skip(fn () => $dbForPlatform->find('rules', $queries));
@@ -574,7 +525,7 @@ $http->on('Task', function () use ($register, $domains) {
$sum = count($results);
foreach ($results as $document) {
$domain = $document->getAttribute('domain');
if (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS')) || str_ends_with($domain, System::getEnv('_APP_DOMAIN_SITES'))) {
if (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
continue;
}
$domains->set(md5($domain), ['value' => 1]);
+2 -5
View File
@@ -2,7 +2,6 @@
use Utopia\Config\Config;
Config::load('template-runtimes', __DIR__ . '/../config/template-runtimes.php');
Config::load('events', __DIR__ . '/../config/events.php');
Config::load('auth', __DIR__ . '/../config/auth.php');
Config::load('apis', __DIR__ . '/../config/apis.php'); // List of APIs
@@ -11,7 +10,6 @@ Config::load('oAuthProviders', __DIR__ . '/../config/oAuthProviders.php');
Config::load('platforms', __DIR__ . '/../config/platforms.php');
Config::load('console', __DIR__ . '/../config/console.php');
Config::load('collections', __DIR__ . '/../config/collections.php');
Config::load('frameworks', __DIR__ . '/../config/frameworks.php');
Config::load('runtimes', __DIR__ . '/../config/runtimes.php');
Config::load('runtimes-v2', __DIR__ . '/../config/runtimes-v2.php');
Config::load('usage', __DIR__ . '/../config/usage.php');
@@ -35,6 +33,5 @@ Config::load('storage-logos', __DIR__ . '/../config/storage/logos.php');
Config::load('storage-mimes', __DIR__ . '/../config/storage/mimes.php');
Config::load('storage-inputs', __DIR__ . '/../config/storage/inputs.php');
Config::load('storage-outputs', __DIR__ . '/../config/storage/outputs.php');
Config::load('specifications', __DIR__ . '/../config/specifications.php');
Config::load('templates-function', __DIR__ . '/../config/templates/function.php');
Config::load('templates-site', __DIR__ . '/../config/templates/site.php');
Config::load('runtime-specifications', __DIR__ . '/../config/runtimes/specifications.php');
Config::load('function-templates', __DIR__ . '/../config/function-templates.php');
+22 -42
View File
@@ -1,6 +1,6 @@
<?php
use Appwrite\Platform\Modules\Compute\Specification;
use Appwrite\Functions\Specification;
const APP_NAME = 'Appwrite';
const APP_DOMAIN = 'appwrite.io';
@@ -32,7 +32,7 @@ const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 4318;
const APP_VERSION_STABLE = '1.7.0';
const APP_VERSION_STABLE = '1.6.2';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
@@ -46,11 +46,9 @@ const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes
const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes
const APP_DATABASE_QUERY_MAX_VALUES = 500;
const APP_STORAGE_UPLOADS = '/storage/uploads';
const APP_STORAGE_SITES = '/storage/sites';
const APP_STORAGE_FUNCTIONS = '/storage/functions';
const APP_STORAGE_BUILDS = '/storage/builds';
const APP_STORAGE_CACHE = '/storage/cache';
const APP_STORAGE_IMPORTS = '/storage/imports'; // Temporary storage for csv imports
const APP_STORAGE_CERTIFICATES = '/storage/certificates';
const APP_STORAGE_CONFIG = '/storage/config';
const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT`
@@ -66,9 +64,9 @@ 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_COMPUTE_CPUS_DEFAULT = 0.5;
const APP_COMPUTE_MEMORY_DEFAULT = 512;
const APP_COMPUTE_SPECIFICATION_DEFAULT = Specification::S_1VCPU_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';
@@ -94,7 +92,6 @@ const DELETE_TYPE_DATABASES = 'databases';
const DELETE_TYPE_DOCUMENT = 'document';
const DELETE_TYPE_COLLECTIONS = 'collections';
const DELETE_TYPE_PROJECTS = 'projects';
const DELETE_TYPE_SITES = 'sites';
const DELETE_TYPE_FUNCTIONS = 'functions';
const DELETE_TYPE_DEPLOYMENTS = 'deployments';
const DELETE_TYPE_USERS = 'users';
@@ -184,7 +181,6 @@ const METRIC_FILES_IMAGES_TRANSFORMED = 'files.imagesTransformed';
const METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED = '{bucketInternalId}.files.imagesTransformed';
const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files';
const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage';
const METRIC_SITES = 'sites';
const METRIC_FUNCTIONS = 'functions';
const METRIC_DEPLOYMENTS = 'deployments';
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
@@ -196,35 +192,22 @@ const METRIC_BUILDS_COMPUTE = 'builds.compute';
const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success';
const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed';
const METRIC_BUILDS_MB_SECONDS = 'builds.mbSeconds';
const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds';
const METRIC_FUNCTION_ID_BUILDS_SUCCESS = '{functionInternalId}.builds.success';
const METRIC_FUNCTION_ID_BUILDS_FAILED = '{functionInternalId}.builds.failed';
const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS = '{functionInternalId}.builds.compute.success';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED = '{functionInternalId}.builds.compute.failed';
const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
const METRIC_FUNCTION_ID_BUILDS_MB_SECONDS = '{functionInternalId}.builds.mbSeconds';
const METRIC_EXECUTIONS = 'executions';
const METRIC_EXECUTIONS_COMPUTE = 'executions.compute';
const METRIC_EXECUTIONS_MB_SECONDS = 'executions.mbSeconds';
const METRIC_RESOURCE_TYPE_ID_EXECUTIONS = '{resourceType}.{resourceInternalId}.executions';
const METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE = '{resourceType}.{resourceInternalId}.executions.compute';
const METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS = '{resourceType}.{resourceInternalId}.executions.mbSeconds';
const METRIC_RESOURCE_TYPE_ID_BUILDS_SUCCESS = '{resourceType}.{resourceInternalId}.builds.success';
const METRIC_RESOURCE_TYPE_ID_BUILDS_FAILED = '{resourceType}.{resourceInternalId}.builds.failed';
const METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE = '{resourceType}.{resourceInternalId}.builds.compute';
const METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE_SUCCESS = '{resourceType}.{resourceInternalId}.builds.compute.success';
const METRIC_RESOURCE_TYPE_ID_BUILDS_COMPUTE_FAILED = '{resourceType}.{resourceInternalId}.builds.compute.failed';
const METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS = '{resourceType}.{resourceInternalId}.builds.mbSeconds';
const METRIC_RESOURCE_TYPE_ID_BUILDS = '{resourceType}.{resourceInternalId}.builds';
const METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE = '{resourceType}.{resourceInternalId}.builds.storage';
const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
const METRIC_RESOURCE_TYPE_EXECUTIONS = '{resourceType}.executions';
const METRIC_RESOURCE_TYPE_EXECUTIONS_COMPUTE = '{resourceType}.executions.compute';
const METRIC_RESOURCE_TYPE_EXECUTIONS_MB_SECONDS = '{resourceType}.executions.mbSeconds';
const METRIC_RESOURCE_TYPE_BUILDS_SUCCESS = '{resourceType}.builds.success';
const METRIC_RESOURCE_TYPE_BUILDS_FAILED = '{resourceType}.builds.failed';
const METRIC_RESOURCE_TYPE_BUILDS_COMPUTE = '{resourceType}.builds.compute';
const METRIC_RESOURCE_TYPE_BUILDS_COMPUTE_SUCCESS = '{resourceType}.builds.compute.success';
const METRIC_RESOURCE_TYPE_BUILDS_COMPUTE_FAILED = '{resourceType}.builds.compute.failed';
const METRIC_RESOURCE_TYPE_BUILDS_MB_SECONDS = '{resourceType}.builds.mbSeconds';
const METRIC_RESOURCE_TYPE_BUILDS = '{resourceType}.builds';
const METRIC_RESOURCE_TYPE_BUILDS_STORAGE = '{resourceType}.builds.storage';
const METRIC_RESOURCE_TYPE_DEPLOYMENTS = '{resourceType}.deployments';
const METRIC_RESOURCE_TYPE_DEPLOYMENTS_STORAGE = '{resourceType}.deployments.storage';
const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions';
const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute';
const METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS = '{functionInternalId}.executions.mbSeconds';
const METRIC_NETWORK_REQUESTS = 'network.requests';
const METRIC_NETWORK_INBOUND = 'network.inbound';
const METRIC_NETWORK_OUTBOUND = 'network.outbound';
@@ -239,18 +222,15 @@ const METRIC_TARGETS = 'targets';
const METRIC_PROVIDER_TYPE_TARGETS = '{providerType}.targets';
const METRIC_KEYS = 'keys';
const METRIC_DOMAINS = 'domains';
const METRIC_SITES_REQUESTS = 'sites.requests';
const METRIC_SITES_INBOUND = 'sites.inbound';
const METRIC_SITES_OUTBOUND = 'sites.outbound';
const METRIC_SITES_ID_REQUESTS = 'sites.{siteInternalId}.requests';
const METRIC_SITES_ID_INBOUND = 'sites.{siteInternalId}.inbound';
const METRIC_SITES_ID_OUTBOUND = 'sites.{siteInternalId}.outbound';
const METRIC_RESOURCE_TYPE_ID_BUILDS = '{resourceType}.{resourceInternalId}.builds';
const METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE = '{resourceType}.{resourceInternalId}.builds.storage';
const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
// Resource types
const RESOURCE_TYPE_PROJECTS = 'projects';
const RESOURCE_TYPE_FUNCTIONS = 'functions';
const RESOURCE_TYPE_SITES = 'sites';
const RESOURCE_TYPE_DATABASES = 'databases';
const RESOURCE_TYPE_BUCKETS = 'buckets';
const RESOURCE_TYPE_PROVIDERS = 'providers';
+1 -15
View File
@@ -133,20 +133,6 @@ Database::addFilter(
}
);
Database::addFilter(
'subQueryDevKeys',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('devKeys', [
Query::equal('projectInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
Database::addFilter(
'subQueryWebhooks',
function (mixed $value) {
@@ -239,7 +225,7 @@ Database::addFilter(
return $database
->find('variables', [
Query::equal('resourceInternalId', [$document->getInternalId()]),
Query::equal('resourceType', ['function', 'site']),
Query::equal('resourceType', ['function']),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
+15 -112
View File
@@ -29,7 +29,6 @@ use Utopia\Cache\Cache;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime as DatabaseDateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
@@ -49,8 +48,9 @@ use Utopia\Storage\Device\S3;
use Utopia\Storage\Device\Wasabi;
use Utopia\Storage\Storage;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
use Utopia\Validator\Hostname;
use Utopia\Validator\WhiteList;
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
// Runtime Execution
@@ -466,7 +466,9 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
};
}, ['pools', 'cache']);
App::setResource('cache', function (Group $pools) {
App::setResource('telemetry', fn () => new NoTelemetry());
App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
$list = Config::getParam('pools-cache', []);
$adapters = [];
@@ -474,12 +476,15 @@ App::setResource('cache', function (Group $pools) {
$adapters[] = $pools
->get($value)
->pop()
->getResource()
;
->getResource();
}
return new Cache(new Sharding($adapters));
}, ['pools']);
$cache = new Cache(new Sharding($adapters));
$cache->setTelemetry($telemetry);
return $cache;
}, ['pools', 'telemetry']);
App::setResource('redis', function () {
$host = System::getEnv('_APP_REDIS_HOST', 'localhost');
@@ -510,14 +515,6 @@ App::setResource('deviceForFiles', function ($project) {
return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
}, ['project']);
App::setResource('deviceForSites', function ($project) {
return getDevice(APP_STORAGE_SITES . '/app-' . $project->getId());
}, ['project']);
App::setResource('deviceForImports', function (Document $project) {
return getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId());
}, ['project']);
App::setResource('deviceForFunctions', function ($project) {
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
}, ['project']);
@@ -791,49 +788,6 @@ App::setResource('smsRates', function () {
return [];
});
App::setResource('devKey', function (Request $request, Document $project, array $servers, Database $dbForPlatform) {
$devKey = $request->getHeader('x-appwrite-dev-key', $request->getParam('devKey', ''));
// Check if given key match project's development keys
$key = $project->find('secret', $devKey, 'devKeys');
if (!$key) {
return new Document([]);
}
// check expiration
$expire = $key->getAttribute('expire');
if (!empty($expire) && $expire < DatabaseDateTime::formatTz(DatabaseDateTime::now())) {
return new Document([]);
}
// update access time
$accessedAt = $key->getAttribute('accessedAt', 0);
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$key->setAttribute('accessedAt', DatabaseDateTime::now());
Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
// add sdk to key
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdk !== 'UNKNOWN' && $sdkValidator->isValid($sdk)) {
$sdks = $key->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
$sdks[] = $sdk;
$key->setAttribute('sdks', $sdks);
/** Update access time as well */
$key->setAttribute('accessedAt', DatabaseDateTime::now());
$key = Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
}
return $key;
}, ['request', 'project', 'servers', 'dbForPlatform']);
App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request) {
$teamInternalId = '';
if ($project->getId() !== 'console') {
@@ -867,24 +821,16 @@ App::setResource(
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
);
App::setResource('previewHostname', function (Request $request, ?Key $apiKey) {
$allowed = false;
App::setResource('previewHostname', function (Request $request) {
if (App::isDevelopment()) {
$allowed = true;
} elseif (!\is_null($apiKey) && $apiKey->getHostnameOverride() === true) {
$allowed = true;
}
if ($allowed) {
$host = $request->getQuery('appwrite-hostname', $request->getHeader('x-appwrite-hostname', '')) ?? '';
$host = $request->getQuery('appwrite-hostname') ?? '';
if (!empty($host)) {
return $host;
}
}
return '';
}, ['request', 'apiKey']);
}, ['request']);
App::setResource('apiKey', function (Request $request, Document $project): ?Key {
$key = $request->getHeader('x-appwrite-key');
@@ -897,46 +843,3 @@ App::setResource('apiKey', function (Request $request, Document $project): ?Key
}, ['request', 'project']);
App::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
App::setResource('resourceToken', function ($project, $dbForProject, $request) {
$tokenJWT = $request->getParam('token');
if (!empty($tokenJWT) && !$project->isEmpty()) { // JWT authentication
$jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
try {
$payload = $jwt->decode($tokenJWT);
} catch (JWTException $error) {
return new Document([]);
}
$tokenId = $payload['tokenId'] ?? '';
$secret = $payload['secret'] ?? '';
if (empty($tokenId) || empty($secret)) {
return new Document([]);
}
$token = Authorization::skip(fn () => $dbForProject->getDocument('resourceTokens', $tokenId));
if ($token->isEmpty() || $token->getAttribute('secret') !== $secret) {
return new Document([]);
}
if ($token->getAttribute('resourceType') === 'file') {
$internalIds = explode(':', $token->getAttribute('resourceInternalId'));
$ids = explode(':', $token->getAttribute('resourceId'));
if (count($internalIds) !== 2 || count($ids) !== 2) {
return new Document([]);
}
return new Document([
'bucketId' => $ids[0],
'fileId' => $ids[1],
'bucketInternalId' => $internalIds[0],
'fileInternalId' => $internalIds[1],
]);
}
}
return new Document([]);
}, ['project', 'dbForProject', 'request']);
-187
View File
@@ -1,187 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 Not Found</title>
<style>
@import url(https://fonts.bunny.net/css?family=fira-code:400|inter:400);
* {
margin: 0;
padding: 0;
}
body {
background-color: #FFFFFF;
}
.main {
display: flex;
min-height: 100vh;
width: 100vw;
align-items: center;
justify-content: center;
}
.content {
margin-left: auto;
margin-right: auto;
max-width: 400px;
}
span {
padding: var(--space-1, 2px) var(--space-3, 6px);
border-radius: var(--border-radius-XS, 6px);
background: var(--color-overlay-on-neutral, rgba(0, 0, 0, 0.06));
color: var(--color-fgColor-neutral-secondary, #56565C);
text-align: center;
font-family: var(--font-family-sansSerif, Inter), sans-serif;
font-size: var(--font-size-S, 14px);
font-style: normal;
font-weight: 400;
line-height: 140%;
letter-spacing: -0.063px;
}
h1 {
color: var(--color-fgColor-neutral-primary, #2D2D31);
text-align: center;
font-family: var(--font-family-sansSerif, Inter), sans-serif;
font-size: var(--font-size-XXXL, 32px);
font-style: normal;
font-weight: 400;
line-height: 140%;
letter-spacing: -0.144px;
margin-top: 8px;
margin-bottom: 32px;
}
button {
border-radius: var(--border-radius-S, 8px);
font-family: var(--font-family-sansSerif, Inter), sans-serif;
font-size: var(--font-size-S, 14px);
font-style: normal;
font-weight: 500;
line-height: 140%;
letter-spacing: -0.063px;
padding: var(--space-3, 6px) var(--space-5, 10px);
cursor: pointer;
border: var(--border-width-S, 1px) solid var(--color-border-neutral-strong, #D8D8DB);
background: var(--color-bgColor-neutral-primary, #FFF);
color: var(--color-fgColor-neutral-secondary, #56565C);
}
.center {
display: flex;
justify-content: center;
}
.brand {
position: absolute;
width: 100%;
bottom: 32px;
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
}
.brand p {
font-family: var(--font-family-monospace, "Fira Code"), monospace;
font-size: var(--font-size-XS, 12px);
font-style: normal;
font-weight: 400;
line-height: 130%;
letter-spacing: 0.96px;
text-transform: uppercase;
color: var(--color-fgColor-neutral-secondary, #56565C);
}
.brand svg {
height: 20px;
}
.logo-dark {
display: none;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #1D1D21;
}
h1 {
color: var(--color-fgColor-neutral-primary, #EDEDF0);
}
button {
border: var(--border-width-S, 1px) solid var(--color-border-neutral-strong, #414146);
background: var(--color-bgColor-neutral-primary, #1D1D21);
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
}
.brand p {
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
}
span {
background: var(--color-overlay-on-neutral, rgba(255, 255, 255, 0.20));
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
}
.logo-light {
display: none;
}
.logo-dark {
display: block;
}
}
</style>
</head>
<body>
<div class="main">
<div class="content">
<div class="center"><span>Page not found</span></div>
<h1>The page youre looking for doesnt exist.</h1>
<div class="center"><a href="/"><button>Go to homepage</button></a></div>
</div>
</div>
<div class="brand">
<p>Powered by</p>
<svg class="logo-dark" width="110" height="20" viewBox="0 0 110 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M31.8649 16.2461C33.6492 16.2461 34.5511 15.3184 34.9433 14.6867H35.1197C35.1981 15.3578 35.6687 15.9895 36.5903 15.9895H38.3353V14.0156H37.8843C37.5706 14.0156 37.4138 13.838 37.4138 13.5617V5.64661H35.1001V6.90986H34.9236C34.4727 6.27823 33.5315 5.39001 31.8061 5.39001C29.0611 5.39001 27.022 7.67965 27.022 10.818C27.022 13.9564 29.1003 16.2461 31.8649 16.2461ZM32.2767 13.9959C30.6493 13.9959 29.3748 12.7919 29.3748 10.8378C29.3748 8.92316 30.6101 7.62044 32.2571 7.62044C33.8256 7.62044 35.1393 8.78499 35.1393 10.8378C35.1393 12.5945 34.0217 13.9959 32.2767 13.9959Z" fill="#EDEDF0" />
<path d="M39.7013 20H42.0149V14.6867H42.1914C42.6227 15.3184 43.5443 16.2461 45.3677 16.2461C48.1127 16.2461 50.1127 13.9169 50.1127 10.818C50.1127 7.69939 47.9755 5.39001 45.2109 5.39001C43.4462 5.39001 42.5835 6.35719 42.1718 6.89012H41.9953V5.64661H39.7013V20ZM44.8776 14.0551C43.2894 14.0551 41.9757 12.8708 41.9757 10.818C41.9757 9.06133 43.0933 7.58096 44.8383 7.58096C46.4657 7.58096 47.7402 8.86395 47.7402 10.818C47.7402 12.7326 46.5049 14.0551 44.8776 14.0551Z" fill="#EDEDF0" />
<path d="M51.3065 20H53.6202V14.6867H53.7966C54.228 15.3184 55.1495 16.2461 56.973 16.2461C59.718 16.2461 61.5273 13.9169 61.5273 10.818C61.5273 7.69939 59.5807 5.39001 56.8161 5.39001C55.0515 5.39001 54.1888 6.35719 53.777 6.89012H53.6005V5.64661H51.3065V20ZM56.4828 14.0551C54.8946 14.0551 53.5809 12.8708 53.5809 10.818C53.5809 9.06133 54.6985 7.58096 56.4436 7.58096C58.071 7.58096 59.3454 8.86395 59.3454 10.818C59.3454 12.7326 58.1102 14.0551 56.4828 14.0551Z" fill="#EDEDF0" />
<path d="M64.5857 16.2296H67.8601L69.7227 8.11721H69.8404L71.7031 16.2296H74.9579L77.5642 5.88678H75.2323L73.3697 14.0189H73.1932L71.3305 5.88678H68.2522L66.3699 14.0189H66.1935L64.3504 5.88678H61.8799L64.5857 16.2296Z" fill="#EDEDF0" />
<path d="M78.7363 16.2296H81.0499V11.1174C81.0499 9.16334 81.9519 7.9593 83.6381 7.9593H84.6576V5.63019H83.893C82.5793 5.63019 81.5793 6.53815 81.1872 7.40663H81.0303V5.88678H78.7363V16.2296Z" fill="#EDEDF0" />
<path d="M96.1391 16.2296H97.943V14.1571H96.1587C95.4529 14.1571 95.1588 13.8413 95.1588 13.111V7.93956H98.0606V5.88678H95.1588V2.98526H92.9628V5.88678H91.0413V7.93956H92.8255V13.1307C92.8255 15.3217 94.1392 16.2296 96.1391 16.2296Z" fill="#EDEDF0" />
<path d="M104.15 16.2461C106.287 16.2461 108.17 15.1802 108.836 13.0287L106.719 12.5155C106.346 13.6603 105.268 14.2525 104.13 14.2525C102.444 14.2525 101.327 13.1472 101.307 11.4102H109.091V10.7588C109.091 7.67965 107.189 5.39001 104.052 5.39001C101.287 5.39001 98.915 7.58096 98.915 10.8378C98.915 13.9959 101.013 16.2461 104.15 16.2461ZM101.327 9.71269C101.464 8.46918 102.581 7.42305 104.052 7.42305C105.464 7.42305 106.621 8.31128 106.738 9.71269H101.327Z" fill="#EDEDF0" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M90.0125 16.2296H87.6989V7.93956H85.895V5.88678H90.0125V16.2296Z" fill="#EDEDF0" />
<path d="M88.6834 4.45145C89.5265 4.45145 90.154 3.81983 90.154 2.99082C90.154 2.18155 89.5265 1.54993 88.6834 1.54993C87.8403 1.54993 87.2129 2.18155 87.2129 2.99082C87.2129 3.81983 87.8403 4.45145 88.6834 4.45145Z" fill="#EDEDF0" />
<path d="M20.2007 13.6935V18.258H8.88588C5.5894 18.258 2.71111 16.4222 1.17116 13.6935C0.947288 13.2968 0.751353 12.8806 0.586995 12.4486C0.26435 11.6021 0.0615332 10.6938 0 9.74603V8.51195C0.0133592 8.30074 0.03441 8.09119 0.0619381 7.88413C0.118209 7.45921 0.203222 7.04343 0.314953 6.63926C1.37195 2.80758 4.8089 0 8.88588 0C12.9629 0 16.3994 2.80758 17.4564 6.63926H12.6184C11.8241 5.39025 10.4493 4.5645 8.88588 4.5645C7.32245 4.5645 5.94767 5.39025 5.15341 6.63926C4.91132 7.01895 4.72349 7.43764 4.60042 7.88413C4.49112 8.27999 4.43282 8.69744 4.43282 9.12899C4.43282 10.4373 4.96962 11.6166 5.83027 12.4486C6.62778 13.2209 7.70299 13.6935 8.88588 13.6935H20.2007Z" fill="#FD366E" />
<path d="M20.2006 7.88412V12.4486H11.9414C12.8021 11.6166 13.3389 10.4373 13.3389 9.12899C13.3389 8.69744 13.2806 8.27999 13.1713 7.88412H20.2006Z" fill="#FD366E" />
</svg>
<svg class="logo-light" width="110" height="20" viewBox="0 0 110 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M31.8648 16.2461C33.649 16.2461 34.5509 15.3184 34.9431 14.6867H35.1195C35.198 15.3578 35.6685 15.9895 36.5901 15.9895H38.3351V14.0156H37.8841C37.5704 14.0156 37.4136 13.838 37.4136 13.5617V5.64661H35.0999V6.90986H34.9235C34.4725 6.27823 33.5314 5.39001 31.8059 5.39001C29.0609 5.39001 27.0218 7.67965 27.0218 10.818C27.0218 13.9564 29.1001 16.2461 31.8648 16.2461ZM32.2765 13.9959C30.6491 13.9959 29.3746 12.7919 29.3746 10.8378C29.3746 8.92316 30.6099 7.62044 32.2569 7.62044C33.8255 7.62044 35.1391 8.78499 35.1391 10.8378C35.1391 12.5945 34.0215 13.9959 32.2765 13.9959Z" fill="#2D2D31" />
<path d="M39.7011 20H42.0147V14.6867H42.1912C42.6226 15.3184 43.5441 16.2461 45.3676 16.2461C48.1126 16.2461 50.1125 13.9169 50.1125 10.818C50.1125 7.69939 47.9753 5.39001 45.2107 5.39001C43.4461 5.39001 42.5833 6.35719 42.1716 6.89012H41.9951V5.64661H39.7011V20ZM44.8774 14.0551C43.2892 14.0551 41.9755 12.8708 41.9755 10.818C41.9755 9.06133 43.0931 7.58096 44.8382 7.58096C46.4656 7.58096 47.74 8.86395 47.74 10.818C47.74 12.7326 46.5048 14.0551 44.8774 14.0551Z" fill="#2D2D31" />
<path d="M51.3063 20H53.62V14.6867H53.7964C54.2278 15.3184 55.1493 16.2461 56.9728 16.2461C59.7178 16.2461 61.5271 13.9169 61.5271 10.818C61.5271 7.69939 59.5805 5.39001 56.8159 5.39001C55.0513 5.39001 54.1886 6.35719 53.7768 6.89012H53.6004V5.64661H51.3063V20ZM56.4826 14.0551C54.8944 14.0551 53.5808 12.8708 53.5808 10.818C53.5808 9.06133 54.6984 7.58096 56.4434 7.58096C58.0708 7.58096 59.3453 8.86395 59.3453 10.818C59.3453 12.7326 58.11 14.0551 56.4826 14.0551Z" fill="#2D2D31" />
<path d="M64.5855 16.2296H67.8599L69.7226 8.11721H69.8402L71.7029 16.2296H74.9577L77.564 5.88678H75.2322L73.3695 14.0189H73.193L71.3303 5.88678H68.252L66.3697 14.0189H66.1933L64.3502 5.88678H61.8797L64.5855 16.2296Z" fill="#2D2D31" />
<path d="M78.7361 16.2296H81.0498V11.1174C81.0498 9.16334 81.9517 7.9593 83.6379 7.9593H84.6575V5.63019H83.8928C82.5791 5.63019 81.5791 6.53815 81.187 7.40663H81.0301V5.88678H78.7361V16.2296Z" fill="#2D2D31" />
<path d="M96.1389 16.2296H97.9428V14.1571H96.1585C95.4527 14.1571 95.1586 13.8413 95.1586 13.111V7.93956H98.0604V5.88678H95.1586V2.98526H92.9626V5.88678H91.0411V7.93956H92.8253V13.1307C92.8253 15.3217 94.139 16.2296 96.1389 16.2296Z" fill="#2D2D31" />
<path d="M104.15 16.2461C106.287 16.2461 108.169 15.1802 108.836 13.0287L106.718 12.5155C106.346 13.6603 105.268 14.2525 104.13 14.2525C102.444 14.2525 101.326 13.1472 101.307 11.4102H109.091V10.7588C109.091 7.67965 107.189 5.39001 104.052 5.39001C101.287 5.39001 98.9148 7.58096 98.9148 10.8378C98.9148 13.9959 101.013 16.2461 104.15 16.2461ZM101.326 9.71269C101.464 8.46918 102.581 7.42305 104.052 7.42305C105.464 7.42305 106.62 8.31128 106.738 9.71269H101.326Z" fill="#2D2D31" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M90.0123 16.2296H87.6987V7.93956H85.8948V5.88678H90.0123V16.2296Z" fill="#2D2D31" />
<path d="M88.6835 4.45145C89.5266 4.45145 90.154 3.81983 90.154 2.99082C90.154 2.18155 89.5266 1.54993 88.6835 1.54993C87.8404 1.54993 87.213 2.18155 87.213 2.99082C87.213 3.81983 87.8404 4.45145 88.6835 4.45145Z" fill="#2D2D31" />
<path d="M20.2007 13.6935V18.258H8.88588C5.5894 18.258 2.71111 16.4222 1.17116 13.6935C0.947288 13.2968 0.751353 12.8806 0.586995 12.4486C0.26435 11.6021 0.0615332 10.6938 0 9.74603V8.51195C0.0133592 8.30074 0.03441 8.09119 0.0619381 7.88413C0.118209 7.45921 0.203222 7.04343 0.314953 6.63926C1.37195 2.80758 4.8089 0 8.88588 0C12.9629 0 16.3994 2.80758 17.4564 6.63926H12.6184C11.8241 5.39025 10.4493 4.5645 8.88588 4.5645C7.32245 4.5645 5.94767 5.39025 5.15341 6.63926C4.91132 7.01895 4.72349 7.43764 4.60042 7.88413C4.49112 8.27999 4.43282 8.69744 4.43282 9.12899C4.43282 10.4373 4.96962 11.6166 5.83027 12.4486C6.62778 13.2209 7.70299 13.6935 8.88588 13.6935H20.2007Z" fill="#FD366E" />
<path d="M20.2007 7.88412V12.4486H11.9415C12.8022 11.6166 13.339 10.4373 13.339 9.12899C13.339 8.69744 13.2807 8.27999 13.1714 7.88412H20.2007Z" fill="#FD366E" />
</svg>
</div>
</body>
</html>
+90 -505
View File
@@ -1,112 +1,22 @@
<?php
use Utopia\System\System;
$development = $this->getParam('development', false);
$type = $this->getParam('type', 'general_server_error');
$code = $this->getParam('code', 500);
$errorID = $this->getParam('errorID', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
$message = $this->getParam('message', '');
$trace = $this->getParam('trace', []);
$title = $this->getParam('title', 'Error');
$exception = $this->getParam('exception', null);
$isSimpleMessage = true;
$label = '';
$labelClass = '';
$buttons = [];
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN');
// TODO: remove this later
if (System::getEnv('_APP_ENV') === 'development') {
$hostname = 'localhost';
}
$url = $protocol . '://' . $hostname;
if($exception !== null && method_exists($exception, 'getCTAs')) {
foreach ($exception->getCTAs() as $index => $cta) {
$class = ($index === 0) ? 'bordered-button' : 'button';
$buttons[] = [
'text' => $cta['label'],
'url' => $cta['url'],
'class' => $class
];
}
}
switch ($type) {
case 'proxy_error_override':
$type = '';
$label = 'Error ' . $code;
$message = $code >= 500 ? 'An unexpected server error occured.' : 'An unexpected client error occured.';
switch($code) {
case 401:
$message = 'You must sign in to access this page.';
break;
case 403:
$message = 'You are not authorized to access this page.';
break;
case 404:
$message = 'The page you are looking for does not exist.';
break;
case 504:
$message = 'The server did not respond in time.';
break;
case 501:
$message = 'This page is not implemented yet.';
break;
}
break;
case 'function_execute_permission_missing':
$label = 'Execution not permitted';
$labelClass = 'warning';
break;
case 'build_not_ready':
$label = 'Deployment is still building';
$message = 'The page will update after the build completes.';
$labelClass = 'warning';
break;
case 'build_failed':
$label = 'Deployment build failed';
$message = 'An error occurred during the build process.';
$labelClass = 'error';
break;
case 'rule_not_found':
$label = 'Nothing is here yet';
$message = 'This page is empty, but you can make it yours.';
break;
case 'deployment_not_found':
$label = 'No active deployments';
$message = 'This page is empty, activate a deployment to make it live.';
break;
case 'build_canceled':
$label = 'Deployment build canceled';
$message = 'This build was canceled and won\'t be deployed.';
break;
case 'general_route_not_found':
$label = 'Page not found';
$message = 'The page you\'re looking for doesn\'t exist.';
break;
default:
$label = 'Error ' . $code;
$message = $message;
$isSimpleMessage = false;
break;
}
$projectName = $this->getParam('projectName', '');
$projectURL = $this->getParam('projectURL', '');
$title = $this->getParam('title', '')
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
<link rel="icon" type="image/svg+xml" href="<?php echo $url; ?>/images/logos/appwrite-icon.svg" />
<link rel="mask-icon" type="image/png" href="<?php echo $url; ?>/images/logos/appwrite-icon.png" />
<meta charset="utf-8" />
<meta name="description" content="" />
<link rel="icon" href="/favicon.png" />
<link
rel="preload"
href="/fonts/inter/inter-v8-latin-600.woff2"
@@ -119,427 +29,102 @@ switch ($type) {
as="font"
type="font/woff2"
crossorigin />
<link
rel="preload"
href="/fonts/poppins/poppins-v19-latin-500.woff2"
as="font"
type="font/woff2"
crossorigin />
<link
rel="preload"
href="/fonts/poppins/poppins-v19-latin-600.woff2"
as="font"
type="font/woff2"
crossorigin />
<link
rel="preload"
href="/fonts/poppins/poppins-v19-latin-700.woff2"
as="font"
type="font/woff2"
crossorigin />
<link
rel="preload"
href="/fonts/source-code-pro/source-code-pro-v20-latin-regular.woff2"
as="font"
type="font/woff2"
crossorigin />
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink" />
<link rel="preload" as="style" type="text/css" href="/fonts/main.css" />
<link rel="stylesheet" href="/fonts/main.css" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="content-security-policy" content="">
<title><?php echo $this->print($title); ?></title>
<style>
@import url(https://fonts.bunny.net/css?family=fira-code:400|inter:400);
* {
margin: 0;
padding: 0;
}
body {
background-color: #FFFFFF;
}
.main {
display: flex;
min-height: 100vh;
width: 100vw;
align-items: center;
justify-content: center;
}
.content {
margin-left: auto;
margin-right: auto;
max-width: 400px;
}
span {
padding: var(--space-1, 2px) var(--space-3, 6px);
border-radius: var(--border-radius-XS, 6px);
background: var(--color-overlay-on-neutral, rgba(0, 0, 0, 0.06));
color: var(--color-fgColor-neutral-secondary, #56565C);
text-align: center;
font-family: var(--font-family-sansSerif, Inter), sans-serif;
font-size: var(--font-size-S, 14px);
font-style: normal;
font-weight: 400;
line-height: 140%;
letter-spacing: -0.063px;
}
h1 {
color: var(--color-fgColor-neutral-primary, #2D2D31);
text-align: center;
font-family: var(--font-family-sansSerif, Inter), sans-serif;
font-size: var(--font-size-XXXL, 32px);
font-style: normal;
font-weight: 400;
line-height: 140%;
letter-spacing: -0.144px;
margin-top: 8px;
margin-bottom: 32px;
}
.content h1 {
margin-bottom: 20px;
}
.content.small-error h1 {
font-size: var(--font-size-M, 20px);
}
.content.large-error h1 {
font-size: var(--font-size-XXXL, 32px);
}
.bordered-button {
border-radius: var(--border-radius-S, 8px);
font-family: var(--font-family-sansSerif, Inter), sans-serif;
font-size: var(--font-size-S, 14px);
font-style: normal;
font-weight: 500;
line-height: 140%;
letter-spacing: -0.063px;
padding: var(--space-3, 6px) var(--space-5, 10px);
cursor: pointer;
border: var(--border-width-S, 1px) solid var(--color-border-neutral-strong, #D8D8DB);
background: var(--color-bgColor-neutral-primary, #FFF);
color: var(--color-fgColor-neutral-secondary, #56565C);
}
button {
border-radius: var(--border-radius-S, 8px);
font-family: var(--font-family-sansSerif, Inter), sans-serif;
font-size: var(--font-size-S, 14px);
font-style: normal;
font-weight: 500;
line-height: 140%;
letter-spacing: -0.063px;
padding: var(--space-3, 6px) var(--space-5, 10px);
cursor: pointer;
border: var(--border-width-S, 1px) solid transparent;
background: var(--color-bgColor-neutral-primary, #FFF);
color: var(--color-fgColor-neutral-secondary, #56565C);
}
.center {
display: flex;
justify-content: center;
gap: 8px;
}
.brand {
position: absolute;
width: 100%;
bottom: 32px;
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
}
.brand p {
font-family: var(--font-family-monospace, "Fira Code"), monospace;
font-size: var(--font-size-XS, 12px);
font-style: normal;
font-weight: 400;
line-height: 130%;
letter-spacing: 0.96px;
text-transform: uppercase;
color: var(--color-fgColor-neutral-secondary, #56565C);
}
.brand svg {
height: 20px;
}
.warning {
background: var(--color-overlay-on-neutral, rgba(254, 124, 67, 0.16));
color: var(--color-fgColor-neutral-secondary, #61250A);
}
.error {
background: var(--color-overlay-on-neutral, rgba(255, 69, 58, 0.16));
color: var(--color-fgColor-neutral-secondary, #B31212);
}
.logo-dark {
display: none;
}
.logo-light {
display: block;
}
.type {
padding: var(--space-1, 2px) var(--space-3, 6px);
border-radius: var(--border-radius-XS, 6px);
border: var(--border-width-S, 1px) solid var(--color-border-neutral-strong, #EDEDF0);
background: var(--color-overlay-on-neutral, rgba(250, 250, 251, 1));
color: var(--color-fgColor-neutral-secondary, #56565C);
text-align: center;
font-family: var(--font-family-monospace, "Fira Code"), monospace;
font-size: var(--font-size-XS, 12px);
font-style: normal;
font-weight: 400;
line-height: 140%;
letter-spacing: 0px;
}
.error-trace {
max-width: 900px;
padding: 20px;
font-family: var(--font-family-sansSerif, Inter), sans-serif;
}
.back-button {
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
color: var(--color-fgColor-neutral-secondary, #56565C);
font-family: var(--font-family-sansSerif, Inter), sans-serif;
font-size: var(--font-size-S, 14px);
font-style: normal;
font-weight: 500;
line-height: 140%;
letter-spacing: -0.45px;
}
.back-button:hover {
text-decoration: underline;
}
.trace-grid {
display: grid;
grid-template-columns: auto 1fr;
gap: 16px;
background: var(--color-bgColor-neutral-secondary, #FFFFFF);
padding: 10px 12px;
border: var(--border-width-S, 1px) solid var(--color-border-neutral-strong, #EDEDF0);
}
.trace-grid-header {
display: flex;
align-items: center;
padding: 10px 12px;
background: var(--color-bgColor-neutral-secondary, #FAFAFB);
border-radius: 8px 8px 0 0;
border: var(--border-width-S, 1px) solid var(--color-border-neutral-strong, #EDEDF0);
font-family: var(--font-family-sansSerif, Inter), sans-serif;
font-size: var(--font-size-S, 14px);
font-weight: 400;
line-height: 140%;
letter-spacing: -0.45px;
color: var(--color-fgColor-neutral-secondary, #56565C);
}
.trace-label {
font-family: var(--font-family-sansSerif, Inter), sans-serif;
font-size: var(--font-size-S, 14px);
font-weight: 400;
line-height: 140%;
letter-spacing: -0.45px;
color: var(--color-fgColor-neutral-secondary, #56565C);
}
.trace-value {
color: var(--color-fgColor-neutral-secondary, #56565C);
font-family: var(--font-family-monospace, "Fira Code"), monospace;
font-size: var(--font-size-S, 14px);
font-weight: 400;
line-height: 140%;
letter-spacing: 0px;
}
.trace-args {
/* grid-column: 1 / -1; */
padding: 10px 12px;
/* white-space: pre-wrap; */
overflow-x: auto;
font-family: var(--font-family-monospace, "Fira Code"), monospace;
font-size: var(--font-size-S, 14px);
font-weight: 400;
line-height: 140%;
color: var(--color-fgColor-neutral-secondary, #56565C);
}
@media (max-width: 768px) {
.content {
margin-left: 16px;
margin-right: 16px;
}
h1 {
font-size: 28px;
}
}
@media (prefers-color-scheme: dark) {
body {
background-color: #1D1D21;
}
h1 {
color: var(--color-fgColor-neutral-primary, #EDEDF0);
}
span {
background: var(--color-overlay-on-neutral, rgba(255, 255, 255, 0.2));
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
}
.bordered-button {
border: var(--border-width-S, 1px) solid var(--color-border-neutral-strong, #414146);
background: var(--color-bgColor-neutral-primary, #1D1D21);
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
}
button {
background: var(--color-bgColor-neutral-primary, #1D1D21);
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
}
.brand p {
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
}
.warning {
background: var(--color-overlay-on-neutral, rgba(254, 124, 67, 0.24));
color: var(--color-fgColor-neutral-secondary, #FFD5C2);
}
.error {
background: var(--color-overlay-on-neutral, rgba(255, 69, 58, 0.28));
color: var(--color-fgColor-neutral-secondary, #FFD5D4);
}
.logo-light {
display: none;
}
.logo-dark {
display: block;
}
.type {
background: var(--color-overlay-on-neutral, rgba(25, 25, 28, 1));
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
border: var(--border-width-S, 1px) solid var(--color-border-neutral-strong, #414146);
}
.back-button {
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
}
.trace-grid {
background: var(--color-bgColor-neutral-secondary, #1D1D21);
border: var(--border-width-S, 1px) solid var(--color-border-neutral-strong, #2D2D31);
}
.trace-grid-header {
background: var(--color-bgColor-neutral-secondary, #19191C);
border: var(--border-width-S, 1px) solid var(--color-border-neutral-strong, #2D2D31);
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
}
.trace-label {
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
}
.trace-value {
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
}
.trace-args {
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
@media(min-width:768px) {
article.card {
padding: 2rem !important;
}
}
</style>
</head>
<body x-data="{ page: 'error' }">
<div class="main">
<div x-show="page === 'error'" class="content <?php echo $isSimpleMessage ? 'large-error' : 'small-error' ?>">
<div class="center"><span class="<?php echo $this->print($labelClass); ?>"><?php echo $this->print($label); ?></span></div>
<h1><?php echo $this->print($message); ?></h1>
<?php if (!empty($type)): ?>
<div class="center">
<span class='type'><?php echo $this->print($type); ?></span>
<body>
<div class="container u-margin-block-start-24">
<article class="card u-padding-16">
<div class="u-flex u-flex-vertical u-gap-16">
<h1 class="heading-level-4 u-trim-1">Error <?php echo $this->print($code); ?></h1>
<p class="text"><?php echo $this->print($message); ?></p>
<div class="u-flex u-flex-vertical u-gap-8">
<p class="text">Type</p>
<p><code class="inline-code"><?php echo $this->print($type); ?></code></p>
</div>
<?php endif; ?>
<div class="center" style="margin-top: 20px;">
<?php if (!empty($buttons)): ?>
<?php foreach ($buttons as $button): ?>
<a href="<?php echo htmlspecialchars($button['url']); ?>">
<button class="<?php echo htmlspecialchars($button['class']); ?>">
<?php echo htmlspecialchars($button['text']); ?>
</button>
</a>
<?php if ($development) : ?>
<h2 class="heading-level-5 u-trim-1">Error Trace</h2>
<?php foreach ($trace as $log) : ?>
<div class="table-with-scroll">
<div class="table-wrapper">
<table class="table is-remove-outer-styles">
<tbody class="table-tbody">
<?php foreach ($log as $key => $value) : ?>
<tr>
<td class="table-col" style="width: 120px"><?php echo $this->print($key, self::FILTER_ESCAPE); ?></td>
<td class="table-col"><code class="grid-code u-max-height-200 u-overflow-x-auto u-overflow-y-auto">
<?php if (is_array($value)) : ?>
<pre><?php echo $this->print(var_export($value, true), self::FILTER_ESCAPE); ?></pre>
<?php else : ?>
<pre><?php echo $this->print($value, self::FILTER_ESCAPE); ?></pre>
<?php endif; ?>
</code>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
<?php if ($development) : ?>
<button class="<?php echo count($buttons) === 0 ? 'bordered-button' : 'button' ?>" x-on:click="page = 'trace'">View error trace</button>
<?php endif; ?>
</div>
</div>
<div x-show="page === 'trace'" class="error-trace">
<button class="back-button" x-on:click="page = 'error'">
Back
</button>
<div class="trace-grid-header">Error trace</div>
<?php foreach ($trace as $index => $traceItem): ?>
<div class="trace-grid">
<?php if (isset($traceItem['file'])): ?>
<div class="trace-label">file</div>
<div class="trace-value"><?php echo $this->print($traceItem['file']); ?></div>
<?php endif; ?>
<?php if (isset($traceItem['line'])): ?>
<div class="trace-label">line</div>
<div class="trace-value"><?php echo $this->print($traceItem['line']); ?></div>
<?php endif; ?>
<?php if (isset($traceItem['function'])): ?>
<div class="trace-label">function</div>
<div class="trace-value"><?php echo $this->print($traceItem['function']); ?></div>
<?php endif; ?>
<?php if (isset($traceItem['args'])): ?>
<div class="trace-label">args</div>
<div class="trace-args"><pre><?php echo $this->print(\var_export($traceItem['args'], true)); ?></pre></div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</div>
<div x-show="page === 'error'" class="brand">
<p>Powered by</p>
<svg class="logo-dark" width="110" height="20" viewBox="0 0 110 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M31.8649 16.2461C33.6492 16.2461 34.5511 15.3184 34.9433 14.6867H35.1197C35.1981 15.3578 35.6687 15.9895 36.5903 15.9895H38.3353V14.0156H37.8843C37.5706 14.0156 37.4138 13.838 37.4138 13.5617V5.64661H35.1001V6.90986H34.9236C34.4727 6.27823 33.5315 5.39001 31.8061 5.39001C29.0611 5.39001 27.022 7.67965 27.022 10.818C27.022 13.9564 29.1003 16.2461 31.8649 16.2461ZM32.2767 13.9959C30.6493 13.9959 29.3748 12.7919 29.3748 10.8378C29.3748 8.92316 30.6101 7.62044 32.2571 7.62044C33.8256 7.62044 35.1393 8.86395 35.1393 10.8378C35.1393 12.5945 34.0217 13.9959 32.2767 13.9959Z" fill="#EDEDF0" />
<path d="M39.7013 20H42.0149V14.6867H42.1914C42.6227 15.3184 43.5443 16.2461 45.3677 16.2461C48.1127 16.2461 50.1127 13.9169 50.1127 10.818C50.1127 7.69939 47.9755 5.39001 45.2109 5.39001C43.4462 5.39001 42.5835 6.35719 42.1718 6.89012H41.9953V5.63019H39.7013V20ZM44.8776 14.0551C43.2894 14.0551 41.9757 12.8708 41.9757 10.818C41.9757 9.06133 43.0933 7.58096 44.8383 7.58096C46.4657 7.58096 47.7402 8.86395 47.7402 10.818C47.7402 12.7326 46.5049 14.0551 44.8776 14.0551Z" fill="#EDEDF0" />
<path d="M51.3065 20H53.6202V14.6867H53.7966C54.228 15.3184 55.1495 16.2461 56.973 16.2461C59.718 16.2461 61.5273 13.9169 61.5273 10.818C61.5273 7.69939 59.5807 5.39001 56.8161 5.39001C55.0515 5.39001 54.1888 6.35719 53.777 6.89012H53.6005V5.64661H51.3065V20ZM56.4828 14.0551C54.8946 14.0551 53.5809 12.8708 53.5809 10.818C53.5809 9.06133 54.6985 7.58096 56.4436 7.58096C58.071 7.58096 59.3454 8.86395 59.3454 10.818C59.3454 12.7326 58.1102 14.0551 56.4828 14.0551Z" fill="#EDEDF0" />
<path d="M64.5857 16.2296H67.8601L69.7227 8.11721H69.8404L71.7031 16.2296H74.9579L77.5642 5.88678H75.2323L73.3697 14.0189H73.1932L71.3305 5.88678H68.2522L66.3699 14.0189H66.1935L64.3504 5.88678H61.8799L64.5857 16.2296Z" fill="#EDEDF0" />
<path d="M78.7363 16.2296H81.0499V11.1174C81.0499 9.16334 81.9519 7.9593 83.6381 7.9593H84.6576V5.63019H83.893C82.5793 5.63019 81.5793 6.53815 81.1872 7.40663H81.0303V5.88678H78.7363V16.2296Z" fill="#EDEDF0" />
<path d="M96.1391 16.2296H97.943V14.1571H96.1587C95.4529 14.1571 95.1588 13.8413 95.1588 13.111V7.93956H98.0606V5.88678H95.1588V2.98526H92.9628V5.88678H91.0413V7.93956H92.8255V13.1307C92.8255 15.3217 94.1392 16.2296 96.1391 16.2296Z" fill="#EDEDF0" />
<path d="M104.15 16.2461C106.287 16.2461 108.17 15.1802 108.836 13.0287L106.719 12.5155C106.346 13.6603 105.268 14.2525 104.13 14.2525C102.444 14.2525 101.327 13.1472 101.307 11.4102H109.091V10.7588C109.091 7.67965 107.189 5.39001 104.052 5.39001C101.287 5.39001 98.915 7.58096 98.915 10.8378C98.915 13.9959 101.013 16.2461 104.15 16.2461ZM101.327 9.71269C101.464 8.46918 102.581 7.42305 104.052 7.42305C105.464 7.42305 106.621 8.31128 106.738 9.71269H101.327Z" fill="#EDEDF0" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M90.0125 16.2296H87.6989V7.93956H85.895V5.88678H90.0125V16.2296Z" fill="#EDEDF0" />
<path d="M88.6834 4.45145C89.5265 4.45145 90.154 3.81983 90.154 2.99082C90.154 2.18155 89.5265 1.54993 88.6834 1.54993C87.8403 1.54993 87.2129 2.18155 87.2129 2.99082C87.2129 3.81983 87.8403 4.45145 88.6834 4.45145Z" fill="#EDEDF0" />
<path d="M20.2007 13.6935V18.258H8.88588C5.5894 18.258 2.71111 16.4222 1.17116 13.6935C0.947288 13.2968 0.751353 12.8806 0.586995 12.4486C0.26435 11.6021 0.0615332 10.6938 0 9.74603V8.51195C0.0133592 8.30074 0.03441 8.09119 0.0619381 7.88413C0.118209 7.45921 0.203222 7.04343 0.314953 6.63926C1.37195 2.80758 4.8089 0 8.88588 0C12.9629 0 16.3994 2.80758 17.4564 6.63926H12.6184C11.8241 5.39025 10.4493 4.5645 8.88588 4.5645C7.32245 4.5645 5.94767 5.39025 5.15341 6.63926C4.91132 7.01895 4.72349 7.43764 4.60042 7.88413C4.49112 8.27999 4.43282 8.69744 4.43282 9.12899C4.43282 10.4373 4.96962 11.6166 5.83027 12.4486C6.62778 13.2209 7.70299 13.6935 8.88588 13.6935H20.2007Z" fill="#FD366E" />
<path d="M20.2006 7.88412V12.4486H11.9414C12.8021 11.6166 13.3389 10.4373 13.3389 9.12899C13.3389 8.69744 13.2806 8.27999 13.1713 7.88412H20.2006Z" fill="#FD366E" />
</svg>
<svg class="logo-light" width="110" height="20" viewBox="0 0 110 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M31.8648 16.2461C33.649 16.2461 34.5509 15.3184 34.9431 14.6867H35.1195C35.198 15.3578 35.6685 15.9895 36.5901 15.9895H38.3351V14.0156H37.8841C37.5704 14.0156 37.4136 13.838 37.4136 13.5617V5.64661H35.0999V6.90986H34.9235C34.4725 6.27823 33.5314 5.39001 31.8059 5.39001C29.0609 5.39001 27.0218 7.67965 27.0218 10.818C27.0218 13.9564 29.1001 16.2461 31.8648 16.2461ZM32.2765 13.9959C30.6491 13.9959 29.3746 12.7919 29.3746 10.8378C29.3746 8.92316 30.6099 7.62044 32.2569 7.62044C33.8255 7.62044 35.1391 8.78499 35.1391 10.8378C35.1391 12.5945 34.0215 13.9959 32.2765 13.9959Z" fill="#2D2D31" />
<path d="M39.7011 20H42.0147V14.6867H42.1912C42.6226 15.3184 43.5441 16.2461 45.3676 16.2461C48.1126 16.2461 50.1125 13.9169 50.1125 10.818C50.1125 7.69939 47.9753 5.39001 45.2107 5.39001C43.4461 5.39001 42.5833 6.35719 42.1716 6.89012H41.9951V5.64661H39.7011V20ZM44.8774 14.0551C43.2892 14.0551 41.9755 12.8708 41.9755 10.818C41.9755 9.06133 43.0931 7.58096 44.8382 7.58096C46.4656 7.58096 47.74 8.86395 47.74 10.818C47.74 12.7326 46.5048 14.0551 44.8774 14.0551Z" fill="#2D2D31" />
<path d="M51.3063 20H53.62V14.6867H53.7964C54.2278 15.3184 55.1493 16.2461 56.9728 16.2461C59.7178 16.2461 61.5271 13.9169 61.5271 10.818C61.5271 7.69939 59.5805 5.39001 56.8159 5.39001C55.0513 5.39001 54.1886 6.35719 53.7768 6.89012H53.6004V5.64661H51.3063V20ZM56.4826 14.0551C54.8944 14.0551 53.5808 12.8708 53.5808 10.818C53.5808 9.06133 54.6984 7.58096 56.4434 7.58096C58.0708 7.58096 59.3453 8.86395 59.3453 10.818C59.3453 12.7326 58.11 14.0551 56.4826 14.0551Z" fill="#2D2D31" />
<path d="M64.5855 16.2296H67.8599L69.7226 8.11721H69.8402L71.7029 16.2296H74.9577L77.564 5.88678H75.2322L73.3695 14.0189H73.193L71.3303 5.88678H68.252L66.3697 14.0189H66.1933L64.3502 5.88678H61.8797L64.5855 16.2296Z" fill="#2D2D31" />
<path d="M78.7361 16.2296H81.0498V11.1174C81.0498 9.16334 81.9517 7.9593 83.6379 7.9593H84.6575V5.63019H83.8928C82.5791 5.63019 81.5791 6.53815 81.187 7.40663H81.0301V5.88678H78.7361V16.2296Z" fill="#2D2D31" />
<path d="M96.1389 16.2296H97.9428V14.1571H96.1585C95.4527 14.1571 95.1586 13.8413 95.1586 13.111V7.93956H98.0604V5.88678H95.1586V2.98526H92.9626V5.88678H91.0411V7.93956H92.8253V13.1307C92.8253 15.3217 94.139 16.2296 96.1389 16.2296Z" fill="#2D2D31" />
<path d="M104.15 16.2461C106.287 16.2461 108.169 15.1802 108.836 13.0287L106.718 12.5155C106.346 13.6603 105.268 14.2525 104.13 14.2525C102.444 14.2525 101.326 13.1472 101.307 11.4102H109.091V10.7588C109.091 7.67965 107.189 5.39001 104.052 5.39001C101.287 5.39001 98.9148 7.58096 98.9148 10.8378C98.9148 13.9959 101.013 16.2461 104.15 16.2461ZM101.326 9.71269C101.464 8.46918 102.581 7.42305 104.052 7.42305C105.464 7.42305 106.62 8.31128 106.738 9.71269H101.326Z" fill="#2D2D31" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M90.0123 16.2296H87.6987V7.93956H85.8948V5.88678H90.0123V16.2296Z" fill="#2D2D31" />
<path d="M88.6835 4.45145C89.5266 4.45145 90.154 3.81983 90.154 2.99082C90.154 2.18155 89.5266 1.54993 88.6835 1.54993C87.8404 1.54993 87.213 2.18155 87.213 2.99082C87.213 3.81983 87.8404 4.45145 88.6835 4.45145Z" fill="#2D2D31" />
<path d="M20.2007 13.6935V18.258H8.88588C5.5894 18.258 2.71111 16.4222 1.17116 13.6935C0.947288 13.2968 0.751353 12.8806 0.586995 12.4486C0.26435 11.6021 0.0615332 10.6938 0 9.74603V8.51195C0.0133592 8.30074 0.03441 8.09119 0.0619381 7.88413C0.118209 7.45921 0.203222 7.04343 0.314953 6.63926C1.37195 2.80758 4.8089 0 8.88588 0C12.9629 0 16.3994 2.80758 17.4564 6.63926H12.6184C11.8241 5.39025 10.4493 4.5645 8.88588 4.5645C7.32245 4.5645 5.94767 5.39025 5.15341 6.63926C4.91132 7.01895 4.72349 7.43764 4.60042 7.88413C4.49112 8.27999 4.43282 8.69744 4.43282 9.12899C4.43282 10.4373 4.96962 11.6166 5.83027 12.4486C6.62778 13.2209 7.70299 13.6935 8.88588 13.6935H20.2007Z" fill="#FD366E" />
<path d="M20.2007 7.88412V12.4486H11.9415C12.8022 11.6166 13.339 10.4373 13.339 9.12899C13.339 8.69744 13.2807 8.27999 13.1714 7.88412H20.2007Z" fill="#FD366E" />
</svg>
</article>
</div>
<script type="text/javascript">
const app = (JSON.parse(localStorage.getItem('appwrite')) ?? {});
const theme = app.theme ?? 'auto';
if (theme === 'auto') {
const darkThemeMq = window.matchMedia('(prefers-color-scheme: dark)');
if (darkThemeMq.matches) {
document.body.setAttribute('class', `theme-dark`);
} else {
document.body.setAttribute('class', `theme-light`);
}
} else {
document.body.setAttribute('class', `theme-${theme}`);
}
</script>
</body>
</html>
+23 -81
View File
@@ -65,8 +65,6 @@ $image = $this->getParam('image', '');
- appwrite-config:/storage/config:rw
- appwrite-certificates:/storage/certificates:rw
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw
depends_on:
- mariadb
- redis
@@ -88,12 +86,10 @@ $image = $this->getParam('image', '');
- _APP_OPTIONS_ABUSE
- _APP_OPTIONS_ROUTER_PROTECTION
- _APP_OPTIONS_FORCE_HTTPS
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
- _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
- _APP_DOMAIN_TARGET_A
- _APP_DOMAIN_TARGET
- _APP_DOMAIN_FUNCTIONS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@@ -137,15 +133,12 @@ $image = $this->getParam('image', '');
- _APP_STORAGE_WASABI_SECRET
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
- _APP_COMPUTE_SIZE_LIMIT
- _APP_FUNCTIONS_SIZE_LIMIT
- _APP_FUNCTIONS_TIMEOUT
- _APP_SITES_TIMEOUT
- _APP_COMPUTE_BUILD_TIMEOUT
- _APP_COMPUTE_CPUS
- _APP_COMPUTE_MEMORY
- _APP_FUNCTIONS_BUILD_TIMEOUT
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_RUNTIMES
- _APP_SITES_RUNTIMES
- _APP_DOMAIN_SITES
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_LOGGING_CONFIG
@@ -175,7 +168,7 @@ $image = $this->getParam('image', '');
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: <?php echo $organization; ?>/console:5.2.53
image: <?php echo $organization; ?>/console:5.2.58
restart: unless-stopped
networks:
- appwrite
@@ -306,7 +299,6 @@ $image = $this->getParam('image', '');
- appwrite-uploads:/storage/uploads:rw
- appwrite-cache:/storage/cache:rw
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw
- appwrite-certificates:/storage/certificates:rw
environment:
@@ -393,9 +385,7 @@ $image = $this->getParam('image', '');
- mariadb
volumes:
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw
- appwrite-uploads:/storage/uploads:rw
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -416,13 +406,12 @@ $image = $this->getParam('image', '');
- _APP_VCS_GITHUB_PRIVATE_KEY
- _APP_VCS_GITHUB_APP_ID
- _APP_FUNCTIONS_TIMEOUT
- _APP_SITES_TIMEOUT
- _APP_COMPUTE_BUILD_TIMEOUT
- _APP_COMPUTE_CPUS
- _APP_COMPUTE_MEMORY
- _APP_COMPUTE_SIZE_LIMIT
- _APP_FUNCTIONS_BUILD_TIMEOUT
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_SIZE_LIMIT
- _APP_OPTIONS_FORCE_HTTPS
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
- _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS
- _APP_DOMAIN
- _APP_STORAGE_DEVICE
- _APP_STORAGE_S3_ACCESS_KEY
@@ -446,7 +435,6 @@ $image = $this->getParam('image', '');
- _APP_STORAGE_WASABI_SECRET
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
- _APP_DOMAIN_SITES
appwrite-worker-certificates:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
@@ -467,9 +455,7 @@ $image = $this->getParam('image', '');
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
- _APP_DOMAIN_TARGET_A
- _APP_DOMAIN_TARGET
- _APP_DOMAIN_FUNCTIONS
- _APP_EMAIL_CERTIFICATES
- _APP_REDIS_HOST
@@ -511,10 +497,9 @@ $image = $this->getParam('image', '');
- _APP_DB_USER
- _APP_DB_PASS
- _APP_FUNCTIONS_TIMEOUT
- _APP_SITES_TIMEOUT
- _APP_COMPUTE_BUILD_TIMEOUT
- _APP_COMPUTE_CPUS
- _APP_COMPUTE_MEMORY
- _APP_FUNCTIONS_BUILD_TIMEOUT
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_USAGE_STATS
@@ -622,9 +607,7 @@ $image = $this->getParam('image', '');
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
- _APP_DOMAIN_TARGET_A
- _APP_DOMAIN_TARGET
- _APP_EMAIL_SECURITY
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@@ -653,9 +636,7 @@ $image = $this->getParam('image', '');
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
- _APP_DOMAIN_TARGET_A
- _APP_DOMAIN_TARGET
- _APP_DOMAIN_FUNCTIONS
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
@@ -763,34 +744,6 @@ $image = $this->getParam('image', '');
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
appwrite-worker-stats-usage-dump:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-stats-usage-dump
<<: *x-logging
container_name: appwrite-worker-stats-usage-dump
restart: unless-stopped
networks:
- appwrite
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
appwrite-task-scheduler-functions:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: schedule-functions
@@ -875,14 +828,6 @@ $image = $this->getParam('image', '');
- appwrite
environment:
- _APP_ASSISTANT_OPENAI_API_KEY
appwrite-browser:
image: appwrite/browser:0.2.2
container_name: appwrite-browser
<<: *x-logging
restart: unless-stopped
networks:
- appwrite
openruntimes-executor:
container_name: openruntimes-executor
@@ -890,7 +835,7 @@ $image = $this->getParam('image', '');
<<: *x-logging
restart: unless-stopped
stop_signal: SIGINT
image: openruntimes/executor:0.7.13
image: openruntimes/executor:0.6.11
networks:
- appwrite
- runtimes
@@ -898,20 +843,18 @@ $image = $this->getParam('image', '');
- /var/run/docker.sock:/var/run/docker.sock
- appwrite-builds:/storage/builds:rw
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
# Host mount nessessary to share files between executor and runtimes.
# It's not possible to share mount file between 2 containers without host mount (copying is too slow)
- /tmp:/tmp:rw
environment:
- OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_COMPUTE_INACTIVE_THRESHOLD
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_COMPUTE_MAINTENANCE_INTERVAL
- OPR_EXECUTOR_NETWORK=$_APP_COMPUTE_RUNTIMES_NETWORK
- OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL
- OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK
- OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME
- OPR_EXECUTOR_DOCKER_HUB_PASSWORD=$_APP_DOCKER_HUB_PASSWORD
- OPR_EXECUTOR_ENV=$_APP_ENV
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES,$_APP_SITES_RUNTIMES
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
- OPR_EXECUTOR_RUNTIME_VERSIONS=v5
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
- OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY
@@ -992,6 +935,5 @@ volumes:
appwrite-uploads:
appwrite-certificates:
appwrite-functions:
appwrite-sites:
appwrite-builds:
appwrite-config:
+2 -15
View File
@@ -15,7 +15,6 @@ use Appwrite\Event\Messaging;
use Appwrite\Event\Migration;
use Appwrite\Event\Realtime;
use Appwrite\Event\StatsUsage;
use Appwrite\Event\StatsUsageDump;
use Appwrite\Event\Webhook;
use Appwrite\Platform\Appwrite;
use Executor\Executor;
@@ -279,10 +278,6 @@ Server::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
Server::setResource('queueForStatsUsageDump', function (Publisher $publisher) {
return new StatsUsageDump($publisher);
}, ['publisher']);
Server::setResource('queueForDatabase', function (Publisher $publisher) {
return new EventDatabase($publisher);
}, ['publisher']);
@@ -339,14 +334,6 @@ Server::setResource('pools', function (Registry $register) {
return $register->get('pools');
}, ['register']);
Server::setResource('deviceForSites', function (Document $project) {
return getDevice(APP_STORAGE_SITES . '/app-' . $project->getId());
}, ['project']);
Server::setResource('deviceForImports', function (Document $project) {
return getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId());
}, ['project']);
Server::setResource('deviceForFunctions', function (Document $project) {
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
}, ['project']);
@@ -378,7 +365,7 @@ Server::setResource('certificates', function () {
});
Server::setResource('logError', function (Registry $register, Document $project) {
return function (Throwable $error, string $namespace, string $action, ?array $extras = null) use ($register, $project) {
return function (Throwable $error, string $namespace, string $action, ?array $extras) use ($register, $project) {
$logger = $register->get('logger');
if ($logger) {
@@ -400,7 +387,7 @@ Server::setResource('logError', function (Registry $register, Document $project)
$log->addExtra('trace', $error->getTraceAsString());
foreach (($extras ?? []) as $key => $value) {
foreach ($extras as $key => $value) {
$log->addExtra($key, $value);
}
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
php /usr/src/code/app/cli.php screenshot $@
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
php /usr/src/code/app/worker.php stats-usage-dump $@
+6 -9
View File
@@ -14,8 +14,7 @@
"test": "vendor/bin/phpunit",
"lint": "vendor/bin/pint --test",
"format": "vendor/bin/pint",
"bench": "vendor/bin/phpbench run --report=benchmark",
"check": "./vendor/bin/phpstan analyse -c phpstan.neon"
"bench": "vendor/bin/phpbench run --report=benchmark"
},
"autoload": {
"psr-4": {
@@ -44,7 +43,7 @@
"ext-openssl": "*",
"ext-zlib": "*",
"ext-sockets": "*",
"appwrite/php-runtimes": "0.19.*",
"appwrite/php-runtimes": "0.16.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "0.52.*",
"utopia-php/analytics": "0.10.*",
@@ -52,7 +51,6 @@
"utopia-php/cache": "0.12.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/detector": "0.1.*",
"utopia-php/database": "0.66.*",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
@@ -62,7 +60,7 @@
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.16.*",
"utopia-php/migration": "0.9.1",
"utopia-php/migration": "0.8.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.8.*",
@@ -73,7 +71,7 @@
"utopia-php/swoole": "0.8.*",
"utopia-php/system": "0.9.*",
"utopia-php/telemetry": "0.1.*",
"utopia-php/vcs": "0.10.*",
"utopia-php/vcs": "0.9.*",
"utopia-php/websocket": "0.3.*",
"matomo/device-detector": "6.1.*",
"dragonmantank/cron-expression": "3.3.2",
@@ -89,7 +87,6 @@
"appwrite/sdk-generator": "0.40.*",
"phpunit/phpunit": "9.*",
"swoole/ide-helper": "5.1.2",
"phpstan/phpstan": "1.8.*",
"textalk/websocket": "1.5.*",
"laravel/pint": "1.*",
"phpbench/phpbench": "1.*"
@@ -102,8 +99,8 @@
"php": "8.3"
},
"allow-plugins": {
"php-http/discovery": true,
"tbachert/spi": true
"php-http/discovery": false,
"tbachert/spi": false
}
}
}
Generated
+27 -130
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": "51959289a3f882160f5a9eeb605d41d7",
"content-hash": "35fd85e8d566d20d8177266469c5ebcb",
"packages": [
{
"name": "adhocore/jwt",
@@ -157,16 +157,16 @@
},
{
"name": "appwrite/php-runtimes",
"version": "0.19.0",
"version": "0.16.5",
"source": {
"type": "git",
"url": "https://github.com/appwrite/runtimes.git",
"reference": "8d21483efc19b9d977e323188989ee67a188464b"
"reference": "1e430646fdf847a7caf3c611dcf3d6d5a28c3fd9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/8d21483efc19b9d977e323188989ee67a188464b",
"reference": "8d21483efc19b9d977e323188989ee67a188464b",
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/1e430646fdf847a7caf3c611dcf3d6d5a28c3fd9",
"reference": "1e430646fdf847a7caf3c611dcf3d6d5a28c3fd9",
"shasum": ""
},
"require": {
@@ -206,9 +206,9 @@
],
"support": {
"issues": "https://github.com/appwrite/runtimes/issues",
"source": "https://github.com/appwrite/runtimes/tree/0.19.0"
"source": "https://github.com/appwrite/runtimes/tree/0.16.5"
},
"time": "2025-03-25T22:37:51+00:00"
"time": "2024-11-25T15:17:06+00:00"
},
{
"name": "beberlei/assert",
@@ -3551,51 +3551,6 @@
},
"time": "2025-04-16T07:10:27+00:00"
},
{
"name": "utopia-php/detector",
"version": "0.1.4",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/detector.git",
"reference": "895a4147463965b5f9cbc083b764b6476f547879"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/detector/zipball/895a4147463965b5f9cbc083b764b6476f547879",
"reference": "895a4147463965b5f9cbc083b764b6476f547879",
"shasum": ""
},
"require": {
"php": ">=8.0"
},
"require-dev": {
"laravel/pint": "1.2.*",
"phpstan/phpstan": "1.8.*",
"phpunit/phpunit": "^9.4"
},
"type": "library",
"autoload": {
"psr-4": {
"Utopia\\Detector\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A simple library for fast and reliable environment identification.",
"keywords": [
"detector",
"framework",
"php",
"utopia"
],
"support": {
"issues": "https://github.com/utopia-php/detector/issues",
"source": "https://github.com/utopia-php/detector/tree/0.1.4"
},
"time": "2025-04-09T11:50:45+00:00"
},
{
"name": "utopia-php/domains",
"version": "0.5.0",
@@ -3996,16 +3951,16 @@
},
{
"name": "utopia-php/migration",
"version": "0.9.1",
"version": "0.8.6",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "f8b54727c7b0abe416a74a2a4c9fa4350c7a59a3"
"reference": "84163e16edc0b2e64c34ad7b7c4cc5f05d762daf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/f8b54727c7b0abe416a74a2a4c9fa4350c7a59a3",
"reference": "f8b54727c7b0abe416a74a2a4c9fa4350c7a59a3",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/84163e16edc0b2e64c34ad7b7c4cc5f05d762daf",
"reference": "84163e16edc0b2e64c34ad7b7c4cc5f05d762daf",
"shasum": ""
},
"require": {
@@ -4046,9 +4001,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.9.1"
"source": "https://github.com/utopia-php/migration/tree/0.8.6"
},
"time": "2025-04-17T05:18:58+00:00"
"time": "2025-04-14T08:22:09+00:00"
},
{
"name": "utopia-php/orchestration",
@@ -4588,16 +4543,16 @@
},
{
"name": "utopia-php/vcs",
"version": "0.10.1",
"version": "0.9.4",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "6be02650cc361764900ade8c129f309df263eb74"
"reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/6be02650cc361764900ade8c129f309df263eb74",
"reference": "6be02650cc361764900ade8c129f309df263eb74",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/1a8d280b176acc99ea8d9e7364b8767cbb206b4a",
"reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a",
"shasum": ""
},
"require": {
@@ -4615,7 +4570,8 @@
"type": "library",
"autoload": {
"psr-4": {
"Utopia\\VCS\\": "src/VCS"
"Utopia\\VCS\\": "src/VCS",
"Utopia\\Detector\\": "src/Detector"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -4631,9 +4587,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.10.1"
"source": "https://github.com/utopia-php/vcs/tree/0.9.4"
},
"time": "2025-03-18T11:44:09+00:00"
"time": "2025-03-13T10:09:45+00:00"
},
{
"name": "utopia-php/websocket",
@@ -4811,16 +4767,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.40.13",
"version": "0.40.14",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "c4bc0ff4589969252b99354368743a99997f0428"
"reference": "bf3bf29b24d2dce9f49e6849e3af2a1567592d02"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/c4bc0ff4589969252b99354368743a99997f0428",
"reference": "c4bc0ff4589969252b99354368743a99997f0428",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/bf3bf29b24d2dce9f49e6849e3af2a1567592d02",
"reference": "bf3bf29b24d2dce9f49e6849e3af2a1567592d02",
"shasum": ""
},
"require": {
@@ -4856,9 +4812,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.40.13"
"source": "https://github.com/appwrite/sdk-generator/tree/0.40.14"
},
"time": "2025-04-23T11:59:44+00:00"
"time": "2025-04-24T14:36:08+00:00"
},
{
"name": "doctrine/annotations",
@@ -5658,65 +5614,6 @@
],
"time": "2025-03-12T08:01:40+00:00"
},
{
"name": "phpstan/phpstan",
"version": "1.8.11",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "46e223dd68a620da18855c23046ddb00940b4014"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014",
"reference": "46e223dd68a620da18855c23046ddb00940b4014",
"shasum": ""
},
"require": {
"php": "^7.2|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
},
"bin": [
"phpstan",
"phpstan.phar"
],
"type": "library",
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
"dev",
"static analysis"
],
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.8.11"
},
"funding": [
{
"url": "https://github.com/ondrejmirtes",
"type": "github"
},
{
"url": "https://github.com/phpstan",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
"type": "tidelift"
}
],
"time": "2022-10-24T15:45:13+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.32",
+25 -87
View File
@@ -72,13 +72,10 @@ services:
- traefik.http.routers.appwrite_api_https.tls=true
volumes:
- appwrite-uploads:/storage/uploads:rw
- appwrite-imports:/storage/imports:rw
- appwrite-cache:/storage/cache:rw
- appwrite-config:/storage/config:rw
- appwrite-certificates:/storage/certificates:rw
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw
- ./phpunit.xml:/usr/src/code/phpunit.xml
- ./tests:/usr/src/code/tests
- ./app:/usr/src/code/app
@@ -113,12 +110,10 @@ services:
- _APP_OPTIONS_ABUSE
- _APP_OPTIONS_ROUTER_PROTECTION
- _APP_OPTIONS_FORCE_HTTPS
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
- _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
- _APP_DOMAIN_TARGET_A
- _APP_DOMAIN_TARGET
- _APP_DOMAIN_FUNCTIONS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@@ -162,15 +157,12 @@ services:
- _APP_STORAGE_WASABI_SECRET
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
- _APP_COMPUTE_SIZE_LIMIT
- _APP_FUNCTIONS_SIZE_LIMIT
- _APP_FUNCTIONS_TIMEOUT
- _APP_SITES_TIMEOUT
- _APP_COMPUTE_BUILD_TIMEOUT
- _APP_COMPUTE_CPUS
- _APP_COMPUTE_MEMORY
- _APP_FUNCTIONS_BUILD_TIMEOUT
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_RUNTIMES
- _APP_SITES_RUNTIMES
- _APP_DOMAIN_SITES
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_LOGGING_CONFIG
@@ -206,13 +198,14 @@ services:
- _APP_DATABASE_SHARED_TABLES_V1
- _APP_DATABASE_SHARED_NAMESPACE
- _APP_FUNCTIONS_CREATION_ABUSE_LIMIT
- _APP_CUSTOM_DOMAIN_DENY_LIST
extra_hosts:
- "host.docker.internal:host-gateway"
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: appwrite/console:5.3.0-sites-rc.40
image: appwrite/console:5.2.58
restart: unless-stopped
networks:
- appwrite
@@ -355,7 +348,6 @@ services:
- appwrite-uploads:/storage/uploads:rw
- appwrite-cache:/storage/cache:rw
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw
- appwrite-certificates:/storage/certificates:rw
- ./app:/usr/src/code/app
@@ -444,9 +436,7 @@ services:
- appwrite
volumes:
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw
- appwrite-uploads:/storage/uploads:rw
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
@@ -472,13 +462,12 @@ services:
- _APP_VCS_GITHUB_PRIVATE_KEY
- _APP_VCS_GITHUB_APP_ID
- _APP_FUNCTIONS_TIMEOUT
- _APP_SITES_TIMEOUT
- _APP_COMPUTE_BUILD_TIMEOUT
- _APP_COMPUTE_CPUS
- _APP_COMPUTE_MEMORY
- _APP_COMPUTE_SIZE_LIMIT
- _APP_FUNCTIONS_BUILD_TIMEOUT
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_SIZE_LIMIT
- _APP_OPTIONS_FORCE_HTTPS
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
- _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS
- _APP_DOMAIN
- _APP_STORAGE_DEVICE
- _APP_STORAGE_S3_ACCESS_KEY
@@ -503,7 +492,6 @@ services:
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
- _APP_DATABASE_SHARED_TABLES
- _APP_DOMAIN_SITES
extra_hosts:
- "host.docker.internal:host-gateway"
@@ -527,9 +515,7 @@ services:
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
- _APP_DOMAIN_TARGET_A
- _APP_DOMAIN_TARGET
- _APP_DOMAIN_FUNCTIONS
- _APP_EMAIL_CERTIFICATES
- _APP_REDIS_HOST
@@ -574,10 +560,9 @@ services:
- _APP_DB_USER
- _APP_DB_PASS
- _APP_FUNCTIONS_TIMEOUT
- _APP_SITES_TIMEOUT
- _APP_COMPUTE_BUILD_TIMEOUT
- _APP_COMPUTE_CPUS
- _APP_COMPUTE_MEMORY
- _APP_FUNCTIONS_BUILD_TIMEOUT
- _APP_FUNCTIONS_CPUS
- _APP_FUNCTIONS_MEMORY
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_USAGE_STATS
@@ -685,7 +670,6 @@ services:
networks:
- appwrite
volumes:
- appwrite-imports:/storage/imports:rw
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
- ./tests:/usr/src/code/tests
@@ -696,9 +680,7 @@ services:
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
- _APP_DOMAIN_TARGET_A
- _APP_DOMAIN_TARGET
- _APP_EMAIL_SECURITY
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@@ -730,9 +712,7 @@ services:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
- _APP_DOMAIN_TARGET_A
- _APP_DOMAIN_TARGET
- _APP_DOMAIN_FUNCTIONS
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
@@ -848,38 +828,6 @@ services:
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-stats-usage-dump:
entrypoint: worker-stats-usage-dump
<<: *x-logging
container_name: appwrite-worker-stats-usage-dump
image: appwrite-dev
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
- _APP_STATS_USAGE_DUAL_WRITING_DBS
appwrite-task-scheduler-functions:
entrypoint: schedule-functions
<<: *x-logging
@@ -971,18 +919,12 @@ services:
environment:
- _APP_ASSISTANT_OPENAI_API_KEY
appwrite-browser:
container_name: appwrite-browser
image: appwrite/browser:0.2.2
networks:
- appwrite
openruntimes-executor:
container_name: openruntimes-executor
hostname: exc1
<<: *x-logging
stop_signal: SIGINT
image: openruntimes/executor:0.7.13
image: openruntimes/executor:0.6.11
restart: unless-stopped
networks:
- appwrite
@@ -991,21 +933,19 @@ services:
- /var/run/docker.sock:/var/run/docker.sock
- appwrite-builds:/storage/builds:rw
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
# Host mount nessessary to share files between executor and runtimes.
# It's not possible to share mount file between 2 containers without host mount (copying is too slow)
- /tmp:/tmp:rw
environment:
- OPR_EXECUTOR_IMAGE_PULL=enabled
- OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_COMPUTE_INACTIVE_THRESHOLD
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_COMPUTE_MAINTENANCE_INTERVAL
- OPR_EXECUTOR_NETWORK=$_APP_COMPUTE_RUNTIMES_NETWORK
- OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL
- OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK
- OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME
- OPR_EXECUTOR_DOCKER_HUB_PASSWORD=$_APP_DOCKER_HUB_PASSWORD
- OPR_EXECUTOR_ENV=$_APP_ENV
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES,$_APP_SITES_RUNTIMES
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
- OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v5
- OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v4
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
- OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY
@@ -1161,9 +1101,7 @@ volumes:
appwrite-redis:
appwrite-cache:
appwrite-uploads:
appwrite-imports:
appwrite-certificates:
appwrite-functions:
appwrite-sites:
appwrite-builds:
appwrite-config:
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);

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