Compare commits

...
Author SHA1 Message Date
fogelito 4d670bb47d Remove use 2025-07-20 12:06:23 +03:00
fogelito a9e53a46a6 formatting 2025-07-20 12:04:55 +03:00
fogelito e2797ab9d4 Disable index array 2025-07-20 11:57:43 +03:00
Steven NguyenandGitHub 8b90acd8c6 Merge pull request #10164 from appwrite/improve-scheme-error
chore: improve invalid scheme error in origin check
2025-07-19 07:16:20 -07:00
Chirag Aggarwal 7fecfc73ca fix: formatting 2025-07-19 10:39:39 +05:30
Chirag Aggarwal c55f039f13 chore: add unit test 2025-07-18 22:23:33 +05:30
Chirag Aggarwal d613237fb4 chore: improve invalid scheme error in origin check 2025-07-18 22:08:43 +05:30
Steven NguyenandGitHub 5742dad1e4 Merge pull request #10161 from appwrite/update-dart-flutter
chore: update dart 16.1.0, flutter 17.0.2 and cli 8.2.2
2025-07-18 08:49:34 -07:00
Chirag Aggarwal ea0654cf5e improve naming 2025-07-18 17:16:01 +05:30
Chirag Aggarwal 99ea9c5d80 chore: update cli to 8.2.2 2025-07-18 17:10:20 +05:30
Chirag Aggarwal 712425c383 fix: version 2025-07-18 15:59:09 +05:30
Chirag Aggarwal 0cc443b47c fix: branch name 2025-07-18 15:56:12 +05:30
Chirag Aggarwal c729a84d51 chore: update dart to 16.1.0 and flutter to 17.0.2 2025-07-18 15:55:11 +05:30
Jake BarnbyandGitHub 33435890dd Merge pull request #10158 from appwrite/fix-redirect-specs
Handle redirect validator in specs + GraphQL type mapper
2025-07-18 12:19:22 +12:00
Jake Barnby 0a5fbb7bc7 Handle redirect validator in specs + GraphQL type mapper 2025-07-18 11:59:32 +12:00
Matej BačoandGitHub 1e61091834 Merge pull request #10157 from appwrite/fix-build-usage-specification
fix: build usage specification
2025-07-17 15:33:38 +02:00
loks0n 94decb26e6 fix: build usage specification 2025-07-17 14:05:05 +01:00
Matej BačoandGitHub a3f9707938 Merge pull request #10155 from appwrite/update-cli-8.2.1
Update cli 8.2.1
2025-07-17 11:55:14 +02:00
Chirag Aggarwal 0a82b6d30d chore: update CLI to 8.2.1 2025-07-17 11:44:30 +05:30
Damodar LohaniandGitHub 0f38bb689f Merge pull request #10138 from appwrite/update-react-native-0.10.1
chore: update react native to 0.10.1 and dotnet to 0.14.0
2025-07-15 10:39:30 +05:45
Steven NguyenandGitHub 00889426de Merge pull request #10150 from appwrite/fix-apple-oauth
fix: prevent injected $user from being shadowed
2025-07-14 21:29:16 -07:00
Steven Nguyen 477682e062 fix: prevent injected $user from being shadowed 2025-07-14 16:05:58 -07:00
Chirag Aggarwal 054b6a995a update dotnet to 0.14.0 2025-07-14 08:54:37 +05:30
Chirag Aggarwal a2ca08023d chore: update composer 2025-07-12 12:18:11 +05:30
Chirag Aggarwal 8a2e0d4970 chore: update react native to 0.10.1 and changelog 2025-07-12 12:04:51 +05:30
Matej BačoandGitHub 51d07832f7 Merge pull request #10136 from appwrite/update-cli-8.2.0
chore: update cli to 8.2.0
2025-07-11 16:39:13 +02:00
Matej BačoandGitHub 8c57d28f8f Merge pull request #10127 from appwrite/chore-speed-up-tests
Chore: speed up tests
2025-07-11 16:21:40 +02:00
Matej Bačo 0bdb64fc77 Fix migration tests 2025-07-11 15:54:53 +02:00
Chirag Aggarwal 32cb5e0a79 chore: update cli to 8.2.0 2025-07-11 16:31:50 +05:30
Matej BačoandGitHub e24671f6f4 Merge pull request #10135 from appwrite/fix-staging-specs
Fix: specs with new env vars
2025-07-11 12:08:21 +02:00
Matej Bačo 9bab764378 Fix specs with new env vars 2025-07-11 11:15:55 +02:00
Steven NguyenandGitHub fd35043dd7 Merge pull request #10119 from appwrite/fix-mobile-oauth2-redirect
fix: update OAuth2 redirect URLs
2025-07-10 16:46:58 -07:00
Steven Nguyen 100aea337f fix: update OAuth2 redirect URLs
1. handle non-standard ports if the request came in on a non-standard port
2. use the _APP_CONSOLE_DOMAIN env var to handle cases where the Console domain may not be the endpoint domain such as on Appwrite Cloud
3. use the _APP_OPTIONS_FORCE_HTTPS env var to determine the protocol to be consistent with the rest of the codebase
2025-07-10 15:51:13 -07:00
Steven NguyenandGitHub 435a319fb6 Merge pull request #10130 from appwrite/fix-oauth2-redirect-validation
fix: success validation in oauth2 redirect
2025-07-10 09:06:43 -07:00
Matej Bačo 18492ab917 Revert unwanted change 2025-07-10 16:16:06 +02:00
Matej Bačo 89d54f847f Speed up build cancellation 2025-07-10 15:52:29 +02:00
Matej Bačo 6dc6ff6ba0 Fix formatting 2025-07-10 14:44:44 +02:00
Matej Bačo 581235f972 Fix more tests 2025-07-10 14:43:56 +02:00
Matej Bačo f02ba4c976 Fix function tests 2025-07-10 14:25:53 +02:00
Matej Bačo eaf9b8b228 Migrate remaining tests to node 2025-07-10 12:55:45 +02:00
Steven Nguyen 11332ed1f9 chore: fix deprecation warning from passing null into json_decode 2025-07-09 22:10:17 -07:00
Steven Nguyen 990e1b91bb fix: omit : and @ if user and pass are empty
So, unparsing should not end up with https://:@appwrite.io just because user and pass are empty strings.
2025-07-09 22:09:18 -07:00
Steven Nguyen f202685450 fix: handle parsing URL that is only a scheme
In react native, a redirect URL may only be a url scheme such as appwrite-callback-myproject://. Since parse_url() fails on this type of URL, we need to add this workaround.
2025-07-09 22:01:38 -07:00
Steven NguyenandGitHub 9b90d8030e Merge pull request #10129 from appwrite/dat-579
Changed the default permission param of upsert document
2025-07-09 20:44:36 -07:00
Steven Nguyen 53086fe975 fix: success validation in oauth2 redirect
We switched to using the Redirect class for validating redirect URLs to cover additional cases like react native expo scheme, but we missed this validation.
2025-07-09 15:11:30 -07:00
ArnabChatterjee20k 1d9aac5a49 updated upsert document test with api key 2025-07-09 23:57:39 +05:30
ArnabChatterjee20k ca61b528b9 reverted the api signature and started handling it internally 2025-07-09 23:49:56 +05:30
ArnabChatterjee20k 50109b833a removed tests for the upsert without permissions for passing the tests 2025-07-09 21:49:07 +05:30
Matej Bačo 5ace34790d Migrate many tests to JS 2025-07-09 16:58:55 +02:00
Matej Bačo 1a3f8bbd48 quality fixes 2025-07-09 14:29:36 +02:00
Matej BačoandGitHub 41fc925103 Merge branch '1.7.x' into chore-speed-up-tests 2025-07-09 13:47:33 +02:00
Matej BačoandGitHub a756e533e8 Merge pull request #10128 from appwrite/reduce-delete-batch-size
Reduce delete batch size
2025-07-09 13:46:03 +02:00
Matej BačoandGitHub 449b9295fd Update src/Appwrite/Platform/Workers/Deletes.php 2025-07-09 13:42:18 +02:00
fogelito 3a298b7a21 Change to 500 2025-07-09 14:32:47 +03:00
ArnabChatterjee20k 13bd2341c0 empty commit to trigger tests 2025-07-09 17:02:44 +05:30
ArnabChatterjee20k c36720e3b0 updated param of permissiosns in upsert and updated tests for upsert without permissions 2025-07-09 16:37:41 +05:30
fogelito 9cc61d89f0 $deleteBatchSize 2025-07-09 14:00:08 +03:00
Matej BačoandGitHub cc1129a7b9 Merge pull request #9952 from appwrite/fix-build-activation-race-condition
Fix build activation race condition
2025-07-09 11:59:40 +02:00
Matej Bačo e42275e0b7 Minify tests 2025-07-09 11:26:05 +02:00
Matej Bačo 26496e68c4 Attempt to simplify code 2025-07-08 14:51:28 +02:00
Khushboo VermaandGitHub f7cbe5b961 Merge branch '1.7.x' into fix-build-activation-race-condition 2025-07-08 17:53:12 +05:30
Khushboo Verma bd63f0cf3d Remove empty check 2025-07-08 17:46:54 +05:30
Khushboo Verma bc9caeaae0 Use ruleId 2025-07-08 17:45:45 +05:30
Khushboo Verma a5a1860ba6 Use createdAt instead of buildStartTime 2025-07-08 17:42:23 +05:30
Khushboo Verma 9d7e218e99 Save result of updateDocument 2025-07-08 17:12:35 +05:30
Matej BačoandGitHub 6d321f6988 Merge pull request #10120 from appwrite/fix-file-tokens
Fix file tokens not working on file-security
2025-07-08 13:41:10 +02:00
Khushboo Verma 61a27bfe55 Simplify auto-activation logic and just update attributes 2025-07-08 16:30:43 +05:30
Darshan 7ca951ed81 remove: {} 2025-07-08 15:43:51 +05:30
Darshan 020814e672 update: inline guest headers. 2025-07-08 15:43:28 +05:30
Darshan 7ea896301f update: test to include preview, view and download with file-security. 2025-07-08 15:42:38 +05:30
Darshan 97d5f0f030 add: e2e. 2025-07-08 15:34:52 +05:30
Darshan 14e52058c7 fix: file tokens not working on file-security. 2025-07-08 13:46:53 +05:30
Khushboo Verma 067b53482f Fix bug 2025-07-08 01:02:55 +05:30
Khushboo Verma 1a286ad3f4 Revert replica changes 2025-07-08 00:53:10 +05:30
Khushboo Verma 80c4982482 Update only specific attribute 2025-07-08 00:51:35 +05:30
Khushboo Verma a23c99ad7a Merge branch 'fix-build-activation-race-condition' of github.com:appwrite/appwrite into fix-build-activation-race-condition 2025-07-07 12:37:06 +05:30
Khushboo Verma ea238bc5ec Fix merge conflicts 2025-07-07 12:36:42 +05:30
Damodar LohaniandGitHub edd7a4ac42 Merge pull request #10088 from appwrite/update-nodejs-17.1.0
Update nodejs 17.1.0
2025-07-07 11:14:25 +05:45
Chirag AggarwalandGitHub f678add130 Merge branch '1.7.x' into update-nodejs-17.1.0 2025-07-07 10:39:56 +05:30
Steven NguyenandGitHub 543b872f60 Merge pull request #10107 from appwrite/fix-origin-validation-extensions
fix: origin validation for web extensions
2025-07-06 14:53:56 -07:00
Chirag Aggarwal 5fffae39d4 chore: update naming and add tests 2025-07-05 17:59:01 +05:30
Chirag Aggarwal 9dcb80649f fix: extension for safari 2025-07-05 10:18:40 +05:30
Chirag Aggarwal 18d30c48d5 fix: add missing check for hostname 2025-07-05 10:15:18 +05:30
Chirag Aggarwal 0fb55f6312 fix: origin validation for web extensions 2025-07-05 07:59:52 +05:30
Matej BačoandGitHub 1cf9963bb6 Merge pull request #10104 from appwrite/chore-fix-site-template-test
Fix site template test
2025-07-04 15:17:09 +02:00
Khushboo Verma 2545977d47 Fix site template test 2025-07-04 17:46:21 +05:30
Torsten DittmannandGitHub dbadfa7bf8 Merge pull request #9650 from appwrite/feat-exp-scheme
feat: exp scheme
2025-07-04 14:09:54 +02:00
Chirag Aggarwal 37c7411405 Trigger Build 2025-07-04 16:51:30 +05:30
Chirag Aggarwal bc8b10a69e fix: origin tests 2025-07-04 16:46:14 +05:30
Chirag Aggarwal 7bbf58d0dd update cli 2025-07-04 15:31:52 +05:30
Chirag AggarwalandGitHub df9ef1ace9 Merge branch '1.7.x' into feat-exp-scheme 2025-07-04 15:22:44 +05:30
Matej BačoandGitHub 32903d9a81 Merge pull request #9999 from appwrite/chore-update-env-var-used-for-domain
Use _APP_CONSOLE_DOMAIN, if not found, then use _APP_DOMAIN
2025-07-04 09:41:42 +02:00
Khushboo VermaandGitHub 131cdefac5 Merge branch '1.7.x' into chore-update-env-var-used-for-domain 2025-07-04 10:40:38 +05:30
Chirag AggarwalandGitHub 231225ee10 Merge branch '1.7.x' into update-nodejs-17.1.0 2025-07-04 09:37:51 +05:30
Jake BarnbyandGitHub d205ac4579 Merge pull request #10099 from appwrite/feat-redis-publisher-scheduler
Add redis publisher to schedule base if available
2025-07-03 18:49:25 +00:00
Jake Barnby 5bb998b02d Add API + CLI stubs 2025-07-03 14:24:40 -04:00
Jake Barnby 7d37f5b066 Lint 2025-07-03 14:18:19 -04:00
Jake Barnby 2a73dddbdb Fix health 2025-07-03 14:09:08 -04:00
Jake Barnby f224c09c10 Use correct publisher in schedule tasks 2025-07-03 14:08:54 -04:00
Jake Barnby 6725b31b65 Add redis publisher to schedule base if available 2025-07-03 13:55:13 -04:00
Chirag AggarwalandGitHub e721a9f11c Merge branch '1.7.x' into feat-exp-scheme 2025-07-03 22:40:46 +05:30
Chirag Aggarwal 72f08e281b chore: make check consistent 2025-07-03 22:39:12 +05:30
Chirag Aggarwal 431a26e151 chore: removing loggin 2025-07-03 22:37:31 +05:30
Chirag Aggarwal 172ba84ec8 chore: add logging 2025-07-03 22:25:46 +05:30
Torsten DittmannandGitHub 7c0216c592 Merge pull request #10098 from appwrite/fix/jwt-auth-db-admin-mode
fix: JWT authentication database selection for admin mode
2025-07-03 18:29:54 +02:00
Chirag Aggarwal b6f11144b8 chore: popular schemes 2025-07-03 21:39:23 +05:30
Ariel WeinbergerandGitHub 199b9f7e6d fix nitpick 2025-07-03 11:06:36 -05:00
Ariel WeinbergerandGitHub d9486f38c4 fix: jwt auth database selection for admin mode
Use dbForPlatform for JWT user lookup in admin mode
2025-07-03 10:54:51 -05:00
Chirag Aggarwal 4747700eae chore: add logging 2025-07-03 21:17:43 +05:30
Chirag Aggarwal 58c2f55d1c fix: revert to main 2025-07-03 18:51:50 +05:30
Chirag Aggarwal 2cf7246c7a fix: changelog 2025-07-03 18:15:50 +05:30
Chirag Aggarwal dd9096fd2b fix: lint 2025-07-03 18:04:28 +05:30
Chirag Aggarwal 60158aec0c chore: update cli to 8.1.1 as well 2025-07-03 18:02:40 +05:30
Chirag Aggarwal bb9a271fa8 chore: add scheme validation 2025-07-03 16:39:41 +05:30
Chirag AggarwalandGitHub ef2dfb6cca Merge branch '1.7.x' into feat-exp-scheme 2025-07-03 16:37:17 +05:30
Chirag Aggarwal f62c3135df add null check for platform 2025-07-03 16:26:56 +05:30
Khushboo VermaandGitHub 642d046d65 Merge branch '1.7.x' into chore-update-env-var-used-for-domain 2025-07-03 00:38:33 +05:30
Jake BarnbyandGitHub d38b25e0b1 Merge pull request #10090 from appwrite/csv-fix
Fix: path and type
2025-07-02 19:04:00 +00:00
Khushboo Verma 9064fcc24e Merge branch 'chore-update-env-var-used-for-domain' of github.com:appwrite/appwrite into chore-update-env-var-used-for-domain 2025-07-03 00:08:58 +05:30
Khushboo Verma 0f37a0e0ef Fix tests 2025-07-03 00:08:29 +05:30
Khushboo Verma 253195698e Merge branch '1.7.x' into chore-update-env-var-used-for-domain 2025-07-03 00:02:51 +05:30
Jake Barnby 55dc965dd6 Update lib 2025-07-02 14:32:05 -04:00
Jake Barnby 2ce3118ba9 Remove path update 2025-07-02 13:36:46 -04:00
Jake Barnby ce8ae2294a Update lock 2025-07-02 12:58:37 -04:00
Jake Barnby b1724e9c9d Update lock 2025-07-02 10:40:35 -04:00
Darshan ed2d12f896 update: full path on appwrite. 2025-07-02 19:55:42 +05:30
Darshan 6f5d3d53c0 bump: for tests. 2025-07-02 19:51:36 +05:30
Darshan 6d792fcb6c revert: lock. 2025-07-02 19:20:30 +05:30
Darshan fc1a9a4e16 fix: path and revert deps. 2025-07-02 19:13:13 +05:30
Darshan 0e50fb7ec5 add: migrations lib logging for errors. 2025-07-02 17:04:26 +05:30
Darshan d34635becb add: logging for errors. 2025-07-02 15:13:42 +05:30
Darshan eb1d4c68ea add: logging. 2025-07-02 14:54:25 +05:30
Darshan beec2e780d Merge branch '1.7.x' into csv-fix 2025-07-02 14:35:38 +05:30
Darshan aa9a82d8ce fix: document attribute type. 2025-07-02 14:32:16 +05:30
Chirag Aggarwal 421510f847 chore: update nodejs to 17.1.0 2025-07-02 11:01:25 +05:30
Jake BarnbyandGitHub 49a5e01a53 Merge pull request #10086 from appwrite/revert-10051-chore-upgrade-utopia-queue
Revert "chore: update composer.json and composer.lock to include appwrite-lab…"
2025-07-01 22:44:59 +00:00
Jake Barnby 1cd69e26f8 Reapply "chore: update composer.json and composer.lock to include appwrite-lab…"
This reverts commit 99ccd64b4c.
2025-07-01 18:24:52 -04:00
Jake BarnbyandGitHub 99ccd64b4c Revert "chore: update composer.json and composer.lock to include appwrite-lab…" 2025-07-01 18:24:05 -04:00
Jake BarnbyandGitHub aa03045382 Merge pull request #10085 from appwrite/fix-tests
Fix tests, for `Cloud`
2025-07-01 14:28:23 +00:00
Darshan 4d0bd1d74f add: delay, retries. 2025-07-01 18:10:19 +05:30
Darshan 385e4e14b8 add: delay, retries. 2025-07-01 17:33:11 +05:30
Matej BačoandGitHub 67bdf8ffdc Merge pull request #10034 from appwrite/fix-push-to-existing-repo
Fix connect to existing repo flow
2025-07-01 12:11:48 +02:00
Khushboo VermaandGitHub 3fa04727c7 Merge branch '1.7.x' into fix-push-to-existing-repo 2025-07-01 14:47:52 +05:30
Khushboo VermaandGitHub 2da811956c Merge branch '1.7.x' into chore-update-env-var-used-for-domain 2025-07-01 12:22:36 +05:30
Khushboo VermaandGitHub 8ef24cb2a9 Merge branch '1.7.x' into fix-build-activation-race-condition 2025-07-01 12:22:17 +05:30
Jake BarnbyandGitHub e561a38524 Merge pull request #10051 from appwrite/chore-upgrade-utopia-queue
chore: update composer.json and composer.lock to include appwrite-lab…
2025-07-01 02:51:31 +00:00
Jake Barnby a1af742c4b Update lock 2025-06-30 22:28:09 -04:00
Jake Barnby 98dccf6d15 Map null on missing default 2025-06-30 22:27:29 -04:00
Jake Barnby 609e1adcf0 Catch 2025-06-30 22:27:18 -04:00
Jake Barnby 72cd501f21 Merge remote-tracking branch 'origin/1.7.x' into chore-upgrade-utopia-queue 2025-06-30 21:01:47 -04:00
Khushboo Verma 58c1e2d6e2 Merge branch '1.7.x' into chore-update-env-var-used-for-domain 2025-07-01 02:29:08 +05:30
Khushboo Verma ddb5c52e7c Resolve merge conflicts 2025-07-01 02:25:38 +05:30
Khushboo Verma 5959457211 Merge branch '1.7.x' into fix-push-to-existing-repo 2025-07-01 01:24:45 +05:30
Steven NguyenandGitHub beb8e17013 Merge pull request #10079 from appwrite/chore-patch-cache-miss
patch: increase cache ttl to ensure hits
2025-06-30 12:04:45 -07:00
Christy JacobandGitHub 448a8df1a2 Merge branch '1.7.x' into chore-patch-cache-miss 2025-06-30 21:12:46 +04:00
Christy Jacob fd7628efe8 refactor: streamline cache handling in App::shutdown() and ensure file modification time updates 2025-06-30 21:03:30 +04:00
Christy Jacob d6326758a8 patch: increase cache ttl to ensure hits 2025-06-30 20:58:00 +04:00
Chirag Aggarwal 2d00052db6 fix: general test 2025-06-30 20:50:29 +05:30
Chirag Aggarwal db56361ad9 fix: add back empty checks 2025-06-30 19:48:05 +05:30
Chirag Aggarwal 843d4d9ea0 chore: use origin validator 2025-06-30 19:29:42 +05:30
Chirag Aggarwal 8a2ccd32b4 fix: remove redundant check 2025-06-30 19:09:15 +05:30
Chirag Aggarwal 39db3a3066 fix: order 2025-06-30 19:07:49 +05:30
Chirag Aggarwal 9242ef85eb fix: order 2025-06-30 19:03:12 +05:30
Chirag Aggarwal 332c82e61e fix: lint 2025-06-30 19:00:54 +05:30
Chirag Aggarwal bfee98ca87 Merge branch '1.7.x' into feat-exp-scheme 2025-06-30 18:38:25 +05:30
Matej BačoandGitHub 36aa22793f Merge pull request #10043 from appwrite/fix-rules-platform-check
Fix: Platform check for Sites with automatic rule
2025-06-30 14:10:14 +02:00
Khushboo Verma ae72688c8e Merge branch '1.7.x' into fix-push-to-existing-repo 2025-06-30 15:45:54 +05:30
Christy Jacob 63a02e11ac Fix default value assignment in Mapper class to handle null cases 2025-06-27 18:42:02 +04:00
Matej Bačo ba7903af55 Merge branch '1.7.x' into fix-rules-platform-check 2025-06-27 12:46:06 +02:00
Christy JacobandGitHub cb19798284 Merge pull request #10070 from appwrite/update-cli-8.1.0
chore: update cli to 8.1.0 and add changelog
2025-06-27 14:26:32 +04:00
Chirag Aggarwal 156daca044 fix: composer 2025-06-27 15:50:32 +05:30
Chirag Aggarwal f056a8ba2f fix: composer 2025-06-27 15:49:08 +05:30
Chirag Aggarwal 151711aba9 Merge branch '1.7.x' into update-cli-8.1.0 2025-06-27 15:48:33 +05:30
Christy Jacob 14a4bb8828 Merge branch 'chore-upgrade-utopia-queue' of github.com:appwrite/appwrite into chore-upgrade-utopia-queue 2025-06-27 13:42:17 +04:00
Christy Jacob a36525373d Merge branch '1.7.x' of github.com:appwrite/appwrite into chore-upgrade-utopia-queue 2025-06-27 13:37:33 +04:00
Chirag Aggarwal 5a48fd6300 chore: update cli to 8.1.0 and add changelog 2025-06-27 11:43:56 +05:30
Chirag AggarwalandGitHub 8027251224 Merge branch '1.7.x' into feat-exp-scheme 2025-06-27 09:21:27 +05:30
Chirag Aggarwal fffdab23f9 chore: update scheme check 2025-06-27 08:29:13 +05:30
Matej BačoandGitHub 718cefdd94 Merge pull request #10066 from appwrite/fix-account-test-flakyness
Fix: flakyness of account tests
2025-06-26 22:18:00 +02:00
Matej Bačo ee225834d9 Fix default param syntax 2025-06-26 21:39:00 +02:00
Matej Bačo 23f350c7d1 Merge branch '1.7.x' into fix-account-test-flakyness 2025-06-26 21:33:09 +02:00
Matej Bačo b12ea0b3b1 Fix merge conflict bug 2025-06-26 21:22:38 +02:00
Matej BačoandGitHub 3a8dafe763 Merge branch '1.7.x' into fix-account-test-flakyness 2025-06-26 21:21:52 +02:00
Matej BačoandGitHub 42a0d64137 Merge pull request #10068 from appwrite/chore-preview-logging
Chore: Preview endpoint logging
2025-06-26 20:53:30 +02:00
Matej Bačo df902d2624 Fix account tests using old token 2025-06-26 20:47:59 +02:00
Matej Bačo a356e86ced Add previe wendpoint logging 2025-06-26 20:17:26 +02:00
Matej Bačo bda9adc52e Fix sms mock endpoint 2025-06-26 20:04:56 +02:00
Matej Bačo 8dcc3c4981 Merge branch '1.7.x' into fix-account-test-flakyness 2025-06-26 19:26:23 +02:00
Christy JacobandGitHub 368f928fa9 Merge pull request #10067 from appwrite/fix-parameter-order
fix: correct parameter order in action function for robots.txt route
2025-06-26 21:05:12 +04:00
Christy Jacob ee2796f870 fix: correct parameter order in action function for robots.txt route 2025-06-26 20:49:52 +04:00
Matej BačoandGitHub d4b53175dc Update tests/e2e/Scopes/Scope.php 2025-06-26 18:33:22 +02:00
Matej Bačo 572ff06f53 Fix robots.txt 5xx error 2025-06-26 18:32:03 +02:00
Matej Bačo 87c100db28 Improve General tests logging 2025-06-26 18:09:07 +02:00
Matej Bačo 918ad8979d Fix flakyness of account tests 2025-06-26 18:07:59 +02:00
Matej BačoandGitHub 2fa3ca27af Merge pull request #10052 from appwrite/fix-logs-order
Fix: logs order
2025-06-26 16:32:35 +02:00
Chirag Aggarwal 1555abcbd6 chore: use lowercase for check 2025-06-26 19:59:58 +05:30
Chirag Aggarwal f8e74d073a chore: fix scheme check, remove logging 2025-06-26 19:48:57 +05:30
Chirag Aggarwal be40f1cbe3 chore: remove check for scheme 2025-06-26 19:44:41 +05:30
Chirag Aggarwal 043cead66b chore: fix logging 2025-06-26 19:32:15 +05:30
Chirag Aggarwal 7e378a3a1e chore: more logging 2025-06-26 19:10:35 +05:30
Chirag Aggarwal e13089b1ce chore: more logging 2025-06-26 19:09:14 +05:30
Chirag Aggarwal 9861f60666 chore: add logging 2025-06-26 18:55:51 +05:30
Chirag Aggarwal 06ce606ac7 Merge branch '1.7.x' into feat-exp-scheme 2025-06-26 18:38:10 +05:30
Christy JacobandGitHub 1dfba85bcc Merge pull request #10054 from appwrite/chore-fix-tasks
refactor: replace Console loop with Swoole Timer for stats resource m…
2025-06-26 15:12:05 +04:00
Christy Jacob 204b00850d Refactor log message documentation in Action class to improve clarity and formatting 2025-06-26 15:09:57 +04:00
Christy JacobGitHubcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
5761320bfb Update src/Appwrite/Platform/Action.php
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-06-26 15:05:55 +04:00
Christy Jacob dbc7e5b42d Fix output format flag in benchmark workflow for consistency 2025-06-26 15:05:10 +04:00
Christy JacobandGitHub 22900e3de8 Merge pull request #10061 from appwrite/fix-missing-injections
Add: missing injections
2025-06-26 13:24:08 +04:00
Matej BačoandGitHub e5621f3218 Merge pull request #10062 from appwrite/add-runtimeEntrypoint
chore: add runtimeEntrypoint param
2025-06-26 10:29:21 +02:00
Chirag Aggarwal 276a294391 chore: update createExecution 2025-06-26 13:37:43 +05:30
Chirag Aggarwal 8bf3a8bf5d chore: add runtimeEntrypoint param 2025-06-26 13:29:06 +05:30
Darshan 72956d1995 fix: misaligned param order. 2025-06-26 13:07:11 +05:30
Darshan 038b5d7418 fix: lint. 2025-06-26 12:40:56 +05:30
Darshan 8f5b53d16c revert: composer.lock. 2025-06-26 12:36:37 +05:30
Darshan d2753bd491 add: missing injections. 2025-06-26 12:34:38 +05:30
Jake Barnby 921b2bcc5d Merge remote-tracking branch 'origin/1.7.x' into chore-upgrade-utopia-queue 2025-06-25 18:58:08 -04:00
Jake BarnbyandGitHub e8982e6b11 Merge pull request #10056 from appwrite/feat-update-logging
Add database worker job logging
2025-06-25 17:59:59 -04:00
Jake Barnby 7508a22ddf Lint 2025-06-25 16:49:12 -04:00
Jake Barnby 620266d9d6 Remove heartbeat 2025-06-25 16:40:02 -04:00
Jake Barnby f0ea206f29 Fix bench 2025-06-25 15:56:07 -04:00
Jake Barnby d52d8f7697 Fix API dump 2025-06-25 15:47:57 -04:00
Jake Barnby 255be3babf Ensure heartbeat is cleared on destruct 2025-06-25 14:52:14 -04:00
Jake Barnby e37cea5146 Add more trigger logging 2025-06-25 14:50:29 -04:00
Jake Barnby d7d7fa3096 Add trigger logging 2025-06-25 14:47:23 -04:00
Jake Barnby 92a04535ac Add database worker job logging 2025-06-25 14:35:17 -04:00
Christy Jacob c479a11850 Add logging functionality to Action class with dump method and refactor disableSubqueries to use class filters 2025-06-25 21:56:30 +04:00
Christy Jacob 3cd31e2c06 Add disableSubqueries method to Action class and integrate it into StatsResources initialization 2025-06-25 21:46:15 +04:00
Christy Jacob 5173e03636 Merge branch '1.7.x' of github.com:appwrite/appwrite into chore-fix-tasks 2025-06-25 21:33:30 +04:00
Christy JacobandGitHub 3fae6ae91c Merge pull request #10011 from appwrite/PLA-3018
Override project via custom domains log
2025-06-25 19:49:26 +04:00
shimon 19152f50dc Merge branch '1.7.x' of github.com:appwrite/appwrite into PLA-3018
# Conflicts:
#	.github/workflows/benchmark.yml
2025-06-25 18:48:53 +03:00
Matej BačoandGitHub bfbcd648f1 Merge pull request #9967 from appwrite/feat-use-first-class-callable-syntax
Use ->action(...)) instead of ->callback([$this, 'action']);
2025-06-25 17:13:50 +02:00
Matej BačoandGitHub f07abce6f8 Merge branch '1.7.x' into feat-use-first-class-callable-syntax 2025-06-25 16:46:30 +02:00
Matej BačoandGitHub 975f653e3e Merge pull request #9969 from appwrite/feat-add-branch-url-for-first-git-deployment
Create commit & branch url for first git deployment when site is linked to repo
2025-06-25 16:45:41 +02:00
Matej Bačo c85cfe132e Improve flaky tests logging 2025-06-25 15:53:59 +02:00
Matej Bačo 4fff9b94f8 Fix benchmark param name 2025-06-25 14:45:58 +02:00
Matej Bačo 4b5fa0652a Merge branch '1.7.x' into fix-rules-platform-check 2025-06-25 14:44:52 +02:00
Matej Bačo f1590652cb Improve benchmark logging 2025-06-25 14:30:50 +02:00
Matej Bačo df82340dae Improve code quality 2025-06-25 14:22:55 +02:00
Matej Bačo 56347e42df Fix syntax bug 2025-06-25 14:20:20 +02:00
Matej Bačo 6cb3e35c64 Merge branch '1.7.x' into fix-logs-order 2025-06-25 13:41:49 +02:00
Matej BačoandGitHub 213cbf055a Merge pull request #10053 from appwrite/fix-build-duration
Fix: build duration calculation
2025-06-25 13:41:24 +02:00
Matej BačoandGitHub f394f3de62 Merge pull request #10055 from appwrite/chore-fix-benchmark-ci
chore: fix benchmark CI
2025-06-25 13:36:11 +02:00
Christy Jacob 93651c776e chore: fix benchmark CI 2025-06-25 15:32:19 +04:00
Christy Jacob 347f035105 chore: fix benchmark CI 2025-06-25 15:27:51 +04:00
Matej Bačo b6d4baf4dc Fix tests 2025-06-25 13:14:08 +02:00
Christy Jacob c268b632fb chore: update benchmark command to use JSON output format and adjust timer interval in StatsResources 2025-06-25 14:52:13 +04:00
Christy Jacob 17428ef433 refactor: replace Console loop with Swoole Timer for stats resource management 2025-06-25 14:18:22 +04:00
Matej Bačo 4b1966997b Fix build duration calculation 2025-06-25 10:30:07 +02:00
shimon e712ba5763 Merge branch '1.7.x' of github.com:appwrite/appwrite into PLA-3018 2025-06-25 10:18:07 +03:00
Chirag Aggarwal 1a9664a679 chore: fix benchmark 2025-06-25 11:43:18 +05:30
Chirag Aggarwal 65b5af1b00 chore: fix tests 2025-06-25 11:27:23 +05:30
Chirag Aggarwal 6dda6e1fda chore: remove console log 2025-06-25 11:25:39 +05:30
Chirag Aggarwal c331a3caae chore: minor fixes 2025-06-25 11:00:07 +05:30
Chirag Aggarwal dbc58655b2 Merge branch '1.7.x' into feat-exp-scheme 2025-06-25 07:53:28 +05:30
Christy Jacob b646eb7758 chore: update composer.json and composer.lock to upgrade utopia-php/queue to version 0.12.0 and appwrite-labs/php-amqplib to version 0.1.1 2025-06-25 00:23:21 +04:00
Matej Bačo c04c2d271c Remove logs, fix edge logs missing 2025-06-24 16:03:28 +02:00
Matej Bačo b5bc240c0d Fix ssr separation; add edge debugging logs 2025-06-24 15:56:06 +02:00
Matej Bačo 5dcc508870 Fix logs parsing 2025-06-24 15:38:39 +02:00
Matej Bačo 6996cc28a0 Fix logs order 2025-06-24 14:47:47 +02:00
Christy Jacob fe080153e6 chore: update benchmark workflow to use JSON output format 2025-06-24 15:28:07 +04:00
Christy Jacob 4759407696 chore: update composer.json and composer.lock to include appwrite-labs/php-amqplib and adjust utopia-php/queue version 2025-06-24 14:57:30 +04:00
Steven NguyenandGitHub 030d37fd5f Merge pull request #10036 from appwrite/update-executor
chore: update executor to fix s3 endpoint bug
2025-06-23 19:23:03 -07:00
shimon 213b1ae20e benchmark cli 2025-06-23 20:57:39 +03:00
shimon 4e0faf67f7 benchmark cli 2025-06-23 20:39:52 +03:00
shimon 3059eecbeb benchmark cli 2025-06-23 20:27:12 +03:00
shimon dd8894ad1e Merge remote-tracking branch 'origin/PLA-3018' into PLA-3018 2025-06-23 20:21:25 +03:00
shimon 28f420f10c Merge branch '1.7.x' of github.com:appwrite/appwrite into PLA-3018 2025-06-23 20:20:43 +03:00
54748b7d32 Update app/controllers/general.php
Co-authored-by: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com>
2025-06-23 20:50:59 +04:00
Matej Bačo b69ff36ddb Manual QA fixes 2025-06-23 11:57:06 +02:00
Matej Bačo c1bd493fbe Move refferer to resource; re-use in OPTIONS; re-use in platform check 2025-06-23 11:28:18 +02:00
Chirag Aggarwal 2ac4d9f901 chore: update executor to fix s3 endpoint bug 2025-06-21 07:59:54 +05:30
Khushboo Verma 1319bf7ec1 Fix connect to existing repo flow 2025-06-21 01:41:41 +04:00
shimon d8c88fb420 composer 2025-06-19 19:44:33 +03:00
shimon 68fc0f898f set log projectId on the router method 2025-06-19 17:15:43 +03:00
shimon 0f8c5fe292 Merge branch '1.7.x', commit 'c0c30fe767f8c197b8793bf562f57146e5ba15a8' of github.com:appwrite/appwrite into PLA-3018 2025-06-19 12:46:34 +03:00
Khushboo Verma e65ed58ca4 Merge branch 'chore-update-env-var-used-for-domain' of github.com:appwrite/appwrite into chore-update-env-var-used-for-domain 2025-06-18 22:17:11 +05:30
Khushboo Verma 095159d89b Merge branch '1.7.x' into chore-update-env-var-used-for-domain 2025-06-18 22:15:19 +05:30
Christy JacobandGitHub 509392c48b Merge branch '1.7.x' into chore-update-env-var-used-for-domain 2025-06-18 13:44:04 +04:00
Khushboo Verma 520006864d Merge branch '1.7.x' into chore-update-env-var-used-for-domain 2025-06-18 11:07:25 +05:30
Khushboo Verma 5a3e44a36d Replace == with === 2025-06-18 11:06:35 +05:30
Khushboo Verma 22cda269b1 Update console 2025-06-17 18:37:02 +05:30
Khushboo Verma e8e7ee01b4 Merge branch 'fix-github-check-urls' into chore-update-env-var-used-for-domain 2025-06-17 14:01:50 +05:30
Khushboo Verma b7ffd2ca94 Revert some changes 2025-06-17 13:57:21 +05:30
Khushboo Verma 9356fb4cb4 Merge branch '1.7.x' into chore-update-env-var-used-for-domain 2025-06-17 12:16:37 +05:30
Khushboo Verma 41bc8272ff Add env var to docker-compose 2025-06-17 12:15:52 +05:30
Khushboo Verma ad60ff9972 Update url in general.php 2025-06-16 19:16:20 +05:30
Khushboo Verma 8c8abfdece Add var_dumps to debug 2025-06-16 18:47:50 +05:30
Khushboo Verma 5e345c0388 Revert some changes 2025-06-16 17:29:01 +05:30
Khushboo Verma 362618c5af Merge branch 'fix-github-check-urls' into chore-update-env-var-used-for-domain 2025-06-16 17:19:55 +05:30
shimon c0c30fe767 remove duplicate use 2025-06-15 12:40:22 +03:00
shimon d7e2552999 Merge branch '1.7.x' of github.com:appwrite/appwrite into PLA-3018 2025-06-15 12:38:50 +03:00
shimon 7d54a6191d remove duplicate use 2025-06-15 12:13:15 +03:00
shimon 4d41c90367 remove duplicate use 2025-06-15 12:00:39 +03:00
shimon 96c035401b Override project resource to update hooks, since x-appwrite-project is not available when executing custom domain function 2025-06-15 11:30:57 +03:00
Khushboo Verma 3ad5a2652e Update console version 2025-06-13 19:14:00 +05:30
Khushboo VermaandGitHub 28db9a289b Merge branch '1.7.x' into feat-add-branch-url-for-first-git-deployment 2025-06-13 19:08:14 +05:30
Khushboo Verma 88d9bdd395 Merge branch 'fix-build-activation-race-condition' of github.com:appwrite/appwrite into fix-build-activation-race-condition 2025-06-13 19:05:12 +05:30
Khushboo Verma 995482ee20 Update console version 2025-06-13 19:04:46 +05:30
Khushboo Verma d2abdea6cf Resolve merge conflicts 2025-06-13 19:04:17 +05:30
Khushboo Verma fc656167d5 Composer update 2025-06-13 18:11:51 +05:30
Khushboo Verma 37388fcf1b Change php version in composer.lock 2025-06-13 18:08:19 +05:30
Khushboo Verma 55be41f633 Resolve merge conflicts 2025-06-13 18:06:47 +05:30
Khushboo Verma cee6420fa2 Use env vars instead of request 2025-06-13 15:50:53 +05:30
Khushboo Verma 8bac836483 Merge branch 'fix-github-check-urls' into chore-update-env-var-used-for-domain 2025-06-13 15:18:31 +05:30
Khushboo Verma 01aa9fbb48 Merge branch 'chore-update-env-var-used-for-domain' of github.com:appwrite/appwrite into chore-update-env-var-used-for-domain 2025-06-13 15:18:17 +05:30
Khushboo VermaandGitHub d7c3100fe9 Merge branch '1.7.x' into chore-update-env-var-used-for-domain 2025-06-13 15:18:02 +05:30
Khushboo Verma 43f9254302 Merge branch '1.7.x' into chore-update-env-var-used-for-domain 2025-06-12 20:32:02 +05:30
Khushboo Verma 710b8bda56 Use _APP_CONSOLE_DOMAIN, if not found, then use _APP_DOMAIN 2025-06-12 17:27:21 +05:30
Khushboo VermaandGitHub dde7570072 Merge branch '1.7.x' into fix-build-activation-race-condition 2025-06-10 16:21:51 +05:30
Khushboo VermaandGitHub ef12f49277 Merge branch '1.7.x' into feat-add-branch-url-for-first-git-deployment 2025-06-10 13:08:34 +05:30
Khushboo Verma fa8e5b5b8e Add commit url 2025-06-04 19:11:07 +05:30
Khushboo Verma 54552bf1b4 Create branch url for first git deployment 2025-06-04 18:34:01 +05:30
Khushboo Verma 6530d31e7a Use ->action(...)) 2025-06-04 14:07:43 +05:30
Khushboo Verma e44d1b6600 Merge branch '1.7.x' into fix-build-activation-race-condition 2025-06-03 14:23:51 +05:30
Khushboo Verma 2088792d09 Fix build activation race condition 2025-06-03 14:23:17 +05:30
Khushboo Verma 7a465dc8f8 Don't auto-activate deployment which started earlier & ended later than active deployment 2025-06-02 17:53:35 +05:30
Luke B. SilverandGitHub d82812cc04 Merge branch '1.6.x' into feat-exp-scheme 2025-04-15 10:11:31 +01:00
loks0n 2427f950b5 fix: realtime 2025-04-14 14:34:22 +01:00
loks0n e551e4ad5b chore: fmt 2025-04-14 14:23:01 +01:00
loks0n 67a313cef3 fix: tests 2025-04-14 13:50:48 +01:00
loks0n 3c2b88dc86 chore: remove unused 2025-04-14 13:02:02 +01:00
loks0n 944624f13a chore: fmt 2025-04-14 12:59:39 +01:00
loks0n 4ac2571135 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-exp-scheme 2025-04-14 12:58:25 +01:00
loks0n 0a4c430ae7 feat: exp scheme 2025-04-14 12:56:42 +01:00
209 changed files with 2235 additions and 1353 deletions
+4 -2
View File
@@ -22,6 +22,7 @@ _APP_OPTIONS_FORCE_HTTPS=disabled
_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled
_APP_OPENSSL_KEY_V1=your-secret-key
_APP_DOMAIN=traefik
_APP_CONSOLE_DOMAIN=localhost
_APP_DOMAIN_FUNCTIONS=functions.localhost
_APP_DOMAIN_SITES=sites.localhost
_APP_DOMAIN_TARGET_CNAME=test.localhost
@@ -84,8 +85,9 @@ _APP_COMPUTE_MAINTENANCE_INTERVAL=600
_APP_COMPUTE_RUNTIMES_NETWORK=runtimes
_APP_EXECUTOR_SECRET=your-secret-key
_APP_EXECUTOR_HOST=http://exc1/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.32
_APP_BROWSER_HOST=http://appwrite-browser:3000/v1
_APP_FUNCTIONS_RUNTIMES=node-22
_APP_SITES_RUNTIMES=static-1,node-22
_APP_MAINTENANCE_INTERVAL=86400
_APP_MAINTENANCE_START_TIME=12:00
_APP_MAINTENANCE_RETENTION_CACHE=2592000
+3 -2
View File
@@ -64,8 +64,9 @@ jobs:
sudo wget -O /usr/share/keyrings/azlux-archive-keyring.gpg https://azlux.fr/repo.gpg
sudo apt update
sudo apt install oha
oha --version
- name: Benchmark PR
run: 'oha -z 180s http://localhost/v1/health/version -j > benchmark.json'
run: 'oha -z 180s http://localhost/v1/health/version --output-format json > benchmark.json'
- name: Cleaning
run: docker compose down -v
- name: Installing latest version
@@ -78,7 +79,7 @@ jobs:
docker compose up -d
sleep 10
- name: Benchmark Latest
run: oha -z 180s http://localhost/v1/health/version -j > benchmark-latest.json
run: oha -z 180s http://localhost/v1/health/version --output-format json > benchmark-latest.json
- name: Prepare comment
run: |
echo '## :sparkles: Benchmark results' > benchmark.txt
+85 -4
View File
@@ -178,6 +178,7 @@ jobs:
- name: Load and Start Appwrite
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
sed -i 's|^_APP_BROWSER_HOST=.*|_APP_BROWSER_HOST=http://invalid-browser/v1|' .env
docker compose up -d
sleep 30
@@ -198,7 +199,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-group devKeys
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots
e2e_shared_mode_test:
name: E2E Shared Mode Service Test
@@ -277,14 +278,12 @@ 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-group devKeys
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots
e2e_dev_keys:
name: E2E Service Test (Dev Keys)
runs-on: ubuntu-latest
needs: setup
strategy:
fail-fast: false
steps:
- name: checkout
uses: actions/checkout@v4
@@ -360,3 +359,85 @@ jobs:
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys
e2e_screenshots_keys:
name: E2E Service Test (Site Screenshots)
runs-on: ubuntu-latest
needs: setup
steps:
- name: checkout
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
docker compose up -d
sleep 30
- name: Run Site tests with browser connected in dedicated table mode
run: |
echo "Keeping original value of _APP_BROWSER_HOST"
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/Sites --debug --group=screenshots
e2e_screenshots_shared_mode:
name: E2E Shared Mode Service Test (Site Screenshots)
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
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 Site tests with browser connected in ${{ matrix.tables-mode }} table mode
run: |
echo "Keeping original value of _APP_BROWSER_HOST"
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/Sites --debug --group=screenshots
+6 -4
View File
@@ -188,16 +188,18 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
return $database;
};
}, ['pools', 'cache']);
CLI::setResource('publisher', function (Group $pools) {
return new BrokerPool(publisher: $pools->get('publisher'));
}, ['pools']);
CLI::setResource('publisherRedis', function () {
// Stub
});
CLI::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
CLI::setResource('queueForStatsResources', function (Publisher $publisher) {
return new StatsResources($publisher);
}, ['publisher']);
CLI::setResource('publisher', function (Group $pools) {
return new BrokerPool(publisher: $pools->get('publisher'));
}, ['pools']);
CLI::setResource('queueForFunctions', function (Publisher $publisher) {
return new Func($publisher);
}, ['publisher']);
+2 -2
View File
@@ -5,7 +5,7 @@
*/
use Appwrite\Auth\Auth;
use Appwrite\Network\Validator\Origin;
use Appwrite\Network\Platform;
use Utopia\Database\Helpers\ID;
use Utopia\System\System;
@@ -23,7 +23,7 @@ $console = [
[
'$collection' => ID::custom('platforms'),
'name' => 'Localhost',
'type' => Origin::CLIENT_TYPE_WEB,
'type' => Platform::TYPE_WEB,
'hostname' => 'localhost',
], // Current host is added on app init
],
+7 -6
View File
@@ -59,7 +59,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '17.0.1',
'version' => '17.0.2',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -134,7 +134,7 @@ return [
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.10.0',
'version' => '0.10.1',
'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' => '8.0.2',
'version' => '8.2.2',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -231,6 +231,7 @@ return [
'gitRepoName' => 'sdk-for-cli',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'repoBranch' => 'master',
'exclude' => [
'services' => [
['name' => 'assistant'],
@@ -250,7 +251,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '17.0.0',
'version' => '17.1.0',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -358,7 +359,7 @@ return [
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.13.0',
'version' => '0.14.0',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@@ -376,7 +377,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '16.0.0',
'version' => '16.1.0',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
+2 -7
View File
@@ -6,13 +6,8 @@ use Utopia\System\System;
* List of Appwrite Sites templates
*/
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN');
// TODO: Development override
if (System::getEnv('_APP_ENV') === 'development') {
$hostname = 'localhost';
}
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN', '');
$url = $protocol . '://' . $hostname;
+94 -38
View File
@@ -21,6 +21,7 @@ use Appwrite\Event\StatsUsage;
use Appwrite\Extend\Exception;
use Appwrite\Hooks\Hooks;
use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Redirect;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
@@ -60,7 +61,6 @@ use Utopia\System\System;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Boolean;
use Utopia\Validator\Host;
use Utopia\Validator\Text;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList;
@@ -1182,16 +1182,23 @@ 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 ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : 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, ['platforms', 'devKey'])
->param('failure', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : 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, ['platforms', 'devKey'])
->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')
->inject('project')
->action(function (string $provider, string $success, string $failure, array $scopes, Request $request, Response $response, Document $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
$protocol = $request->getProtocol();
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
if ($protocol === 'https' && $port !== '443') {
$callbackBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
if (!$providerEnabled) {
@@ -1216,12 +1223,20 @@ App::get('/v1/account/sessions/oauth2/:provider')
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
$host = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
$redirectBase = $protocol . '://' . $host;
if ($protocol === 'https' && $port !== '443') {
$redirectBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$redirectBase .= ':' . $port;
}
if (empty($success)) {
$success = $protocol . '://' . $request->getHostname() . $oauthDefaultSuccess;
$success = $redirectBase . $oauthDefaultSuccess;
}
if (empty($failure)) {
$failure = $protocol . '://' . $request->getHostname() . $oauthDefaultFailure;
$failure = $redirectBase . $oauthDefaultFailure;
}
$oauth2 = new $className($appId, $appSecret, $callback, [
@@ -1251,9 +1266,14 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->inject('request')
->inject('response')
->action(function (string $projectId, string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response) {
$domain = $request->getHostname();
$protocol = $request->getProtocol();
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
if ($protocol === 'https' && $port !== '443') {
$callbackBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$params = $request->getParams();
$params['project'] = $projectId;
@@ -1262,7 +1282,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
->redirect($protocol . '://' . $domain . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
->redirect($callbackBase . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
. \http_build_query($params));
});
@@ -1282,9 +1302,14 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->inject('request')
->inject('response')
->action(function (string $projectId, string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response) {
$domain = $request->getHostname();
$protocol = $request->getProtocol();
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
if ($protocol === 'https' && $port !== '443') {
$callbackBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$params = $request->getParams();
$params['project'] = $projectId;
@@ -1293,7 +1318,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
->redirect($protocol . '://' . $domain . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
->redirect($callbackBase . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
. \http_build_query($params));
});
@@ -1317,15 +1342,24 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->inject('request')
->inject('response')
->inject('project')
->inject('platforms')
->inject('devKey')
->inject('user')
->inject('dbForProject')
->inject('geodb')
->inject('queueForEvents')
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) {
$protocol = $request->getProtocol();
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, array $platforms, Document $devKey, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
if ($protocol === 'https' && $port !== '443') {
$callbackBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
$validateURL = new URL();
$redirect = new Redirect($platforms);
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
@@ -1352,11 +1386,11 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$state = $defaultState;
}
if (!$validateURL->isValid($state['success'])) {
if ($devKey->isEmpty() && !$redirect->isValid($state['success'])) {
throw new Exception(Exception::PROJECT_INVALID_SUCCESS_URL);
}
if (!empty($state['failure']) && !$validateURL->isValid($state['failure'])) {
if ($devKey->isEmpty() && !empty($state['failure']) && !$redirect->isValid($state['failure'])) {
throw new Exception(Exception::PROJECT_INVALID_FAILURE_URL);
}
$failure = [];
@@ -1423,13 +1457,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$name = '';
$nameOAuth = $oauth2->getUserName($accessToken);
$userParam = \json_decode($request->getParam('user'), true);
$userParam = $request->getParam('user');
if (!empty($nameOAuth)) {
$name = $nameOAuth;
} elseif (is_array($userParam)) {
$nameParam = $userParam['name'];
if (is_array($nameParam) && isset($nameParam['firstName']) && isset($nameParam['lastName'])) {
$name = $nameParam['firstName'] . ' ' . $nameParam['lastName'];
} elseif ($userParam !== null) {
$userDecoded = \json_decode($userParam, true);
if (isset($userDecoded['name']['firstName']) && isset($userDecoded['name']['lastName'])) {
$name = $userDecoded['name']['firstName'] . ' ' . $userDecoded['name']['lastName'];
}
}
$email = $oauth2->getUserEmail($accessToken);
@@ -1747,8 +1781,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$state['success']['query'] = URLParser::unparseQuery($query);
$state['success'] = URLParser::unparse($state['success']);
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration));
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
@@ -1779,16 +1811,23 @@ 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 ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : 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, ['platforms', 'devKey'])
->param('failure', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : 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, ['platforms', 'devKey'])
->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')
->inject('project')
->action(function (string $provider, string $success, string $failure, array $scopes, Request $request, Response $response, Document $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
$protocol = $request->getProtocol();
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
if ($protocol === 'https' && $port !== '443') {
$callbackBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
if (!$providerEnabled) {
@@ -1813,12 +1852,20 @@ App::get('/v1/account/tokens/oauth2/:provider')
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
$host = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
$redirectBase = $protocol . '://' . $host;
if ($protocol === 'https' && $port !== '443') {
$redirectBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$redirectBase .= ':' . $port;
}
if (empty($success)) {
$success = $protocol . '://' . $request->getHostname() . $oauthDefaultSuccess;
$success = $redirectBase . $oauthDefaultSuccess;
}
if (empty($failure)) {
$failure = $protocol . '://' . $request->getHostname() . $oauthDefaultFailure;
$failure = $redirectBase . $oauthDefaultFailure;
}
$oauth2 = new $className($appId, $appSecret, $callback, [
@@ -1860,7 +1907,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 ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : 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, ['platforms', 'devKey'])
->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')
@@ -1961,7 +2008,16 @@ App::post('/v1/account/tokens/magic-url')
$dbForProject->purgeCachedDocument('users', $user->getId());
if (empty($url)) {
$url = $request->getProtocol() . '://' . $request->getHostname() . '/console/auth/magic-url';
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$host = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
$port = $request->getPort();
$callbackBase = $protocol . '://' . $host;
if ($protocol === 'https' && $port !== '443') {
$callbackBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$url = $callbackBase . '/console/auth/magic-url';
}
$url = Template::parseURL($url);
@@ -3146,7 +3202,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 ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : 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, ['platforms', 'devKey'])
->inject('request')
->inject('response')
->inject('user')
@@ -3413,7 +3469,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 ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : 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, ['platforms', 'devKey']) // TODO add built-in confirm page
->inject('request')
->inject('response')
->inject('project')
+2 -1
View File
@@ -2958,6 +2958,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
$collection->getAttribute('attributes'),
$dbForProject->getAdapter()->getMaxIndexLength(),
$dbForProject->getAdapter()->getInternalIndexesKeys(),
$dbForProject->getAdapter()->getSupportForIndexArray(),
);
if (!$validator->isValid($index)) {
@@ -4309,7 +4310,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
}
$data['$id'] = $documentId;
$data['$permissions'] = $permissions;
$data['$permissions'] = $permissions ?? [];
$newDocument = new Document($data);
$operations = 0;
+14 -4
View File
@@ -523,11 +523,16 @@ App::get('/v1/health/queue/databases')
->param('name', 'database_db_main', new Text(256), 'Queue name for which to check the queue size', true)
->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('publisherRedis')
->inject('response')
->action(function (string $name, int|string $threshold, Publisher $publisher, Response $response) {
->action(function (string $name, int|string $threshold, Publisher $publisher, ?Publisher $publisherRedis, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue($name));
$isRedisFallback = \str_contains(System::getEnv('_APP_WORKER_REDIS_FALLBACK', ''), 'databases');
$size = $isRedisFallback
? $publisherRedis->getQueueSize(new Queue($name))
: $publisher->getQueueSize(new 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}.");
@@ -655,11 +660,16 @@ App::get('/v1/health/queue/migrations')
))
->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('publisherRedis')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
->action(function (int|string $threshold, Publisher $publisher, ?Publisher $publisherRedis, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::MIGRATIONS_QUEUE_NAME));
$isRedisFallback = \str_contains(System::getEnv('_APP_WORKER_REDIS_FALLBACK', ''), 'migrations');
$size = $isRedisFallback
? $publisherRedis->getQueueSize(new Queue(Event::MIGRATIONS_QUEUE_NAME))
: $publisher->getQueueSize(new Queue(Event::MIGRATIONS_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}.");
+3 -3
View File
@@ -361,7 +361,7 @@ App::post('/v1/migrations/csv')
$hasCompression = $compression !== Compression::NONE;
$migrationId = ID::unique();
$newPath = $deviceForImports->getPath('/' . $migrationId . '_' . $fileId . '.csv');
$newPath = $deviceForImports->getPath($migrationId . '_' . $fileId . '.csv');
if ($hasEncryption || $hasCompression) {
$source = $deviceForFiles->read($path);
@@ -410,8 +410,8 @@ App::post('/v1/migrations/csv')
'resources' => $resources,
'resourceId' => $resourceId,
'resourceType' => Resource::TYPE_DATABASE,
'statusCounters' => [],
'resourceData' => [],
'statusCounters' => '{}',
'resourceData' => '{}',
'errors' => [],
'options' => [
'path' => $newPath,
+2 -2
View File
@@ -8,8 +8,8 @@ use Appwrite\Event\Mail;
use Appwrite\Event\Validator\Event;
use Appwrite\Extend\Exception;
use Appwrite\Hooks\Hooks;
use Appwrite\Network\Platform;
use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Origin;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
@@ -1790,7 +1790,7 @@ App::post('/v1/projects/:projectId/platforms')
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('type', null, new WhiteList([Origin::CLIENT_TYPE_WEB, Origin::CLIENT_TYPE_FLUTTER_WEB, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_LINUX, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_APPLE_IOS, Origin::CLIENT_TYPE_APPLE_MACOS, Origin::CLIENT_TYPE_APPLE_WATCHOS, Origin::CLIENT_TYPE_APPLE_TVOS, Origin::CLIENT_TYPE_ANDROID, Origin::CLIENT_TYPE_UNITY, Origin::CLIENT_TYPE_REACT_NATIVE_IOS, Origin::CLIENT_TYPE_REACT_NATIVE_ANDROID], true), 'Platform type.')
->param('type', null, new WhiteList([Platform::TYPE_WEB, Platform::TYPE_FLUTTER_WEB, Platform::TYPE_FLUTTER_IOS, Platform::TYPE_FLUTTER_ANDROID, Platform::TYPE_FLUTTER_LINUX, Platform::TYPE_FLUTTER_MACOS, Platform::TYPE_FLUTTER_WINDOWS, Platform::TYPE_APPLE_IOS, Platform::TYPE_APPLE_MACOS, Platform::TYPE_APPLE_WATCHOS, Platform::TYPE_APPLE_TVOS, Platform::TYPE_ANDROID, Platform::TYPE_UNITY, Platform::TYPE_REACT_NATIVE_IOS, Platform::TYPE_REACT_NATIVE_ANDROID], true), 'Platform type.')
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
->param('key', '', new Text(256), 'Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.', true)
->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true)
+26 -4
View File
@@ -18,6 +18,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Buckets;
use Appwrite\Utopia\Database\Validator\Queries\Files;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
@@ -953,17 +954,20 @@ 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)
// NOTE: this is only for the sdk generator and is not used in the action below and is utilised in `resources.php` for `resourceToken`.
->param('token', '', new Text(512), 'File token for accessing this file.', true)
->inject('request')
->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, ?string $token, Response $response, Database $dbForProject, Document $resourceToken, Device $deviceForFiles, Device $deviceForLocal) {
->inject('project')
->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, ?string $token, Request $request, Response $response, Database $dbForProject, Document $resourceToken, Device $deviceForFiles, Device $deviceForLocal, Document $project) {
if (!\extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
}
/* @type Document $bucket */
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
@@ -984,6 +988,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
if ($fileSecurity && !$valid && !$isToken) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
/* @type Document $file */
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
@@ -1035,8 +1040,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$output = empty($type) ? (array_search($mime, $outputs) ?? 'jpg') : $type;
}
$startTime = \microtime(true);
$source = $deviceForFiles->read($path);
$downloadTime = \microtime(true) - $startTime;
if (!empty($cipher)) { // Decrypt
$source = OpenSSL::decrypt(
$source,
@@ -1048,6 +1057,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
);
}
$decryptionTime = \microtime(true) - $startTime - $downloadTime;
switch ($algorithm) {
case Compression::ZSTD:
$compressor = new Zstd();
@@ -1059,6 +1070,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
break;
}
$decompressionTime = \microtime(true) - $startTime - $downloadTime - $decryptionTime;
try {
$image = new Image($source);
} catch (ImagickException $e) {
@@ -1089,6 +1102,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$data = $image->output($output, $quality);
$renderingTime = \microtime(true) - $startTime - $downloadTime - $decryptionTime - $decompressionTime;
$totalTime = \microtime(true) - $startTime;
Console::info("File preview rendered,project=" . $project->getId() . ",bucket=" . $bucketId . ",file=" . $file->getId() . ",uri=" . $request->getURI() . ",total=" . $totalTime . ",rendering=" . $renderingTime . ",decryption=" . $decryptionTime . ",decompression=" . $decompressionTime . ",download=" . $downloadTime);
$contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg'];
//Do not update transformedAt if it's a console user
@@ -1140,7 +1159,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->inject('resourceToken')
->inject('deviceForFiles')
->action(function (string $bucketId, string $fileId, ?string $token, Request $request, Response $response, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
/* @type Document $bucket */
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
@@ -1158,9 +1177,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($fileSecurity && !$valid) {
if ($fileSecurity && !$valid && !$isToken) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
/* @type Document $file */
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
@@ -1300,6 +1320,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->inject('resourceToken')
->inject('deviceForFiles')
->action(function (string $bucketId, string $fileId, ?string $token, Response $response, Request $request, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
/* @type Document $bucket */
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
@@ -1317,9 +1338,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($fileSecurity && !$valid) {
if ($fileSecurity && !$valid && !$isToken) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
/* @type Document $file */
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
+2 -2
View File
@@ -11,6 +11,7 @@ use Appwrite\Event\Messaging;
use Appwrite\Event\StatsUsage;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Redirect;
use Appwrite\Platform\Workers\Deletes;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
@@ -50,7 +51,6 @@ use Utopia\Locale\Locale;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Host;
use Utopia\Validator\Text;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList;
@@ -466,7 +466,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 ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : 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, ['platforms', 'devKey']) // 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')
+2
View File
@@ -649,6 +649,8 @@ App::get('/v1/users')
$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.");
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$response->dynamic(new Document([
'users' => $users,
+14 -8
View File
@@ -4,6 +4,7 @@ use Appwrite\Auth\OAuth2\Github as OAuth2Github;
use Appwrite\Event\Build;
use Appwrite\Event\Delete;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Redirect;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
@@ -53,7 +54,6 @@ use Utopia\Detector\Detector\Runtime;
use Utopia\Detector\Detector\Strategy;
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;
@@ -116,8 +116,10 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
}
$commentStatus = $isAuthorized ? 'waiting' : 'failed';
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
$authorizeUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}";
$authorizeUrl = $protocol . '://' . $hostname . "/console/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}";
$action = $isAuthorized ? ['type' => 'logs'] : ['type' => 'authorize', 'url' => $authorizeUrl];
@@ -378,7 +380,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
}
$owner = $github->getOwnerName($providerInstallationId);
$providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/$resourceCollection/$resourceType-$resourceId";
$providerTargetUrl = $protocol . '://' . $hostname . "/console/project-$region-$projectId/$resourceCollection/$resourceType-$resourceId";
$github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'pending', $message, $providerTargetUrl, $name);
}
@@ -424,8 +426,8 @@ App::get('/v1/vcs/github/authorize')
type: MethodType::WEBAUTH,
hide: true,
))
->param('success', '', fn ($clients) => new Host($clients), 'URL to redirect back to console after a successful installation attempt.', true, ['clients'])
->param('failure', '', fn ($clients) => new Host($clients), 'URL to redirect back to console after a failed installation attempt.', true, ['clients'])
->param('success', '', fn ($platforms) => new Redirect($platforms), 'URL to redirect back to console after a successful installation attempt.', true, ['platforms'])
->param('failure', '', fn ($platforms) => new Redirect($platforms), 'URL to redirect back to console after a failed installation attempt.', true, ['platforms'])
->inject('request')
->inject('response')
->inject('project')
@@ -437,6 +439,8 @@ App::get('/v1/vcs/github/authorize')
]);
$appName = System::getEnv('_APP_VCS_GITHUB_APP_NAME');
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
if (empty($appName)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'GitHub App name is not configured. Please configure VCS (Version Control System) variables in .env file.');
@@ -444,7 +448,7 @@ App::get('/v1/vcs/github/authorize')
$url = "https://github.com/apps/$appName/installations/new?" . \http_build_query([
'state' => $state,
'redirect_uri' => $request->getProtocol() . '://' . $request->getHostname() . "/v1/vcs/github/callback"
'redirect_uri' => $protocol . '://' . $hostname . "/v1/vcs/github/callback"
]);
$response
@@ -494,10 +498,12 @@ App::get('/v1/vcs/github/callback')
}
$region = $project->getAttribute('region', 'default');
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
$defaultState = [
'success' => $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/settings/git-installations",
'failure' => $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/settings/git-installations",
'success' => $protocol . '://' . $hostname . "/console/project-$region-$projectId/settings/git-installations",
'failure' => $protocol . '://' . $hostname . "/console/project-$region-$projectId/settings/git-installations",
];
$state = \array_merge($defaultState, $state ?? []);
+37 -53
View File
@@ -49,14 +49,13 @@ use Utopia\Logger\Log\User;
use Utopia\Logger\Logger;
use Utopia\Platform\Service;
use Utopia\System\System;
use Utopia\Validator\Hostname;
use Utopia\Validator\Text;
Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost');
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey)
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey)
{
$host = $request->getHostname() ?? '';
if (!empty($previewHostname)) {
@@ -76,7 +75,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
$errorView = __DIR__ . '/../views/general/error.phtml';
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . '://' . System::getEnv('_APP_DOMAIN', '');
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . '://' . System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
if ($rule->isEmpty()) {
$appDomainFunctionsFallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
@@ -118,6 +117,11 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
$project->setAttribute('accessedAt', DateTime::now());
Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
}
/**
* Set projectId to update the Error hook logger, since x-appwrite-project is not available when executing custom domain function
*/
$log->addTag('projectId', $project->getId());
}
if (array_key_exists('proxy', $project->getAttribute('services', []))) {
@@ -262,7 +266,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
if (!$authorized) {
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . "://" . System::getEnv('_APP_DOMAIN');
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . "://" . System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
@@ -448,7 +452,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
}
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN');
$endpoint = $protocol . '://' . $hostname . "/v1";
@@ -787,13 +791,14 @@ App::init()
->inject('swooleRequest')
->inject('request')
->inject('response')
->inject('log')
->inject('console')
->inject('project')
->inject('dbForPlatform')
->inject('getProjectDB')
->inject('locale')
->inject('localeCodes')
->inject('clients')
->inject('platforms')
->inject('geodb')
->inject('queueForStatsUsage')
->inject('queueForEvents')
@@ -804,7 +809,9 @@ App::init()
->inject('previewHostname')
->inject('devKey')
->inject('apiKey')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, Executor $executor, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey) {
->inject('httpReferrer')
->inject('httpReferrerSafe')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $platforms, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, Executor $executor, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, string $httpReferrer, string $httpReferrerSafe) {
/*
* Appwrite Router
*/
@@ -812,7 +819,7 @@ App::init()
$mainDomain = System::getEnv('_APP_DOMAIN', '');
// Only run Router when external domain
if ($host !== $mainDomain || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
$utopia->getRoute()?->label('router', true);
}
}
@@ -936,42 +943,9 @@ App::init()
$locale->setDefault($localeParam);
}
$referrer = $request->getReferer();
$origin = \parse_url($request->getOrigin($referrer), PHP_URL_HOST);
$protocol = \parse_url($request->getOrigin($referrer), PHP_URL_SCHEME);
$port = \parse_url($request->getOrigin($referrer), PHP_URL_PORT);
$refDomainOrigin = 'localhost';
$validator = new Hostname($clients);
if ($validator->isValid($origin)) {
$refDomainOrigin = $origin;
} elseif (!empty($origin)) {
// Auto-allow domains with linked rule
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
} else {
$rule = Authorization::skip(
fn () => $dbForPlatform->find('rules', [
Query::equal('domain', [$origin]),
Query::limit(1)
])
)[0] ?? new Document();
}
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) {
$refDomainOrigin = $origin;
}
}
$refDomain = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $refDomainOrigin . (!empty($port) ? ':' . $port : '');
$refDomain = (!$route->getLabel('origin', false)) // This route is publicly accessible
? $refDomain
: (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
$origin = \parse_url($request->getOrigin($httpReferrer), PHP_URL_HOST);
$selfDomain = new Domain($request->getHostname());
$endDomain = new Domain((string)$origin);
Config::setParam(
'domainVerification',
($selfDomain->getRegisterable() === $endDomain->getRegisterable()) &&
@@ -1043,7 +1017,7 @@ App::init()
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Dev-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent')
->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies')
->addHeader('Access-Control-Allow-Origin', $refDomain)
->addHeader('Access-Control-Allow-Origin', $httpReferrerSafe)
->addHeader('Access-Control-Allow-Credentials', 'true');
if (!$devKey->isEmpty()) {
@@ -1056,14 +1030,16 @@ App::init()
* Skip this check for non-web platforms which are not required to send an origin header
*/
$origin = $request->getOrigin($request->getReferer(''));
$originValidator = new Origin(\array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
$originValidator = new Origin($platforms);
if (
!$originValidator->isValid($origin)
&& $devKey->isEmpty()
$devKey->isEmpty()
&& !empty($origin)
&& !$originValidator->isValid($origin)
&& \in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE])
&& $route->getLabel('origin', false) !== '*'
&& empty($request->getHeader('x-appwrite-key', ''))
&& \parse_url($httpReferrerSafe, PHP_URL_HOST) === 'localhost'
) {
throw new AppwriteException(AppwriteException::GENERAL_UNKNOWN_ORIGIN, $originValidator->getDescription());
}
@@ -1074,6 +1050,7 @@ App::options()
->inject('swooleRequest')
->inject('request')
->inject('response')
->inject('log')
->inject('dbForPlatform')
->inject('getProjectDB')
->inject('queueForEvents')
@@ -1086,7 +1063,7 @@ App::options()
->inject('project')
->inject('devKey')
->inject('apiKey')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey) {
/*
* Appwrite Router
*/
@@ -1094,7 +1071,7 @@ App::options()
$mainDomain = System::getEnv('_APP_DOMAIN', '');
// Only run Router when external domain
if ($host !== $mainDomain || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
$utopia->getRoute()?->label('router', true);
}
}
@@ -1267,7 +1244,12 @@ App::error()
$log->addTag('url', $request->getURI());
$log->addTag('verboseType', get_class($error));
$log->addTag('code', $error->getCode());
$log->addTag('projectId', $project->getId());
$tags = $log->getTags();
if (!isset($tags['projectId'])) {
$log->addTag('projectId', $project->getId());
}
$log->addTag('hostname', $request->getHostname());
$log->addTag('locale', (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', '')));
@@ -1385,6 +1367,7 @@ App::get('/robots.txt')
->inject('swooleRequest')
->inject('request')
->inject('response')
->inject('log')
->inject('dbForPlatform')
->inject('getProjectDB')
->inject('queueForEvents')
@@ -1395,7 +1378,7 @@ App::get('/robots.txt')
->inject('isResourceBlocked')
->inject('previewHostname')
->inject('apiKey')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
@@ -1403,7 +1386,7 @@ App::get('/robots.txt')
$template = new View(__DIR__ . '/../views/general/robots.phtml');
$response->text($template->render(false));
} else {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
$utopia->getRoute()?->label('router', true);
}
}
@@ -1417,6 +1400,7 @@ App::get('/humans.txt')
->inject('swooleRequest')
->inject('request')
->inject('response')
->inject('log')
->inject('dbForPlatform')
->inject('getProjectDB')
->inject('queueForEvents')
@@ -1427,7 +1411,7 @@ App::get('/humans.txt')
->inject('isResourceBlocked')
->inject('previewHostname')
->inject('apiKey')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
@@ -1435,7 +1419,7 @@ App::get('/humans.txt')
$template = new View(__DIR__ . '/../views/general/humans.phtml');
$response->text($template->render(false));
} else {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
$utopia->getRoute()?->label('router', true);
}
}
-26
View File
@@ -129,32 +129,6 @@ App::get('/v1/mock/tests/general/oauth2/failure')
]);
});
App::patch('/v1/mock/functions-v2')
->desc('Update Function Version to V2 (outdated code syntax)')
->groups(['mock', 'api', 'functions'])
->label('scope', 'functions.write')
->label('docs', false)
->param('functionId', '', new UID(), 'Function ID.')
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, Response $response, Database $dbForProject) {
$isDevelopment = System::getEnv('_APP_ENV', 'development') === 'development';
if (!$isDevelopment) {
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
}
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('version', 'v2'));
$response->noContent();
});
App::post('/v1/mock/api-key-unprefixed')
->desc('Create API Key (without standard prefix)')
->groups(['mock', 'api', 'projects'])
+14 -5
View File
@@ -22,6 +22,7 @@ use Utopia\Abuse\Abuse;
use Utopia\App;
use Utopia\Cache\Adapter\Filesystem;
use Utopia\Cache\Cache;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
@@ -563,7 +564,7 @@ App::init()
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
);
$timestamp = 60 * 60 * 24 * 30;
$timestamp = 60 * 60 * 24 * 180; // Temporarily increase the TTL to 180 day to ensure files in the cache are still fetched.
$data = $cache->load($key, $timestamp);
if (!empty($data) && !$cacheLog->isEmpty()) {
@@ -575,7 +576,6 @@ App::init()
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) {
@@ -797,6 +797,12 @@ App::shutdown()
}
if (!empty($queueForDatabase->getType())) {
Console::info("Triggering database event: \n" . \json_encode([
'projectId' => $project->getId(),
'databaseId' => $queueForDatabase->getDatabase()?->getId(),
'collectionId' => $queueForDatabase->getCollection()?->getId(),
'documentId' => $queueForDatabase->getDocument()?->getId(),
]));
$queueForDatabase->trigger();
}
@@ -824,6 +830,10 @@ App::shutdown()
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
}
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
);
$key = $request->cacheIdentifier();
$signature = md5($data['payload']);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
@@ -841,12 +851,11 @@ App::shutdown()
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
$cacheLog->setAttribute('accessedAt', $now);
Authorization::skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
// Overwrite the file every APP_CACHE_UPDATE seconds to update the file modified time that is used in the TTL checks in cache->load()
$cache->save($key, $data['payload']);
}
if ($signature !== $cacheLog->getAttribute('signature')) {
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
);
$cache->save($key, $data['payload']);
}
}
+2 -1
View File
@@ -36,7 +36,8 @@ App::setMode(System::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
if (!App::isProduction()) {
// Allow specific domains to skip public domain validation in dev environment
// Useful for existing tests involving webhooks
PublicDomain::allow(['request-catcher']);
PublicDomain::allow(['request-catcher-sms']);
PublicDomain::allow(['request-catcher-webhook']);
}
$register->set('logger', function () {
// Register error logger
+85 -32
View File
@@ -19,6 +19,7 @@ use Appwrite\Event\StatsUsage;
use Appwrite\Event\Webhook;
use Appwrite\Extend\Exception;
use Appwrite\GraphQL\Schema;
use Appwrite\Network\Platform;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Request;
use Executor\Executor;
@@ -79,9 +80,15 @@ App::setResource('localeCodes', function () {
App::setResource('publisher', function (Group $pools) {
return new BrokerPool(publisher: $pools->get('publisher'));
}, ['pools']);
App::setResource('publisherRedis', function () {
// Stub
});
App::setResource('consumer', function (Group $pools) {
return new BrokerPool(consumer: $pools->get('consumer'));
}, ['pools']);
App::setResource('consumerRedis', function () {
// Stub
});
App::setResource('queueForMessaging', function (Publisher $publisher) {
return new Messaging($publisher);
}, ['publisher']);
@@ -121,11 +128,11 @@ App::setResource('queueForCertificates', function (Publisher $publisher) {
App::setResource('queueForMigrations', function (Publisher $publisher) {
return new Migration($publisher);
}, ['publisher']);
App::setResource('clients', function ($request, $console, $project) {
App::setResource('platforms', function (Request $request, Document $console, Document $project) {
$console->setAttribute('platforms', [ // Always allow current host
'$collection' => ID::custom('platforms'),
'name' => 'Current Host',
'type' => Origin::CLIENT_TYPE_WEB,
'type' => Platform::TYPE_WEB,
'hostname' => $request->getHostname(),
], Document::SET_TYPE_APPEND);
@@ -138,39 +145,32 @@ App::setResource('clients', function ($request, $console, $project) {
}
$console->setAttribute('platforms', [
'$collection' => ID::custom('platforms'),
'type' => Origin::CLIENT_TYPE_WEB,
'type' => Platform::TYPE_WEB,
'name' => $hostname,
'hostname' => $hostname,
], Document::SET_TYPE_APPEND);
}
/**
* Get All verified client URLs for both console and current projects
* + Filter for duplicated entries
*/
$clientsConsole = \array_map(
fn ($node) => $node['hostname'],
\array_filter(
$console->getAttribute('platforms', []),
fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname']))
)
);
$clients = $clientsConsole;
$platforms = $project->getAttribute('platforms', []);
foreach ($platforms as $node) {
if (
isset($node['type']) &&
($node['type'] === Origin::CLIENT_TYPE_WEB ||
$node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) &&
!empty($node['hostname'])
) {
$clients[] = $node['hostname'];
}
// Add `exp` and `appwrite-callback-{projectId}` schemes
if (!$project->isEmpty() && $project->getId() !== 'console') {
$project->setAttribute('platforms', [
'$collection' => ID::custom('platforms'),
'type' => Platform::TYPE_SCHEME,
'name' => 'Expo',
'key' => 'exp',
], Document::SET_TYPE_APPEND);
$project->setAttribute('platforms', [
'$collection' => ID::custom('platforms'),
'type' => Platform::TYPE_SCHEME,
'name' => 'Appwrite Callback',
'key' => 'appwrite-callback-' . $project->getId(),
], Document::SET_TYPE_APPEND);
}
return \array_unique($clients);
return [
...$console->getAttribute('platforms', []),
...$project->getAttribute('platforms', []),
];
}, ['request', 'console', 'project']);
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) {
@@ -222,7 +222,9 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
Auth::$unique = $session['id'] ?? '';
Auth::$secret = $session['secret'] ?? '';
if (APP_MODE_ADMIN !== $mode) {
if ($mode === APP_MODE_ADMIN) {
$user = $dbForPlatform->getDocument('users', Auth::$unique);
} else {
if ($project->isEmpty()) {
$user = new Document([]);
} else {
@@ -232,8 +234,6 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
$user = $dbForProject->getDocument('users', Auth::$unique);
}
}
} else {
$user = $dbForPlatform->getDocument('users', Auth::$unique);
}
if (
@@ -264,7 +264,11 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
$jwtUserId = $payload['userId'] ?? '';
if (!empty($jwtUserId)) {
$user = $dbForProject->getDocument('users', $jwtUserId);
if ($mode === APP_MODE_ADMIN) {
$user = $dbForPlatform->getDocument('users', $jwtUserId);
} else {
$user = $dbForProject->getDocument('users', $jwtUserId);
}
}
$jwtSessionId = $payload['sessionId'] ?? '';
@@ -944,3 +948,52 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
}
return new Document([]);
}, ['project', 'dbForProject', 'request']);
App::setResource('httpReferrer', function (Request $request): string {
$referrer = $request->getReferer();
return $referrer;
}, ['request']);
App::setResource('httpReferrerSafe', function (Request $request, string $httpReferrer, array $platforms, Database $dbForPlatform, Document $project, App $utopia): string {
$origin = \parse_url($request->getOrigin($httpReferrer), PHP_URL_HOST);
$protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME);
$port = \parse_url($request->getOrigin($httpReferrer), PHP_URL_PORT);
$referrer = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
// Safe if route is publicly accessible
$route = $utopia->getRoute();
if ($route->getLabel('origin', false)) {
return $referrer;
}
// Safe if added as web platform
$originValidator = new Origin($platforms);
if ($originValidator->isValid($request->getOrigin($httpReferrer))) {
return $referrer;
}
// Safe if rule with same project ID exists
if (!empty($origin)) {
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
} else {
$rule = Authorization::skip(
fn () => $dbForPlatform->find('rules', [
Query::equal('domain', [$origin]),
Query::limit(1)
])
)[0] ?? new Document();
}
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) {
return $referrer;
}
}
// Unsafe; Localhost is always safe for ease of local development
$origin = 'localhost';
$protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME);
$port = \parse_url($request->getOrigin($httpReferrer), PHP_URL_PORT);
$referrer = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
return $referrer;
}, ['request', 'httpReferrer', 'platforms', 'dbForPlatform', 'project', 'utopia']);
+3 -3
View File
@@ -532,7 +532,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
}
$timelimit = $app->getResource('timelimit');
$console = $app->getResource('console'); /** @var Document $console */
$platforms = $app->getResource('platforms');
$user = $app->getResource('user'); /** @var Document $user */
/*
@@ -557,9 +557,9 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
* Skip this check for non-web platforms which are not required to send an origin header.
*/
$origin = $request->getOrigin();
$originValidator = new Origin(array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
$originValidator = new Origin($platforms);
if (!$originValidator->isValid($origin) && $project->getId() !== 'console') {
if (!empty($origin) && !$originValidator->isValid($origin) && $project->getId() !== 'console') {
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription());
}
+1 -1
View File
@@ -15,7 +15,7 @@ $labelClass = '';
$buttons = [];
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN');
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
// TODO: remove this later
if (System::getEnv('_APP_ENV') === 'development') {
$hostname = 'localhost';
+1 -1
View File
@@ -864,7 +864,7 @@ $image = $this->getParam('image', '');
<<: *x-logging
restart: unless-stopped
stop_signal: SIGINT
image: openruntimes/executor:0.7.20
image: openruntimes/executor:0.7.22
networks:
- appwrite
- runtimes
+8
View File
@@ -247,10 +247,18 @@ Server::setResource('publisher', function (Group $pools) {
return new BrokerPool(publisher: $pools->get('publisher'));
}, ['pools']);
Server::setResource('publisherRedis', function () {
// Stub
});
Server::setResource('consumer', function (Group $pools) {
return new BrokerPool(consumer: $pools->get('consumer'));
}, ['pools']);
Server::setResource('consumerRedis', function () {
// Stub
});
Server::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
+2 -2
View File
@@ -52,7 +52,7 @@
"utopia-php/cache": "0.13.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.71.*",
"utopia-php/database": "dev-validate-create-index-array as 0.71.10",
"utopia-php/detector": "0.1.*",
"utopia-php/domains": "0.8.*",
"utopia-php/dsn": "0.2.1",
@@ -62,7 +62,7 @@
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.18.*",
"utopia-php/migration": "0.10.*",
"utopia-php/migration": "0.11.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.8.*",
Generated
+125 -110
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": "55bc52686a08d64930e6af7411ac0654",
"content-hash": "b7f645a2dfd3313b7202189c133ea954",
"packages": [
{
"name": "adhocore/jwt",
@@ -69,16 +69,16 @@
},
{
"name": "appwrite/appwrite",
"version": "11.1.0",
"version": "15.0.0",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-for-php.git",
"reference": "1d043f543acdb17b9fdb440b1b2dd208e400bad3"
"reference": "deb97b62e0abed8a4fd5c5d48e77365cf89867cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/1d043f543acdb17b9fdb440b1b2dd208e400bad3",
"reference": "1d043f543acdb17b9fdb440b1b2dd208e400bad3",
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/deb97b62e0abed8a4fd5c5d48e77365cf89867cf",
"reference": "deb97b62e0abed8a4fd5c5d48e77365cf89867cf",
"shasum": ""
},
"require": {
@@ -104,10 +104,10 @@
"support": {
"email": "team@appwrite.io",
"issues": "https://github.com/appwrite/sdk-for-php/issues",
"source": "https://github.com/appwrite/sdk-for-php/tree/11.1.0",
"source": "https://github.com/appwrite/sdk-for-php/tree/15.0.0",
"url": "https://appwrite.io/support"
},
"time": "2024-06-26T07:03:23+00:00"
"time": "2025-05-18T09:47:10+00:00"
},
{
"name": "appwrite/php-clamav",
@@ -1113,16 +1113,16 @@
},
{
"name": "open-telemetry/api",
"version": "1.3.0",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/api.git",
"reference": "4e3bb38e069876fb73c2ce85c89583bf2b28cd86"
"reference": "b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/4e3bb38e069876fb73c2ce85c89583bf2b28cd86",
"reference": "4e3bb38e069876fb73c2ce85c89583bf2b28cd86",
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7",
"reference": "b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7",
"shasum": ""
},
"require": {
@@ -1142,7 +1142,7 @@
]
},
"branch-alias": {
"dev-main": "1.1.x-dev"
"dev-main": "1.4.x-dev"
}
},
"autoload": {
@@ -1179,7 +1179,7 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-05-07T12:32:21+00:00"
"time": "2025-06-19T23:36:51+00:00"
},
{
"name": "open-telemetry/context",
@@ -1242,16 +1242,16 @@
},
{
"name": "open-telemetry/exporter-otlp",
"version": "1.3.1",
"version": "1.3.2",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/exporter-otlp.git",
"reference": "8b3ca1f86d01429c73b407bf1a2075d9c187001e"
"reference": "196f3a1dbce3b2c0f8110d164232c11ac00ddbb2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/8b3ca1f86d01429c73b407bf1a2075d9c187001e",
"reference": "8b3ca1f86d01429c73b407bf1a2075d9c187001e",
"url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/196f3a1dbce3b2c0f8110d164232c11ac00ddbb2",
"reference": "196f3a1dbce3b2c0f8110d164232c11ac00ddbb2",
"shasum": ""
},
"require": {
@@ -1302,7 +1302,7 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-05-21T12:02:20+00:00"
"time": "2025-06-16T00:24:51+00:00"
},
{
"name": "open-telemetry/gen-otlp-protobuf",
@@ -1369,22 +1369,22 @@
},
{
"name": "open-telemetry/sdk",
"version": "1.5.0",
"version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/sdk.git",
"reference": "cd0d7367599717fc29e04eb8838ec061e6c2c657"
"reference": "1c0371794e4c0700afd4a9d4d8511cb5e3f78e6a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/cd0d7367599717fc29e04eb8838ec061e6c2c657",
"reference": "cd0d7367599717fc29e04eb8838ec061e6c2c657",
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/1c0371794e4c0700afd4a9d4d8511cb5e3f78e6a",
"reference": "1c0371794e4c0700afd4a9d4d8511cb5e3f78e6a",
"shasum": ""
},
"require": {
"ext-json": "*",
"nyholm/psr7-server": "^1.1",
"open-telemetry/api": "~1.0 || ~1.1",
"open-telemetry/api": "~1.4.0",
"open-telemetry/context": "^1.0",
"open-telemetry/sem-conv": "^1.0",
"php": "^8.1",
@@ -1407,6 +1407,10 @@
"type": "library",
"extra": {
"spi": {
"OpenTelemetry\\API\\Configuration\\ConfigEnv\\EnvComponentLoader": [
"OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderHttpConfig",
"OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderPeerConfig"
],
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager"
]
@@ -1455,20 +1459,20 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-05-22T02:33:34+00:00"
"time": "2025-06-19T23:36:51+00:00"
},
{
"name": "open-telemetry/sem-conv",
"version": "1.32.0",
"version": "1.32.1",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/sem-conv.git",
"reference": "16585cc0dbc3032a318e274043454679430d2ebf"
"reference": "94daa85ea61a8e2b7e1b0af6be0e875bedda7c22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/16585cc0dbc3032a318e274043454679430d2ebf",
"reference": "16585cc0dbc3032a318e274043454679430d2ebf",
"url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/94daa85ea61a8e2b7e1b0af6be0e875bedda7c22",
"reference": "94daa85ea61a8e2b7e1b0af6be0e875bedda7c22",
"shasum": ""
},
"require": {
@@ -1512,7 +1516,7 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-05-05T03:58:53+00:00"
"time": "2025-06-24T02:32:27+00:00"
},
{
"name": "paragonie/constant_time_encoding",
@@ -1874,16 +1878,16 @@
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.44",
"version": "3.0.46",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "1d0b5e7e1434678411787c5a0535e68907cf82d9"
"reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/1d0b5e7e1434678411787c5a0535e68907cf82d9",
"reference": "1d0b5e7e1434678411787c5a0535e68907cf82d9",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
"reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
"shasum": ""
},
"require": {
@@ -1964,7 +1968,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.44"
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.46"
},
"funding": [
{
@@ -1980,7 +1984,7 @@
"type": "tidelift"
}
],
"time": "2025-06-15T09:59:26+00:00"
"time": "2025-06-26T16:29:55+00:00"
},
{
"name": "psr/container",
@@ -2323,21 +2327,20 @@
},
{
"name": "ramsey/uuid",
"version": "4.8.1",
"version": "4.9.0",
"source": {
"type": "git",
"url": "https://github.com/ramsey/uuid.git",
"reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28"
"reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
"reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0",
"reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0",
"shasum": ""
},
"require": {
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13",
"ext-json": "*",
"php": "^8.0",
"ramsey/collection": "^1.2 || ^2.0"
},
@@ -2396,9 +2399,9 @@
],
"support": {
"issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.8.1"
"source": "https://github.com/ramsey/uuid/tree/4.9.0"
},
"time": "2025-06-01T06:28:46+00:00"
"time": "2025-06-25T14:20:11+00:00"
},
{
"name": "spomky-labs/otphp",
@@ -2544,16 +2547,16 @@
},
{
"name": "symfony/http-client",
"version": "v7.3.0",
"version": "v7.3.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "57e4fb86314015a695a750ace358d07a7e37b8a9"
"reference": "4403d87a2c16f33345dca93407a8714ee8c05a64"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/57e4fb86314015a695a750ace358d07a7e37b8a9",
"reference": "57e4fb86314015a695a750ace358d07a7e37b8a9",
"url": "https://api.github.com/repos/symfony/http-client/zipball/4403d87a2c16f33345dca93407a8714ee8c05a64",
"reference": "4403d87a2c16f33345dca93407a8714ee8c05a64",
"shasum": ""
},
"require": {
@@ -2565,6 +2568,7 @@
},
"conflict": {
"amphp/amp": "<2.5",
"amphp/socket": "<1.1",
"php-http/discovery": "<1.15",
"symfony/http-foundation": "<6.4"
},
@@ -2577,7 +2581,6 @@
"require-dev": {
"amphp/http-client": "^4.2.1|^5.0",
"amphp/http-tunnel": "^1.0|^2.0",
"amphp/socket": "^1.1",
"guzzlehttp/promises": "^1.4|^2.0",
"nyholm/psr7": "^1.0",
"php-http/httplug": "^1.0|^2.0",
@@ -2619,7 +2622,7 @@
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v7.3.0"
"source": "https://github.com/symfony/http-client/tree/v7.3.1"
},
"funding": [
{
@@ -2635,7 +2638,7 @@
"type": "tidelift"
}
],
"time": "2025-05-02T08:23:16+00:00"
"time": "2025-06-28T07:58:39+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -2957,16 +2960,16 @@
},
{
"name": "tbachert/spi",
"version": "v1.0.3",
"version": "v1.0.5",
"source": {
"type": "git",
"url": "https://github.com/Nevay/spi.git",
"reference": "506a79c98e1a51522e76ee921ccb6c62d52faf3a"
"reference": "e7078767866d0a9e0f91d3f9d42a832df5e39002"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Nevay/spi/zipball/506a79c98e1a51522e76ee921ccb6c62d52faf3a",
"reference": "506a79c98e1a51522e76ee921ccb6c62d52faf3a",
"url": "https://api.github.com/repos/Nevay/spi/zipball/e7078767866d0a9e0f91d3f9d42a832df5e39002",
"reference": "e7078767866d0a9e0f91d3f9d42a832df5e39002",
"shasum": ""
},
"require": {
@@ -2984,7 +2987,7 @@
"extra": {
"class": "Nevay\\SPI\\Composer\\Plugin",
"branch-alias": {
"dev-main": "0.2.x-dev"
"dev-main": "1.0.x-dev"
},
"plugin-optional": true
},
@@ -3003,9 +3006,9 @@
],
"support": {
"issues": "https://github.com/Nevay/spi/issues",
"source": "https://github.com/Nevay/spi/tree/v1.0.3"
"source": "https://github.com/Nevay/spi/tree/v1.0.5"
},
"time": "2025-04-02T19:38:14+00:00"
"time": "2025-06-29T15:42:06+00:00"
},
{
"name": "thecodingmachine/safe",
@@ -3490,16 +3493,16 @@
},
{
"name": "utopia-php/database",
"version": "0.71.7",
"version": "dev-validate-create-index-array",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "aa0116b2380125907fc18c82662be8e74c54091f"
"reference": "9e2895b92cb094436973d5f8f30d75014bf03ae6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/aa0116b2380125907fc18c82662be8e74c54091f",
"reference": "aa0116b2380125907fc18c82662be8e74c54091f",
"url": "https://api.github.com/repos/utopia-php/database/zipball/9e2895b92cb094436973d5f8f30d75014bf03ae6",
"reference": "9e2895b92cb094436973d5f8f30d75014bf03ae6",
"shasum": ""
},
"require": {
@@ -3540,9 +3543,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.71.7"
"source": "https://github.com/utopia-php/database/tree/validate-create-index-array"
},
"time": "2025-06-17T23:59:10+00:00"
"time": "2025-07-20T08:28:54+00:00"
},
{
"name": "utopia-php/detector",
@@ -3939,16 +3942,16 @@
},
{
"name": "utopia-php/messaging",
"version": "0.18.0",
"version": "0.18.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/messaging.git",
"reference": "c151aa5d4d475c788ca15c210b5b2017e21c41d6"
"reference": "5d1245207a61d7ca065daddad7ac5f1d5640152f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/c151aa5d4d475c788ca15c210b5b2017e21c41d6",
"reference": "c151aa5d4d475c788ca15c210b5b2017e21c41d6",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/5d1245207a61d7ca065daddad7ac5f1d5640152f",
"reference": "5d1245207a61d7ca065daddad7ac5f1d5640152f",
"shasum": ""
},
"require": {
@@ -3984,26 +3987,26 @@
],
"support": {
"issues": "https://github.com/utopia-php/messaging/issues",
"source": "https://github.com/utopia-php/messaging/tree/0.18.0"
"source": "https://github.com/utopia-php/messaging/tree/0.18.1"
},
"time": "2025-05-15T05:00:03+00:00"
"time": "2025-06-26T18:26:07+00:00"
},
{
"name": "utopia-php/migration",
"version": "0.10.1",
"version": "0.11.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "ea1c585df7ec5f346f061a11581fc9a91679966f"
"reference": "d528a454d5c1ed6564b2843a39ff13297bcdb1af"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/ea1c585df7ec5f346f061a11581fc9a91679966f",
"reference": "ea1c585df7ec5f346f061a11581fc9a91679966f",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/d528a454d5c1ed6564b2843a39ff13297bcdb1af",
"reference": "d528a454d5c1ed6564b2843a39ff13297bcdb1af",
"shasum": ""
},
"require": {
"appwrite/appwrite": "11.*",
"appwrite/appwrite": "15.*",
"ext-curl": "*",
"ext-openssl": "*",
"php": ">=8.1",
@@ -4040,9 +4043,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.10.1"
"source": "https://github.com/utopia-php/migration/tree/0.11.1"
},
"time": "2025-05-26T15:29:19+00:00"
"time": "2025-07-11T13:46:37+00:00"
},
{
"name": "utopia-php/orchestration",
@@ -4807,16 +4810,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.41.8",
"version": "0.41.17",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "93ffb24b25b376ca4423e3a5caf6f916673af4b2"
"reference": "ef5e9d952032deecffee7f825a941c40ed4c84b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/93ffb24b25b376ca4423e3a5caf6f916673af4b2",
"reference": "93ffb24b25b376ca4423e3a5caf6f916673af4b2",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/ef5e9d952032deecffee7f825a941c40ed4c84b8",
"reference": "ef5e9d952032deecffee7f825a941c40ed4c84b8",
"shasum": ""
},
"require": {
@@ -4852,9 +4855,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.41.8"
"source": "https://github.com/appwrite/sdk-generator/tree/0.41.17"
},
"time": "2025-06-18T13:20:45+00:00"
"time": "2025-07-18T08:18:48+00:00"
},
{
"name": "doctrine/annotations",
@@ -5081,16 +5084,16 @@
},
{
"name": "laravel/pint",
"version": "v1.22.1",
"version": "v1.24.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "941d1927c5ca420c22710e98420287169c7bcaf7"
"reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/941d1927c5ca420c22710e98420287169c7bcaf7",
"reference": "941d1927c5ca420c22710e98420287169c7bcaf7",
"url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a",
"reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a",
"shasum": ""
},
"require": {
@@ -5101,10 +5104,10 @@
"php": "^8.2.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.75.0",
"illuminate/view": "^11.44.7",
"larastan/larastan": "^3.4.0",
"laravel-zero/framework": "^11.36.1",
"friendsofphp/php-cs-fixer": "^3.82.2",
"illuminate/view": "^11.45.1",
"larastan/larastan": "^3.5.0",
"laravel-zero/framework": "^11.45.0",
"mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^2.3.1",
"pestphp/pest": "^2.36.0"
@@ -5114,6 +5117,9 @@
],
"type": "project",
"autoload": {
"files": [
"overrides/Runner/Parallel/ProcessFactory.php"
],
"psr-4": {
"App\\": "app/",
"Database\\Seeders\\": "database/seeders/",
@@ -5143,20 +5149,20 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
"time": "2025-05-08T08:38:12+00:00"
"time": "2025-07-10T18:09:32+00:00"
},
{
"name": "matthiasmullie/minify",
"version": "1.3.74",
"version": "1.3.75",
"source": {
"type": "git",
"url": "https://github.com/matthiasmullie/minify.git",
"reference": "a2593286a4135d03c6a6a9e9aeded5d41e931ce4"
"reference": "76ba4a5f555fd7bf4aa408af608e991569076671"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/a2593286a4135d03c6a6a9e9aeded5d41e931ce4",
"reference": "a2593286a4135d03c6a6a9e9aeded5d41e931ce4",
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/76ba4a5f555fd7bf4aa408af608e991569076671",
"reference": "76ba4a5f555fd7bf4aa408af608e991569076671",
"shasum": ""
},
"require": {
@@ -5205,7 +5211,7 @@
],
"support": {
"issues": "https://github.com/matthiasmullie/minify/issues",
"source": "https://github.com/matthiasmullie/minify/tree/1.3.74"
"source": "https://github.com/matthiasmullie/minify/tree/1.3.75"
},
"funding": [
{
@@ -5213,7 +5219,7 @@
"type": "github"
}
],
"time": "2025-06-12T08:06:04+00:00"
"time": "2025-06-25T09:56:19+00:00"
},
{
"name": "matthiasmullie/path-converter",
@@ -5270,16 +5276,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.13.1",
"version": "1.13.3",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c"
"reference": "faed855a7b5f4d4637717c2b3863e277116beb36"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c",
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36",
"reference": "faed855a7b5f4d4637717c2b3863e277116beb36",
"shasum": ""
},
"require": {
@@ -5318,7 +5324,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.1"
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.3"
},
"funding": [
{
@@ -5326,7 +5332,7 @@
"type": "tidelift"
}
],
"time": "2025-04-29T12:36:36+00:00"
"time": "2025-07-05T12:25:42+00:00"
},
{
"name": "nikic/php-parser",
@@ -7252,16 +7258,16 @@
},
{
"name": "symfony/console",
"version": "v7.3.0",
"version": "v7.3.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44"
"reference": "9e27aecde8f506ba0fd1d9989620c04a87697101"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/66c1440edf6f339fd82ed6c7caa76cb006211b44",
"reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44",
"url": "https://api.github.com/repos/symfony/console/zipball/9e27aecde8f506ba0fd1d9989620c04a87697101",
"reference": "9e27aecde8f506ba0fd1d9989620c04a87697101",
"shasum": ""
},
"require": {
@@ -7326,7 +7332,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.3.0"
"source": "https://github.com/symfony/console/tree/v7.3.1"
},
"funding": [
{
@@ -7342,7 +7348,7 @@
"type": "tidelift"
}
],
"time": "2025-05-24T10:34:04+00:00"
"time": "2025-06-27T19:55:54+00:00"
},
{
"name": "symfony/filesystem",
@@ -8231,9 +8237,18 @@
"time": "2024-03-07T20:33:40+00:00"
}
],
"aliases": [],
"aliases": [
{
"package": "utopia-php/database",
"version": "dev-validate-create-index-array",
"alias": "0.71.10",
"alias_normalized": "0.71.10.0"
}
],
"minimum-stability": "stable",
"stability-flags": {},
"stability-flags": {
"utopia-php/database": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
+19 -6
View File
@@ -116,6 +116,7 @@ services:
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_CONSOLE_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
- _APP_DOMAIN_TARGET_A
@@ -213,7 +214,7 @@ services:
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: appwrite/console:6.0.41
image: appwrite/console:6.1.12
restart: unless-stopped
networks:
- appwrite
@@ -323,7 +324,8 @@ services:
depends_on:
- redis
- mariadb
- request-catcher
- request-catcher-sms
- request-catcher-webhook
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -454,6 +456,7 @@ services:
- redis
- mariadb
environment:
- _APP_BROWSER_HOST
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
@@ -481,6 +484,7 @@ services:
- _APP_OPTIONS_FORCE_HTTPS
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
- _APP_DOMAIN
- _APP_CONSOLE_DOMAIN
- _APP_STORAGE_DEVICE
- _APP_STORAGE_S3_ACCESS_KEY
- _APP_STORAGE_S3_SECRET
@@ -951,7 +955,7 @@ services:
hostname: exc1
<<: *x-logging
stop_signal: SIGINT
image: openruntimes/executor:0.7.20
image: openruntimes/executor:0.7.22
restart: unless-stopped
networks:
- appwrite
@@ -974,7 +978,7 @@ services:
- OPR_EXECUTOR_ENV=$_APP_ENV
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES,$_APP_SITES_RUNTIMES
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
- OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v5
- 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
@@ -1075,15 +1079,24 @@ services:
networks:
- appwrite
request-catcher: # used mainly for dev tests
request-catcher-webhook: # used mainly for dev tests (mock HTTP webhook)
image: appwrite/requestcatcher:1.0.0
container_name: appwrite-requestcatcher
container_name: appwrite-requestcatcher-webhook
<<: *x-logging
ports:
- "9504:5000"
networks:
- appwrite
request-catcher-sms: # used mainly for dev tests (mock SMS auth secret)
image: appwrite/requestcatcher:1.0.0
container_name: appwrite-requestcatcher-sms
<<: *x-logging
ports:
- "9507:5000"
networks:
- appwrite
adminer:
image: adminer
container_name: appwrite-adminer
@@ -0,0 +1,16 @@
import 'package:dart_appwrite/dart_appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setSession(''); // The user session to authenticate with
Databases databases = Databases(client);
Document result = await databases.upsertDocument(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documentId: '<DOCUMENT_ID>',
data: {},
permissions: ["read("any")"], // (optional)
);
@@ -10,5 +10,5 @@ Databases databases = Databases(client);
DocumentList result = await databases.upsertDocuments(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documents: [], // (optional)
documents: [],
);
@@ -0,0 +1,18 @@
using Appwrite;
using Appwrite.Models;
using Appwrite.Services;
Client client = new Client()
.SetEndPoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.SetProject("<YOUR_PROJECT_ID>") // Your project ID
.SetSession(""); // The user session to authenticate with
Databases databases = new Databases(client);
Document result = await databases.UpsertDocument(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
documentId: "<DOCUMENT_ID>",
data: [object],
permissions: ["read("any")"] // optional
);
@@ -12,5 +12,5 @@ Databases databases = new Databases(client);
DocumentList result = await databases.UpsertDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
documents: new List<object>() // optional
documents: new List<object>()
);
@@ -0,0 +1,16 @@
const sdk = require('node-appwrite');
const client = new sdk.Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setSession(''); // The user session to authenticate with
const databases = new sdk.Databases(client);
const result = await databases.upsertDocument(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'<DOCUMENT_ID>', // documentId
{}, // data
["read("any")"] // permissions (optional)
);
@@ -10,5 +10,5 @@ const databases = new sdk.Databases(client);
const result = await databases.upsertDocuments(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
[] // documents (optional)
[] // documents
);
+104 -1
View File
@@ -1 +1,104 @@
# Change Log
# Change Log
## 8.2.2
* Fix object comparison logic when pushing settings
* Type generation fixes:
* Dart: Fixed import casing to snake_case, removed `extends Document` and hardcoded attributes, removed unnecessary imports
* Java: Fixed indentation to 4 spaces, updated imports to `java.util.Objects`, fixed enum casing in strict mode as per [Oracle official docs](https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html)
* Javascript: Updated optional values formatting from `|null` to `| null`
* Kotlin: Fixed indentation to 4 spaces per [Kotlinlang official docs](https://kotlinlang.org/docs/coding-conventions.html#indentation)
* PHP: Fixed indentation to 4 spaces per [PHP Fig official docs](https://www.php-fig.org/psr/psr-2/)
* Swift: Fixed indentation to 4 spaces, improved `decodeIfPresent` usage for optionals, added missing `public` to `init` method
* Typescript: Fixed indentation to 4 spaces per [Typescript coding guidelines](https://github.com/microsoft/TypeScript/wiki/Coding-guidelines)
## 8.2.1
* Added `--with-variables` option to the Sites command for adding/updating environment variables
* Fixed Functions environment variables not being pushed with `--with-variables`
* Removed `awaitPools` when wiping old variables
> **Note:** Storing environment variables in the `vars` attribute of `appwrite.json` is now deprecated due to security risks. Variables are now synced directly from the `.env` file in the root directory of the functions or sites folder.
## 8.2.0
* Add `encrypt` attribute support
* Add improved warnings on attribute recreation and deletion
* Fix `null` parsing error when using create attribute command
* Type generation fixes and improvements:
* Add `--strict` / `-s` flag to `appwrite types` command to generate types in strict mode. This automatically converts the casing of attributes to match the language's naming conventions
* Add automatic package import to `dart` language which uses package detection to import the correct package
* Add `Document` class extension to generated types in `dart` and `js` language to support internal attributes like `$id` and `$collectionId` etc.
* Add proper enum support to `js` language
* Fix indentation in `java`, `kotlin` and `swift` to use 2 spaces instead of 4 for consistency across all languages
* Fix doc comments to use correct syntax in various languages (for eg. `///` instead of `/*`)
* Update enums in `dart` to use lowerCamelCase in `strict` mode as per [constant_identifier_names](https://dart.dev/tools/diagnostics/constant_identifier_names?utm_source=dartdev&utm_medium=redir&utm_id=diagcode&utm_content=constant_identifier_names)
## 8.1.1
* Fix circular dependency issue due to usage of `success` method in `utils.js` file from `parser.js` file
* Type generation fixes:
* Add ability to generate types directly to a specific file by passing a file path to `appwrite types output_path`, instead of just a directory
* Fix non-required attributes to not be null if default value is provided
* Fix `Models` import error
* Improve formatting and add auto-generated comments
## 8.1.0
* Add multi-region support to `init` command
* Update `init` command to clear previous configuration in `appwrite.json`
* Update localConfig to store multi-region endpoint
* Fix throw error when creating unknown attribute instead of timing out
* Fix equal comparison of large numbers and BigNumber instances using proper equality checks
* Fix duplication of reasons when comparing localConfig with remoteConfig
* Fix `firstOrNull()` to `firstOrNull` in types generation for dart
* Refactor to use `isCloud()` method consistently
## 8.0.2
* Add Type generation fixes:
* Properly handle enum attributes in dart, java and kotlin
* Fix initialisation of null attributes in dart's fromMap method
* Fix relationships and enums in swift
## 8.0.1
* Add `resourceId` and `resourceType` attributes to `createRedirectRule`
* Add `providerReference` to vcs command for getting repository contents
* Add warning comment to `bulk updateDocuments` method
* Fix type generation for enums in Typescript and PHP language
## 8.0.0
* Add `types` command to generate language specific typings for collections. Currently supports - `php`, `swift`, `dart`, `js`, `ts`, `kotlin` and `java`
* Update bulk operation docs to include experiment feature warnings
* Remove assistant service and commands
## 7.0.0
* Add `sites` command
* Add `tokens` command
* Add `devKeys` support to `projects` command
* Add `init site`, `pull site` and `push site` commands
* Add bulk operation methods like `createDocuments`, `deleteDocuments` etc.
* Add new upsert methods: `upsertDocument` and `upsertDocuments`
* Update GET requests to not include content-type header
## 6.2.3
* Fix hot swapping error in `python-ml` function
## 6.2.2
* Fix GitHub builds by adding `qemu-system` package
* Fix attribute creation timed out
## 6.2.1
* Add `listOrganizations` method to `organizations` service and fix init project command
## 6.2.0
* Add specifications support to CLI
* Update package version
* Fix: Missed specifications param when updating a function
+8
View File
@@ -1,5 +1,13 @@
# Change Log
## 16.1.0
* Add `gif` support to `ImageFormat` enum
* Add support for `dart38` and `flutter332` runtimes
* Add `upsertDocument()` method to Databases service
* Fix `convertTo()` method in `Document` and `Preferences` models to correctly accept `Map<String, dynamic>`
* Fix `upsertDocuments()` method to have `documents` attribute as required parameter
## 16.0.0
* Add `<REGION>` to doc examples due to the new multi region endpoints
+63 -1
View File
@@ -1 +1,63 @@
# Change Log
# Change Log
## 0.14.0
* Refactor from Newtonsoft.Json to System.Text.Json for serialization/deserialization
* Update package dependencies in `Package.csproj.twig`
* Migrate all serialization/deserialization logic in `Client.cs.twig`, `Query.cs.twig`, and `Extensions.cs.twig`
* Update model attributes from `[JsonProperty]` to `[JsonPropertyName]` in `Model.cs.twig`
* Create new `ObjectToInferredTypesConverter.cs.twig` for proper object type handling
* Replace `JsonConverter` with `JsonConverter<object>` in `ValueClassConverter.cs.twig`
* Update error handling to use `JsonDocument` instead of `JObject`
## 0.13.0
* Add `<REGION>` to doc examples due to the new multi region endpoints
* Add doc examples and methods for bulk api transactions: `createDocuments`, `deleteDocuments` etc.
* Add doc examples, class and methods for new `Sites` service
* Add doc examples, class and methods for new `Tokens` service
* Add enums for `BuildRuntime `, `Adapter`, `Framework`, `DeploymentDownloadType` and `VCSDeploymentType`
* Update enum for `runtimes` with Pythonml312, Dart219, Flutter327 and Flutter329
* Add `token` param to `getFilePreview` and `getFileView` for File tokens usage
* Add `queries` and `search` params to `listMemberships` method
* Remove `search` param from `listExecutions` method
## 0.12.0
* fix: remove content-type from GET requests by @loks0n in https://github.com/appwrite/sdk-for-dotnet/pull/59
* update: min and max are not optional in methods like `UpdateIntegerAttribute` etc.
* chore: regenerate sdk by @ChiragAgg5k in https://github.com/appwrite/sdk-for-dotnet/pull/60
* chore: fix build error by @ChiragAgg5k in https://github.com/appwrite/sdk-for-dotnet/pull/61
## 0.11.0
* Add new push message parameters by @abnegate in https://github.com/appwrite/sdk-for-dotnet/pull/56
## 0.10.0
* fix: chunk upload by @byawitz in https://github.com/appwrite/sdk-for-dotnet/pull/52
## 0.9.0
* Support for Appwrite 1.6
* Added `key` attribute to `Runtime` response model.
* Added `buildSize` attribute to `Deployments` response model.
* Added `scheduledAt` attribute to `Executions` response model.
* Added `scopes` attribute to `Functions` response model.
* Added `specifications` attribute to `Functions` response model.
* Added new response model for `Specifications`.
* Added new response model for `Builds`.
* Added `createJWT()` : Enables creating a JWT using the `userId`.
* Added `listSpecifications()`: Enables listing available runtime specifications.
* Added `deleteExecution()` : Enables deleting executions.
* Added `updateDeploymentBuild()`: Enables cancelling a deployment.
* Added `scheduledAt` parameter to `createExecution()`: Enables creating a delayed execution
#### Breaking changes
You can find the new syntax for breaking changes in the [Appwrite API references](https://appwrite.io/docs/references). Select version `1.6.x`.
* Removed `otp` parameter from `deleteMFAAuthenticator`.
* Added `scopes` parameter for create/update function.
* Renamed `templateBranch` to `templateVersion` in `createFunction()`.
* Renamed `downloadDeployment()` to `getDeploymentDownload()`
> **Please note: This version is compatible with Appwrite 1.6 and later only. If you do not update your Appwrite SDK, old SDKs will not break your app. Appwrite APIs are backwards compatible.**
+12 -1
View File
@@ -1,9 +1,20 @@
# Change Log
## 16.1.1
## 17.0.2
* Add `gif` support to `ImageFormat` enum
* Fix `convertTo()` method in `Document` and `Preferences` models to correctly accept `Map<String, dynamic>`
## 17.0.1
* Fix `devKeys` support by conditionally including credentials during requests
## 17.0.0
* Update `flutter_web_auth_2` dependency to version 4.1.0
* Update `auth.html` example in README.md to align with `flutter_web_auth_2` documentation
* Breaking changes:
* Minimum iOS version supported is now 17.4 due to the updated requirements of `flutter_web_auth_2` version 4.1.0
## 16.1.0
+58 -3
View File
@@ -1,13 +1,68 @@
# Change Log
## 17.1.0
* Add `upsertDocument` method
* Add `dart-3.8` and `flutter-3.32` runtimes
* Add `gif` image format
* Update bulk operation methods to reflect warning message
* Fix file parameter handling in chunked upload method
## 17.0.0
* Add `<REGION>` to doc examples due to the new multi region endpoints
* Add `REGION` to doc examples due to the new multi region endpoints
* Add doc examples and methods for bulk api transactions: `createDocuments`, `deleteDocuments` etc.
* Add doc examples, class and methods for new `Sites` service
* Add doc examples, class and methods for new `Tokens` service
* Add enums for `BuildRuntime `, `Adapter`, `Framework`, `DeploymentDownloadType` and `VCSDeploymentType`
* Add enums for `BuildRuntime`, `Adapter`, `Framework`, `DeploymentDownloadType` and `VCSDeploymentType`
* Updates enum for `runtimes` with Pythonml312, Dart219, Flutter327 and Flutter329
* Add `token` param to `getFilePreview` and `getFileView` for File tokens usage
* Add `queries` and `search` params to `listMemberships` method
* Removes `search` param from `listExecutions` method
* Removes `search` param from `listExecutions` method
## 16.0.0
* Fix: remove content-type from GET requests
* Update (breaking): min and max params are now optional in `updateFloatAttribute` and `updateIntegerAttribute` methods (changes their positioning in method definition)
## 15.0.1
* Remove titles from all function descriptions
* Fix typing for collection "attribute" key
* Remove unnecessary awaits and asyncs
* Ensure `AppwriteException` response is always string
## 15.0.0
* Fix: pong response & chunked upload
## 14.2.0
* Add new push message parameters
## 14.1.0
* Support updating attribute name and size
## 14.0.0
* Support for Appwrite 1.6
* Add `key` attribute to `Runtime` response model.
* Add `buildSize` attribute to `Deployments` response model
* Add `scheduledAt` attribute to `Executions` response model
* Add `scopes` attribute to `Functions` response model
* Add `specifications` attribute to `Functions` response model
* Add new response model for `Specifications`
* Add new response model for `Builds`
* Add `createJWT()` : Enables creating a JWT using the `userId`
* Add `listSpecifications()`: Enables listing available runtime specifications
* Add `deleteExecution()` : Enables deleting executions
* Add `updateDeploymentBuild()`: Enables cancelling a deployment
* Add `scheduledAt` parameter to `createExecution()`: Enables creating a delayed execution
* Breaking changes
* Remove `otp` parameter from `deleteMFAAuthenticator`.
* Add `scopes` parameter for create/update function.
* Rename `templateBranch` to `templateVersion` in `createFunction()`.
* Rename `downloadDeployment()` to `getDeploymentDownload()`
> You can find the new syntax for breaking changes in the [Appwrite API references](https://appwrite.io/docs/references). Select version `1.6.x`.
+5
View File
@@ -1,5 +1,10 @@
# Change log
## 0.10.1
* Fix URL based methods like `getFileViewURL`, `getFilePreviewURL` etc. by adding the missing `projectId` to searchParams
* Add `gif` to ImageFormat enum
## 0.10.0
* Add generate file URL methods like`getFilePreviewURL`, `getFileViewURL` etc.
+10
View File
@@ -54,6 +54,16 @@ class Database extends Event
return $this;
}
/**
* Returns set database for this event.
*
* @return null|Document
*/
public function getDatabase(): ?Document
{
return $this->database;
}
/**
* Set the collection for this database event.
*
+21 -18
View File
@@ -226,7 +226,7 @@ class Mapper
];
if (!$rule['required']) {
$fields[$escapedKey]['defaultValue'] = $rule['default'];
$fields[$escapedKey]['defaultValue'] = $rule['default'] ?? null;
}
}
@@ -267,48 +267,51 @@ class Mapper
}
switch ((!empty($validator)) ? $validator::class : '') {
case 'Appwrite\Network\Validator\CNAME':
case 'Appwrite\Task\Validator\Cron':
case 'Appwrite\Utopia\Database\Validator\CustomId':
case 'Utopia\Validator\Domain':
case 'Appwrite\Network\Validator\Email':
case 'Appwrite\Auth\Validator\Password':
case 'Appwrite\Event\Validator\Event':
case 'Appwrite\Event\Validator\FunctionEvent':
case 'Appwrite\Network\Validator\CNAME':
case 'Appwrite\Network\Validator\Email':
case 'Appwrite\Network\Validator\Redirect':
case 'Appwrite\Network\Validator\DNS':
case 'Appwrite\Network\Validator\Origin':
case 'Appwrite\Task\Validator\Cron':
case 'Appwrite\Utopia\Database\Validator\CustomId':
case 'Utopia\Database\Validator\Key':
case 'Utopia\Database\Validator\UID':
case 'Utopia\Validator\Domain':
case 'Utopia\Validator\HexColor':
case 'Utopia\Validator\Host':
case 'Utopia\Validator\IP':
case 'Utopia\Database\Validator\Key':
case 'Utopia\Validator\Origin':
case 'Appwrite\Auth\Validator\Password':
case 'Utopia\Validator\Text':
case 'Utopia\Database\Validator\UID':
case 'Utopia\Validator\URL':
case 'Utopia\Validator\WhiteList':
default:
$type = Type::string();
break;
case 'Utopia\Database\Validator\Authorization':
case 'Appwrite\Utopia\Database\Validator\Queries\Attributes':
case 'Appwrite\Utopia\Database\Validator\Queries\Base':
case 'Appwrite\Utopia\Database\Validator\Queries\Buckets':
case 'Appwrite\Utopia\Database\Validator\Queries\Collections':
case 'Appwrite\Utopia\Database\Validator\Queries\Attributes':
case 'Appwrite\Utopia\Database\Validator\Queries\Indexes':
case 'Appwrite\Utopia\Database\Validator\Queries\Databases':
case 'Appwrite\Utopia\Database\Validator\Queries\Deployments':
case 'Appwrite\Utopia\Database\Validator\Queries\Installations':
case 'Utopia\Database\Validator\Queries\Documents':
case 'Appwrite\Utopia\Database\Validator\Queries\Executions':
case 'Appwrite\Utopia\Database\Validator\Queries\Files':
case 'Appwrite\Utopia\Database\Validator\Queries\Functions':
case 'Appwrite\Utopia\Database\Validator\Queries\Rules':
case 'Appwrite\Utopia\Database\Validator\Queries\Indexes':
case 'Appwrite\Utopia\Database\Validator\Queries\Installations':
case 'Appwrite\Utopia\Database\Validator\Queries\Memberships':
case 'Utopia\Database\Validator\Permissions':
case 'Appwrite\Utopia\Database\Validator\Queries\Projects':
case 'Utopia\Database\Validator\Queries':
case 'Utopia\Database\Validator\Roles':
case 'Appwrite\Utopia\Database\Validator\Queries\Rules':
case 'Appwrite\Utopia\Database\Validator\Queries\Teams':
case 'Appwrite\Utopia\Database\Validator\Queries\Users':
case 'Appwrite\Utopia\Database\Validator\Queries\Variables':
case 'Utopia\Database\Validator\Authorization':
case 'Utopia\Database\Validator\Permissions':
case 'Utopia\Database\Validator\Queries':
case 'Utopia\Database\Validator\Queries\Documents':
case 'Utopia\Database\Validator\Roles':
$type = Type::listOf(Type::string());
break;
case 'Utopia\Validator\Boolean':
+159
View File
@@ -0,0 +1,159 @@
<?php
namespace Appwrite\Network;
class Platform
{
public const TYPE_UNKNOWN = 'unknown';
public const TYPE_WEB = 'web';
public const TYPE_FLUTTER_IOS = 'flutter-ios';
public const TYPE_FLUTTER_ANDROID = 'flutter-android';
public const TYPE_FLUTTER_MACOS = 'flutter-macos';
public const TYPE_FLUTTER_WINDOWS = 'flutter-windows';
public const TYPE_FLUTTER_LINUX = 'flutter-linux';
public const TYPE_FLUTTER_WEB = 'flutter-web';
public const TYPE_APPLE_IOS = 'apple-ios';
public const TYPE_APPLE_MACOS = 'apple-macos';
public const TYPE_APPLE_WATCHOS = 'apple-watchos';
public const TYPE_APPLE_TVOS = 'apple-tvos';
public const TYPE_ANDROID = 'android';
public const TYPE_UNITY = 'unity';
public const TYPE_REACT_NATIVE_IOS = 'react-native-ios';
public const TYPE_REACT_NATIVE_ANDROID = 'react-native-android';
public const TYPE_SCHEME = 'scheme';
public const SCHEME_HTTP = 'http';
public const SCHEME_HTTPS = 'https';
public const SCHEME_CHROME_EXTENSION = 'chrome-extension';
public const SCHEME_FIREFOX_EXTENSION = 'moz-extension';
public const SCHEME_SAFARI_EXTENSION = 'safari-web-extension';
public const SCHEME_EDGE_EXTENSION = 'ms-browser-extension';
public const SCHEME_IOS = 'appwrite-ios';
public const SCHEME_MACOS = 'appwrite-macos';
public const SCHEME_WATCHOS = 'appwrite-watchos';
public const SCHEME_TVOS = 'appwrite-tvos';
public const SCHEME_ANDROID = 'appwrite-android';
public const SCHEME_WINDOWS = 'appwrite-windows';
public const SCHEME_LINUX = 'appwrite-linux';
/**
* @var array<string, string> Map scheme types to user-friendly platform names.
*/
private static array $names = [
self::SCHEME_HTTP => 'Web',
self::SCHEME_HTTPS => 'Web',
self::SCHEME_IOS => 'iOS',
self::SCHEME_MACOS => 'macOS',
self::SCHEME_WATCHOS => 'watchOS',
self::SCHEME_TVOS => 'tvOS',
self::SCHEME_ANDROID => 'Android',
self::SCHEME_WINDOWS => 'Windows',
self::SCHEME_LINUX => 'Linux',
self::SCHEME_CHROME_EXTENSION => 'Web (Chrome Extension)',
self::SCHEME_FIREFOX_EXTENSION => 'Web (Firefox Extension)',
self::SCHEME_SAFARI_EXTENSION => 'Web (Safari Extension)',
self::SCHEME_EDGE_EXTENSION => 'Web (Edge Extension)',
];
/**
* Get user-friendly platform name from a scheme.
*
* @param string|null $scheme
* @return string Empty string if scheme is not found.
*/
public static function getNameByScheme(?string $scheme): string
{
return self::$names[$scheme] ?? '';
}
public static function getHostnames(array $platforms): array
{
$hostnames = [];
foreach ($platforms as $platform) {
$type = strtolower($platform['type'] ?? self::TYPE_UNKNOWN);
$hostname = strtolower($platform['hostname'] ?? '');
$key = strtolower($platform['key'] ?? '');
switch ($type) {
case self::TYPE_WEB:
case self::TYPE_FLUTTER_WEB:
if (!empty($hostname)) {
$hostnames[] = $hostname;
}
break;
case self::TYPE_FLUTTER_IOS:
case self::TYPE_FLUTTER_ANDROID:
case self::TYPE_FLUTTER_MACOS:
case self::TYPE_FLUTTER_WINDOWS:
case self::TYPE_FLUTTER_LINUX:
case self::TYPE_ANDROID:
case self::TYPE_APPLE_IOS:
case self::TYPE_APPLE_MACOS:
case self::TYPE_APPLE_WATCHOS:
case self::TYPE_APPLE_TVOS:
case self::TYPE_REACT_NATIVE_IOS:
case self::TYPE_REACT_NATIVE_ANDROID:
case self::TYPE_UNITY:
if (!empty($key)) {
$hostnames[] = $key;
}
break;
default:
break;
}
}
return array_unique($hostnames);
}
public static function getSchemes(array $platforms): array
{
$schemes = [];
foreach ($platforms as $platform) {
$type = strtolower($platform['type'] ?? self::TYPE_UNKNOWN);
$scheme = strtolower($platform['key'] ?? '');
switch ($type) {
case self::TYPE_SCHEME:
if (!empty($scheme) && preg_match('/^[a-z][a-z0-9+.-]*$/', $scheme)) {
$schemes[] = $scheme;
}
break;
case self::TYPE_WEB:
case self::TYPE_FLUTTER_WEB:
$schemes[] = self::SCHEME_HTTP;
$schemes[] = self::SCHEME_HTTPS;
break;
case self::TYPE_FLUTTER_IOS:
case self::TYPE_APPLE_IOS:
case self::TYPE_REACT_NATIVE_IOS:
$schemes[] = self::SCHEME_IOS;
break;
case self::TYPE_FLUTTER_ANDROID:
case self::TYPE_ANDROID:
case self::TYPE_REACT_NATIVE_ANDROID:
$schemes[] = self::SCHEME_ANDROID;
break;
case self::TYPE_FLUTTER_MACOS:
case self::TYPE_APPLE_MACOS:
$schemes[] = self::SCHEME_MACOS;
break;
case self::TYPE_FLUTTER_WINDOWS:
case self::TYPE_UNITY:
$schemes[] = self::SCHEME_WINDOWS;
break;
case self::TYPE_FLUTTER_LINUX:
$schemes[] = self::SCHEME_LINUX;
break;
case self::TYPE_APPLE_WATCHOS:
$schemes[] = self::SCHEME_WATCHOS;
break;
case self::TYPE_APPLE_TVOS:
$schemes[] = self::SCHEME_TVOS;
break;
default:
break;
}
}
return array_unique($schemes);
}
}
+80 -117
View File
@@ -2,150 +2,91 @@
namespace Appwrite\Network\Validator;
use Appwrite\Network\Platform;
use Utopia\Validator;
use Utopia\Validator\Hostname;
class Origin extends Validator
{
public const CLIENT_TYPE_UNKNOWN = 'unknown';
public const CLIENT_TYPE_WEB = 'web';
public const CLIENT_TYPE_FLUTTER_IOS = 'flutter-ios';
public const CLIENT_TYPE_FLUTTER_ANDROID = 'flutter-android';
public const CLIENT_TYPE_FLUTTER_MACOS = 'flutter-macos';
public const CLIENT_TYPE_FLUTTER_WINDOWS = 'flutter-windows';
public const CLIENT_TYPE_FLUTTER_LINUX = 'flutter-linux';
public const CLIENT_TYPE_FLUTTER_WEB = 'flutter-web';
public const CLIENT_TYPE_APPLE_IOS = 'apple-ios';
public const CLIENT_TYPE_APPLE_MACOS = 'apple-macos';
public const CLIENT_TYPE_APPLE_WATCHOS = 'apple-watchos';
public const CLIENT_TYPE_APPLE_TVOS = 'apple-tvos';
public const CLIENT_TYPE_ANDROID = 'android';
public const CLIENT_TYPE_UNITY = 'unity';
public const CLIENT_TYPE_REACT_NATIVE_IOS = 'react-native-ios';
public const CLIENT_TYPE_REACT_NATIVE_ANDROID = 'react-native-android';
public const SCHEME_TYPE_HTTP = 'http';
public const SCHEME_TYPE_HTTPS = 'https';
public const SCHEME_TYPE_IOS = 'appwrite-ios';
public const SCHEME_TYPE_MACOS = 'appwrite-macos';
public const SCHEME_TYPE_WATCHOS = 'appwrite-watchos';
public const SCHEME_TYPE_TVOS = 'appwrite-tvos';
public const SCHEME_TYPE_ANDROID = 'appwrite-android';
public const SCHEME_TYPE_WINDOWS = 'appwrite-windows';
public const SCHEME_TYPE_LINUX = 'appwrite-linux';
protected array $hostnames = [];
protected array $schemes = [];
protected ?string $scheme = null;
protected ?string $host = null;
protected string $origin = '';
/**
* @var array
* Constructor
*
* @param array<\Utopia\Database\Document> $platforms
*/
protected $platforms = [
self::SCHEME_TYPE_HTTP => 'Web',
self::SCHEME_TYPE_HTTPS => 'Web',
self::SCHEME_TYPE_IOS => 'iOS',
self::SCHEME_TYPE_MACOS => 'macOS',
self::SCHEME_TYPE_WATCHOS => 'watchOS',
self::SCHEME_TYPE_TVOS => 'tvOS',
self::SCHEME_TYPE_ANDROID => 'Android',
self::SCHEME_TYPE_WINDOWS => 'Windows',
self::SCHEME_TYPE_LINUX => 'Linux',
];
/**
* @var array
*/
protected $clients = [
];
/**
* @var string
*/
protected $client = self::CLIENT_TYPE_UNKNOWN;
/**
* @var string
*/
protected $host = '';
/**
* @param string $target
*/
public function __construct($platforms)
public function __construct(array $platforms)
{
foreach ($platforms as $platform) {
$type = (isset($platform['type'])) ? $platform['type'] : '';
switch ($type) {
case self::CLIENT_TYPE_WEB:
case self::CLIENT_TYPE_FLUTTER_WEB:
$this->clients[] = (isset($platform['hostname'])) ? $platform['hostname'] : '';
break;
case self::CLIENT_TYPE_FLUTTER_IOS:
case self::CLIENT_TYPE_FLUTTER_ANDROID:
case self::CLIENT_TYPE_FLUTTER_MACOS:
case self::CLIENT_TYPE_FLUTTER_WINDOWS:
case self::CLIENT_TYPE_FLUTTER_LINUX:
case self::CLIENT_TYPE_ANDROID:
case self::CLIENT_TYPE_APPLE_IOS:
case self::CLIENT_TYPE_APPLE_MACOS:
case self::CLIENT_TYPE_APPLE_WATCHOS:
case self::CLIENT_TYPE_APPLE_TVOS:
case self::CLIENT_TYPE_REACT_NATIVE_IOS:
case self::CLIENT_TYPE_REACT_NATIVE_ANDROID:
$this->clients[] = (isset($platform['key'])) ? $platform['key'] : '';
break;
default:
# code...
break;
}
}
$this->hostnames = Platform::getHostnames($platforms);
$this->schemes = Platform::getSchemes($platforms);
}
public function getDescription(): string
{
if (!\array_key_exists($this->client, $this->platforms)) {
return 'Unsupported platform';
}
return 'Invalid Origin. Register your new client (' . $this->host . ') as a new '
. $this->platforms[$this->client] . ' platform on your project console dashboard';
}
/**
* Check if Origin has been allowed
* for access to the API
*
* @param mixed $origin
*
* Check if Origin is valid.
* @param mixed $origin The Origin URI.
* @return bool
*/
public function isValid($origin): bool
{
if (!is_string($origin)) {
$this->origin = $origin;
$this->scheme = null;
$this->host = null;
if (!is_string($origin) || empty($origin)) {
return false;
}
$scheme = \parse_url($origin, PHP_URL_SCHEME);
$host = \parse_url($origin, PHP_URL_HOST);
$this->scheme = $this->parseScheme($origin);
$this->host = strtolower(parse_url($origin, PHP_URL_HOST) ?? '');
$this->host = $host;
$this->client = $scheme;
$webPlatforms = [
Platform::SCHEME_HTTP,
Platform::SCHEME_HTTPS,
Platform::SCHEME_CHROME_EXTENSION,
Platform::SCHEME_FIREFOX_EXTENSION,
Platform::SCHEME_SAFARI_EXTENSION,
Platform::SCHEME_EDGE_EXTENSION,
];
if (in_array($this->scheme, $webPlatforms, true)) {
$validator = new Hostname($this->hostnames);
return $validator->isValid($this->host);
}
if (empty($host)) {
if (!empty($this->scheme) && in_array($this->scheme, $this->schemes, true)) {
return true;
}
$validator = new Hostname($this->clients);
return false;
}
return $validator->isValid($host);
/**
* Get Description
* @return string
*/
public function getDescription(): string
{
$platform = $this->scheme ? Platform::getNameByScheme($this->scheme) : '';
$host = $this->host ? '(' . $this->host . ')' : '';
if (empty($this->host) && empty($this->scheme)) {
return 'Invalid Origin.';
}
if (empty($platform)) {
return 'Invalid Scheme. The scheme used (' . $this->scheme . ') in the Origin (' . $this->origin . ') is not supported. If you are using a custom scheme, please change it to `appwrite-callback-<PROJECT_ID>`';
}
return 'Invalid Origin. Register your new client ' . $host . ' as a new '
. $platform . ' platform on your project console dashboard';
}
/**
* Is array
*
* Function will return true if object is array.
*
* @return bool
*/
public function isArray(): bool
@@ -155,13 +96,35 @@ class Origin extends Validator
/**
* Get Type
*
* Returns validator type.
*
* @return string
*/
public function getType(): string
{
return self::TYPE_STRING;
}
/**
* Parses the scheme from a URI string.
*
* @param string $uri The URI string to parse.
* @return string|null The extracted scheme string (e.g., "http", "exp", "mailto")
*/
public function parseScheme(string $uri): ?string
{
$uri = trim($uri);
if ($uri === '') {
return null; // No scheme in empty string
}
$scheme = parse_url($uri, PHP_URL_SCHEME);
if ($scheme === false) {
if (preg_match('/^([a-z][a-z0-9+.-]*):/i', $uri, $matches)) {
return $matches[1];
} else {
return null;
}
} else {
return $scheme;
}
}
}
@@ -0,0 +1,25 @@
<?php
namespace Appwrite\Network\Validator;
use Appwrite\Network\Platform;
class Redirect extends Origin
{
/**
* Get Description
* @return string
*/
public function getDescription(): string
{
$platform = Platform::getNameByScheme($this->scheme);
$host = $this->host ? '(' . $this->host . ')' : '';
if (empty($this->host) && empty($this->scheme)) {
return 'Invalid URI.';
}
return 'Invalid URI. Register your new client ' . $host . ' as a new '
. $platform . ' platform on your project console dashboard';
}
}
+62
View File
@@ -3,7 +3,10 @@
namespace Appwrite\Platform;
use Swoole\Coroutine as Co;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Platform\Action as UtopiaAction;
@@ -16,6 +19,12 @@ class Action extends UtopiaAction
*/
protected mixed $logError;
protected array $filters = [
'subQueryKeys', 'subQueryWebhooks', 'subQueryPlatforms', 'subQueryProjectVariables', 'subQueryBlocks', 'subQueryDevKeys', // Project
'subQueryAuthenticators', 'subQuerySessions', 'subQueryTokens', 'subQueryChallenges', 'subQueryMemberships', 'subQueryTargets', 'subQueryTopicTargets',// Users
'subQueryVariables', // Sites
];
/**
* Foreach Document
* Call provided callback for each document in the collection
@@ -87,4 +96,57 @@ class Action extends UtopiaAction
$latestDocument = $results[array_key_last($results)];
}
}
public function disableSubqueries()
{
$filters = $this->filters;
foreach ($filters as $filter) {
Database::addFilter(
$filter,
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return [];
}
);
}
}
/**
* Dump Log Message
*
* Logs messages to console with timestamp, method context, and project details.
* Supports multiple log types: success, error, log, warning, and info (default).
*
* @param string $method The calling method name
* @param string $log The log message
* @param string $type The log type (success, error, log, warning, info)
* @param Document|null $project The project document for context
* @param string $collectionId The collection identifier
* @return void
*/
public function dump(string $method, string $log, string $type = 'info', ?Document $project = null, string $collectionId = ''): void
{
if (empty($project)) {
$project = new Document([]);
}
switch ($type) {
case 'success':
Console::success("[" . DateTime::now() . "] " . $method . ' ' . $type . ' ' . $project->getSequence() . ' ' . $project->getId() . ' ' . $collectionId . ' ' . $log);
break;
case 'error':
Console::error("[" . DateTime::now() . "] " . $method . ' ' . $type . ' ' . $project->getSequence() . ' ' . $project->getId() . ' ' . $collectionId . ' ' . $log);
break;
case 'log':
Console::log("[" . DateTime::now() . "] " . $method . ' ' . $type . ' ' . $project->getSequence() . ' ' . $project->getId() . ' ' . $collectionId . ' ' . $log);
break;
case 'warning':
Console::warning("[" . DateTime::now() . "] " . $method . ' ' . $type . ' ' . $project->getSequence() . ' ' . $project->getId() . ' ' . $collectionId . ' ' . $log);
break;
default:
Console::info("[" . DateTime::now() . "] " . $method . ' ' . $type . ' ' . $project->getSequence() . ' ' . $project->getId() . ' ' . $collectionId . ' ' . $log);
}
}
}
@@ -6,6 +6,7 @@ use Appwrite\Event\Build;
use Appwrite\Extend\Exception;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
@@ -226,6 +227,73 @@ class Base extends Action
]))
);
if (!empty($commitDetails['commitHash'])) {
$domain = "commit-" . substr($commitDetails['commitHash'], 0, 16) . ".{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
'projectInternalId' => $project->getSequence(),
'domain' => $domain,
'type' => 'deployment',
'trigger' => 'deployment',
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getSequence(),
'deploymentResourceType' => 'site',
'deploymentResourceId' => $site->getId(),
'deploymentResourceInternalId' => $site->getSequence(),
'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 branch preview
if (!empty($providerBranch)) {
$branchPrefix = substr($providerBranch, 0, 16);
if (strlen($providerBranch) > 16) {
$remainingChars = substr($providerBranch, 16);
$branchPrefix .= '-' . substr(hash('sha256', $remainingChars), 0, 7);
}
$resourceProjectHash = substr(hash('sha256', $site->getId() . $project->getId()), 0, 7);
$domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
'projectInternalId' => $project->getSequence(),
'domain' => $domain,
'type' => 'deployment',
'trigger' => 'deployment',
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getSequence(),
'deploymentResourceType' => 'site',
'deploymentResourceId' => $site->getId(),
'deploymentResourceInternalId' => $site->getSequence(),
'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
}
}
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($site)
@@ -57,7 +57,7 @@ class Get extends Action
->param('type', '', new WhiteList(['rules']), 'Resource type.')
->inject('response')
->inject('dbForPlatform')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -87,7 +87,7 @@ class Create extends Action
->inject('deviceForLocal')
->inject('queueForBuilds')
->inject('plan')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -62,7 +62,7 @@ class Delete extends Action
->inject('queueForDeletes')
->inject('queueForEvents')
->inject('deviceForFunctions')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -61,7 +61,7 @@ class Get extends Action
->inject('dbForProject')
->inject('deviceForFunctions')
->inject('deviceForBuilds')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -62,7 +62,7 @@ class Create extends Action
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('deviceForFunctions')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -49,7 +49,7 @@ class Get extends Action
->param('deploymentId', '', new UID(), 'Deployment ID.')
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -59,7 +59,7 @@ class Update extends Action
->inject('project')
->inject('queueForEvents')
->inject('executor')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -75,7 +75,7 @@ class Create extends Base
->inject('project')
->inject('queueForBuilds')
->inject('gitHub')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -72,7 +72,7 @@ class Create extends Base
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('gitHub')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -57,7 +57,7 @@ class XList extends Action
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -94,7 +94,7 @@ class Create extends Base
->inject('queueForFunctions')
->inject('geodb')
->inject('executor')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -62,7 +62,7 @@ class Delete extends Base
->inject('dbForProject')
->inject('dbForPlatform')
->inject('queueForEvents')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -52,7 +52,7 @@ class Get extends Base
->param('executionId', '', new UID(), 'Execution ID.')
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -58,7 +58,7 @@ class XList extends Base
->param('queries', [], new Executions(), '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(', ', Executions::ALLOWED_ATTRIBUTES), true)
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -114,7 +114,7 @@ class Create extends Base
->inject('dbForPlatform')
->inject('request')
->inject('gitHub')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -61,7 +61,7 @@ class Delete extends Base
->inject('queueForDeletes')
->inject('queueForEvents')
->inject('dbForPlatform')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -62,7 +62,7 @@ class Update extends Base
->inject('dbForProject')
->inject('queueForEvents')
->inject('dbForPlatform')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -49,7 +49,7 @@ class Get extends Base
->param('functionId', '', new UID(), 'Function ID.')
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -104,7 +104,7 @@ class Update extends Base
->inject('dbForPlatform')
->inject('gitHub')
->inject('executor')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -56,7 +56,7 @@ class XList extends Base
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -47,7 +47,7 @@ class XList extends Base
]
))
->inject('response')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(Response $response)
@@ -48,7 +48,7 @@ class XList extends Base
))
->inject('response')
->inject('plan')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(Response $response, array $plan)
@@ -63,8 +63,8 @@ class XList extends Base
$spec['enabled'] = in_array($spec['slug'], $plan['runtimeSpecifications']);
}
$maxCpus = System::getEnv('_APP_FUNCTIONS_CPUS', 0);
$maxMemory = System::getEnv('_APP_FUNCTIONS_MEMORY', 0);
$maxCpus = System::getEnv('_APP_COMPUTE_CPUS', 0);
$maxMemory = System::getEnv('_APP_COMPUTE_MEMORY', 0);
// Only add specs that are within the limits set by environment variables
// Treat 0 as no limit
@@ -49,7 +49,7 @@ class Get extends Base
))
->param('templateId', '', new Text(128), 'Template ID.')
->inject('response')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $templateId, Response $response)
@@ -53,7 +53,7 @@ class XList extends Base
->param('limit', 25, new Range(1, 5000), 'Limit the number of templates returned in the response. Default limit is 25, and maximum limit is 5000.', true)
->param('offset', 0, new Range(0, 5000), 'Offset the list of returned templates. Maximum offset is 5000.', true)
->inject('response')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(array $runtimes, array $usecases, int $limit, int $offset, Response $response)
@@ -55,7 +55,7 @@ class Get extends Base
->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $functionId, string $range, Response $response, Database $dbForProject)
@@ -52,7 +52,7 @@ class XList extends Base
->param('range', '30d', new WhiteList(['24h', '30d', '90d']), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $range, Response $response, Database $dbForProject)
@@ -65,7 +65,7 @@ class Create extends Base
->inject('dbForProject')
->inject('dbForPlatform')
->inject('project')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -57,7 +57,7 @@ class Delete extends Base
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -53,7 +53,7 @@ class Get extends Base
->param('variableId', '', new UID(), 'Variable unique ID.', false)
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $functionId, string $variableId, Response $response, Database $dbForProject)
@@ -61,7 +61,7 @@ class Update extends Base
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -53,7 +53,7 @@ class XList extends Base
->param('functionId', '', new UID(), 'Function unique ID.', false)
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $functionId, Response $response, Database $dbForProject)
@@ -75,7 +75,7 @@ class Builds extends Action
->inject('log')
->inject('executor')
->inject('plan')
->callback([$this, 'action']);
->callback($this->action(...));
}
/**
@@ -209,6 +209,9 @@ class Builds extends Action
Executor $executor,
array $plan
): void {
$startTime = DateTime::now();
$durationStart = \microtime(true);
$resourceKey = match ($resource->getCollection()) {
'functions' => 'functionId',
'sites' => 'siteId',
@@ -260,11 +263,8 @@ class Builds extends Action
->setParam($resourceKey, $resource->getId())
->setParam('deploymentId', $deployment->getId());
$startTime = DateTime::now();
$durationStart = \microtime(true);
if ($deployment->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
return;
}
@@ -275,8 +275,7 @@ class Builds extends Action
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) {
$resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
$resource = $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document(['latestDeploymentStatus' => $deployment->getAttribute('status', '')]));
}
$queueForRealtime
@@ -392,7 +391,7 @@ class Builds extends Action
Console::execute('mkdir -p ' . \escapeshellarg('/tmp/builds/' . $deploymentId), '', $stdout, $stderr);
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
return;
}
@@ -445,7 +444,7 @@ class Builds extends Action
Console::execute('rsync -av --exclude \'.git\' ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory . '/') . ' ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $stdout, $stderr);
// Commit and push
$exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . \escapeshellarg($tmpDirectory) . ' && git add . && git commit -m "Create ' . \escapeshellarg($resource->getAttribute('name', '')) . ' function" && git push origin ' . \escapeshellarg($branchName), '', $stdout, $stderr);
$exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . \escapeshellarg($tmpDirectory) . ' && git checkout -b ' . \escapeshellarg($branchName) . ' && git add . && git commit -m "Create ' . \escapeshellarg($resource->getAttribute('name', '')) . ' function" && git push origin ' . \escapeshellarg($branchName), '', $stdout, $stderr);
if ($exit !== 0) {
throw new \Exception('Unable to push code repository: ' . $stderr);
@@ -525,8 +524,7 @@ class Builds extends Action
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) {
$resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
$resource = $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document(['latestDeploymentStatus' => $deployment->getAttribute('status', '')]));
}
$queueForRealtime
@@ -658,7 +656,7 @@ class Builds extends Action
$err = null;
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
return;
}
@@ -747,6 +745,13 @@ class Builds extends Action
if ($separator !== false) {
$logs = \substr($logs, 0, $separator);
$insideSeparation = true;
$leftover = \substr($logs, $separator + strlen('{APPWRITE_DETECTION_SEPARATOR_START}'));
$separator = \strpos($leftover, '{APPWRITE_DETECTION_SEPARATOR_END}');
if ($separator !== false) {
$logs .= \substr($leftover, $separator + strlen('{APPWRITE_DETECTION_SEPARATOR_END}'));
$insideSeparation = false;
}
}
} else {
$separator = \strpos($logs, '{APPWRITE_DETECTION_SEPARATOR_END}');
@@ -802,7 +807,7 @@ class Builds extends Action
]);
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
return;
}
@@ -810,9 +815,6 @@ class Builds extends Action
throw $err;
}
$endTime = DateTime::now();
$durationEnd = \microtime(true);
$buildSizeLimit = (int)System::getEnv('_APP_COMPUTE_BUILD_SIZE_LIMIT', '2000000000');
if (isset($plan['buildSize'])) {
$buildSizeLimit = $plan['buildSize'] * 1000 * 1000;
@@ -821,10 +823,6 @@ class Builds extends Action
throw new \Exception('Build size should be less than ' . number_format($buildSizeLimit / (1000 * 1000), 2) . ' MBs.');
}
/** Update the build document */
$deployment->setAttribute('buildStartedAt', DateTime::format((new \DateTime())->setTimestamp(floor($response['startTime']))));
$deployment->setAttribute('buildEndedAt', $endTime);
$deployment->setAttribute('buildDuration', \intval(\ceil($durationEnd - $durationStart)));
$deployment->setAttribute('buildPath', $response['path']);
$deployment->setAttribute('buildSize', $response['size']);
$deployment->setAttribute('totalSize', $deployment->getAttribute('buildSize', 0) + $deployment->getAttribute('sourceSize', 0));
@@ -836,18 +834,10 @@ class Builds extends Action
// Separate logs for SSR detection
$detectionLogs = '';
$separator = \strpos($logs, '{APPWRITE_DETECTION_SEPARATOR_START}');
if ($separator !== false) {
$detectionLogs = \substr($logs, $separator + strlen('{APPWRITE_DETECTION_SEPARATOR}'));
$separatorEnd = \strpos($detectionLogs, '{APPWRITE_DETECTION_SEPARATOR_END}');
$logs .= \substr($detectionLogs, $separatorEnd + strlen('{APPWRITE_DETECTION_SEPARATOR_END}'));
$detectionLogs = \substr($detectionLogs, 0, $separatorEnd);
$logs = \substr($logs, 0, $separator);
}
if ($resource->getCollection() === 'sites') {
$date = \date('H:i:s');
$logs .= "[$date] [appwrite] Screenshot capturing started. \n";
if (\str_contains($logs, '{APPWRITE_DETECTION_SEPARATOR_START}')) {
[$logsBefore, $detectionLogsStart] = \explode('{APPWRITE_DETECTION_SEPARATOR_START}', $logs, 2);
[$detectionLogs, $logsAfter] = \explode('{APPWRITE_DETECTION_SEPARATOR_END}', $detectionLogsStart, 2);
$logs = ($logsBefore ?? '') . ($logsAfter ?? '');
}
$deployment->setAttribute('buildLogs', $logs);
@@ -866,9 +856,7 @@ class Builds extends Action
$adapter = $resource->getAttribute('adapter', '');
if (empty($adapter)) {
$resource->setAttribute('adapter', $detection->getName());
$resource->setAttribute('fallbackFile', $detection->getFallbackFile() ?? '');
$resource = $dbForProject->updateDocument('sites', $resource->getId(), $resource);
$resource = $dbForProject->updateDocument('sites', $resource->getId(), new Document(['adapter' => $detection->getName(), 'fallbackFile' => $detection->getFallbackFile() ?? '']));
$deployment->setAttribute('adapter', $detection->getName());
$deployment->setAttribute('fallbackFile', $detection->getFallbackFile() ?? '');
@@ -877,16 +865,24 @@ class Builds extends Action
}
}
$deployment->setAttribute('buildLogs', $logs);
$this->afterBuildSuccess($dbForProject, $deployment);
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
$queueForRealtime
->setPayload($deployment->getArrayCopy())
->trigger();
$this->afterBuildSuccess($queueForRealtime, $dbForProject, $deployment);
$logs = $deployment->getAttribute('buildLogs', '');
if ($resource->getCollection() === 'sites') {
$date = \date('H:i:s');
$logs .= "[$date] [appwrite] Screenshot capturing started. \n";
$deployment->setAttribute('buildLogs', $logs);
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
$queueForRealtime
->setPayload($deployment->getArrayCopy())
->trigger();
}
/** Screenshot site */
if ($resource->getCollection() === 'sites') {
try {
@@ -959,8 +955,9 @@ class Builds extends Action
$config['sleep'] = $framework['screenshotSleep'];
}
$browserEndpoint = Config::getParam('_APP_BROWSER_HOST', 'http://appwrite-browser:3000/v1');
$fetchResponse = $client->fetch(
url: 'http://appwrite-browser:3000/v1/screenshots',
url: $browserEndpoint . '/screenshots',
method: 'POST',
body: $config
);
@@ -1062,8 +1059,7 @@ class Builds extends Action
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) {
$resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
$resource = $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document(['latestDeploymentStatus' => $deployment->getAttribute('status', '')]));
}
$queueForRealtime
@@ -1077,14 +1073,38 @@ class Builds extends Action
Console::success("Build id: $deploymentId created");
/** Set auto deploy */
$activateBuild = false;
if ($deployment->getAttribute('activate') === true) {
$resource->setAttribute('live', true);
// Check if current active deployment started later than this deployment
$resource = $dbForProject->getDocument($resource->getCollection(), $resource->getId());
$currentActiveDeploymentId = $resource->getAttribute('deploymentId', '');
if (!empty($currentActiveDeploymentId)) {
$currentActiveDeployment = $dbForProject->getDocument('deployments', $currentActiveDeploymentId);
if (!$currentActiveDeployment->isEmpty()) {
$currentActiveStartTime = $currentActiveDeployment->getCreatedAt();
$deploymentStartTime = $deployment->getCreatedAt();
// Skip auto-activation if current active deployment started later than deployment that is being activated
if ($currentActiveStartTime < $deploymentStartTime) {
$activateBuild = true;
} else {
Console::info('Skipping auto-activation as current deployment is more recent');
}
}
} else {
$activateBuild = true;
}
}
if ($activateBuild) {
switch ($resource->getCollection()) {
case 'functions':
$resource->setAttribute('deploymentId', $deployment->getId());
$resource->setAttribute('deploymentInternalId', $deployment->getSequence());
$resource->setAttribute('deploymentCreatedAt', $deployment->getCreatedAt());
$resource = $dbForProject->updateDocument('functions', $resource->getId(), $resource);
$resource = $dbForProject->updateDocument('functions', $resource->getId(), new Document([
'live' => true,
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getSequence(),
'deploymentCreatedAt' => $deployment->getCreatedAt(),
]));
$queries = [
Query::equal('projectInternalId', [$project->getSequence()]),
@@ -1098,19 +1118,21 @@ class Builds extends Action
$rulesUpdated = false;
$dbForPlatform->forEach('rules', function (Document $rule) use ($dbForPlatform, $deployment, &$rulesUpdated) {
$rulesUpdated = true;
$rule = $rule
->setAttribute('deploymentId', $deployment->getId())
->setAttribute('deploymentInternalId', $deployment->getSequence());
$dbForPlatform->updateDocument('rules', $rule->getId(), $rule);
$rule = $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getSequence(),
]));
}, $queries);
break;
case 'sites':
$resource->setAttribute('deploymentId', $deployment->getId());
$resource->setAttribute('deploymentInternalId', $deployment->getSequence());
$resource->setAttribute('deploymentScreenshotDark', $deployment->getAttribute('screenshotDark', ''));
$resource->setAttribute('deploymentScreenshotLight', $deployment->getAttribute('screenshotLight', ''));
$resource->setAttribute('deploymentCreatedAt', $deployment->getCreatedAt());
$resource = $dbForProject->updateDocument('sites', $resource->getId(), $resource);
$resource = $dbForProject->updateDocument('sites', $resource->getId(), new Document([
'live' => true,
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getSequence(),
'deploymentScreenshotDark' => $deployment->getAttribute('screenshotDark', ''),
'deploymentScreenshotLight' => $deployment->getAttribute('screenshotLight', ''),
'deploymentCreatedAt' => $deployment->getCreatedAt(),
]));
$queries = [
Query::equal('projectInternalId', [$project->getSequence()]),
Query::equal('type', ['deployment']),
@@ -1121,10 +1143,10 @@ class Builds extends Action
];
$dbForPlatform->forEach('rules', function (Document $rule) use ($dbForPlatform, $deployment) {
$rule = $rule
->setAttribute('deploymentId', $deployment->getId())
->setAttribute('deploymentInternalId', $deployment->getSequence());
$dbForPlatform->updateDocument('rules', $rule->getId(), $rule);
$rule = $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getSequence(),
]));
}, $queries);
break;
@@ -1166,11 +1188,10 @@ class Builds extends Action
'region' => $project->getAttribute('region')
]));
} catch (Duplicate $err) {
$rule = $dbForPlatform->getDocument('rules', $ruleId);
$rule = $rule
->setAttribute('deploymentId', $deployment->getId())
->setAttribute('deploymentInternalId', $deployment->getSequence());
$dbForPlatform->updateDocument('rules', $rule->getId(), $rule);
$rule = $dbForPlatform->updateDocument('rules', $ruleId, new Document([
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getSequence(),
]));
}
$queries = [
@@ -1183,16 +1204,25 @@ class Builds extends Action
];
$dbForPlatform->foreach('rules', function (Document $rule) use ($dbForPlatform, $deployment) {
$rule = $rule
->setAttribute('deploymentId', $deployment->getId())
->setAttribute('deploymentInternalId', $deployment->getSequence());
$dbForPlatform->updateDocument('rules', $rule->getId(), $rule);
$rule = $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getSequence(),
]));
}, $queries);
}
}
$endTime = DateTime::now();
$durationEnd = \microtime(true);
$deployment->setAttribute('buildEndedAt', $endTime);
$deployment->setAttribute('buildDuration', \intval(\ceil($durationEnd - $durationStart)));
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
$queueForRealtime
->setPayload($deployment->getArrayCopy())
->trigger();
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
return;
}
@@ -1215,7 +1245,7 @@ class Builds extends Action
Console::error($th->getTraceAsString());
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
$this->cancelDeployment($deployment->getId(), $dbForProject, $queueForRealtime);
return;
}
@@ -1225,16 +1255,12 @@ class Builds extends Action
$message = "" . $message;
}
$separator = \strpos($message, '{APPWRITE_DETECTION_SEPARATOR_START}');
if ($separator !== false) {
$error = \substr($message, $separator + strlen('{APPWRITE_DETECTION_SEPARATOR_START}'));
$message = \substr($message, 0, $separator);
$message .= "\n" . $error;
}
$message = \str_replace('{APPWRITE_DETECTION_SEPARATOR_START}', '', $message);
$message = \str_replace('{APPWRITE_DETECTION_SEPARATOR_END}', '', $message);
// Combine with previous logs if deployment got past build process
$previousLogs = '';
if (!empty($deployment->getAttribute('buildEndedAt', ''))) {
if (!is_null($deployment->getAttribute('buildSize', null))) {
$previousLogs = $deployment->getAttribute('buildLogs', '');
if (!empty($previousLogs)) {
$message = $previousLogs . "\n" . $message;
@@ -1251,8 +1277,7 @@ class Builds extends Action
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) {
$resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
$resource = $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document(['latestDeploymentStatus' => $deployment->getAttribute('status', '')]));
}
$queueForRealtime
@@ -1278,6 +1303,8 @@ class Builds extends Action
protected function sendUsage(Document $resource, Document $deployment, Document $project, StatsUsage $queue): void
{
$spec = Config::getParam('specifications')[$resource->getAttribute('specification', APP_COMPUTE_SPECIFICATION_DEFAULT)];
switch ($deployment->getAttribute('status')) {
case 'ready':
$queue
@@ -1319,12 +1346,14 @@ class Builds extends Action
/**
* Hook to run after build success
*
* @param Realtime $queueForRealtime
* @param Database $dbForProject
* @param Document $deployment
* @return void
*/
protected function afterBuildSuccess(Database $dbForProject, Document &$deployment): void
protected function afterBuildSuccess(Realtime $queueForRealtime, Database $dbForProject, Document &$deployment): void
{
assert($queueForRealtime instanceof Realtime);
assert($dbForProject instanceof Database);
assert($deployment instanceof Document);
}
@@ -1440,7 +1469,7 @@ class Builds extends Action
$name = "{$resourceName} ({$projectName})";
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN');
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
$projectId = $project->getId();
$region = $project->getAttribute('region', 'default');
@@ -1504,4 +1533,22 @@ class Builds extends Action
}
}
}
private function cancelDeployment(string $deploymentId, Database $dbForProject, Realtime $queueForRealtime)
{
Console::info('Build has been canceled');
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
$logs = $deployment->getAttribute('buildLogs', '');
$date = \date('H:i:s');
$logs .= "[$date] [appwrite] Build has been canceled. \n";
$deployment->setAttribute('buildLogs', $logs);
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
$queueForRealtime
->setPayload($deployment->getArrayCopy())
->trigger();
}
}
@@ -57,7 +57,7 @@ class Create extends Action
->inject('user')
->inject('response')
->inject('dbForPlatform')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $projectId, string $name, ?string $expire, Document $user, Response $response, Database $dbForPlatform)
@@ -49,7 +49,7 @@ class Delete extends Action
->param('keyId', '', new UID(), 'Key unique ID.')
->inject('response')
->inject('dbForPlatform')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $projectId, string $keyId, Response $response, Database $dbForPlatform)
@@ -49,7 +49,7 @@ class Get extends Action
->param('keyId', '', new UID(), 'Key unique ID.')
->inject('response')
->inject('dbForPlatform')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $projectId, string $keyId, Response $response, Database $dbForPlatform)
@@ -52,7 +52,7 @@ class Update extends Action
->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.')
->inject('response')
->inject('dbForPlatform')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $projectId, string $keyId, string $name, ?string $expire, Response $response, Database $dbForPlatform)
{
@@ -53,7 +53,7 @@ class XList extends Action
->param('queries', [], new DevKeys(), '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(', ', DevKeys::ALLOWED_ATTRIBUTES), true)
->inject('response')
->inject('dbForPlatform')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $projectId, ?array $queries, Response $response, Database $dbForPlatform)
@@ -66,7 +66,7 @@ class Create extends Action
->inject('queueForCertificates')
->inject('queueForEvents')
->inject('dbForPlatform')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $domain, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform)
@@ -71,7 +71,7 @@ class Create extends Action
->inject('queueForEvents')
->inject('dbForPlatform')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $domain, string $functionId, string $branch, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject)
@@ -74,7 +74,7 @@ class Create extends Action
->inject('queueForEvents')
->inject('dbForPlatform')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $domain, string $url, int $statusCode, string $resourceId, string $resourceType, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject)
@@ -71,7 +71,7 @@ class Create extends Action
->inject('queueForEvents')
->inject('dbForPlatform')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $domain, string $siteId, string $branch, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject)
@@ -86,7 +86,7 @@ class Create extends Action
->inject('deviceForLocal')
->inject('queueForBuilds')
->inject('plan')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -62,7 +62,7 @@ class Delete extends Action
->inject('queueForDeletes')
->inject('queueForEvents')
->inject('deviceForSites')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -60,7 +60,7 @@ class Get extends Action
->inject('dbForProject')
->inject('deviceForSites')
->inject('deviceForBuilds')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -65,7 +65,7 @@ class Create extends Action
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('deviceForSites')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -49,7 +49,7 @@ class Get extends Action
->param('deploymentId', '', new UID(), 'Deployment ID.')
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $siteId, string $deploymentId, Response $response, Database $dbForProject)
@@ -57,7 +57,7 @@ class Update extends Action
->inject('project')
->inject('queueForEvents')
->inject('executor')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -77,7 +77,7 @@ class Create extends Base
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('gitHub')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -72,7 +72,7 @@ class Create extends Base
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('gitHub')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(
@@ -57,7 +57,7 @@ class XList extends Action
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $siteId, array $queries, string $search, Response $response, Database $dbForProject)
@@ -46,7 +46,7 @@ class XList extends Base
]
))
->inject('response')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(Response $response)
@@ -55,7 +55,7 @@ class Delete extends Base
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $siteId, string $logId, Response $response, Database $dbForProject, Event $queueForEvents)
@@ -50,7 +50,7 @@ class Get extends Base
->param('logId', '', new UID(), 'Log ID.')
->inject('response')
->inject('dbForProject')
->callback([$this, 'action']);
->callback($this->action(...));
}
public function action(string $siteId, string $logId, Response $response, Database $dbForProject)

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