Compare commits

...
Author SHA1 Message Date
Chirag Aggarwal cc7b5b7dcf fix: missing nullable in datetime 2025-11-14 15:05:32 +05:30
Jake BarnbyandGitHub a1dff370a4 Merge pull request #10815 from appwrite/fix-response-models-multi-methods
fix: generation of all response models in case of multi method endpoints
2025-11-14 06:40:50 +00:00
Jake BarnbyandGitHub d0057ec404 Merge pull request #10800 from appwrite/feat-operators
Fix operators in transactions
2025-11-14 05:13:52 +00:00
Jake Barnby ab90563989 Fix tests 2025-11-14 17:31:17 +13:00
Jake BarnbyandGitHub e783f4c234 Merge pull request #10813 from appwrite/feat-csv-export
Feat csv export
2025-11-14 04:16:31 +00:00
Jake Barnby 082afcec19 Merge branch 'feat-operators' of github.com:appwrite/appwrite into feat-operators 2025-11-14 16:40:24 +13:00
Jake Barnby 2ab36f4cd6 Use helper for tests 2025-11-14 16:40:00 +13:00
Jake Barnby 72c71785a6 Fix doc 2025-11-14 16:35:10 +13:00
Jake BarnbyandGitHub 463eb0e66a Merge pull request #10785 from appwrite/dat-810
Added error message for the backups route
2025-11-14 03:30:35 +00:00
Jake Barnby 0588fb405d Fix keys 2025-11-14 16:16:06 +13:00
Jake Barnby b9211d7141 Add delete old CSV job for maintenance 2025-11-14 15:55:41 +13:00
Jake Barnby a11cf6b002 Merge remote-tracking branch 'origin/1.8.x' into feat-csv-export 2025-11-14 15:34:36 +13:00
Luke B. SilverandGitHub a2799b16f8 Merge pull request #10778 from appwrite/fix-nullable-validation
fix: null validation for optional params
2025-11-13 11:39:23 +00:00
Jake BarnbyandGitHub 298e6f9260 Merge branch '1.8.x' into feat-operators 2025-11-13 11:29:05 +00:00
Jake Barnby 3df2efb7ae Fix file reads 2025-11-14 00:15:18 +13:00
Jake BarnbyandGitHub b0a0454c55 Merge pull request #10738 from appwrite/fix-webp-library
Fix webp upload and previews
2025-11-13 11:01:10 +00:00
Jake Barnby b2d228e102 Update specs 2025-11-13 23:42:13 +13:00
Chirag Aggarwal b695ddc7e3 Merge branch '1.8.x' into fix-nullable-validation 2025-11-13 16:06:33 +05:30
Chirag Aggarwal 3bbd87ecb2 fix: openapi too 2025-11-13 16:05:27 +05:30
Chirag Aggarwal fe8c4cb479 fix: generation of all response models in case of multi method endpoints 2025-11-13 15:59:12 +05:30
Matej BačoandGitHub 5e4c74d501 Merge pull request #10486 from appwrite/feat-add-branch-deployments
Add branch-only deployments to Sites without ref tags
2025-11-13 10:33:46 +01:00
Jake Barnby c78da61304 Merge remote-tracking branch 'origin/1.8.x' into feat-csv-export 2025-11-13 22:14:10 +13:00
Matej BačoandGitHub fe8593fecc Merge pull request #10751 from appwrite/ser-279
fix: Throw error when file token expiry is in the past
2025-11-13 09:34:46 +01:00
Damodar LohaniandGitHub b07674ccd3 Merge pull request #10812 from appwrite/fix-upgrade-utopia-database
Upgrade utopia-php database
2025-11-13 13:31:29 +05:45
Damodar Lohani aa5e3bf9a9 Merge remote-tracking branch 'origin/1.8.x' into fix-upgrade-utopia-database 2025-11-13 07:00:45 +00:00
Damodar Lohani c1b50e9941 Upgrade utopia-php database 2025-11-13 06:59:03 +00:00
Jake BarnbyandGitHub 3a76249d80 Merge pull request #10811 from appwrite/release-flutter-dart-add-screenshot-examples
release flutter + dart and add screenshot api docs
2025-11-13 06:58:32 +00:00
Chirag Aggarwal b2991709dc update version 2025-11-13 11:38:50 +05:30
Chirag Aggarwal fcc07533e1 update changelog 2025-11-13 11:34:35 +05:30
Chirag Aggarwal 3a6d907b42 release flutter + dart and add screenshot api docs 2025-11-13 11:29:38 +05:30
Atharva Deosthale d411b741b3 address comments 2025-11-12 23:06:18 +05:30
Hemachandar bc19e66775 fix tests 2025-11-12 20:27:42 +05:30
Hemachandar 08c9db7c60 use inline validator 2025-11-12 19:23:57 +05:30
Jake Barnby e8f2e78ac4 Use internal bucket 2025-11-12 22:18:25 +13:00
Jake Barnby 37f16ef0bb Fix array conversion 2025-11-12 21:54:37 +13:00
Jake Barnby 46d785bbb8 Fix tests 2025-11-12 21:04:17 +13:00
Damodar LohaniandGitHub a9c9f602ec Merge pull request #10807 from appwrite/feat-update-flutter-default-build-runtime
Update Flutter framework default build runtime version to 3.35
2025-11-12 13:16:20 +05:45
Damodar LohaniandGitHub d90b4c2f19 update framework config 2025-11-12 12:43:34 +05:45
Damodar LohaniandGitHub 79d733486c Update Flutter framework default build runtime version to 3.35 2025-11-12 12:35:31 +05:45
Damodar LohaniandGitHub d862245090 Merge pull request #10806 from appwrite/lohanidamodar-patch-2
Fix: add default fallback to Flutter framework
2025-11-12 12:30:11 +05:45
Damodar LohaniandGitHub aacaadcce0 Fix: add default fallback to Flutter framework 2025-11-12 12:17:15 +05:45
Damodar LohaniandGitHub 69ee6e0659 Merge pull request #10805 from appwrite/lohanidamodar-patch-2
Feat: enable Flutter 3.35 and Dart 3.9 for sites
2025-11-12 10:51:00 +05:45
Jake Barnby e564793089 Merge remote-tracking branch 'origin/1.8.x' into feat-operators
# Conflicts:
#	composer.lock
2025-11-12 17:02:58 +13:00
Jake Barnby c9582978ed Fix operator parsing on txn ops 2025-11-12 16:48:42 +13:00
Jake Barnby ae21fadf0d Fix event firing on txn ops 2025-11-12 16:48:26 +13:00
Damodar LohaniandGitHub cfd62cf0a1 Update template-runtimes.php 2025-11-12 09:32:12 +05:45
Damodar LohaniandGitHub 279682941a enable dart 2025-11-12 09:31:04 +05:45
Damodar LohaniandGitHub eae26d90af Feat: enable Flutter 3.35 for sites 2025-11-12 09:25:35 +05:45
Jake BarnbyandGitHub b46ebb95a3 Merge pull request #10688 from appwrite/users-add-attributes
Users add new email attributes
2025-11-12 03:19:24 +00:00
Damodar LohaniandGitHub ffcb6a4677 Merge pull request #10804 from appwrite/feat-upgrade-runtimes
Feat: upgrade runtime
2025-11-12 07:26:17 +05:45
Damodar LohaniandGitHub 142e359dba Merge branch '1.8.x' into feat-upgrade-runtimes 2025-11-12 07:16:09 +05:45
Jake BarnbyandGitHub 4130983210 Merge pull request #10407 from appwrite/dat-647
updated migration error attribute size
2025-11-12 01:05:13 +00:00
Damodar Lohani e01ac65ce8 Feat: upgrade runtime 2025-11-12 00:26:27 +00:00
fogelito 97454c0768 EmailValidator chnage to email 2025-11-11 15:33:16 +02:00
fogelito 8b4a56cd9b $emailCanonical 2025-11-11 15:25:10 +02:00
fogelito da0a6b167f EmailValidator 2025-11-11 15:00:09 +02:00
fogelito 27d0c511ac patch users email 2025-11-11 12:09:40 +02:00
fogelito 8a58f7b6d7 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into users-add-attributes 2025-11-11 11:36:01 +02:00
fogelito cc97d78b5a Add try catch 2025-11-11 11:35:18 +02:00
Luke B. SilverandGitHub 91c24b671b Merge pull request #10791 from appwrite/release-cli-php
chore: release cli and php
2025-11-11 09:22:57 +00:00
fogelito b28e536967 Add try catch 2025-11-11 10:35:39 +02:00
Jake Barnby ab5aac00c4 Format 2025-11-11 21:25:21 +13:00
Jake Barnby 353e84bb74 Fix keys 2025-11-11 21:10:11 +13:00
Jake Barnby 94614d32d0 Handle deep nested ops 2025-11-11 21:09:12 +13:00
Jake Barnby 1efe9fa693 Merge remote-tracking branch 'origin/1.8.x' into feat-operators
# Conflicts:
#	composer.lock
2025-11-11 20:21:15 +13:00
Jake Barnby 74ff9a955b Fix transactions + operators 2025-11-11 20:20:08 +13:00
fogelito ce7f40dd34 Revert response model 2025-11-11 08:16:09 +02:00
fogelito 49318b5dac Merge branch '1.8.x' of https://github.com/appwrite/appwrite into users-add-attributes 2025-11-11 08:09:11 +02:00
Chirag AggarwalandGitHub da736631b2 Merge branch '1.8.x' into release-cli-php 2025-11-11 10:55:52 +05:30
Matej BačoandGitHub 9db6f9cb75 Merge pull request #10782 from appwrite/add-elevenlabs-site-template
Add ElevenLabs text-to-speech sites template
2025-11-10 16:35:26 +01:00
fogelito 6096c42ff9 More updates 2025-11-10 17:30:40 +02:00
Aditya Oberai b06354b99a update tanstack starter and elevenlabs template scores 2025-11-10 15:12:20 +00:00
Aditya OberaiandGitHub a797951bec Merge branch '1.8.x' into add-elevenlabs-site-template 2025-11-10 20:38:21 +05:30
bb0007a1e8 Update app/config/templates/site.php
Co-authored-by: Matej Bačo <matejbacocom@gmail.com>
2025-11-10 20:38:07 +05:30
Aditya Oberai 507717fdf7 generate screenshots from docker command 2025-11-10 14:49:44 +00:00
fogelito 5afae248ae Test Update 2025-11-10 15:00:18 +02:00
fogelito 98255fe628 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into users-add-attributes
# Conflicts:
#	composer.lock
2025-11-10 14:52:25 +02:00
Chirag Aggarwal 057397197b update to 18.0.1 2025-11-10 18:20:31 +05:30
Aditya Oberai c398d5fba9 Add default value for ELEVENLABS_API_KEY env variable 2025-11-10 12:17:29 +00:00
Chirag Aggarwal 4dc700927e fix: examples 2025-11-10 15:39:54 +05:30
Chirag Aggarwal 4a148e6ef9 remove broken execute 2025-11-10 15:06:22 +05:30
Chirag Aggarwal 2ff0ae61c8 update php 2025-11-10 15:00:09 +05:30
Chirag Aggarwal b06460b562 chore: release cli and php 2025-11-10 14:19:26 +05:30
Chirag Aggarwal 74492ded44 update description 2025-11-10 11:35:25 +05:30
Chirag AggarwalandGitHub 9ecffbf4e9 Merge branch '1.8.x' into fix-nullable-validation 2025-11-10 10:19:13 +05:30
Chirag Aggarwal 0ed4b5dbe7 fix imports + add spec changes 2025-11-10 10:16:58 +05:30
Chirag Aggarwal eb17ffd5d2 wrap all optional values with default null with nullable 2025-11-10 10:15:49 +05:30
Jake BarnbyandGitHub 0bf8cead51 Merge pull request #10789 from appwrite/sync-specs
chore: sync specs
2025-11-10 04:33:40 +00:00
Damodar LohaniandGitHub add1142d1f Merge pull request #10786 from appwrite/feat-function-sites-stats
Feat: stats sites and functions runtimes and frameworks
2025-11-10 09:52:36 +05:45
Chirag Aggarwal 86896a6d3c chore: sync specs 2025-11-10 08:51:56 +05:30
Damodar Lohani 4fd4af312d Fix array wrong reference 2025-11-09 08:18:37 +00:00
Damodar Lohani 4b7a7496ff Feat: stats sites and functions runtimes and frameworks
- Sites frameworks count
- Functions runtimes count
2025-11-09 07:17:40 +00:00
ArnabChatterjee20k eb0e42b79c added error message for the backups route 2025-11-09 12:13:08 +05:30
Aditya OberaiandGitHub 0c93e7d545 Update app/config/templates/site.php 2025-11-07 23:29:36 +05:30
Aditya Oberai 469d0859fc Add ElevenLabs text-to-speech sites template 2025-11-07 17:07:25 +00:00
ArnabChatterjee20k 7c408d675e pr followups 2025-11-07 14:30:22 +05:30
Luke B. SilverandGitHub 4a96d89ee6 Merge pull request #10765 from appwrite/feat-sdk-release-runtime-config
Set configs for SDK release at runtime
2025-11-07 08:57:43 +00:00
ArnabChatterjee20k 4d47bc2ebf Merge remote-tracking branch 'upstream/1.8.x' into dat-647 2025-11-07 13:48:59 +05:30
ArnabChatterjee20k 119d6c1789 Merge remote-tracking branch 'upstream/1.8.x' into dat-647 2025-11-07 13:19:28 +05:30
Steven NguyenandGitHub 2aa2b8df91 chore: set GITHUB_TOKEN and git email at runtime 2025-11-07 00:06:01 +00:00
Steven NguyenandGitHub 4f1940ac0c Merge pull request #10739 from appwrite/copilot/sub-pr-10738
Add test coverage for webp upload and preview functionality
2025-11-06 15:07:52 -08:00
Steven NguyenandGitHub ef938a1c39 Merge pull request #10732 from appwrite/copilot/fix-google-token-disappears
Fix Google OAuth2 refresh token support
2025-11-06 10:28:02 -08:00
Steven NguyenandGitHub ebee879855 Merge pull request #10748 from appwrite/copilot/update-issue-triage-workflow
Migrate issue-triage workflow from event-based to scheduled batch processing with duplicate detection
2025-11-06 08:14:25 -08:00
Matej BačoandGitHub e342009c9e Merge pull request #10771 from appwrite/Update-facts-on-Github-sites-and-functions
feat: Add new tips to Comment class
2025-11-06 13:50:27 +01:00
Matej BačoandGitHub 41d89f1b7a Merge pull request #10773 from appwrite/chore-remove-locale-cache
Chore: remove locale cache
2025-11-06 13:49:32 +01:00
Matej Bačo ad6aff6af2 remove locale cache 2025-11-06 13:24:17 +01:00
Harsh MahajanandGitHub 9b52f6f8f6 Add new tips to Comment class 2025-11-06 14:17:31 +05:30
Matej BačoandGitHub ad46d057d5 Merge pull request #10593 from appwrite/feat-SER-193-Update-facts-on-github-sites-and-functions
Feat: update facts on GitHub sites and functions
2025-11-06 09:35:53 +01:00
Damodar LohaniandGitHub 68ae8ce348 Merge pull request #10762 from appwrite/release-python
chore: release python sdk to 13.6.1
2025-11-06 09:24:43 +05:45
Chirag Aggarwal 87b0bfcdfe Merge branch '1.8.x' into release-python 2025-11-06 08:33:07 +05:30
copilot-swe-agent[bot]andstnguyen90 8a9d0a3f33 Rename logo-after.webp to image.webp
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
2025-11-05 19:30:21 +00:00
Luke B. SilverandGitHub d37bab37e6 Merge pull request #10766 from appwrite/bump-database-3.1.5
Bump database version 3.1.5
2025-11-05 13:52:13 +00:00
fogelito 7810d54238 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into bump-database-3.1.5 2025-11-05 14:34:23 +02:00
Matej BačoandGitHub b3f9404b93 Merge pull request #10767 from appwrite/fix-tanstack-builds
Fix: tanstack builds crashing
2025-11-05 13:28:08 +01:00
fogelito d4b5d7df73 lock file 2025-11-05 13:54:07 +02:00
Matej Bačo e93f479157 Fix tanstack builds craashing 2025-11-05 12:48:39 +01:00
fogelito a1b1f97162 lock file 2025-11-05 13:36:43 +02:00
fogelito 1f0e3e0f51 lock file 2025-11-05 13:21:52 +02:00
Steven NguyenandGitHub 4ce2426c3d Merge pull request #10566 from Priyanshuthapliyal2005/fix/sitesCreateDeployment
fix: sites deployment activation instructions in createDeployment
2025-11-04 13:10:36 -08:00
Steven NguyenandGitHub 25b3680b1d Merge branch '1.8.x' into fix/sitesCreateDeployment 2025-11-04 11:36:37 -08:00
Steven NguyenandGitHub c910c98b32 chore: install ssh and gh commands for dev
This will allow creating github releases for SDKs
2025-11-04 16:18:49 +00:00
fogelito fe9d49c346 Response Model 2025-11-04 17:13:53 +02:00
Matej BačoandGitHub 6c1e4434a6 Merge pull request #10763 from appwrite/update-readme-file
Update README.md
2025-11-04 15:52:25 +01:00
VeereshandGitHub ba6e389f81 Update README.md 2025-11-04 19:33:30 +05:30
Chirag Aggarwal c442dd3ef1 chore: release python sdk to 13.6.1 2025-11-04 17:19:42 +05:30
Luke B. SilverandGitHub bf5aa7b86c Merge pull request #10761 from appwrite/feat-bump-utopia-dns
feat: bump utopia dns
2025-11-04 11:16:44 +00:00
loks0n b42cc11eba feat: bump utopia dns 2025-11-04 10:56:34 +00:00
Jake BarnbyandGitHub 79573b1d47 Merge pull request #10737 from appwrite/main
Sync main into 1.8.x
2025-11-04 19:58:18 +13:00
copilot-swe-agent[bot]andstnguyen90 2d332264c6 Replace with simple webp image for testing
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
2025-11-04 06:16:41 +00:00
copilot-swe-agent[bot]andstnguyen90 2f843153da Add duplicate/related issues detection and listing to triage workflow
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
2025-11-03 20:02:03 +00:00
copilot-swe-agent[bot]andstnguyen90 df8fb23e75 Fix test assertion for file count with offset
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
2025-11-03 18:13:21 +00:00
Hemachandar 60e57eb9c5 lint 2025-11-03 17:29:48 +05:30
Hemachandar 10a9845898 fix: Throw error when file token expiry is in the past 2025-11-03 16:55:13 +05:30
Jake BarnbyandGitHub 55103a1d04 Merge pull request #10750 from appwrite/feat-csv-export
CSV export email updates
2025-11-03 19:26:49 +13:00
Jake BarnbyandGitHub 6e71a3ca4c Merge branch '1.8.x' into feat-csv-export 2025-11-03 19:26:25 +13:00
Jake BarnbyandGitHub 1f173eb3c3 Merge pull request #10735 from appwrite/feat-operators
Add operator support
2025-11-03 19:25:44 +13:00
Jake Barnby 2db205d186 Update changelogs 2025-11-03 19:06:16 +13:00
Jake Barnby 1d84f1666d Fix copy 2025-11-03 18:36:07 +13:00
Jake Barnby aa6390062f Fix text centering 2025-11-03 18:35:59 +13:00
Jake Barnby 455b9b3ca7 Fix logo size 2025-11-03 18:35:43 +13:00
Jake Barnby 45ead583c6 Generate docs 2025-11-03 17:03:45 +13:00
Jake Barnby 750ccdf33d Merge remote-tracking branch 'origin/1.8.x' into feat-operators 2025-11-03 16:06:09 +13:00
fogelito 6a41f8b2b9 Remove todos 2025-11-02 11:16:12 +02:00
fogelito 503145734b Move user Document out of try block 2025-11-02 11:06:56 +02:00
fogelito 371cab0658 try catch EmailCanonical 2025-11-02 10:59:28 +02:00
fogelito ca8cb6f260 Add utopia-php/emails 2025-11-02 09:13:55 +02:00
Eldad A. FuxandGitHub 745e9e2bea Merge pull request #10675 from appwrite/feat-screenshots-endpoint
POC - website screenshots
2025-11-01 18:03:37 +01:00
copilot-swe-agent[bot]andstnguyen90 1f9afc8cab Update issue-triage workflow to run daily and process last 24h issues
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
2025-11-01 15:47:43 +00:00
copilot-swe-agent[bot] c5d4551cab Initial plan 2025-11-01 15:39:50 +00:00
Matej BačoandGitHub 230148c06e Merge pull request #10747 from appwrite/feat-next-js-standalone
Feat: Next.js standalone support
2025-10-31 16:26:32 +01:00
Matej Bačo 3197ebb655 Merge branch '1.8.x' into feat-next-js-standalone 2025-10-31 13:56:55 +01:00
Matej Bačo af3a0bb34b Update detection lib 2025-10-31 13:51:38 +01:00
Matej BačoandGitHub ea1099b112 Merge pull request #10746 from appwrite/ser-453
Update base template for session alert email
2025-10-31 13:47:22 +01:00
Hemachandar f9542e2be2 Update base template for session alert email 2025-10-31 17:52:47 +05:30
Jake Barnby 3e291492fb Update versions 2025-11-01 01:16:24 +13:00
Jake Barnby e7dc7a6fba Update specs 2025-11-01 01:05:15 +13:00
Luke B. SilverandGitHub b525605d1b Merge pull request #10745 from appwrite/sdk-release-doc
doc: add tutorial for releasing sdks
2025-10-31 12:01:48 +00:00
Jake Barnby dc968a1fa4 Merge remote-tracking branch 'origin/1.8.x' into feat-operators
# Conflicts:
#	composer.lock
2025-11-01 00:56:06 +13:00
Chirag Aggarwal ebee3536f5 final changes 2025-10-31 17:24:03 +05:30
Chirag Aggarwal 8c8073d7aa grammer fixes 2025-10-31 17:22:02 +05:30
Jake Barnby d284c29e2a Update generator 2025-11-01 00:50:36 +13:00
Jake Barnby 0fae37a999 Fix colors 2025-11-01 00:49:08 +13:00
Chirag Aggarwal fce4f72ca9 doc: add tutorial for releasing sdks 2025-10-31 17:18:54 +05:30
Jake Barnby 77968be454 Fix template 2025-11-01 00:10:39 +13:00
Luke B. SilverandGitHub 4f905e06e2 Merge pull request #10744 from appwrite/regen-sdks
regen sdks
2025-10-31 09:34:52 +00:00
Chirag Aggarwal ed39fd7999 regen sdks 2025-10-31 14:31:16 +05:30
Jake BarnbyandGitHub 71cd882324 Merge pull request #10743 from appwrite/update-sdk-gen
update sdk gen
2025-10-31 21:54:43 +13:00
Chirag Aggarwal 68d7ff9c55 update sdk gen 2025-10-31 13:05:29 +05:30
Jake BarnbyandGitHub 8ca083ed27 Merge pull request #10729 from appwrite/update-sdks-2
update sdks
2025-10-31 19:54:45 +13:00
Jake Barnby 600da2a504 Add operator support 2025-10-31 11:47:20 +13:00
copilot-swe-agent[bot]andstnguyen90 d1511d5ba1 Refactor webp tests per review feedback
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
2025-10-30 22:26:30 +00:00
copilot-swe-agent[bot]andstnguyen90 4a8651ab39 Add webp test cases for upload/view and preview
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
2025-10-30 21:58:02 +00:00
copilot-swe-agent[bot]andstnguyen90 de4f473da1 Initial plan for adding webp test cases
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
2025-10-30 21:56:12 +00:00
copilot-swe-agent[bot] ce539972a5 Initial plan 2025-10-30 21:51:50 +00:00
Steven NguyenandGitHub 458100cd69 Upgrade base image to version 0.10.5
Ensure libwebp is installed in the base image when imagemagick is installed so that imagemagick works with webp.
2025-10-30 14:42:18 -07:00
fogelito fea1630909 Remove redundant emailVerification 2025-10-30 18:18:09 +02:00
fogelito e40e88c322 Update emails attributes 2025-10-30 18:09:36 +02:00
Chirag Aggarwal 6bd52875dc update to major 2025-10-30 18:13:32 +05:30
Chirag Aggarwal 88dffcffbf format 2025-10-30 11:01:42 +05:30
Chirag Aggarwal 07214de370 update sdks 2025-10-30 10:03:36 +05:30
Damodar Lohani d56bd44f67 Fix label parsing 2025-10-30 02:07:15 +00:00
Damodar LohaniandGitHub a39970c59a Merge branch '1.8.x' into feat-screenshots-endpoint 2025-10-30 07:26:43 +05:45
Damodar Lohani b30890c1d6 remove duplicate configs 2025-10-30 01:37:53 +00:00
Damodar Lohani 5c1f6244c8 remove dump 2025-10-30 01:32:22 +00:00
Damodar Lohani 7156036120 remove viewport fullpage duplicate 2025-10-30 01:31:45 +00:00
copilot-swe-agent[bot]andstnguyen90 627adb43e0 Add access_type=offline and prompt=consent to Google OAuth2 login URL to enable refresh tokens
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
2025-10-29 23:12:28 +00:00
copilot-swe-agent[bot] 9ad08694ed Initial plan 2025-10-29 23:01:33 +00:00
Steven NguyenandGitHub a869ae3323 Merge pull request #10725 from appwrite/copilot/analyze-action-failure
Add missing models: read permission to AI Moderator workflow
2025-10-29 07:33:44 -07:00
Chirag Aggarwal baa7a170d9 update sdks 2025-10-29 18:54:29 +05:30
Steven NguyenandGitHub e5cd093a7b Apply suggestion from @stnguyen90 2025-10-29 00:45:49 -07:00
copilot-swe-agent[bot]andstnguyen90 2078e52798 Add contents: read permission to AI Moderator workflow
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
2025-10-29 07:16:52 +00:00
copilot-swe-agent[bot] 08c51b8761 Initial plan 2025-10-29 07:12:09 +00:00
Steven NguyenandGitHub 37dfce0f5b Merge pull request #10718 from appwrite/add-workflow-workflows-issue-triage.md-5094
Add agentic workflow issue-triage
2025-10-29 00:04:22 -07:00
Damodar LohaniandGitHub 8f87b9cb5e Merge branch '1.8.x' into feat-screenshots-endpoint 2025-10-29 09:19:47 +05:45
Damodar Lohani b303bd1051 add correct assertion 2025-10-29 03:34:25 +00:00
Damodar Lohani 60a4266204 validate data length 2025-10-29 03:30:59 +00:00
Damodar Lohani 637bf4f231 add abuse limit 2025-10-29 03:30:43 +00:00
Steven NguyenandGitHub 92f776b525 Merge pull request #10717 from appwrite/copilot/add-ai-moderator-repo
Add GitHub AI Moderator workflow
2025-10-28 11:46:35 -07:00
Steven NguyenandGitHub eec3d210ee Add agentic workflow issue-triage 2025-10-28 03:00:30 +00:00
copilot-swe-agent[bot]andstnguyen90 b729c8119d Fix parameter name to use 'token' instead of 'github-token'
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
2025-10-28 02:18:07 +00:00
copilot-swe-agent[bot]andstnguyen90 30a093a0d5 Add GitHub AI Moderator workflow
Co-authored-by: stnguyen90 <1477010+stnguyen90@users.noreply.github.com>
2025-10-28 02:16:40 +00:00
copilot-swe-agent[bot] dee3ddffca Initial plan 2025-10-28 02:11:53 +00:00
fogelito 037d60633b Add emailCanonical 2025-10-27 15:59:20 +02:00
fogelito 218bb4c537 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into users-add-attributes 2025-10-27 13:37:33 +02:00
Damodar LohaniandGitHub 74bcfe1e08 Merge pull request #10706 from appwrite/feat-screenshot-endpoint-stats
Feat: usage stats for screenshot generated
2025-10-27 12:57:48 +05:45
Damodar Lohani 2afba50a8c Feat: usage stats for screenshot generated 2025-10-27 06:44:04 +00:00
eldadfux 61f4c7957a Add new configuration variable '_APP_BROWSER_HOST' for browser service communication; update avatar screenshot API to use System::getEnv for environment variable retrieval 2025-10-25 10:22:53 +01:00
eldadfux f13c029751 Refactor avatar screenshot API to replace 'viewport' parameter with separate 'viewportWidth' and 'viewportHeight' parameters; update related tests for validation of new dimensions and edge cases. 2025-10-24 22:53:10 +01:00
Steven NguyenandGitHub 6ded8dce67 Merge pull request #10700 from appwrite/chore-issue-auto-labeler
Create workflow to auto add labels to issues
2025-10-24 14:50:46 -07:00
Eldad Fux d8bde641e9 Update appwrite-browser image to version 0.3.1; enhance avatar screenshot API by adding 'waitUntil' parameter and refactoring image processing logic for improved efficiency. 2025-10-24 21:42:10 +01:00
Steven NguyenandSteven Nguyen 21b95c2645 chore: create workflow to auto add labels to issues 2025-10-24 11:24:57 -07:00
Harsh MahajanandGitHub 1468231de7 Update Comment.php 2025-10-23 20:20:16 +05:30
fogelito c13a70e226 Add users attributes 2025-10-23 12:38:18 +03:00
Eldad Fux e00a73f442 Enhance avatar screenshot API by adding 'scale' parameter for browser scaling; update related tests to validate new functionality and edge cases. 2025-10-22 10:22:17 +01:00
Harsh MahajanandGitHub 32dae9ad7c Update Comment.php 2025-10-22 10:05:18 +05:30
Harsh MahajanandGitHub be5c5f9420 Merge branch 'main' into feat-SER-193-Update-facts-on-github-sites-and-functions 2025-10-22 09:53:45 +05:30
Jake BarnbyandGitHub 72c608bf3b Merge pull request #10677 from appwrite/chore-sync-1.8.0
Chore sync 1.8.0 into main
2025-10-22 01:09:41 +00:00
Eldad Fux dfe87a0d37 Rename avatar screenshot endpoint from '/v1/avatars/screenshot' to '/v1/avatars/screenshots' for consistency; update related tests accordingly. 2025-10-22 01:40:31 +01:00
Eldad Fux bb77319703 Update browser endpoint in avatar screenshot API to use service name for improved reliability 2025-10-22 01:30:04 +01:00
Eldad Fux dc7bb627d5 Enhance avatar screenshot API with new parameters and validations; add GraphQL support and extensive tests for various scenarios 2025-10-22 01:24:31 +01:00
Eldad Fux f35b80ba1a Refactor avatar screenshot handling and add comprehensive tests for various header and parameter validations 2025-10-21 21:20:31 +01:00
Eldad Fux 6cc5d1595d Fixes 2025-10-21 21:03:13 +01:00
Steven Nguyen 60b4835539 Merge branch 'feat-1.8.0-release-prep' into chore-sync-1.8.0 2025-10-21 12:24:26 -07:00
Eldad Fux 5073a86581 POC - website screenshots 2025-10-21 15:37:05 +01:00
Harsh MahajanGitHubcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
64c3b7a2ff Update src/Appwrite/Vcs/Comment.php
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-10-02 23:19:34 +05:30
Harsh Mahajan 61a8f589ac port issue 2025-10-02 17:44:21 +00:00
Harsh Mahajan 92ccb6e4ea feat(vcs): expand GitHub comment tips 2025-10-02 17:43:30 +00:00
Priyanshu ThapliyalandGitHub cc6f5fea6d Merge branch '1.8.x' into fix/sitesCreateDeployment 2025-10-01 22:21:58 +05:30
Priyanshu Thapliyal 12e3af6ad7 add specs files 2025-10-01 08:44:14 +00:00
Priyanshu Thapliyal f0b6eeb303 fix: sites deployment activation instructions in createDeployment 2025-10-01 08:31:42 +00:00
Atharva Deosthale 5963b2baf7 change request filter to versions below 1.9.0 2025-09-23 18:00:14 +05:30
Atharva Deosthale 7d4486b9e6 more fixes to tests 2025-09-19 18:42:25 +05:30
Atharva Deosthale 1a19e01d69 attempt to fix tests 2025-09-19 18:30:42 +05:30
Atharva Deosthale 2a145bc284 address reviews regarding tests 2025-09-19 18:18:44 +05:30
Atharva Deosthale 9fe3e94a66 e2e tests 2025-09-18 13:12:46 +05:30
Atharva Deosthale 8e32fb05d1 specs 2025-09-18 12:28:08 +05:30
Atharva Deosthale de8538b622 use request filters for 1.8.0 to convert into and 2025-09-18 12:27:24 +05:30
Atharva Deosthale fca2727966 add validation for type 2025-09-14 15:28:25 +05:30
Atharva Deosthale cb03bfe74c specs and sdk 2025-09-14 15:22:51 +05:30
Atharva Deosthale 2b1e3bd947 delete the new route and just do it in template route 2025-09-14 15:15:44 +05:30
Atharva Deosthale 4858317891 fix: format, lints 2025-09-13 18:02:13 +05:30
Atharva Deosthale 6e16340f16 feat: add branch deployments to appwrite 2025-09-13 17:56:19 +05:30
ArnabChatterjee20k ac604b11e3 added migration script 2025-09-01 13:13:52 +05:30
ArnabChatterjee20k f68d49c4d6 updated the migration error size attribute 2025-08-29 18:19:29 +05:30
728 changed files with 30178 additions and 6068 deletions
+2
View File
@@ -5,3 +5,5 @@ src/** linguist-detectable=false
tests/** linguist-detectable=false
public/scripts/** linguist-detectable=false
public/dist/scripts/** linguist-detectable=false
.github/workflows/*.lock.yml linguist-generated=true merge=ours
+83
View File
@@ -0,0 +1,83 @@
# Fixes and upgrades for the Appwrite Auth / Users / Teams services.
"product / auth":
- "(auth|session|login|logout|register|2fa|mfa|users|teams|memberships|invite|oauth|oauth2|sso|jwt)"
# Fixes and upgrades for the Appwrite Realtime API.
"api / realtime":
- "(realtime|subscribe|websockets)"
# Console, UI and UX issues
"product / console":
- "(console)"
# Fixes and upgrades for the Appwrite Storage.
"product / storage":
- "(storage|bucket|file|image|preview|download)"
# Fixes and upgrades for the Appwrite Database.
"product / databases":
- "(database|collection|tables|attribute|column|document|row|query|queries|indexes|search|filter|sort|pagination)"
# Fixes and upgrades for the Appwrite Functions.
"product / functions":
- "(function|runtime|deployment|execution|trigger|cron|schedule)"
# Fixes and upgrades for the Appwrite Docs.
# "product / docs":
# -
# Fixes and upgrades for the Appwrite Migrations.
"product / migrations":
- "(migrate|migration)"
# Fixes and upgrades for the Appwrite Messaging.
"product / messaging":
- "(messaging|email|sms|push|provider|topic|target|notification)"
# Fixes and upgrades for the Appwrite Platform.
# "product / platform":
# -
# Fixes and upgrades for database relationships
"feature / relationships":
- "(relationship)"
# Issues found only on Appwrite Cloud
# "product / cloud":
# -
# Fixes and upgrades for the Appwrite VCS.
"product / vcs":
- "(repo|push|vcs|repository)"
# Fixes and upgrades for the Appwrite GraphQL API.
"api / graphql":
- "(graphql|gql|mutation)"
# Fixes and upgrades for the Appwrite Assistant.
"product / assistant":
- "(assistant)"
# Fixes and upgrades for the Appwrite Domains.
"product / domains":
- "(domain|dns|ssl|certificate)"
# Fixes and upgrades for the Appwrite Locale.
"product / locale":
- "(locale|i18n|internationalization|localization|l10n|translation|timezone|country)"
# Fixes and upgrades for the Appwrite Avatars.
"product / avatars":
- "(avatar|initial|flag|icon)"
# Fixes and upgrades for Appwrite Sites.
"product / sites":
- "(site|web|hosting|domain|ssl|certificate|nextjs|nuxt|react|angular|vue|svelte|astro)"
# Fixes and upgrades for the Appwrite CLI.
"sdk / cli":
- "(cli|command line)"
# Issues only found when self-hosting Appwrite
"product / self-hosted":
- "(self-host|self host)"
+32
View File
@@ -0,0 +1,32 @@
name: AI Moderator
on:
issues:
types: [opened, edited]
issue_comment:
types: [created, edited]
pull_request:
types: [opened, edited]
pull_request_review:
types: [submitted, edited]
pull_request_review_comment:
types: [created, edited]
discussion:
types: [created, edited]
discussion_comment:
types: [created, edited]
permissions:
models: read
issues: write
pull-requests: write
discussions: write
jobs:
moderate:
runs-on: ubuntu-latest
steps:
- name: AI Moderator
uses: github/ai-moderator@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
+22
View File
@@ -0,0 +1,22 @@
name: Auto Label Issue
on:
issues:
types: [opened]
permissions:
issues: write
contents: read
jobs:
labeler:
runs-on: ubuntu-latest
steps:
- name: Issue Labeler
uses: github/issue-labeler@v3.4
with:
configuration-path: .github/labeler.yml
enable-versioned-regex: false
include-title: 1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
File diff suppressed because it is too large Load Diff
+85
View File
@@ -0,0 +1,85 @@
---
on:
schedule:
- cron: '0 0 * * *' # Run daily at midnight UTC
workflow_dispatch: # Enable manual trigger
stop-after: +30d # workflow will no longer trigger after 30 days. Remove this and recompile to run indefinitely
reaction: eyes
permissions: read-all
network: defaults
safe-outputs:
add-labels:
max: 5
add-comment:
tools:
web-fetch:
web-search:
timeout_minutes: 10
source: githubnext/agentics/workflows/issue-triage.md@0837fb7b24c3b84ee77fb7c8cfa8735c48be347a
---
# Agentic Triage
<!-- Note - this file can be customized to your needs. Replace this section directly, or add further instructions here. After editing run 'gh aw compile' -->
You're a triage assistant for GitHub issues. Your task is to analyze issues created in the last 24 hours and perform initial triage tasks for each of them.
1. First, use the `list_issues` tool to retrieve all issues created in the last 24 hours. Filter issues by using the `since` parameter with a timestamp from 24 hours ago (calculate: current time minus 24 hours in ISO 8601 format).
2. For each issue found, perform the following triage tasks:
3. Select appropriate labels for the issue from the provided list.
4. Retrieve the issue content using the `get_issue` tool. If the issue is obviously spam, or generated by bot, or something else that is not an actual issue to be worked on, then add an issue comment to the issue with a one sentence analysis and move to the next issue.
5. Next, use the GitHub tools to gather additional context about the issue:
- Fetch the list of labels available in this repository. Use 'gh label list' bash command to fetch the labels. This will give you the labels you can use for triaging issues.
- Fetch any comments on the issue using the `get_issue_comments` tool
- **Search for duplicate and related issues**: Use the `search_issues` tool to find similar issues by searching for key terms from the issue title and description. Look for both open and closed issues that might be related or duplicates.
6. Analyze the issue content, considering:
- The issue title and description
- The type of issue (bug report, feature request, question, etc.)
- Technical areas mentioned
- Severity or priority indicators
- User impact
- Components affected
7. Write notes, ideas, nudges, resource links, debugging strategies and/or reproduction steps for the team to consider relevant to the issue.
8. Select appropriate labels from the available labels list provided above:
- Choose labels that accurately reflect the issue's nature
- Be specific but comprehensive
- Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority)
- Consider platform labels (android, ios) if applicable
- Search for similar issues, and if you find similar issues consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue.
- Only select labels from the provided list above
- It's okay to not add any labels if none are clearly applicable
9. Apply the selected labels:
- Use the `update_issue` tool to apply the labels to the issue
- DO NOT communicate directly with users
- If no labels are clearly applicable, do not apply any labels
10. Add an issue comment to the issue with your analysis:
- Start with "🎯 Agentic Issue Triage"
- Provide a brief summary of the issue
- **If duplicate or related issues were found**, add a section listing them with links (e.g., "### 🔗 Potentially Related Issues" followed by a bullet list of related issues with their titles and links)
- Mention any relevant details that might help the team understand the issue better
- Include any debugging strategies or reproduction steps if applicable
- Suggest resources or links that might be helpful for resolving the issue or learning skills related to the issue or the particular area of the codebase affected by it
- Mention any nudges or ideas that could help the team in addressing the issue
- If you have possible reproduction steps, include them in the comment
- If you have any debugging strategies, include them in the comment
- If appropriate break the issue down to sub-tasks and write a checklist of things to do.
- Use collapsed-by-default sections in the GitHub markdown to keep the comment tidy. Collapse all sections except the short main summary at the top.
11. After processing all issues, provide a summary of how many issues were triaged. If no issues were created in the last 24 hours, simply note that no new issues needed triage.
+2 -3
View File
@@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \
--no-plugins --no-scripts --prefer-dist \
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
FROM appwrite/base:0.10.4 AS final
FROM appwrite/base:0.10.5 AS final
LABEL maintainer="team@appwrite.io"
@@ -28,8 +28,6 @@ RUN \
apk add boost boost-dev; \
fi
RUN apk add libwebp
WORKDIR /usr/src/code
COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor
@@ -98,6 +96,7 @@ RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
# Enable Extensions
RUN if [ "$DEBUG" = "true" ]; then cp /usr/src/code/dev/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini; fi
RUN if [ "$DEBUG" = "true" ]; then mkdir -p /tmp/xdebug; fi
RUN if [ "$DEBUG" = "true" ]; then apk add --update --no-cache openssh-client github-cli; fi
RUN if [ "$DEBUG" = "false" ]; then rm -rf /usr/src/code/dev; fi
RUN if [ "$DEBUG" = "false" ]; then rm -f /usr/local/lib/php/extensions/no-debug-non-zts-20230831/xdebug.so; fi
+1 -1
View File
@@ -1,4 +1,4 @@
> We just announced Transactions API for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-transactions-api)
> We just announced DB operators for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-db-operators)
> Appwrite Cloud is now Generally Available - [Learn more](https://appwrite.io/cloud-ga)
+55
View File
@@ -364,6 +364,61 @@ return [
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('emailCanonical'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 320,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('emailIsFree'),
'type' => Database::VAR_BOOLEAN,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('emailIsDisposable'),
'type' => Database::VAR_BOOLEAN,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('emailIsCorporate'),
'type' => Database::VAR_BOOLEAN,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('emailIsCanonical'),
'type' => Database::VAR_BOOLEAN,
'format' => '',
'size' => 0,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
+1 -1
View File
@@ -2345,7 +2345,7 @@ return [
'$id' => ID::custom('errors'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 65535,
'size' => 1_000_000,
'signed' => true,
'required' => true,
'default' => null,
+2 -1
View File
@@ -273,7 +273,7 @@ return [
'key' => 'flutter',
'name' => 'Flutter',
'screenshotSleep' => 5000,
'buildRuntime' => 'flutter-3.29',
'buildRuntime' => 'flutter-3.35',
'runtimes' => getVersions($templateRuntimes['FLUTTER']['versions'], 'flutter'),
'adapters' => [
'static' => [
@@ -282,6 +282,7 @@ return [
'installCommand' => 'flutter pub get',
'outputDirectory' => './build/web',
'startCommand' => 'bash helpers/server.sh',
'fallbackFile' => 'index.html'
],
],
],
@@ -20,8 +20,8 @@
}
a.button {
color: #ffffff !important;
background-color: #2D2D31 !important;
border-color: #414146 !important;
background-color: {{accentColor}} !important;
border-color: {{accentColor}} !important;
}
h1, h2, h3 {
color: #373b4d !important;
@@ -129,6 +129,7 @@
color: #ffffff;
border-radius: 8px;
height: 48px;
line-height: 24px;
padding: 12px 20px;
box-sizing: border-box;
cursor: pointer;
@@ -184,7 +185,7 @@
<tr>
<td>
<img
height="32px"
height="26px"
src="{{logoUrl}}"
alt="Appwrite logo"
/>
@@ -0,0 +1,8 @@
<p>{{hello}}</p>
<p>{{body}}</p>
<p>{{footer}}</p>
<p style="margin-bottom: 32px">
{{thanks}}
<br/>
{{signature}}
</p>
+2 -2
View File
@@ -60,11 +60,11 @@
"emails.csvExport.success.subject": "Your CSV export is ready",
"emails.csvExport.success.preview": "Your data export has been completed successfully.",
"emails.csvExport.success.hello": "Hello {{user}},",
"emails.csvExport.success.body": "Your CSV export is ready for download. Click the link below to download your data export.",
"emails.csvExport.success.body": "Your CSV export is ready to download. Click the button below to download your data export.",
"emails.csvExport.success.footer": "This download link will expire in 1 hour.",
"emails.csvExport.success.thanks": "Thanks,",
"emails.csvExport.success.buttonText": "Download CSV",
"emails.csvExport.success.signature": "{{project}} team",
"emails.csvExport.success.signature": "Appwrite team",
"emails.csvExport.failure.subject": "Your CSV export failed - file too large",
"emails.csvExport.failure.preview": "Your data export failed because the file size exceeds your plan limit.",
"emails.csvExport.failure.hello": "Hello {{user}},",
+16 -16
View File
@@ -11,7 +11,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '21.3.0',
'version' => '21.4.0',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -60,7 +60,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '20.2.1',
'version' => '20.3.1',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -79,7 +79,7 @@ return [
[
'key' => 'apple',
'name' => 'Apple',
'version' => '13.3.0',
'version' => '13.4.0',
'url' => 'https://github.com/appwrite/sdk-for-apple',
'package' => 'https://github.com/appwrite/sdk-for-apple',
'enabled' => true,
@@ -116,7 +116,7 @@ return [
[
'key' => 'android',
'name' => 'Android',
'version' => '11.2.1',
'version' => '11.3.0',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
'enabled' => true,
@@ -139,7 +139,7 @@ return [
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.17.1',
'version' => '0.18.0',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
@@ -207,7 +207,7 @@ return [
[
'key' => 'web',
'name' => 'Console',
'version' => '0.1.1',
'version' => '0.2.0',
'url' => '',
'package' => '',
'enabled' => true,
@@ -226,7 +226,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '10.2.3',
'version' => '11.1.1',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -262,7 +262,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '20.2.1',
'version' => '20.3.0',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -281,7 +281,7 @@ return [
[
'key' => 'php',
'name' => 'PHP',
'version' => '17.4.1',
'version' => '18.0.1',
'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true,
@@ -300,7 +300,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '13.4.1',
'version' => '13.6.1',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -319,7 +319,7 @@ return [
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '19.2.1',
'version' => '19.3.0',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@@ -338,7 +338,7 @@ return [
[
'key' => 'go',
'name' => 'Go',
'version' => 'v0.13.1',
'version' => 'v0.14.0',
'url' => 'https://github.com/appwrite/sdk-for-go',
'package' => 'https://github.com/appwrite/sdk-for-go',
'enabled' => true,
@@ -357,7 +357,7 @@ return [
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.21.2',
'version' => '0.22.0',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@@ -376,7 +376,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '19.2.1',
'version' => '19.4.0',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@@ -395,7 +395,7 @@ return [
[
'key' => 'kotlin',
'name' => 'Kotlin',
'version' => '12.2.1',
'version' => '12.3.0',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,
@@ -418,7 +418,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '13.2.2',
'version' => '13.3.0',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -14,7 +14,7 @@ return [
],
'DART' => [
'name' => 'dart',
'versions' => ['3.8', '3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16']
'versions' => ['3.9', '3.8', '3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16']
],
'GO' => [
'name' => 'go',
@@ -38,6 +38,6 @@ return [
],
'FLUTTER' => [
'name' => 'flutter',
'versions' => ['3.32', '3.24']
'versions' => ['3.35', '3.32', '3.24']
],
];
+31 -2
View File
@@ -24,6 +24,7 @@ class UseCases
public const ECOMMERCE = 'ecommerce';
public const DOCUMENTATION = 'documentation';
public const BLOG = 'blog';
public const AI = 'artificial intelligence';
}
const TEMPLATE_FRAMEWORKS = [
@@ -83,7 +84,7 @@ const TEMPLATE_FRAMEWORKS = [
'installCommand' => '',
'buildCommand' => 'flutter build web',
'outputDirectory' => './build/web',
'buildRuntime' => 'flutter-3.29',
'buildRuntime' => 'flutter-3.35',
'adapter' => 'static',
'fallbackFile' => '',
],
@@ -970,7 +971,7 @@ return [
'name' => 'TanStack Start starter',
'useCases' => [UseCases::STARTER],
'tagline' => 'Simple TanStack Start application integrated with Appwrite SDK.',
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'score' => 9, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'screenshotDark' => $url . '/images/sites/templates/starter-for-tanstack-start-dark.png',
'screenshotLight' => $url . '/images/sites/templates/starter-for-tanstack-start-light.png',
'frameworks' => [
@@ -1443,4 +1444,32 @@ return [
'providerVersion' => '0.3.*',
'variables' => []
],
[
'key' => 'text-to-speech',
'name' => 'Text-to-speech with ElevenLabs',
'tagline' => 'Next.js app that transforms text into natural, human-like speech using ElevenLabs',
'score' => 10, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
'useCases' => [UseCases::AI],
'screenshotDark' => $url . '/images/sites/templates/text-to-speech-dark.png',
'screenshotLight' => $url . '/images/sites/templates/text-to-speech-light.png',
'frameworks' => [
getFramework('NEXTJS', [
'providerRootDirectory' => './nextjs/text-to-speech',
]),
],
'vcsProvider' => 'github',
'providerRepositoryId' => 'templates-for-sites',
'providerOwner' => 'appwrite',
'providerVersion' => '0.6.*',
'variables' => [
[
'name' => 'ELEVENLABS_API_KEY',
'description' => 'Your ElevenLabs API key',
'value' => '',
'placeholder' => 'sk_.....',
'required' => true,
'type' => 'password'
],
]
],
];
+10
View File
@@ -952,6 +952,16 @@ return [
'question' => '',
'filter' => ''
],
[
'name' => '_APP_BROWSER_HOST',
'description' => 'The host used by Appwrite to communicate with the browser service for screenshots.',
'introduction' => '1.8.0',
'default' => 'http://appwrite-browser:3000/v1',
'required' => false,
'overwrite' => true,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_EXECUTOR_RUNTIME_NETWORK',
'description' => 'Deprecated with 0.14.0, use \'OPEN_RUNTIMES_NETWORK\' instead.',
+104 -7
View File
@@ -20,7 +20,7 @@ use Appwrite\Event\Messaging;
use Appwrite\Event\StatsUsage;
use Appwrite\Extend\Exception;
use Appwrite\Hooks\Hooks;
use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Email as EmailValidator;
use Appwrite\Network\Validator\Redirect;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\SDK\AuthType;
@@ -57,6 +57,7 @@ use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
use Utopia\Emails\Email;
use Utopia\Locale\Locale;
use Utopia\Storage\Validator\FileName;
use Utopia\System\System;
@@ -75,6 +76,14 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
$subject = $locale->getText("emails.sessionAlert.subject");
$preview = $locale->getText("emails.sessionAlert.preview");
$customTemplate = $project->getAttribute('templates', [])['email.sessionAlert-' . $locale->default] ?? [];
$smtpBaseTemplate = $project->getAttribute('smtpBaseTemplate', 'email-base');
$validator = new FileName();
if (!$validator->isValid($smtpBaseTemplate)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid template path');
}
$bodyTemplate = __DIR__ . '/../../config/locale/templates/' . $smtpBaseTemplate . '.tpl';
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-session-alert.tpl');
$message
@@ -157,12 +166,25 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
'country' => $locale->getText('countries.' . $session->getAttribute('countryCode'), $locale->getText('locale.country.unknown')),
];
if ($smtpBaseTemplate === APP_BRANDED_EMAIL_BASE_TEMPLATE) {
$emailVariables = array_merge($emailVariables, [
'accentColor' => APP_EMAIL_ACCENT_COLOR,
'logoUrl' => APP_EMAIL_LOGO_URL,
'twitterUrl' => APP_SOCIAL_TWITTER,
'discordUrl' => APP_SOCIAL_DISCORD,
'githubUrl' => APP_SOCIAL_GITHUB_APPWRITE,
'termsUrl' => APP_EMAIL_TERMS_URL,
'privacyUrl' => APP_EMAIL_PRIVACY_URL,
]);
}
$email = $user->getAttribute('email');
$queueForMails
->setSubject($subject)
->setPreview($preview)
->setBody($body)
->setBodyTemplate($bodyTemplate)
->setVariables($emailVariables)
->setRecipient($email)
->trigger();
@@ -316,7 +338,7 @@ App::post('/v1/account')
))
->label('abuse-limit', 10)
->param('userId', '', new CustomId(), 'User 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('email', '', new EmailValidator(), 'User email.')
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be between 8 and 256 chars.', false, ['project', 'passwordsDictionary'])
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
->inject('request')
@@ -373,6 +395,13 @@ App::post('/v1/account')
$passwordHistory = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
$password = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
try {
$emailCanonical = new Email($email);
} catch (Throwable) {
$emailCanonical = null;
}
try {
$userId = $userId == 'unique()' ? ID::unique() : $userId;
$user->setAttributes([
@@ -401,7 +430,13 @@ App::post('/v1/account')
'authenticators' => null,
'search' => implode(' ', [$userId, $email, $name]),
'accessedAt' => DateTime::now(),
'emailCanonical' => $emailCanonical?->getCanonical(),
'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(),
'emailIsCorporate' => $emailCanonical?->isCorporate(),
'emailIsDisposable' => $emailCanonical?->isDisposable(),
'emailIsFree' => $emailCanonical?->isFree(),
]);
$user->removeAttribute('$sequence');
$user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
try {
@@ -882,7 +917,7 @@ App::post('/v1/account/sessions/email')
))
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},email:{param-email}')
->param('email', '', new Email(), 'User email.')
->param('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
->inject('request')
->inject('response')
@@ -1577,6 +1612,12 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
}
try {
$emailCanonical = new Email($email);
} catch (Throwable) {
$emailCanonical = null;
}
try {
$userId = ID::unique();
$user->setAttributes([
@@ -1604,7 +1645,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'authenticators' => null,
'search' => implode(' ', [$userId, $email, $name]),
'accessedAt' => DateTime::now(),
'emailCanonical' => $emailCanonical?->getCanonical(),
'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(),
'emailIsCorporate' => $emailCanonical?->isCorporate(),
'emailIsDisposable' => $emailCanonical?->isDisposable(),
'emailIsFree' => $emailCanonical?->isFree(),
]);
$user->removeAttribute('$sequence');
$userDoc = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$dbForProject->createDocument('targets', new Document([
@@ -1675,6 +1722,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
if (empty($user->getAttribute('email'))) {
$user->setAttribute('email', $oauth2->getUserEmail($accessToken));
try {
$emailCanonical = new Email($user->getAttribute('email'));
} catch (Throwable) {
$emailCanonical = null;
}
$user->setAttribute('emailCanonical', $emailCanonical?->getCanonical());
$user->setAttribute('emailIsCanonical', $emailCanonical?->isCanonicalSupported());
$user->setAttribute('emailIsCorporate', $emailCanonical?->isCorporate());
$user->setAttribute('emailIsDisposable', $emailCanonical?->isDisposable());
$user->setAttribute('emailIsFree', $emailCanonical?->isFree());
}
if (empty($user->getAttribute('name'))) {
@@ -1923,7 +1982,7 @@ App::post('/v1/account/tokens/magic-url')
->label('abuse-limit', 60)
->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. If the email address has never been used, a new account is created using the provided userId. Otherwise, if the email address is already attached to an account, the user ID is ignored.')
->param('email', '', new Email(), 'User email.')
->param('email', '', new EmailValidator(), 'User email.')
->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')
@@ -1969,6 +2028,12 @@ App::post('/v1/account/tokens/magic-url')
$userId = $userId === 'unique()' ? ID::unique() : $userId;
try {
$emailCanonical = new Email($email);
} catch (Throwable) {
$emailCanonical = null;
}
$user->setAttributes([
'$id' => $userId,
'$permissions' => [
@@ -1993,6 +2058,11 @@ App::post('/v1/account/tokens/magic-url')
'authenticators' => null,
'search' => implode(' ', [$userId, $email]),
'accessedAt' => DateTime::now(),
'emailCanonical' => $emailCanonical?->getCanonical(),
'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(),
'emailIsCorporate' => $emailCanonical?->isCorporate(),
'emailIsDisposable' => $emailCanonical?->isDisposable(),
'emailIsFree' => $emailCanonical?->isFree(),
]);
$user->removeAttribute('$sequence');
@@ -2176,7 +2246,7 @@ App::post('/v1/account/tokens/email')
->label('abuse-limit', 10)
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
->param('userId', '', new CustomId(), 'User 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. If the email address has never been used, a new account is created using the provided userId. Otherwise, if the email address is already attached to an account, the user ID is ignored.')
->param('email', '', new Email(), 'User email.')
->param('email', '', new EmailValidator(), 'User email.')
->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')
@@ -2219,6 +2289,12 @@ App::post('/v1/account/tokens/email')
$userId = $userId === 'unique()' ? ID::unique() : $userId;
try {
$emailCanonical = new Email($email);
} catch (Throwable) {
$emailCanonical = null;
}
$user->setAttributes([
'$id' => $userId,
'$permissions' => [
@@ -2241,6 +2317,11 @@ App::post('/v1/account/tokens/email')
'memberships' => null,
'search' => implode(' ', [$userId, $email]),
'accessedAt' => DateTime::now(),
'emailCanonical' => $emailCanonical?->getCanonical(),
'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(),
'emailIsCorporate' => $emailCanonical?->isCorporate(),
'emailIsDisposable' => $emailCanonical?->isDisposable(),
'emailIsFree' => $emailCanonical?->isFree(),
]);
$user->removeAttribute('$sequence');
@@ -2588,6 +2669,11 @@ App::post('/v1/account/tokens/phone')
'memberships' => null,
'search' => implode(' ', [$userId, $phone]),
'accessedAt' => DateTime::now(),
'emailCanonical' => null,
'emailIsCanonical' => null,
'emailIsCorporate' => null,
'emailIsDisposable' => null,
'emailIsFree' => null,
]);
$user->removeAttribute('$sequence');
@@ -3016,7 +3102,7 @@ App::patch('/v1/account/email')
],
contentType: ContentType::JSON
))
->param('email', '', new Email(), 'User email.')
->param('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
->inject('requestTimestamp')
->inject('response')
@@ -3051,9 +3137,20 @@ App::patch('/v1/account/email')
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
}
try {
$emailCanonical = new Email($email);
} catch (Throwable) {
$emailCanonical = null;
}
$user
->setAttribute('email', $email)
->setAttribute('emailVerification', false) // After this user needs to confirm mail again
->setAttribute('emailCanonical', $emailCanonical?->getCanonical())
->setAttribute('emailIsCanonical', $emailCanonical?->isCanonicalSupported())
->setAttribute('emailIsCorporate', $emailCanonical?->isCorporate())
->setAttribute('emailIsDisposable', $emailCanonical?->isDisposable())
->setAttribute('emailIsFree', $emailCanonical?->isFree())
;
if (empty($passwordUpdate)) {
@@ -3290,7 +3387,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('email', '', new EmailValidator(), 'User email.')
->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')
+184
View File
@@ -1,5 +1,6 @@
<?php
use Appwrite\Event\StatsUsage;
use Appwrite\Extend\Exception;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
@@ -23,6 +24,8 @@ use Utopia\Fetch\Client;
use Utopia\Image\Image;
use Utopia\Logger\Logger;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Boolean;
use Utopia\Validator\HexColor;
use Utopia\Validator\Range;
@@ -635,6 +638,187 @@ App::get('/v1/avatars/initials')
->file($image->getImageBlob());
});
App::get('/v1/avatars/screenshots')
->desc('Get webpage screenshot')
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
->label('usage.metric', METRIC_AVATARS_SCREENSHOTS_GENERATED)
->label('abuse-limit', 60)
->label('cache', true)
->label('cache.resourceType', 'avatar/screenshot')
->label('cache.resource', 'screenshot/{request.url}/{request.width}/{request.height}/{request.scale}/{request.theme}/{request.userAgent}/{request.fullpage}/{request.locale}/{request.timezone}/{request.latitude}/{request.longitude}/{request.accuracy}/{request.touch}/{request.permissions}/{request.sleep}/{request.quality}/{request.output}')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getScreenshot',
description: '/docs/references/avatars/get-screenshot.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
type: MethodType::LOCATION,
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_NONE,
)
],
contentType: ContentType::IMAGE_PNG
))
->param('url', '', new URL(['http', 'https']), 'Website URL which you want to capture.')
->param('headers', [], new Assoc(), 'HTTP headers to send with the browser request. Defaults to empty.', true)
->param('viewportWidth', 1280, new Range(1, 1920), 'Browser viewport width. Pass an integer between 1 to 1920. Defaults to 1280.', true)
->param('viewportHeight', 720, new Range(1, 1080), 'Browser viewport height. Pass an integer between 1 to 1080. Defaults to 720.', true)
->param('scale', 1, new Range(0.1, 3, Range::TYPE_FLOAT), 'Browser scale factor. Pass a number between 0.1 to 3. Defaults to 1.', true)
->param('theme', 'light', new WhiteList(['light', 'dark']), 'Browser theme. Pass "light" or "dark". Defaults to "light".', true)
->param('userAgent', '', new Text(512), 'Custom user agent string. Defaults to browser default.', true)
->param('fullpage', false, new Boolean(true), 'Capture full page scroll. Pass 0 for viewport only, or 1 for full page. Defaults to 0.', true)
->param('locale', '', new Text(10), 'Browser locale (e.g., "en-US", "fr-FR"). Defaults to browser default.', true)
->param('timezone', '', new WhiteList(timezone_identifiers_list()), 'IANA timezone identifier (e.g., "America/New_York", "Europe/London"). Defaults to browser default.', true)
->param('latitude', 0, new Range(-90, 90, Range::TYPE_FLOAT), 'Geolocation latitude. Pass a number between -90 to 90. Defaults to 0.', true)
->param('longitude', 0, new Range(-180, 180, Range::TYPE_FLOAT), 'Geolocation longitude. Pass a number between -180 to 180. Defaults to 0.', true)
->param('accuracy', 0, new Range(0, 100000, Range::TYPE_FLOAT), 'Geolocation accuracy in meters. Pass a number between 0 to 100000. Defaults to 0.', true)
->param('touch', false, new Boolean(true), 'Enable touch support. Pass 0 for no touch, or 1 for touch enabled. Defaults to 0.', true)
->param('permissions', [], new ArrayList(new WhiteList(['geolocation', 'camera', 'microphone', 'notifications', 'midi', 'push', 'clipboard-read', 'clipboard-write', 'payment-handler', 'usb', 'bluetooth', 'accelerometer', 'gyroscope', 'magnetometer', 'ambient-light-sensor', 'background-sync', 'persistent-storage', 'screen-wake-lock', 'web-share', 'xr-spatial-tracking'])), 'Browser permissions to grant. Pass an array of permission names like ["geolocation", "camera", "microphone"]. Defaults to empty.', true)
->param('sleep', 0, new Range(0, 10), 'Wait time in seconds before taking the screenshot. Pass an integer between 0 to 10. Defaults to 0.', true)
->param('width', 0, new Range(0, 2000), 'Output image width. Pass 0 to use original width, or an integer between 1 to 2000. Defaults to 0 (original width).', true)
->param('height', 0, new Range(0, 2000), 'Output image height. Pass 0 to use original height, or an integer between 1 to 2000. Defaults to 0 (original height).', true)
->param('quality', -1, new Range(-1, 100), 'Screenshot quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true)
->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true)
->inject('response')
->inject('queueForStatsUsage')
->action(function (string $url, array $headers, int $viewportWidth, int $viewportHeight, float $scale, string $theme, string $userAgent, bool $fullpage, string $locale, string $timezone, float $latitude, float $longitude, float $accuracy, bool $touch, array $permissions, int $sleep, int $width, int $height, int $quality, string $output, Response $response, StatsUsage $queueForStatsUsage) {
if (!\extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
}
$domain = new Domain(\parse_url($url, PHP_URL_HOST));
if (!$domain->isKnown()) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
$client = new Client();
$client->setTimeout(30);
$client->addHeader('content-type', Client::CONTENT_TYPE_APPLICATION_JSON);
// Convert indexed array to empty array (should not happen due to Assoc validator)
if (is_array($headers) && count($headers) > 0 && array_keys($headers) === range(0, count($headers) - 1)) {
$headers = [];
}
// Create a new object to ensure proper JSON serialization
$headersObject = new \stdClass();
foreach ($headers as $key => $value) {
$headersObject->$key = $value;
}
// Create the config with headers as an object
// The custom browser service accepts: url, theme, headers, sleep, viewport, userAgent, fullPage, locale, timezoneId, geolocation, hasTouch, scale
$config = [
'url' => $url,
'theme' => $theme,
'headers' => $headersObject,
'sleep' => $sleep * 1000, // Convert seconds to milliseconds
'waitUntil' => 'load',
'viewport' => [
'width' => $viewportWidth,
'height' => $viewportHeight
]
];
// Add scale if not default
if ($scale != 1) {
$config['deviceScaleFactor'] = $scale;
}
// Add optional parameters that were set, preserving arrays as arrays
if (!empty($userAgent)) {
$config['userAgent'] = $userAgent;
}
if ($fullpage) {
$config['fullPage'] = true;
}
if (!empty($locale)) {
$config['locale'] = $locale;
}
if (!empty($timezone)) {
$config['timezoneId'] = $timezone;
}
// Add geolocation if any coordinates are provided
if ($latitude != 0 || $longitude != 0) {
$config['geolocation'] = [
'latitude' => $latitude,
'longitude' => $longitude,
'accuracy' => $accuracy
];
}
if ($touch) {
$config['hasTouch'] = true;
}
// Add permissions if provided (preserve as array)
if (!empty($permissions)) {
$config['permissions'] = $permissions; // Keep as array
}
try {
$browserEndpoint = System::getEnv('_APP_BROWSER_HOST', 'http://appwrite-browser:3000/v1');
$fetchResponse = $client->fetch(
url: $browserEndpoint . '/screenshots',
method: 'POST',
body: $config
);
if ($fetchResponse->getStatusCode() >= 400) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED, 'Screenshot service failed: ' . $fetchResponse->getBody());
}
$screenshot = $fetchResponse->getBody();
if (empty($screenshot)) {
throw new Exception(Exception::AVATAR_IMAGE_NOT_FOUND, 'Screenshot not generated');
}
// Determine if image processing is needed
$needsProcessing = ($width > 0 || $height > 0) || $quality !== -1 || !empty($output);
if ($needsProcessing) {
// Process image with cropping, quality adjustment, or format conversion
$image = new Image($screenshot);
$image->crop($width, $height);
$output = $output ?: 'png'; // Default to PNG if not specified
$resizedScreenshot = $image->output($output, $quality);
unset($image);
} else {
// Return original screenshot without processing
$resizedScreenshot = $screenshot;
$output = 'png'; // Screenshots are typically PNG by default
}
// Set content type based on output format
$outputs = Config::getParam('storage-outputs');
$contentType = $outputs[$output] ?? $outputs['png'];
$queueForStatsUsage->addMetric(METRIC_AVATARS_SCREENSHOTS_GENERATED, 1);
$response
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType($contentType)
->file($resizedScreenshot);
} catch (\Throwable $th) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED, 'Screenshot generation failed: ' . $th->getMessage());
}
});
App::get('/v1/cards/cloud')
->desc('Get front Of Cloud Card')
->groups(['api', 'avatars'])
-5
View File
@@ -37,7 +37,6 @@ App::get('/v1/locale')
$currencies = Config::getParam('locale-currencies');
$output = [];
$ip = $request->getIP();
$time = (60 * 60 * 24 * 45); // 45 days cache
$output['ip'] = $ip;
@@ -68,10 +67,6 @@ App::get('/v1/locale')
$output['currency'] = $currency;
}
$response
->addHeader('Cache-Control', 'public, max-age=' . $time)
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
;
$response->dynamic(new Document($output), Response::MODEL_LOCALE);
});
+71 -70
View File
@@ -49,6 +49,7 @@ use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\JSON;
use Utopia\Validator\Nullable;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
@@ -80,12 +81,12 @@ App::post('/v1/messaging/providers/mailgun')
->param('name', '', new Text(128), 'Provider name.')
->param('apiKey', '', new Text(0), 'Mailgun API Key.', true)
->param('domain', '', new Text(0), 'Mailgun Domain.', true)
->param('isEuRegion', null, new Boolean(), 'Set as EU region.', true)
->param('isEuRegion', null, new Nullable(new Boolean()), 'Set as EU region.', true)
->param('fromName', '', new Text(128, 0), 'Sender Name.', true)
->param('fromEmail', '', new Email(), 'Sender email address.', true)
->param('replyToName', '', new Text(128, 0), 'Name set in the reply to field for the mail. Default value is sender name. Reply to name must have reply to email as well.', true)
->param('replyToEmail', '', new Email(), 'Email set in the reply to field for the mail. Default value is sender email. Reply to email must have reply to name as well.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -177,7 +178,7 @@ App::post('/v1/messaging/providers/sendgrid')
->param('fromEmail', '', new Email(), 'Sender email address.', true)
->param('replyToName', '', new Text(128, 0), 'Name set in the reply to field for the mail. Default value is sender name.', true)
->param('replyToEmail', '', new Email(), 'Email set in the reply to field for the mail. Default value is sender email.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -259,7 +260,7 @@ App::post('/v1/messaging/providers/resend')
->param('fromEmail', '', new Email(), 'Sender email address.', true)
->param('replyToName', '', new Text(128, 0), 'Name set in the reply to field for the mail. Default value is sender name.', true)
->param('replyToEmail', '', new Email(), 'Email set in the reply to field for the mail. Default value is sender email.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -366,7 +367,7 @@ App::post('/v1/messaging/providers/smtp')
->param('fromEmail', '', new Email(), 'Sender email address.', true)
->param('replyToName', '', new Text(128, 0), 'Name set in the reply to field for the mail. Default value is sender name.', true)
->param('replyToEmail', '', new Email(), 'Email set in the reply to field for the mail. Default value is sender email.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -453,7 +454,7 @@ App::post('/v1/messaging/providers/msg91')
->param('templateId', '', new Text(0), 'Msg91 template ID', true)
->param('senderId', '', new Text(0), 'Msg91 sender ID.', true)
->param('authKey', '', new Text(0), 'Msg91 auth key.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -536,7 +537,7 @@ App::post('/v1/messaging/providers/telesign')
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('customerId', '', new Text(0), 'Telesign customer ID.', true)
->param('apiKey', '', new Text(0), 'Telesign API key.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -620,7 +621,7 @@ App::post('/v1/messaging/providers/textmagic')
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('username', '', new Text(0), 'Textmagic username.', true)
->param('apiKey', '', new Text(0), 'Textmagic apiKey.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -704,7 +705,7 @@ App::post('/v1/messaging/providers/twilio')
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('accountSid', '', new Text(0), 'Twilio account secret ID.', true)
->param('authToken', '', new Text(0), 'Twilio authentication token.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -788,7 +789,7 @@ App::post('/v1/messaging/providers/vonage')
->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('apiKey', '', new Text(0), 'Vonage API key.', true)
->param('apiSecret', '', new Text(0), 'Vonage API secret.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -888,8 +889,8 @@ App::post('/v1/messaging/providers/fcm')
])
->param('providerId', '', new CustomId(), 'Provider 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('name', '', new Text(128), 'Provider name.')
->param('serviceAccountJSON', null, new JSON(), 'FCM service account JSON.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('serviceAccountJSON', null, new Nullable(new JSON()), 'FCM service account JSON.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -982,7 +983,7 @@ App::post('/v1/messaging/providers/apns')
->param('teamId', '', new Text(0), 'APNS team ID.', true)
->param('bundleId', '', new Text(0), 'APNS bundle ID.', true)
->param('sandbox', false, new Boolean(), 'Use APNS sandbox environment.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -1270,8 +1271,8 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
->param('name', '', new Text(128), 'Provider name.', true)
->param('apiKey', '', new Text(0), 'Mailgun API Key.', true)
->param('domain', '', new Text(0), 'Mailgun Domain.', true)
->param('isEuRegion', null, new Boolean(), 'Set as EU region.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('isEuRegion', null, new Nullable(new Boolean()), 'Set as EU region.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('fromName', '', new Text(128), 'Sender Name.', true)
->param('fromEmail', '', new Email(), 'Sender email address.', true)
->param('replyToName', '', new Text(128), 'Name set in the reply to field for the mail. Default value is sender name.', true)
@@ -1381,7 +1382,7 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('apiKey', '', new Text(0), 'Sendgrid API key.', true)
->param('fromName', '', new Text(128), 'Sender Name.', true)
->param('fromEmail', '', new Email(), 'Sender email address.', true)
@@ -1479,7 +1480,7 @@ App::patch('/v1/messaging/providers/resend/:providerId')
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('apiKey', '', new Text(0), 'Resend API key.', true)
->param('fromName', '', new Text(128), 'Sender Name.', true)
->param('fromEmail', '', new Email(), 'Sender email address.', true)
@@ -1597,17 +1598,17 @@ App::patch('/v1/messaging/providers/smtp/:providerId')
->param('providerId', '', new UID(), 'Provider ID.')
->param('name', '', new Text(128), 'Provider name.', true)
->param('host', '', new Text(0), 'SMTP hosts. Either a single hostname or multiple semicolon-delimited hostnames. You can also specify a different port for each host such as `smtp1.example.com:25;smtp2.example.com`. You can also specify encryption type, for example: `tls://smtp1.example.com:587;ssl://smtp2.example.com:465"`. Hosts will be tried in order.', true)
->param('port', null, new Range(1, 65535), 'SMTP port.', true)
->param('port', null, new Nullable(new Range(1, 65535)), 'SMTP port.', true)
->param('username', '', new Text(0), 'Authentication username.', true)
->param('password', '', new Text(0), 'Authentication password.', true)
->param('encryption', '', new WhiteList(['none', 'ssl', 'tls']), 'Encryption type. Can be \'ssl\' or \'tls\'', true)
->param('autoTLS', null, new Boolean(), 'Enable SMTP AutoTLS feature.', true)
->param('autoTLS', null, new Nullable(new Boolean()), 'Enable SMTP AutoTLS feature.', true)
->param('mailer', '', new Text(0), 'The value to use for the X-Mailer header.', true)
->param('fromName', '', new Text(128), 'Sender Name.', true)
->param('fromEmail', '', new Email(), 'Sender email address.', true)
->param('replyToName', '', new Text(128), 'Name set in the Reply To field for the mail. Default value is Sender Name.', true)
->param('replyToEmail', '', new Text(128), 'Email set in the Reply To field for the mail. Default value is Sender Email.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -1725,7 +1726,7 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('templateId', '', new Text(0), 'Msg91 template ID.', true)
->param('senderId', '', new Text(0), 'Msg91 sender ID.', true)
->param('authKey', '', new Text(0), 'Msg91 auth key.', true)
@@ -1812,7 +1813,7 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('customerId', '', new Text(0), 'Telesign customer ID.', true)
->param('apiKey', '', new Text(0), 'Telesign API key.', true)
->param('from', '', new Text(256), 'Sender number.', true)
@@ -1901,7 +1902,7 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('username', '', new Text(0), 'Textmagic username.', true)
->param('apiKey', '', new Text(0), 'Textmagic apiKey.', true)
->param('from', '', new Text(256), 'Sender number.', true)
@@ -1990,7 +1991,7 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('accountSid', '', new Text(0), 'Twilio account secret ID.', true)
->param('authToken', '', new Text(0), 'Twilio authentication token.', true)
->param('from', '', new Text(256), 'Sender number.', true)
@@ -2079,7 +2080,7 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('apiKey', '', new Text(0), 'Vonage API key.', true)
->param('apiSecret', '', new Text(0), 'Vonage API secret.', true)
->param('from', '', new Text(256), 'Sender number.', true)
@@ -2187,8 +2188,8 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
])
->param('providerId', '', new UID(), 'Provider ID.')
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('serviceAccountJSON', null, new JSON(), 'FCM service account JSON.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('serviceAccountJSON', null, new Nullable(new JSON()), 'FCM service account JSON.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -2282,12 +2283,12 @@ App::patch('/v1/messaging/providers/apns/:providerId')
])
->param('providerId', '', new UID(), 'Provider ID.')
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Boolean(), 'Set as enabled.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('authKey', '', new Text(0), 'APNS authentication key.', true)
->param('authKeyId', '', new Text(0), 'APNS authentication key ID.', true)
->param('teamId', '', new Text(0), 'APNS team ID.', true)
->param('bundleId', '', new Text(0), 'APNS bundle ID.', true)
->param('sandbox', null, new Boolean(), 'Use APNS sandbox environment.', true)
->param('sandbox', null, new Nullable(new Boolean()), 'Use APNS sandbox environment.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -2676,8 +2677,8 @@ App::patch('/v1/messaging/topics/:topicId')
]
))
->param('topicId', '', new UID(), 'Topic ID.')
->param('name', null, new Text(128), 'Topic Name.', true)
->param('subscribe', null, new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with subscribe permission. By default all users are granted with any subscribe permission. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
->param('name', null, new Nullable(new Text(128)), 'Topic Name.', true)
->param('subscribe', null, new Nullable(new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of role strings with subscribe permission. By default all users are granted with any subscribe permission. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -3190,7 +3191,7 @@ App::post('/v1/messaging/messages/email')
->param('attachments', [], new ArrayList(new CompoundUID()), 'Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.', true)
->param('draft', false, new Boolean(), 'Is message a draft', true)
->param('html', false, new Boolean(), 'Is content of type HTML', true)
->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('dbForPlatform')
@@ -3363,7 +3364,7 @@ App::post('/v1/messaging/messages/sms')
->param('users', [], new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('draft', false, new Boolean(), 'Is message a draft', true)
->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('dbForPlatform')
@@ -3486,7 +3487,7 @@ App::post('/v1/messaging/messages/push')
->param('topics', [], new ArrayList(new UID()), 'List of Topic IDs.', true)
->param('users', [], new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('data', null, new JSON(), 'Additional key-value pair data for push notification.', true)
->param('data', null, new Nullable(new JSON()), 'Additional key-value pair data for push notification.', true)
->param('action', '', new Text(256), 'Action for push notification.', true)
->param('image', '', new CompoundUID(), 'Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.', true)
->param('icon', '', new Text(256), 'Icon for push notification. Available only for Android and Web Platform.', true)
@@ -3495,7 +3496,7 @@ App::post('/v1/messaging/messages/push')
->param('tag', '', new Text(256), 'Tag for push notification. Available only for Android Platform.', true)
->param('badge', -1, new Integer(), 'Badge for push notification. Available only for iOS Platform.', true)
->param('draft', false, new Boolean(), 'Is message a draft', true)
->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->param('contentAvailable', false, new Boolean(), 'If set to true, the notification will be delivered in the background. Available only for iOS Platform.', true)
->param('critical', false, new Boolean(), 'If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.', true)
->param('priority', 'high', new WhiteList(['normal', 'high']), 'Set the notification priority. "normal" will consider device state and may not deliver notifications immediately. "high" will always attempt to immediately deliver the notification.', true)
@@ -3981,17 +3982,17 @@ App::patch('/v1/messaging/messages/email/:messageId')
]
))
->param('messageId', '', new UID(), 'Message ID.')
->param('topics', null, new ArrayList(new UID()), 'List of Topic IDs.', true)
->param('users', null, new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', null, new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('subject', null, new Text(998), 'Email Subject.', true)
->param('content', null, new Text(64230), 'Email Content.', true)
->param('draft', null, new Boolean(), 'Is message a draft', true)
->param('html', null, new Boolean(), 'Is content of type HTML', true)
->param('cc', null, new ArrayList(new UID()), 'Array of target IDs to be added as CC.', true)
->param('bcc', null, new ArrayList(new UID()), 'Array of target IDs to be added as BCC.', true)
->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->param('attachments', null, new ArrayList(new CompoundUID()), 'Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.', true)
->param('topics', null, new Nullable(new ArrayList(new UID())), 'List of Topic IDs.', true)
->param('users', null, new Nullable(new ArrayList(new UID())), 'List of User IDs.', true)
->param('targets', null, new Nullable(new ArrayList(new UID())), 'List of Targets IDs.', true)
->param('subject', null, new Nullable(new Text(998)), 'Email Subject.', true)
->param('content', null, new Nullable(new Text(64230)), 'Email Content.', true)
->param('draft', null, new Nullable(new Boolean()), 'Is message a draft', true)
->param('html', null, new Nullable(new Boolean()), 'Is content of type HTML', true)
->param('cc', null, new Nullable(new ArrayList(new UID())), 'Array of target IDs to be added as CC.', true)
->param('bcc', null, new Nullable(new ArrayList(new UID())), 'Array of target IDs to be added as BCC.', true)
->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->param('attachments', null, new Nullable(new ArrayList(new CompoundUID())), 'Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('dbForPlatform')
@@ -4207,12 +4208,12 @@ App::patch('/v1/messaging/messages/sms/:messageId')
)
])
->param('messageId', '', new UID(), 'Message ID.')
->param('topics', null, new ArrayList(new UID()), 'List of Topic IDs.', true)
->param('users', null, new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', null, new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('content', null, new Text(64230), 'Email Content.', true)
->param('draft', null, new Boolean(), 'Is message a draft', true)
->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->param('topics', null, new Nullable(new ArrayList(new UID())), 'List of Topic IDs.', true)
->param('users', null, new Nullable(new ArrayList(new UID())), 'List of User IDs.', true)
->param('targets', null, new Nullable(new ArrayList(new UID())), 'List of Targets IDs.', true)
->param('content', null, new Nullable(new Text(64230)), 'Email Content.', true)
->param('draft', null, new Nullable(new Boolean()), 'Is message a draft', true)
->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('dbForPlatform')
@@ -4369,24 +4370,24 @@ App::patch('/v1/messaging/messages/push/:messageId')
]
))
->param('messageId', '', new UID(), 'Message ID.')
->param('topics', null, new ArrayList(new UID()), 'List of Topic IDs.', true)
->param('users', null, new ArrayList(new UID()), 'List of User IDs.', true)
->param('targets', null, new ArrayList(new UID()), 'List of Targets IDs.', true)
->param('title', null, new Text(256), 'Title for push notification.', true)
->param('body', null, new Text(64230), 'Body for push notification.', true)
->param('data', null, new JSON(), 'Additional Data for push notification.', true)
->param('action', null, new Text(256), 'Action for push notification.', true)
->param('image', null, new CompoundUID(), 'Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.', true)
->param('icon', null, new Text(256), 'Icon for push notification. Available only for Android and Web platforms.', true)
->param('sound', null, new Text(256), 'Sound for push notification. Available only for Android and iOS platforms.', true)
->param('color', null, new Text(256), 'Color for push notification. Available only for Android platforms.', true)
->param('tag', null, new Text(256), 'Tag for push notification. Available only for Android platforms.', true)
->param('badge', null, new Integer(), 'Badge for push notification. Available only for iOS platforms.', true)
->param('draft', null, new Boolean(), 'Is message a draft', true)
->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->param('contentAvailable', null, new Boolean(), 'If set to true, the notification will be delivered in the background. Available only for iOS Platform.', true)
->param('critical', null, new Boolean(), 'If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.', true)
->param('priority', null, new WhiteList(['normal', 'high']), 'Set the notification priority. "normal" will consider device battery state and may send notifications later. "high" will always attempt to immediately deliver the notification.', true)
->param('topics', null, new Nullable(new ArrayList(new UID())), 'List of Topic IDs.', true)
->param('users', null, new Nullable(new ArrayList(new UID())), 'List of User IDs.', true)
->param('targets', null, new Nullable(new ArrayList(new UID())), 'List of Targets IDs.', true)
->param('title', null, new Nullable(new Text(256)), 'Title for push notification.', true)
->param('body', null, new Nullable(new Text(64230)), 'Body for push notification.', true)
->param('data', null, new Nullable(new JSON()), 'Additional Data for push notification.', true)
->param('action', null, new Nullable(new Text(256)), 'Action for push notification.', true)
->param('image', null, new Nullable(new CompoundUID()), 'Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.', true)
->param('icon', null, new Nullable(new Text(256)), 'Icon for push notification. Available only for Android and Web platforms.', true)
->param('sound', null, new Nullable(new Text(256)), 'Sound for push notification. Available only for Android and iOS platforms.', true)
->param('color', null, new Nullable(new Text(256)), 'Color for push notification. Available only for Android platforms.', true)
->param('tag', null, new Nullable(new Text(256)), 'Tag for push notification. Available only for Android platforms.', true)
->param('badge', null, new Nullable(new Integer()), 'Badge for push notification. Available only for iOS platforms.', true)
->param('draft', null, new Nullable(new Boolean()), 'Is message a draft', true)
->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->param('contentAvailable', null, new Nullable(new Boolean()), 'If set to true, the notification will be delivered in the background. Available only for iOS Platform.', true)
->param('critical', null, new Nullable(new Boolean()), 'If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.', true)
->param('priority', null, new Nullable(new WhiteList(['normal', 'high'])), 'Set the notification priority. "normal" will consider device battery state and may send notifications later. "high" will always attempt to immediately deliver the notification.', true)
->inject('queueForEvents')
->inject('dbForProject')
->inject('dbForPlatform')
+4 -4
View File
@@ -468,7 +468,6 @@ App::post('/v1/migrations/csv/exports')
]
))
->param('resourceId', null, new CompoundUID(), 'Composite ID in the format {databaseId:collectionId}, identifying a collection within a database to export.')
->param('bucketId', '', new UID(), 'Storage bucket unique ID where the exported CSV will be stored.')
->param('filename', '', new Text(255), 'The name of the file to be created for the export, excluding the .csv extension.')
->param('columns', [], new ArrayList(new Text(Database::LENGTH_KEY)), 'List of attributes to export. If empty, all attributes will be exported. You can use the `*` wildcard to export all attributes from the collection.', true)
->param('queries', [], new ArrayList(new Text(0)), 'Array of query strings generated using the Query class provided by the SDK to filter documents to export. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
@@ -480,12 +479,12 @@ App::post('/v1/migrations/csv/exports')
->inject('user')
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('project')
->inject('queueForEvents')
->inject('queueForMigrations')
->action(function (
string $resourceId,
string $bucketId,
string $filename,
array $columns,
array $queries,
@@ -497,6 +496,7 @@ App::post('/v1/migrations/csv/exports')
Document $user,
Response $response,
Database $dbForProject,
Database $dbForPlatform,
Document $project,
Event $queueForEvents,
Migration $queueForMigrations
@@ -507,7 +507,7 @@ App::post('/v1/migrations/csv/exports')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'default'));
if ($bucket->isEmpty()) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
@@ -553,7 +553,7 @@ App::post('/v1/migrations/csv/exports')
'resourceData' => '{}',
'errors' => [],
'options' => [
'bucketId' => $bucketId,
'bucketId' => 'default', // Always use internal bucket
'filename' => $filename,
'columns' => $columns,
'queries' => $queries,
+3 -2
View File
@@ -18,6 +18,7 @@ use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DateTimeValidator;
use Utopia\Database\Validator\UID;
use Utopia\Validator\Boolean;
use Utopia\Validator\Nullable;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
@@ -526,8 +527,8 @@ App::put('/v1/project/variables/:variableId')
))
->param('variableId', '', new UID(), 'Variable unique ID.', false)
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true)
->param('secret', null, new Boolean(), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
->param('value', null, new Nullable(new Text(8192, 0)), 'Variable value. Max length: 8192 chars.', true)
->param('secret', null, new Nullable(new Boolean()), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
->inject('project')
->inject('response')
->inject('dbForProject')
+8 -7
View File
@@ -46,6 +46,7 @@ use Utopia\Validator\Boolean;
use Utopia\Validator\Hostname;
use Utopia\Validator\Integer;
use Utopia\Validator\Multiple;
use Utopia\Validator\Nullable;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\URL;
@@ -678,9 +679,9 @@ App::patch('/v1/projects/:projectId/oauth2')
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'Provider Name')
->param('appId', null, new Text(256), 'Provider app ID. Max length: 256 chars.', true)
->param('secret', null, new text(512), 'Provider secret key. Max length: 512 chars.', true)
->param('enabled', null, new Boolean(), 'Provider status. Set to \'false\' to disable new session creation.', true)
->param('appId', null, new Nullable(new Text(256)), 'Provider app ID. Max length: 256 chars.', true)
->param('secret', null, new Nullable(new text(512)), 'Provider secret key. Max length: 512 chars.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Provider status. Set to \'false\' to disable new session creation.', true)
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $provider, ?string $appId, ?string $secret, ?bool $enabled, Response $response, Database $dbForPlatform) {
@@ -1476,8 +1477,8 @@ App::post('/v1/projects/:projectId/keys')
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('expire', null, new Nullable(new DatetimeValidator()), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForPlatform) {
@@ -1615,8 +1616,8 @@ App::put('/v1/projects/:projectId/keys/:keyId')
->param('projectId', '', new UID(), 'Project unique ID.')
->param('keyId', '', new UID(), 'Key unique ID.')
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('expire', null, new Nullable(new DatetimeValidator()), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForPlatform) {
+12 -9
View File
@@ -50,6 +50,7 @@ use Utopia\System\System;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\HexColor;
use Utopia\Validator\Nullable;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
@@ -77,7 +78,7 @@ App::post('/v1/storage/buckets')
))
->param('bucketId', '', 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('name', '', new Text(128), 'Bucket name')
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true)
->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1000 * 1000, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1000 * 1000), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan'])
@@ -290,7 +291,7 @@ App::put('/v1/storage/buckets/:bucketId')
))
->param('bucketId', '', new UID(), 'Bucket unique ID.')
->param('name', null, new Text(128), 'Bucket name', false)
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true)
->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1000 * 1000, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1000 * 1000), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan'])
@@ -418,7 +419,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new CustomId(), 'File ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('file', [], new File(), 'Binary file. Appwrite SDKs provide helpers to handle file input. [Learn about file input](https://appwrite.io/docs/products/storage/upload-download#input-file).', skipValidation: true)
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE])), 'An array of permission strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->inject('request')
->inject('response')
->inject('dbForProject')
@@ -1477,12 +1478,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->inject('response')
->inject('request')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('project')
->inject('mode')
->inject('deviceForFiles')
->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Database $dbForPlatform, Document $project, string $mode, Device $deviceForFiles) {
$decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
try {
@@ -1499,15 +1499,18 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$isInternal = $decoded['internal'] ?? false;
$dbForProject = $isInternal ? $dbForPlatform : $dbForProject;
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
@@ -1645,8 +1648,8 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
))
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File unique ID.')
->param('name', null, new Text(255), 'Name of the file', true)
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->param('name', null, new Nullable(new Text(255)), 'Name of the file', true)
->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE])), 'An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
->inject('response')
->inject('dbForProject')
->inject('user')
+49 -34
View File
@@ -10,7 +10,7 @@ use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
use Appwrite\Event\StatsUsage;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Email as EmailValidator;
use Appwrite\Network\Validator\Redirect;
use Appwrite\Platform\Workers\Deletes;
use Appwrite\SDK\AuthType;
@@ -48,6 +48,7 @@ use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
use Utopia\Emails\Email;
use Utopia\Locale\Locale;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
@@ -468,7 +469,7 @@ App::post('/v1/teams/:teamId/memberships')
))
->label('abuse-limit', 10)
->param('teamId', '', new UID(), 'Team ID.')
->param('email', '', new Email(), 'Email of the new team member.', true)
->param('email', '', new EmailValidator(), 'Email of the new team member.', true)
->param('userId', '', new UID(), 'ID of the user to be added to a team.', true)
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('roles', [], function (Document $project) {
@@ -567,38 +568,52 @@ App::post('/v1/teams/:teamId/memberships')
}
try {
$userId = ID::unique();
$invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([
'$id' => $userId,
'$permissions' => [
Permission::read(Role::any()),
Permission::read(Role::user($userId)),
Permission::update(Role::user($userId)),
Permission::delete(Role::user($userId)),
],
'email' => empty($email) ? null : $email,
'phone' => empty($phone) ? null : $phone,
'emailVerification' => false,
'status' => true,
// TODO: Set password empty?
'password' => Auth::passwordHash(Auth::passwordGenerator(), Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS),
'hash' => Auth::DEFAULT_ALGO,
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
/**
* Set the password update time to 0 for users created using
* team invite and OAuth to allow password updates without an
* old password
*/
'passwordUpdate' => null,
'registration' => DateTime::now(),
'reset' => false,
'name' => $name,
'prefs' => new \stdClass(),
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
])));
$emailCanonical = new Email($email);
} catch (Throwable) {
$emailCanonical = null;
}
$userId = ID::unique();
$userDocument = new Document([
'$id' => $userId,
'$permissions' => [
Permission::read(Role::any()),
Permission::read(Role::user($userId)),
Permission::update(Role::user($userId)),
Permission::delete(Role::user($userId)),
],
'email' => empty($email) ? null : $email,
'phone' => empty($phone) ? null : $phone,
'emailVerification' => false,
'status' => true,
// TODO: Set password empty?
'password' => Auth::passwordHash(Auth::passwordGenerator(), Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS),
'hash' => Auth::DEFAULT_ALGO,
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
/**
* Set the password update time to 0 for users created using
* team invite and OAuth to allow password updates without an
* old password
*/
'passwordUpdate' => null,
'registration' => DateTime::now(),
'reset' => false,
'name' => $name,
'prefs' => new \stdClass(),
'sessions' => null,
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $name]),
'emailCanonical' => $emailCanonical?->getCanonical(),
'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(),
'emailIsCorporate' => $emailCanonical?->isCorporate(),
'emailIsDisposable' => $emailCanonical?->isDisposable(),
'emailIsFree' => $emailCanonical?->isFree(),
]);
try {
$invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', $userDocument));
} catch (Duplicate $th) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
+37 -13
View File
@@ -16,7 +16,7 @@ use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Hooks\Hooks;
use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\Email as EmailValidator;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Deprecated;
@@ -49,12 +49,14 @@ use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
use Utopia\Emails\Email;
use Utopia\Locale\Locale;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
@@ -97,6 +99,12 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
}
}
try {
$emailCanonical = new Email($email);
} catch (Throwable) {
$emailCanonical = null;
}
$password = (!empty($password)) ? ($hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password) : null;
$user = new Document([
'$id' => $userId,
@@ -124,6 +132,11 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
'tokens' => null,
'memberships' => null,
'search' => implode(' ', [$userId, $email, $phone, $name]),
'emailCanonical' => $emailCanonical?->getCanonical(),
'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(),
'emailIsCorporate' => $emailCanonical?->isCorporate(),
'emailIsDisposable' => $emailCanonical?->isDisposable(),
'emailIsFree' => $emailCanonical?->isFree(),
]);
if ($hash === 'plaintext') {
@@ -208,8 +221,8 @@ App::post('/v1/users')
]
))
->param('userId', '', new CustomId(), 'User 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', null, new Email(), 'User email.', true)
->param('phone', null, new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('email', null, new Nullable(new EmailValidator()), 'User email.', true)
->param('phone', null, new Nullable(new Phone()), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'Plain text user password. Must be at least 8 chars.', true, ['project', 'passwordsDictionary'])
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
->inject('response')
@@ -243,7 +256,7 @@ App::post('/v1/users/bcrypt')
]
))
->param('userId', '', new CustomId(), 'User 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('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using Bcrypt.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
->inject('response')
@@ -278,7 +291,7 @@ App::post('/v1/users/md5')
]
))
->param('userId', '', new CustomId(), 'User 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('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using MD5.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
->inject('response')
@@ -313,7 +326,7 @@ App::post('/v1/users/argon2')
]
))
->param('userId', '', new CustomId(), 'User 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('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using Argon2.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
->inject('response')
@@ -348,7 +361,7 @@ App::post('/v1/users/sha')
]
))
->param('userId', '', new CustomId(), 'User 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('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using SHA.')
->param('passwordVersion', '', new WhiteList(['sha1', 'sha224', 'sha256', 'sha384', 'sha512/224', 'sha512/256', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512']), "Optional SHA version used to hash password. Allowed values are: 'sha1', 'sha224', 'sha256', 'sha384', 'sha512/224', 'sha512/256', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512'", true)
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
@@ -390,7 +403,7 @@ App::post('/v1/users/phpass')
]
))
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or pass the string `ID.unique()`to auto generate it. 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('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using PHPass.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
->inject('response')
@@ -425,7 +438,7 @@ App::post('/v1/users/scrypt')
]
))
->param('userId', '', new CustomId(), 'User 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('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using Scrypt.')
->param('passwordSalt', '', new Text(128), 'Optional salt used to hash password.')
->param('passwordCpu', 8, new Integer(), 'Optional CPU cost used to hash password.')
@@ -473,7 +486,7 @@ App::post('/v1/users/scrypt-modified')
]
))
->param('userId', '', new CustomId(), 'User 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('email', '', new EmailValidator(), 'User email.')
->param('password', '', new Password(), 'User password hashed using Scrypt Modified.')
->param('passwordSalt', '', new Text(128), 'Salt used to hash password.')
->param('passwordSaltSeparator', '', new Text(128), 'Salt separator used to hash password.')
@@ -527,7 +540,7 @@ App::post('/v1/users/:userId/targets')
switch ($providerType) {
case 'email':
$validator = new Email();
$validator = new EmailValidator();
if (!$validator->isValid($identifier)) {
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
}
@@ -1402,7 +1415,7 @@ App::patch('/v1/users/:userId/email')
]
))
->param('userId', '', new UID(), 'User ID.')
->param('email', '', new Email(allowEmpty: true), 'User email.')
->param('email', '', new EmailValidator(allowEmpty: true), 'User email.')
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -1437,9 +1450,20 @@ App::patch('/v1/users/:userId/email')
$oldEmail = $user->getAttribute('email');
try {
$emailCanonical = new Email($email);
} catch (Throwable) {
$emailCanonical = null;
}
$user
->setAttribute('email', $email)
->setAttribute('emailVerification', false)
->setAttribute('emailCanonical', $emailCanonical?->getCanonical())
->setAttribute('emailIsCanonical', $emailCanonical?->isCanonicalSupported())
->setAttribute('emailIsCorporate', $emailCanonical?->isCorporate())
->setAttribute('emailIsDisposable', $emailCanonical?->isDisposable())
->setAttribute('emailIsFree', $emailCanonical?->isFree())
;
try {
@@ -1700,7 +1724,7 @@ App::patch('/v1/users/:userId/targets/:targetId')
switch ($providerType) {
case 'email':
$validator = new Email();
$validator = new EmailValidator();
if (!$validator->isValid($identifier)) {
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
}
+4
View File
@@ -23,6 +23,7 @@ use Appwrite\Utopia\Request\Filters\V17 as RequestV17;
use Appwrite\Utopia\Request\Filters\V18 as RequestV18;
use Appwrite\Utopia\Request\Filters\V19 as RequestV19;
use Appwrite\Utopia\Request\Filters\V20 as RequestV20;
use Appwrite\Utopia\Request\Filters\V21 as RequestV21;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16;
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17;
@@ -906,6 +907,9 @@ App::init()
$dbForProject = $getProjectDB($project);
$request->addFilter(new RequestV20($dbForProject, $route->getPathValues($request)));
}
if (version_compare($requestFormat, '1.9.0', '<')) {
$request->addFilter(new RequestV21());
}
}
$domain = $request->getHostname();
+21 -2
View File
@@ -54,7 +54,20 @@ $parseLabel = function (string $label, array $responsePayload, array $requestPar
};
if (array_key_exists($replace, $params)) {
$label = \str_replace($find, $params[$replace], $label);
$replacement = $params[$replace];
// Convert to string if it's not already a string
if (!is_string($replacement)) {
if (is_array($replacement)) {
$replacement = json_encode($replacement);
} elseif (is_object($replacement) && method_exists($replacement, '__toString')) {
$replacement = (string)$replacement;
} elseif (is_scalar($replacement)) {
$replacement = (string)$replacement;
} else {
throw new Exception(Exception::GENERAL_SERVER_ERROR, "The server encountered an error while parsing the label: $label. Please create an issue on GitHub to allow us to investigate further https://github.com/appwrite/appwrite/issues/new/choose");
}
}
$label = \str_replace($find, $replacement, $label);
}
}
return $label;
@@ -221,7 +234,9 @@ App::init()
->inject('apiKey')
->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey) {
$route = $utopia->getRoute();
if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted' && str_starts_with($route->getPath(), '/v1/backups')) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Database Backups are available on Appwrite Cloud');
}
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
@@ -580,6 +595,10 @@ App::init()
$data = $cache->load($key, $timestamp);
if (!empty($data) && !$cacheLog->isEmpty()) {
$usageMetric = $route->getLabel('usage.metric', null);
if ($usageMetric === METRIC_AVATARS_SCREENSHOTS_GENERATED) {
$queueForStatsUsage->disableMetric(METRIC_AVATARS_SCREENSHOTS_GENERATED);
}
$parts = explode('/', $cacheLog->getAttribute('resourceType', ''));
$type = $parts[0] ?? null;
+4
View File
@@ -138,6 +138,7 @@ const DELETE_TYPE_TOPIC = 'topic';
const DELETE_TYPE_TARGET = 'target';
const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets';
const DELETE_TYPE_SESSION_TARGETS = 'session_targets';
const DELETE_TYPE_CSV_EXPORTS = 'csv_exports';
const DELETE_TYPE_MAINTENANCE = 'maintenance';
// Message types
@@ -270,6 +271,9 @@ const METRIC_SITES_OUTBOUND = 'sites.outbound';
const METRIC_SITES_ID_REQUESTS = 'sites.{siteInternalId}.requests';
const METRIC_SITES_ID_INBOUND = 'sites.{siteInternalId}.inbound';
const METRIC_SITES_ID_OUTBOUND = 'sites.{siteInternalId}.outbound';
const METRIC_AVATARS_SCREENSHOTS_GENERATED = 'avatars.screenshotsGenerated';
const METRIC_FUNCTIONS_RUNTIME = 'functions.runtimes.{runtime}';
const METRIC_SITES_FRAMEWORK = 'sites.frameworks.{framework}';
// Resource types
const RESOURCE_TYPE_PROJECTS = 'projects';
+1 -1
View File
@@ -859,7 +859,7 @@ $image = $this->getParam('image', '');
- _APP_ASSISTANT_OPENAI_API_KEY
appwrite-browser:
image: appwrite/browser:0.2.4
image: appwrite/browser:0.3.1
container_name: appwrite-browser
<<: *x-logging
restart: unless-stopped
+1 -2
View File
@@ -1,5 +1,4 @@
{
"name": "appwrite/server-ce",
"description": "End to end backend server for frontend and mobile apps.",
"type": "project",
@@ -56,7 +55,7 @@
"utopia-php/detector": "0.2.*",
"utopia-php/domains": "0.9.*",
"utopia-php/emails": "0.6.*",
"utopia-php/dns": "0.3.*",
"utopia-php/dns": "1.1.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
"utopia-php/fetch": "0.4.*",
Generated
+104 -99
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": "66e5a72649d7f816ba8ec979faca4cd8",
"content-hash": "ad28b7155175986191bd19bbcd13d623",
"packages": [
{
"name": "adhocore/jwt",
@@ -161,16 +161,16 @@
},
{
"name": "appwrite/php-runtimes",
"version": "0.19.1",
"version": "0.19.2",
"source": {
"type": "git",
"url": "https://github.com/appwrite/runtimes.git",
"reference": "7bd0cc3cb97de625d7b07230bd91b121f88e72ae"
"reference": "e5c142519df5aced37de9c302971c29c079ce3d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/7bd0cc3cb97de625d7b07230bd91b121f88e72ae",
"reference": "7bd0cc3cb97de625d7b07230bd91b121f88e72ae",
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/e5c142519df5aced37de9c302971c29c079ce3d9",
"reference": "e5c142519df5aced37de9c302971c29c079ce3d9",
"shasum": ""
},
"require": {
@@ -210,9 +210,9 @@
],
"support": {
"issues": "https://github.com/appwrite/runtimes/issues",
"source": "https://github.com/appwrite/runtimes/tree/0.19.1"
"source": "https://github.com/appwrite/runtimes/tree/0.19.2"
},
"time": "2025-05-27T07:12:56+00:00"
"time": "2025-11-11T13:44:44+00:00"
},
{
"name": "beberlei/assert",
@@ -756,16 +756,16 @@
},
{
"name": "google/protobuf",
"version": "v4.33.0",
"version": "v4.33.1",
"source": {
"type": "git",
"url": "https://github.com/protocolbuffers/protobuf-php.git",
"reference": "b50269e23204e5ae859a326ec3d90f09efe3047d"
"reference": "0cd73ccf0cd26c3e72299cce1ea6144091a57e12"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/b50269e23204e5ae859a326ec3d90f09efe3047d",
"reference": "b50269e23204e5ae859a326ec3d90f09efe3047d",
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/0cd73ccf0cd26c3e72299cce1ea6144091a57e12",
"reference": "0cd73ccf0cd26c3e72299cce1ea6144091a57e12",
"shasum": ""
},
"require": {
@@ -794,9 +794,9 @@
"proto"
],
"support": {
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.0"
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.1"
},
"time": "2025-10-15T20:10:28+00:00"
"time": "2025-11-12T21:58:05+00:00"
},
{
"name": "league/csv",
@@ -2673,16 +2673,16 @@
},
{
"name": "symfony/http-client",
"version": "v7.3.4",
"version": "v7.3.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62"
"reference": "3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/4b62871a01c49457cf2a8e560af7ee8a94b87a62",
"reference": "4b62871a01c49457cf2a8e560af7ee8a94b87a62",
"url": "https://api.github.com/repos/symfony/http-client/zipball/3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de",
"reference": "3c0a55a2c8e21e30a37022801c11c7ab5a6cb2de",
"shasum": ""
},
"require": {
@@ -2749,7 +2749,7 @@
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v7.3.4"
"source": "https://github.com/symfony/http-client/tree/v7.3.6"
},
"funding": [
{
@@ -2769,7 +2769,7 @@
"type": "tidelift"
}
],
"time": "2025-09-11T10:12:26+00:00"
"time": "2025-11-05T17:41:46+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -3176,16 +3176,16 @@
},
{
"name": "symfony/service-contracts",
"version": "v3.6.0",
"version": "v3.6.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4"
"reference": "45112560a3ba2d715666a509a0bc9521d10b6c43"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
"reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43",
"reference": "45112560a3ba2d715666a509a0bc9521d10b6c43",
"shasum": ""
},
"require": {
@@ -3239,7 +3239,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.6.0"
"source": "https://github.com/symfony/service-contracts/tree/v3.6.1"
},
"funding": [
{
@@ -3250,12 +3250,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-04-25T09:37:31+00:00"
"time": "2025-07-15T11:30:57+00:00"
},
{
"name": "tbachert/spi",
@@ -3840,16 +3844,16 @@
},
{
"name": "utopia-php/database",
"version": "3.0.4",
"version": "3.4.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "a83997d5555d6cd873b829a2459292211c6ab13f"
"reference": "e10b4faa4f3a3ef30a5f6d76acdb605469924aec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/a83997d5555d6cd873b829a2459292211c6ab13f",
"reference": "a83997d5555d6cd873b829a2459292211c6ab13f",
"url": "https://api.github.com/repos/utopia-php/database/zipball/e10b4faa4f3a3ef30a5f6d76acdb605469924aec",
"reference": "e10b4faa4f3a3ef30a5f6d76acdb605469924aec",
"shasum": ""
},
"require": {
@@ -3892,22 +3896,22 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/3.0.4"
"source": "https://github.com/utopia-php/database/tree/3.4.0"
},
"time": "2025-10-28T08:21:21+00:00"
"time": "2025-11-13T06:34:20+00:00"
},
{
"name": "utopia-php/detector",
"version": "0.2.1",
"version": "0.2.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/detector.git",
"reference": "89f96e864220de13800cf398a1f1686a85401eaa"
"reference": "9a41be5f21efe2d865de79b08dff94fff85ce5e9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/detector/zipball/89f96e864220de13800cf398a1f1686a85401eaa",
"reference": "89f96e864220de13800cf398a1f1686a85401eaa",
"url": "https://api.github.com/repos/utopia-php/detector/zipball/9a41be5f21efe2d865de79b08dff94fff85ce5e9",
"reference": "9a41be5f21efe2d865de79b08dff94fff85ce5e9",
"shasum": ""
},
"require": {
@@ -3937,35 +3941,36 @@
],
"support": {
"issues": "https://github.com/utopia-php/detector/issues",
"source": "https://github.com/utopia-php/detector/tree/0.2.1"
"source": "https://github.com/utopia-php/detector/tree/0.2.2"
},
"time": "2025-10-27T13:38:33+00:00"
"time": "2025-10-31T12:43:31+00:00"
},
{
"name": "utopia-php/dns",
"version": "0.3.0",
"version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/dns.git",
"reference": "8fd4161bc3a8021a670c1101b40f6b09a97f1a54"
"reference": "1e6b4bac735329c9e5ec69a6a5d899ec2d050707"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/dns/zipball/8fd4161bc3a8021a670c1101b40f6b09a97f1a54",
"reference": "8fd4161bc3a8021a670c1101b40f6b09a97f1a54",
"url": "https://api.github.com/repos/utopia-php/dns/zipball/1e6b4bac735329c9e5ec69a6a5d899ec2d050707",
"reference": "1e6b4bac735329c9e5ec69a6a5d899ec2d050707",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/cli": "0.15.*",
"utopia-php/telemetry": "^0.1.1"
"php": ">=8.3",
"utopia-php/console": "0.0.*",
"utopia-php/domains": "0.9.*",
"utopia-php/telemetry": "0.1.*",
"utopia-php/validators": "^0.0.2"
},
"require-dev": {
"laravel/pint": "1.2.*",
"phpstan/phpstan": "1.8.*",
"phpunit/phpunit": "^9.3",
"rregeer/phpunit-coverage-check": "^0.3.1",
"swoole/ide-helper": "4.6.6"
"laravel/pint": "1.25.*",
"phpstan/phpstan": "2.0.*",
"phpunit/phpunit": "12.4.*",
"swoole/ide-helper": "5.1.8"
},
"type": "library",
"autoload": {
@@ -3993,9 +3998,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/dns/issues",
"source": "https://github.com/utopia-php/dns/tree/0.3.0"
"source": "https://github.com/utopia-php/dns/tree/1.1.3"
},
"time": "2025-08-04T11:05:53+00:00"
"time": "2025-11-06T19:08:29+00:00"
},
{
"name": "utopia-php/domains",
@@ -5253,16 +5258,16 @@
},
{
"name": "webmozart/assert",
"version": "1.12.0",
"version": "1.12.1",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "541057574806f942c94662b817a50f63f7345360"
"reference": "9be6926d8b485f55b9229203f962b51ed377ba68"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/541057574806f942c94662b817a50f63f7345360",
"reference": "541057574806f942c94662b817a50f63f7345360",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68",
"reference": "9be6926d8b485f55b9229203f962b51ed377ba68",
"shasum": ""
},
"require": {
@@ -5305,9 +5310,9 @@
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/1.12.0"
"source": "https://github.com/webmozarts/assert/tree/1.12.1"
},
"time": "2025-10-20T12:43:39+00:00"
"time": "2025-10-29T15:56:20+00:00"
},
{
"name": "webonyx/graphql-php",
@@ -5378,16 +5383,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "1.4.15",
"version": "1.5.4",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "b4a2fd9e92903c2e156f17fc5dafe102e6cfdfda"
"reference": "958947b6483a79e11c3812f23bb3056199fa4105"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/b4a2fd9e92903c2e156f17fc5dafe102e6cfdfda",
"reference": "b4a2fd9e92903c2e156f17fc5dafe102e6cfdfda",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/958947b6483a79e11c3812f23bb3056199fa4105",
"reference": "958947b6483a79e11c3812f23bb3056199fa4105",
"shasum": ""
},
"require": {
@@ -5423,9 +5428,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/1.4.15"
"source": "https://github.com/appwrite/sdk-generator/tree/1.5.4"
},
"time": "2025-10-28T04:52:59+00:00"
"time": "2025-11-12T12:43:42+00:00"
},
{
"name": "doctrine/annotations",
@@ -6078,24 +6083,24 @@
},
{
"name": "phpbench/container",
"version": "2.2.2",
"version": "2.2.3",
"source": {
"type": "git",
"url": "https://github.com/phpbench/container.git",
"reference": "a59b929e00b87b532ca6d0edd8eca0967655af33"
"reference": "0c7b2d36c1ea53fe27302fb8873ded7172047196"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpbench/container/zipball/a59b929e00b87b532ca6d0edd8eca0967655af33",
"reference": "a59b929e00b87b532ca6d0edd8eca0967655af33",
"url": "https://api.github.com/repos/phpbench/container/zipball/0c7b2d36c1ea53fe27302fb8873ded7172047196",
"reference": "0c7b2d36c1ea53fe27302fb8873ded7172047196",
"shasum": ""
},
"require": {
"psr/container": "^1.0|^2.0",
"symfony/options-resolver": "^4.2 || ^5.0 || ^6.0 || ^7.0"
"symfony/options-resolver": "^4.2 || ^5.0 || ^6.0 || ^7.0 || ^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.16",
"php-cs-fixer/shim": "^3.89",
"phpstan/phpstan": "^0.12.52",
"phpunit/phpunit": "^8"
},
@@ -6123,22 +6128,22 @@
"description": "Simple, configurable, service container.",
"support": {
"issues": "https://github.com/phpbench/container/issues",
"source": "https://github.com/phpbench/container/tree/2.2.2"
"source": "https://github.com/phpbench/container/tree/2.2.3"
},
"time": "2023-10-30T13:38:26+00:00"
"time": "2025-11-06T09:05:13+00:00"
},
{
"name": "phpbench/phpbench",
"version": "1.4.2",
"version": "1.4.3",
"source": {
"type": "git",
"url": "https://github.com/phpbench/phpbench.git",
"reference": "bb61ae6c54b3d58642be154eb09f4e73c3511018"
"reference": "b641dde59d969ea42eed70a39f9b51950bc96878"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpbench/phpbench/zipball/bb61ae6c54b3d58642be154eb09f4e73c3511018",
"reference": "bb61ae6c54b3d58642be154eb09f4e73c3511018",
"url": "https://api.github.com/repos/phpbench/phpbench/zipball/b641dde59d969ea42eed70a39f9b51950bc96878",
"reference": "b641dde59d969ea42eed70a39f9b51950bc96878",
"shasum": ""
},
"require": {
@@ -6153,26 +6158,26 @@
"phpbench/container": "^2.2",
"psr/log": "^1.1 || ^2.0 || ^3.0",
"seld/jsonlint": "^1.1",
"symfony/console": "^6.1 || ^7.0",
"symfony/filesystem": "^6.1 || ^7.0",
"symfony/finder": "^6.1 || ^7.0",
"symfony/options-resolver": "^6.1 || ^7.0",
"symfony/process": "^6.1 || ^7.0",
"symfony/console": "^6.1 || ^7.0 || ^8.0",
"symfony/filesystem": "^6.1 || ^7.0 || ^8.0",
"symfony/finder": "^6.1 || ^7.0 || ^8.0",
"symfony/options-resolver": "^6.1 || ^7.0 || ^8.0",
"symfony/process": "^6.1 || ^7.0 || ^8.0",
"webmozart/glob": "^4.6"
},
"require-dev": {
"dantleech/invoke": "^2.0",
"ergebnis/composer-normalize": "^2.39",
"friendsofphp/php-cs-fixer": "^3.0",
"jangregor/phpstan-prophecy": "^1.0",
"php-cs-fixer/shim": "^3.9",
"phpspec/prophecy": "^1.22",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^10.4 || ^11.0",
"rector/rector": "^1.2",
"symfony/error-handler": "^6.1 || ^7.0",
"symfony/var-dumper": "^6.1 || ^7.0"
"symfony/error-handler": "^6.1 || ^7.0 || ^8.0",
"symfony/var-dumper": "^6.1 || ^7.0 || ^8.0"
},
"suggest": {
"ext-xdebug": "For Xdebug profiling extension."
@@ -6215,7 +6220,7 @@
],
"support": {
"issues": "https://github.com/phpbench/phpbench/issues",
"source": "https://github.com/phpbench/phpbench/tree/1.4.2"
"source": "https://github.com/phpbench/phpbench/tree/1.4.3"
},
"funding": [
{
@@ -6223,7 +6228,7 @@
"type": "github"
}
],
"time": "2025-10-26T14:21:59+00:00"
"time": "2025-11-06T19:07:31+00:00"
},
{
"name": "phpstan/phpstan",
@@ -7872,16 +7877,16 @@
},
{
"name": "symfony/console",
"version": "v7.3.5",
"version": "v7.3.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7"
"reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/cdb80fa5869653c83cfe1a9084a673b6daf57ea7",
"reference": "cdb80fa5869653c83cfe1a9084a673b6daf57ea7",
"url": "https://api.github.com/repos/symfony/console/zipball/c28ad91448f86c5f6d9d2c70f0cf68bf135f252a",
"reference": "c28ad91448f86c5f6d9d2c70f0cf68bf135f252a",
"shasum": ""
},
"require": {
@@ -7946,7 +7951,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.3.5"
"source": "https://github.com/symfony/console/tree/v7.3.6"
},
"funding": [
{
@@ -7966,20 +7971,20 @@
"type": "tidelift"
}
],
"time": "2025-10-14T15:46:26+00:00"
"time": "2025-11-04T01:21:42+00:00"
},
{
"name": "symfony/filesystem",
"version": "v7.3.2",
"version": "v7.3.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd"
"reference": "e9bcfd7837928ab656276fe00464092cc9e1826a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd",
"reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/e9bcfd7837928ab656276fe00464092cc9e1826a",
"reference": "e9bcfd7837928ab656276fe00464092cc9e1826a",
"shasum": ""
},
"require": {
@@ -8016,7 +8021,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v7.3.2"
"source": "https://github.com/symfony/filesystem/tree/v7.3.6"
},
"funding": [
{
@@ -8036,7 +8041,7 @@
"type": "tidelift"
}
],
"time": "2025-07-07T08:17:47+00:00"
"time": "2025-11-05T09:52:27+00:00"
},
{
"name": "symfony/finder",
@@ -8892,7 +8897,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
@@ -8916,5 +8921,5 @@
"platform-overrides": {
"php": "8.3"
},
"plugin-api-version": "2.2.0"
"plugin-api-version": "2.6.0"
}
+7
View File
@@ -0,0 +1,7 @@
services:
appwrite:
# volumes:
# - ~/.ssh:/root/.ssh
environment:
- GH_TOKEN=
- GIT_EMAIL=
+1 -1
View File
@@ -958,7 +958,7 @@ services:
appwrite-browser:
container_name: appwrite-browser
image: appwrite/browser:0.2.4
image: appwrite/browser:0.3.1
networks:
- appwrite
@@ -10,6 +10,7 @@ Account account = new Account(client);
account.listIdentities(
listOf(), // queries (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -10,6 +10,7 @@ Account account = new Account(client);
account.listLogs(
listOf(), // queries (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -0,0 +1,41 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Avatars;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getScreenshot(
"https://example.com", // url
mapOf( "a" to "b" ), // headers (optional)
1, // viewportWidth (optional)
1, // viewportHeight (optional)
0.1, // scale (optional)
theme.LIGHT, // theme (optional)
"<USER_AGENT>", // userAgent (optional)
false, // fullpage (optional)
"<LOCALE>", // locale (optional)
timezone.AFRICA_ABIDJAN, // timezone (optional)
-90, // latitude (optional)
-180, // longitude (optional)
0, // accuracy (optional)
false, // touch (optional)
listOf(), // permissions (optional)
0, // sleep (optional)
0, // width (optional)
0, // height (optional)
-1, // quality (optional)
output.JPG, // output (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -13,6 +13,7 @@ databases.listDocuments(
"<COLLECTION_ID>", // collectionId
listOf(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -11,6 +11,7 @@ Functions functions = new Functions(client);
functions.listExecutions(
"<FUNCTION_ID>", // functionId
listOf(), // queries (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -12,6 +12,7 @@ storage.listFiles(
"<BUCKET_ID>", // bucketId
listOf(), // queries (optional)
"<SEARCH>", // search (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -13,6 +13,7 @@ tablesDB.listRows(
"<TABLE_ID>", // tableId
listOf(), // queries (optional)
"<TRANSACTION_ID>", // transactionId (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -12,6 +12,7 @@ teams.listMemberships(
"<TEAM_ID>", // teamId
listOf(), // queries (optional)
"<SEARCH>", // search (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -11,6 +11,7 @@ Teams teams = new Teams(client);
teams.list(
listOf(), // queries (optional)
"<SEARCH>", // search (optional)
false, // total (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
@@ -10,4 +10,5 @@ val account = Account(client)
val result = account.listIdentities(
queries = listOf(), // (optional)
total = false, // (optional)
)
@@ -10,4 +10,5 @@ val account = Account(client)
val result = account.listLogs(
queries = listOf(), // (optional)
total = false, // (optional)
)
@@ -0,0 +1,32 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Avatars
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val avatars = Avatars(client)
val result = avatars.getScreenshot(
url = "https://example.com",
headers = mapOf( "a" to "b" ), // (optional)
viewportWidth = 1, // (optional)
viewportHeight = 1, // (optional)
scale = 0.1, // (optional)
theme = theme.LIGHT, // (optional)
userAgent = "<USER_AGENT>", // (optional)
fullpage = false, // (optional)
locale = "<LOCALE>", // (optional)
timezone = timezone.AFRICA_ABIDJAN, // (optional)
latitude = -90, // (optional)
longitude = -180, // (optional)
accuracy = 0, // (optional)
touch = false, // (optional)
permissions = listOf(), // (optional)
sleep = 0, // (optional)
width = 0, // (optional)
height = 0, // (optional)
quality = -1, // (optional)
output = output.JPG, // (optional)
)
@@ -13,4 +13,5 @@ val result = databases.listDocuments(
collectionId = "<COLLECTION_ID>",
queries = listOf(), // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
total = false, // (optional)
)
@@ -11,4 +11,5 @@ val functions = Functions(client)
val result = functions.listExecutions(
functionId = "<FUNCTION_ID>",
queries = listOf(), // (optional)
total = false, // (optional)
)
@@ -12,4 +12,5 @@ val result = storage.listFiles(
bucketId = "<BUCKET_ID>",
queries = listOf(), // (optional)
search = "<SEARCH>", // (optional)
total = false, // (optional)
)
@@ -13,4 +13,5 @@ val result = tablesDB.listRows(
tableId = "<TABLE_ID>",
queries = listOf(), // (optional)
transactionId = "<TRANSACTION_ID>", // (optional)
total = false, // (optional)
)
@@ -12,4 +12,5 @@ val result = teams.listMemberships(
teamId = "<TEAM_ID>",
queries = listOf(), // (optional)
search = "<SEARCH>", // (optional)
total = false, // (optional)
)
@@ -11,4 +11,5 @@ val teams = Teams(client)
val result = teams.list(
queries = listOf(), // (optional)
search = "<SEARCH>", // (optional)
total = false, // (optional)
)
@@ -7,6 +7,7 @@ let client = Client()
let account = Account(client)
let identityList = try await account.listIdentities(
queries: [] // optional
queries: [], // optional
total: false // optional
)
@@ -7,6 +7,7 @@ let client = Client()
let account = Account(client)
let logList = try await account.listLogs(
queries: [] // optional
queries: [], // optional
total: false // optional
)
@@ -0,0 +1,32 @@
import Appwrite
import AppwriteEnums
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
let avatars = Avatars(client)
let bytes = try await avatars.getScreenshot(
url: "https://example.com",
headers: [:], // optional
viewportWidth: 1, // optional
viewportHeight: 1, // optional
scale: 0.1, // optional
theme: .light, // optional
userAgent: "<USER_AGENT>", // optional
fullpage: false, // optional
locale: "<LOCALE>", // optional
timezone: .africaAbidjan, // optional
latitude: -90, // optional
longitude: -180, // optional
accuracy: 0, // optional
touch: false, // optional
permissions: [], // optional
sleep: 0, // optional
width: 0, // optional
height: 0, // optional
quality: -1, // optional
output: .jpg // optional
)
@@ -10,6 +10,7 @@ let documentList = try await databases.listDocuments(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
queries: [], // optional
transactionId: "<TRANSACTION_ID>" // optional
transactionId: "<TRANSACTION_ID>", // optional
total: false // optional
)
@@ -8,6 +8,7 @@ let functions = Functions(client)
let executionList = try await functions.listExecutions(
functionId: "<FUNCTION_ID>",
queries: [] // optional
queries: [], // optional
total: false // optional
)
@@ -9,6 +9,7 @@ let storage = Storage(client)
let fileList = try await storage.listFiles(
bucketId: "<BUCKET_ID>",
queries: [], // optional
search: "<SEARCH>" // optional
search: "<SEARCH>", // optional
total: false // optional
)
@@ -10,6 +10,7 @@ let rowList = try await tablesDB.listRows(
databaseId: "<DATABASE_ID>",
tableId: "<TABLE_ID>",
queries: [], // optional
transactionId: "<TRANSACTION_ID>" // optional
transactionId: "<TRANSACTION_ID>", // optional
total: false // optional
)
@@ -9,6 +9,7 @@ let teams = Teams(client)
let membershipList = try await teams.listMemberships(
teamId: "<TEAM_ID>",
queries: [], // optional
search: "<SEARCH>" // optional
search: "<SEARCH>", // optional
total: false // optional
)
@@ -8,6 +8,7 @@ let teams = Teams(client)
let teamList = try await teams.list(
queries: [], // optional
search: "<SEARCH>" // optional
search: "<SEARCH>", // optional
total: false // optional
)
@@ -8,4 +8,5 @@ Account account = Account(client);
IdentityList result = await account.listIdentities(
queries: [], // optional
total: false, // optional
);
@@ -8,4 +8,5 @@ Account account = Account(client);
LogList result = await account.listLogs(
queries: [], // optional
total: false, // optional
);
@@ -0,0 +1,65 @@
import 'package:appwrite/appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
Avatars avatars = Avatars(client);
// Downloading file
UInt8List bytes = await avatars.getScreenshot(
url: 'https://example.com',
headers: {}, // optional
viewportWidth: 1, // optional
viewportHeight: 1, // optional
scale: 0.1, // optional
theme: .light, // optional
userAgent: '<USER_AGENT>', // optional
fullpage: false, // optional
locale: '<LOCALE>', // optional
timezone: .africaAbidjan, // optional
latitude: -90, // optional
longitude: -180, // optional
accuracy: 0, // optional
touch: false, // optional
permissions: [], // optional
sleep: 0, // optional
width: 0, // optional
height: 0, // optional
quality: -1, // optional
output: .jpg, // optional
)
final file = File('path_to_file/filename.ext');
file.writeAsBytesSync(bytes);
// Displaying image preview
FutureBuilder(
future: avatars.getScreenshot(
url:'https://example.com' ,
headers:{} , // optional
viewportWidth:1 , // optional
viewportHeight:1 , // optional
scale:0.1 , // optional
theme: .light, // optional
userAgent:'<USER_AGENT>' , // optional
fullpage:false , // optional
locale:'<LOCALE>' , // optional
timezone: .africaAbidjan, // optional
latitude:-90 , // optional
longitude:-180 , // optional
accuracy:0 , // optional
touch:false , // optional
permissions:[] , // optional
sleep:0 , // optional
width:0 , // optional
height:0 , // optional
quality:-1 , // optional
output: .jpg, // optional
), // Works for both public file and private file, for private files you need to be logged in
builder: (context, snapshot) {
return snapshot.hasData && snapshot.data != null
? Image.memory(snapshot.data)
: CircularProgressIndicator();
}
);
@@ -11,4 +11,5 @@ DocumentList result = await databases.listDocuments(
collectionId: '<COLLECTION_ID>',
queries: [], // optional
transactionId: '<TRANSACTION_ID>', // optional
total: false, // optional
);
@@ -9,4 +9,5 @@ Functions functions = Functions(client);
ExecutionList result = await functions.listExecutions(
functionId: '<FUNCTION_ID>',
queries: [], // optional
total: false, // optional
);
@@ -10,4 +10,5 @@ FileList result = await storage.listFiles(
bucketId: '<BUCKET_ID>',
queries: [], // optional
search: '<SEARCH>', // optional
total: false, // optional
);
@@ -11,4 +11,5 @@ RowList result = await tablesDB.listRows(
tableId: '<TABLE_ID>',
queries: [], // optional
transactionId: '<TRANSACTION_ID>', // optional
total: false, // optional
);
@@ -10,4 +10,5 @@ MembershipList result = await teams.listMemberships(
teamId: '<TEAM_ID>',
queries: [], // optional
search: '<SEARCH>', // optional
total: false, // optional
);
@@ -9,4 +9,5 @@ Teams teams = Teams(client);
TeamList result = await teams.list(
queries: [], // optional
search: '<SEARCH>', // optional
total: false, // optional
);
@@ -7,7 +7,8 @@ const client = new Client()
const account = new Account(client);
const result = await account.listIdentities({
queries: [] // optional
queries: [], // optional
total: false // optional
});
console.log(result);
@@ -7,7 +7,8 @@ const client = new Client()
const account = new Account(client);
const result = await account.listLogs({
queries: [] // optional
queries: [], // optional
total: false // optional
});
console.log(result);
@@ -0,0 +1,32 @@
import { Client, Avatars, , , } from "react-native-appwrite";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const avatars = new Avatars(client);
const result = avatars.getScreenshot({
url: 'https://example.com',
headers: {}, // optional
viewportWidth: 1, // optional
viewportHeight: 1, // optional
scale: 0.1, // optional
theme: .Light, // optional
userAgent: '<USER_AGENT>', // optional
fullpage: false, // optional
locale: '<LOCALE>', // optional
timezone: .AfricaAbidjan, // optional
latitude: -90, // optional
longitude: -180, // optional
accuracy: 0, // optional
touch: false, // optional
permissions: [], // optional
sleep: 0, // optional
width: 0, // optional
height: 0, // optional
quality: -1, // optional
output: .Jpg // optional
});
console.log(result);
@@ -10,7 +10,8 @@ const result = await databases.listDocuments({
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
queries: [], // optional
transactionId: '<TRANSACTION_ID>' // optional
transactionId: '<TRANSACTION_ID>', // optional
total: false // optional
});
console.log(result);
@@ -8,7 +8,8 @@ const functions = new Functions(client);
const result = await functions.listExecutions({
functionId: '<FUNCTION_ID>',
queries: [] // optional
queries: [], // optional
total: false // optional
});
console.log(result);
@@ -9,7 +9,8 @@ const storage = new Storage(client);
const result = await storage.listFiles({
bucketId: '<BUCKET_ID>',
queries: [], // optional
search: '<SEARCH>' // optional
search: '<SEARCH>', // optional
total: false // optional
});
console.log(result);
@@ -10,7 +10,8 @@ const result = await tablesDB.listRows({
databaseId: '<DATABASE_ID>',
tableId: '<TABLE_ID>',
queries: [], // optional
transactionId: '<TRANSACTION_ID>' // optional
transactionId: '<TRANSACTION_ID>', // optional
total: false // optional
});
console.log(result);
@@ -9,7 +9,8 @@ const teams = new Teams(client);
const result = await teams.listMemberships({
teamId: '<TEAM_ID>',
queries: [], // optional
search: '<SEARCH>' // optional
search: '<SEARCH>', // optional
total: false // optional
});
console.log(result);
@@ -8,7 +8,8 @@ const teams = new Teams(client);
const result = await teams.list({
queries: [], // optional
search: '<SEARCH>' // optional
search: '<SEARCH>', // optional
total: false // optional
});
console.log(result);
@@ -0,0 +1,6 @@
GET /v1/avatars/screenshots HTTP/1.1
Host: cloud.appwrite.io
X-Appwrite-Response-Format: 1.8.0
X-Appwrite-Project: <YOUR_PROJECT_ID>
X-Appwrite-Session:
X-Appwrite-JWT: <YOUR_JWT>
@@ -7,7 +7,8 @@ const client = new Client()
const account = new Account(client);
const result = await account.listIdentities({
queries: [] // optional
queries: [], // optional
total: false // optional
});
console.log(result);
@@ -7,7 +7,8 @@ const client = new Client()
const account = new Account(client);
const result = await account.listLogs({
queries: [] // optional
queries: [], // optional
total: false // optional
});
console.log(result);
@@ -0,0 +1,32 @@
import { Client, Avatars, , , } from "appwrite";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const avatars = new Avatars(client);
const result = avatars.getScreenshot({
url: 'https://example.com',
headers: {}, // optional
viewportWidth: 1, // optional
viewportHeight: 1, // optional
scale: 0.1, // optional
theme: .Light, // optional
userAgent: '<USER_AGENT>', // optional
fullpage: false, // optional
locale: '<LOCALE>', // optional
timezone: .AfricaAbidjan, // optional
latitude: -90, // optional
longitude: -180, // optional
accuracy: 0, // optional
touch: false, // optional
permissions: [], // optional
sleep: 0, // optional
width: 0, // optional
height: 0, // optional
quality: -1, // optional
output: .Jpg // optional
});
console.log(result);
@@ -10,7 +10,8 @@ const result = await databases.listDocuments({
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
queries: [], // optional
transactionId: '<TRANSACTION_ID>' // optional
transactionId: '<TRANSACTION_ID>', // optional
total: false // optional
});
console.log(result);
@@ -8,7 +8,8 @@ const functions = new Functions(client);
const result = await functions.listExecutions({
functionId: '<FUNCTION_ID>',
queries: [] // optional
queries: [], // optional
total: false // optional
});
console.log(result);

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