Compare commits

...
Author SHA1 Message Date
Darshan 8f85e335df regen: specs. 2026-01-13 11:59:44 +05:30
Darshan 7e6eae9464 Merge branch '1.8.x' into 'docdb-sync-18x'. 2026-01-13 11:58:20 +05:30
Darshan 941cb14f49 regen: specs 2026-01-09 15:14:07 +05:30
Darshan fe1d89dc6e add: missing admin auth. 2026-01-09 15:10:59 +05:30
Darshan ba23fe449c specs. 2026-01-08 17:43:43 +05:30
Darshan 1fc93efe70 fix: deps 2026-01-08 17:43:29 +05:30
Darshan b614c67b56 Merge branch '1.8.x' into 'documents-db-api-for-cloud'. 2026-01-08 17:37:08 +05:30
Darshan 2d6122a4bf regen: specs. 2025-12-30 18:53:57 +05:30
Darshan ecbdceb407 add: admin scopes for console sdk. 2025-12-30 18:52:53 +05:30
ArnabChatterjee20kandGitHub c7a3c42225 Merge pull request #11044 from appwrite/docs-db-cloud-compat
Sync 1.8.x
2025-12-30 17:53:11 +05:30
Darshan 4cdc8a97a0 lint. 2025-12-30 17:51:47 +05:30
Darshan cf0ea48fc8 Merge branch 'documents-db-api-for-cloud' into docs-db-cloud-compat 2025-12-30 17:50:47 +05:30
Darshan 3771da5353 remove: duplicate constant. 2025-12-30 17:44:56 +05:30
Darshan abd2b7681b remove: duplicate constant. 2025-12-30 17:42:46 +05:30
Darshan 7f35419636 Merge branch '1.8.x' into 'documents-db-api-for-cloud'. 2025-12-30 17:33:20 +05:30
ArnabChatterjee20k d33b91b25c Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api-for-cloud 2025-12-13 22:41:41 +05:30
ArnabChatterjee20k b34a553c3b added new files for cloud compat 2025-12-13 19:37:32 +05:30
ArnabChatterjee20k 5965599eaf updated dependencies 2025-12-13 19:09:54 +05:30
ArnabChatterjee20k a2039c0b76 added deletion test for multidb 2025-12-09 18:34:25 +05:30
ArnabChatterjee20k c549ddd149 updated project deletion worker to delete per database 2025-12-09 18:27:10 +05:30
ArnabChatterjee20k de88ec4065 updated missing skip metrics and tablesdb tests 2025-12-08 20:30:37 +05:30
ArnabChatterjee20k ec63dc22d4 updated controllers 2025-12-08 19:09:54 +05:30
ArnabChatterjee20k 7c7b98399c linting 2025-12-08 17:51:01 +05:30
ArnabChatterjee20k 9d679e726e updated composer lock 2025-12-08 17:46:00 +05:30
ArnabChatterjee20k 070ec03c0c Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-12-08 17:31:04 +05:30
ArnabChatterjee20k be89224c36 updated console client test 2025-12-05 19:14:29 +05:30
ArnabChatterjee20k 8edd3c87cb added documentsdb total reads and writes in the project response 2025-12-05 19:09:35 +05:30
ArnabChatterjee20k 9ac3cd73a8 Refactor database metrics handling for DocumentsDB
- Introduced new methods to retrieve metrics for write and read operations based on database type.
- Updated bulk delete, update, upsert, and create actions to use the new metric methods.
- Enhanced document retrieval and update actions to utilize the new metric structure.
- Added support for DocumentsDB metrics in usage statistics and response models.
- Created new response models for DocumentsDB usage and updated existing models to accommodate new metrics.
- Adjusted migration and stats resources to include DocumentsDB metrics.
- Updated tests to validate the new metrics and ensure correct functionality for DocumentsDB usage.
2025-12-05 18:58:13 +05:30
ArnabChatterjee20k 203170c36d updated database and collection stats 2025-12-04 15:57:18 +05:30
ArnabChatterjee20k 6f5bfdb726 updated stats 2025-12-03 20:18:53 +05:30
b499857871 Update src/Appwrite/Utopia/Database/Validator/Queries/Base.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2025-12-03 19:12:56 +05:30
1388b0ca21 Update app/config/variables.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2025-12-03 19:12:38 +05:30
ArnabChatterjee20k af095bdf4a updated stats 2025-12-03 19:11:46 +05:30
ArnabChatterjee20k 16f49d13c8 updated composer lock 2025-12-02 18:36:29 +05:30
ArnabChatterjee20k e72d7979e0 updated composer lock 2025-12-02 18:31:44 +05:30
ArnabChatterjee20k 7ad6bb048c merged 1.8.x 2025-12-02 16:35:40 +05:30
ArnabChatterjee20k 3db22fd2db fixed enum route 2025-11-21 20:03:44 +05:30
ArnabChatterjee20k a423067b36 Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-11-21 19:53:52 +05:30
ArnabChatterjee20k 33aab0357f updated tests 2025-11-21 19:52:28 +05:30
ArnabChatterjee20k 34e9c69a62 updated tests condition 2025-11-21 19:11:26 +05:30
ArnabChatterjee20k b658cddc71 updated get project dsn to be of the project db type(dedicate, shared) so that databasesdb and projectdb are not in conflict 2025-11-21 17:22:19 +05:30
ArnabChatterjee20k 2f1e47e35a updated migrations 2025-11-20 18:56:14 +05:30
ArnabChatterjee20k 1004435e9e skipped operator tests for mongodb 2025-11-20 14:07:05 +05:30
ArnabChatterjee20k a9e749a81f updated migration service extractor for csv import/export 2025-11-20 14:06:42 +05:30
ArnabChatterjee20k 553fe15ba9 resolved merge conflicts 2025-11-19 20:01:02 +05:30
ArnabChatterjee20k fec758d237 Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-11-19 20:00:17 +05:30
ArnabChatterjee20k 21aae468f2 updated database type function 2025-11-14 19:35:53 +05:30
ArnabChatterjee20k f007b83ccc updated composer 2025-11-14 19:18:03 +05:30
ArnabChatterjee20k 28be12d827 Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-11-14 19:16:08 +05:30
ArnabChatterjee20k 81128e8446 reverted changes 2025-11-06 17:51:33 +05:30
ArnabChatterjee20k 2bb6b0a2a9 updated database dsn for create in the create database 2025-10-31 16:45:01 +05:30
ArnabChatterjee20k 5d9ef0dcca added metadata create on database 2025-10-31 16:33:54 +05:30
ArnabChatterjee20k c8f966b0e5 added the default database type for the transactions context 2025-10-30 13:44:26 +05:30
ArnabChatterjee20k 3d2f21be7c updated composer lock 2025-10-30 13:02:49 +05:30
ArnabChatterjee20k 3e876f5bd8 fixed merge conflicts 2025-10-30 12:57:41 +05:30
ArnabChatterjee20k a5e56438c7 Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-10-30 12:35:57 +05:30
ArnabChatterjee20k 4e79259c4e Refactor database handling to streamline DSN retrieval for documents and tables databases 2025-10-30 12:27:52 +05:30
ArnabChatterjee20k fae79e5c3f updated tests from feat-mongodb branch 2025-10-29 13:49:18 +05:30
ArnabChatterjee20k b169be9dda updated migration merge conflicts 2025-10-29 11:41:46 +05:30
ArnabChatterjee20k 8f4496346d Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-10-28 18:41:03 +05:30
ArnabChatterjee20k f306c03030 updated tests and index creation 2025-10-28 18:32:17 +05:30
ArnabChatterjee20k a8469a9b92 updated composer lock 2025-10-27 18:10:41 +05:30
ArnabChatterjee20k b63510acb5 Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-10-27 18:10:08 +05:30
ArnabChatterjee20k 3e612ec88d flask test fixing via retry 2025-10-27 18:08:44 +05:30
ArnabChatterjee20k 1be36ae552 updated deps 2025-10-27 16:19:04 +05:30
ArnabChatterjee20k 445b657207 Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-10-27 16:15:10 +05:30
ArnabChatterjee20k 5098973e0d reverted longtext resource file 2025-10-17 18:14:16 +05:30
ArnabChatterjee20k d0976ff429 updated registers config 2025-10-17 14:16:45 +05:30
ArnabChatterjee20k 157783f782 merged feat-mongodb 2025-10-17 13:58:27 +05:30
ArnabChatterjee20k e3a70373e1 updated docuemntsdb transaction path names 2025-10-16 22:19:53 +05:30
ArnabChatterjee20k a6e5563f8e reverted docker compose 2025-10-16 21:51:56 +05:30
ArnabChatterjee20k 470d84a843 * updated transactions
* updated tests
2025-10-16 21:45:30 +05:30
ArnabChatterjee20k 80037f7e8d updated http php for mongodb setup 2025-10-16 15:57:13 +05:30
ArnabChatterjee20k cc848c90a7 updated lock file 2025-10-16 14:25:22 +05:30
ArnabChatterjee20k e0c293c5b4 Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-10-16 14:25:14 +05:30
ArnabChatterjee20k 2d82013261 updated docker compose setup for mongodb which was causing issue for mongo transactions 2025-10-16 14:22:28 +05:30
ArnabChatterjee20k f28588ecb6 updated migration 2025-10-15 14:24:59 +05:30
ArnabChatterjee20k 6579ea5b56 Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-10-15 14:16:49 +05:30
ArnabChatterjee20k 9373688be2 updated migration worker to pass the dsn resolver for the destination database 2025-10-15 10:33:46 +05:30
ArnabChatterjee20k 2cec06be18 Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-10-14 21:18:38 +05:30
ArnabChatterjee20k 3a60bb306c * updated migration tests
* database dsn fetching
* removed dsn from response models
2025-10-14 21:13:26 +05:30
ArnabChatterjee20k e2ddb0d6d0 updated migration 2025-10-14 13:23:48 +05:30
Darshan 0049265c8a Merge remote-tracking branch 'origin/documents-db-api' into documents-db-api 2025-10-14 12:49:41 +05:30
Darshan 1f27867565 update: method name. 2025-10-14 12:49:34 +05:30
ArnabChatterjee20k faf0cdec35 changed default fallbackForDB to mariadb 2025-10-14 12:46:01 +05:30
ArnabChatterjee20k 6073eab496 * updated env to have mongodb support for the tablesdb/legacy
* setAttributeSupport to support context schema and schemaless in mongodb
* updated the connection registers
2025-10-13 15:47:40 +05:30
ArnabChatterjee20k 50ddc611a4 updated migrations 2025-10-12 20:49:02 +05:30
ArnabChatterjee20k 0ef2cfa753 reverted docker compose 2025-10-12 20:26:39 +05:30
ArnabChatterjee20k 790e503ad9 updated composer lock 2025-10-12 20:19:57 +05:30
ArnabChatterjee20k f6dbc0c882 Merge remote-tracking branch 'upstream/feat-mongodb' into documents-db-api 2025-10-12 20:17:53 +05:30
ArnabChatterjee20k a6c226aac9 removed the documentsdb changes from the migration tests 2025-10-10 20:38:54 +05:30
ArnabChatterjee20k 2d41a3353d * migration fix for tablesdb, legacy with multitype callback
* updated patch script
2025-10-10 20:29:05 +05:30
ArnabChatterjee20k 34e4208b3e addressed comments
* renamed getDatabaseDB to getDatabasesDB
* renamed dbForDatabase to dbForDatabases
* removed call_user_func and using newer callable syntax
2025-10-10 20:00:43 +05:30
ArnabChatterjee20k d51486c62a * fixed stats usage events(hacky fix)
* updated redundant routes(happened during merge conflicts)
2025-10-10 19:39:59 +05:30
ArnabChatterjee20k f62f00d4d9 Merge remote-tracking branch 'upstream/documents-db-api' into documents-db-api 2025-10-10 15:26:33 +05:30
ArnabChatterjee20k 44e6ef3146 event generation fix 2025-10-10 15:24:42 +05:30
Jake BarnbyandGitHub dea49d54c9 Merge pull request #10628 from appwrite/fix-enums
Update: enums
2025-10-10 22:16:45 +13:00
Darshan 8a9523fa48 update: enums. 2025-10-10 14:40:26 +05:30
Darshan 5dba0183c0 update: enums. 2025-10-10 14:40:01 +05:30
ArnabChatterjee20k 0108f86180 linting + merge conflicts resolving 2025-10-10 13:39:16 +05:30
ArnabChatterjee20k bc71d83697 empty commit 2025-10-10 10:58:11 +05:30
ArnabChatterjee20k 0754c6263e merged changes from 1.8.x 2025-10-10 10:55:48 +05:30
ArnabChatterjee20k 2c037803f0 updated sdk names and enums for documentsdb 2025-10-09 18:41:10 +05:30
ArnabChatterjee20k 48410d0cf6 * added getDatabaseDB in stats worker, migration worker
* added prefixed events for multitype db support with backward compat
* added realtime tests, migration tests
2025-10-09 18:26:05 +05:30
ArnabChatterjee20k 66b0f3c9df * added waiting time in usage test so that aggregator can work
* added documentsdb usage test
2025-10-09 18:24:04 +05:30
ArnabChatterjee20k d8910eea73 fix - updated documentsdb sdk namespace to documentsDB 2025-10-09 10:30:57 +05:30
Jake Barnby 38b04566b6 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php
#	src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php
#	src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php
2025-10-09 17:36:22 +13:00
Jake Barnby c89af1201a Fix tests 2025-10-09 16:37:17 +13:00
Jake Barnby 9f85ca83b4 Fix roles param 2025-10-09 14:33:05 +13:00
Jake Barnby d5d09061e9 Fix index validator params 2025-10-09 13:46:14 +13:00
ArnabChatterjee20k f7f965a812 * added prefixed channels for realtime channels along with backward compatibility
* added documentsdb tests
2025-10-09 00:35:38 +05:30
Jake Barnby 143be8239f Fix inject 2025-10-09 00:41:25 +13:00
ArnabChatterjee20k d806bdbca7 added project region in the get database dsn 2025-10-08 11:17:12 +05:30
ArnabChatterjee20k 19b1196633 * new db pool implementation for connecting to db type
* fixed migration tests
2025-10-08 11:09:32 +05:30
Jake Barnby da519046dd Merge branch 'feat-mongodb' of github.com:appwrite/appwrite into feat-mongodb 2025-10-08 00:05:05 +13:00
Jake Barnby 0c5573ab05 Fix list wrapping UID injections 2025-10-08 00:04:52 +13:00
ArnabChatterjee20k 88f2b1ad50 added patch script for adding database dsn in the databases collection 2025-10-07 13:18:11 +05:30
ArnabChatterjee20k f5ea19b15d updated tests 2025-10-07 12:45:21 +05:30
Jake BarnbyandGitHub fa60f03536 Merge branch '1.8.x' into feat-mongodb 2025-10-07 18:52:28 +13:00
Jake Barnby 28392647e0 Pass adapter max ID length to validator 2025-10-07 18:52:05 +13:00
ArnabChatterjee20k d2645ecfb1 Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-10-07 10:47:26 +05:30
Jake Barnby 4b0eddcc84 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	composer.json
#	composer.lock
2025-10-07 18:16:02 +13:00
Jake Barnby 94e40c0127 Update database 2025-10-04 10:15:13 +13:00
ArnabChatterjee20k 44b75d50f4 revmoved databasse logs xlist action for documentsdb 2025-10-03 19:56:20 +05:30
ArnabChatterjee20k df23aacbc0 updated routes and endpoints 2025-10-03 19:51:00 +05:30
ArnabChatterjee20k 7d4dfe06e2 * removed dbForDocumentsRecords
* using callback based db resolution
* updated env
2025-10-03 19:50:29 +05:30
Jake Barnby 498235a2ef Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb 2025-10-03 23:19:33 +13:00
Jake Barnby 52c9671157 Update deps 2025-10-03 23:19:09 +13:00
ArnabChatterjee20k e50af4c981 add references docs for the documentsdb 2025-10-02 19:24:35 +05:30
ArnabChatterjee20k 4fdab6fd52 updated database db resolution in the resources itself instead of switch in the worker action 2025-10-02 18:58:11 +05:30
ArnabChatterjee20k c54450f582 updated composer 2025-10-02 18:57:15 +05:30
ArnabChatterjee20k 6bf9693146 Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-10-02 18:56:48 +05:30
Jake Barnby 61a1efd0fe Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb 2025-10-02 19:25:56 +13:00
Jake Barnby faf837e389 Update db for index validation fix 2025-10-02 15:29:27 +13:00
Jake Barnby 3ecd7e6a95 Fix fts create on array 2025-10-01 22:02:41 +13:00
Jake Barnby 36782650c6 Revert "Add CI debug"
This reverts commit 2d3dea84e3.
2025-10-01 21:36:03 +13:00
Jake Barnby 2f6bc9115f Fix workflows init 2025-10-01 21:35:55 +13:00
Jake Barnby 2d3dea84e3 Add CI debug 2025-10-01 21:10:26 +13:00
Jake Barnby b12f4008eb Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	composer.lock
2025-10-01 20:13:48 +13:00
Jake Barnby 408bca400b Fix spatial tests 2025-10-01 19:33:11 +13:00
Jake Barnby 6bacec52c5 Fix storage test 2025-10-01 17:11:02 +13:00
Jake Barnby 9d70b5cab2 Fix domain test 2025-10-01 17:09:07 +13:00
Jake Barnby d9baf5b628 Fix sites error 2025-10-01 12:31:07 +13:00
Jake Barnby 9e837c2eb9 Fix storage error 2025-10-01 12:31:02 +13:00
Jake Barnby f3460db369 Fix fulltext error 2025-10-01 12:30:48 +13:00
ArnabChatterjee20k 422fd8b157 updated get endpoint 2025-09-30 20:02:30 +05:30
ArnabChatterjee20k bf061b460b added documentsdb in the github actions 2025-09-30 19:29:36 +05:30
ArnabChatterjee20k a035760b00 updated injections and reverted reduntant changes 2025-09-30 19:15:43 +05:30
ArnabChatterjee20k 85c0454c49 linting 2025-09-30 18:45:53 +05:30
Jake Barnby b57839402f Update DB 2025-10-01 02:14:51 +13:00
ArnabChatterjee20k 7bfb0401c6 linting 2025-09-30 18:44:27 +05:30
ArnabChatterjee20k c097f4a450 updated composer lock 2025-09-30 18:43:02 +05:30
ArnabChatterjee20k a3faa108c5 Merge remote-tracking branch 'upstream/1.8.x' into documents-db-api 2025-09-30 18:42:18 +05:30
ArnabChatterjee20k c89b76ba80 *updated tests
*updated create documents to use the on next callback
2025-09-30 18:19:44 +05:30
Jake Barnby 763158fe81 Fix usage tests 2025-10-01 01:30:18 +13:00
Jake Barnby 766c3293d2 Fix index text checks 2025-10-01 00:47:07 +13:00
Jake Barnby 1226c0834c Fix upsert 2025-10-01 00:13:09 +13:00
Jake Barnby f0983cd9ff Remove mounts 2025-10-01 00:03:07 +13:00
Jake Barnby daeb721995 Fix tests 2025-09-30 23:44:26 +13:00
Jake Barnby 0555a27656 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb 2025-09-30 22:58:38 +13:00
Jake Barnby da3284ce84 Update deps 2025-09-30 22:58:29 +13:00
ArnabChatterjee20k 80636e8a91 updated timeouts 2025-09-30 00:03:12 +05:30
Jake Barnby 53f8a3a78b Update DB 2025-09-29 19:39:51 +13:00
ArnabChatterjee20k 206845e106 updated tests and delete delete documents 2025-09-28 14:43:44 +05:30
ArnabChatterjee20k 64d015065f * added new databasetype dsn
* updated worker to use the dsn to choose the database resource
2025-09-26 19:52:25 +05:30
ArnabChatterjee20k 56af56e341 updated endpoints 2025-09-26 18:01:39 +05:30
Jake Barnby aa77253b14 Fix OSV 2025-09-26 22:17:01 +12:00
Jake Barnby 78c5743e75 Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb
# Conflicts:
#	composer.lock
2025-09-26 22:14:40 +12:00
Jake Barnby 156a944b54 Merge remote-tracking branch 'origin/feat-mongodb-index-validation' into feat-mongodb 2025-09-26 22:05:31 +12:00
Jake Barnby b07fd5afe6 Merge remote-tracking branch 'origin/feat-mongodb-pulls' into feat-mongodb 2025-09-26 22:05:24 +12:00
ArnabChatterjee20k 3086e1cbc1 updated increment, and decrements routes 2025-09-25 19:58:59 +05:30
ArnabChatterjee20k b032a55045 updated the endpoints 2025-09-25 00:48:09 +05:30
ArnabChatterjee20k d71c56b11f * Migrating endpoints
* New adapter resolution and injection
* Removed attribute creation for documentsdb
2025-09-23 23:15:46 +05:30
shimon b828621feb Update composer dependencies and enhance index validation in database module 2025-09-21 19:14:40 +03:00
shimon 3edc055c9d Skip spatial column tests for MongoDB support in DatabasesBase trait 2025-09-21 16:02:36 +03:00
shimon ceda513083 Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb-pulls
# Conflicts:
#	composer.lock
#	src/Appwrite/Platform/Workers/StatsUsage.php
#	tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
2025-09-21 15:55:11 +03:00
Shimon NewmanandGitHub 2d47b88246 Merge pull request #10527 from appwrite/feat-mongodb-pulls
Feat mongodb pulls
2025-09-21 15:46:15 +03:00
shimon b88e74095b Merge remote-tracking branch 'origin/feat-mongodb-pulls' into feat-mongodb-pulls
# Conflicts:
#	composer.json
2025-09-21 15:44:41 +03:00
shimon 4d85c5ca4f composer 2025-09-21 15:42:44 +03:00
shimon c89e649d05 composer 2025-09-21 15:38:35 +03:00
shimon d9bdf01cee db lib ver 2025-09-21 15:30:09 +03:00
shimon 54e3a4b25d db lib ver 2025-09-21 15:27:41 +03:00
ArnabChatterjee20k 98ee242812 migrated tests 2025-09-20 18:21:00 +05:30
ArnabChatterjee20k 79267c8123 migrated endpoints 2025-09-20 18:20:50 +05:30
ArnabChatterjee20k d2ad0e7c74 documentsdb database creation endpoints 2025-09-19 19:44:46 +05:30
shimon b1e55702c8 Refactor MongoDB test cases for improved readability and consistency. Adjust whitespace in conditional statements and remove redundant test methods related to spatial columns. Add new tests for spatial distance queries and update existing tests to ensure proper functionality. 2025-09-16 19:14:09 +03:00
shimon f16eba2f9f Update PHPUnit configuration to stop on failure set to false, and clean up whitespace in multiple files for consistency. Adjust comments and ensure proper formatting in test cases and API handling. 2025-09-16 19:13:38 +03:00
shimon 7c0bd1da88 uncomment inversion tests 2025-09-16 17:51:29 +03:00
shimon cc87de8934 Merge branch 'feat-mongodb-pulls' of github.com:appwrite/appwrite into feat-mongodb-pulls
# Conflicts:
#	composer.lock
2025-09-16 17:43:47 +03:00
shimon 5f8dc9998c Refactor database document handling by replacing createOrUpdateDocuments with upsertDocuments in multiple classes. Clean up common.php by removing commented-out index definitions and ensuring proper indexing for MongoDB compatibility. Update UsageTest to correct API endpoint paths for database operations. 2025-09-16 17:39:41 +03:00
shimon cc7778ceea database lib check 2025-09-16 11:10:06 +03:00
shimon f5fc099d87 Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb-pulls
# Conflicts:
#	composer.lock
#	tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
2025-09-16 10:26:43 +03:00
shimon 89d06d00c0 Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb-pulls
# Conflicts:
#	.github/workflows/tests.yml
#	composer.lock
#	tests/e2e/Services/Databases/TablesDB/DatabasesBase.php
2025-09-11 16:54:49 +03:00
shimon 882cd8c83e Merge remote-tracking branch 'origin/feat-mongodb-pulls' into feat-mongodb-pulls
# Conflicts:
#	tests/e2e/Services/Databases/TablesDB/DatabasesCustomClientTest.php
2025-09-11 16:43:39 +03:00
shimon 80cc6a0bb3 DatabasesCustomClientTest.php 2025-09-11 16:42:33 +03:00
shimon fc4ad175dd Update composer.json and composer.lock to reflect new versioning for utopia-php/database and other dependencies. Adjusted package versions for brick/math, open-telemetry/api, and symfony components. Added symfony/polyfill-php83 for compatibility with PHP 8.3 features. Minor formatting changes in MigrationsBase.php for code clarity. 2025-09-11 16:38:41 +03:00
shimon a0def8edf8 Updates 2025-08-31 17:57:58 +03:00
shimon bdb8204910 Refactor database attribute tests to streamline size validation and add support for additional integer index. Updated size constraints for 'description' and 'tagline' attributes, and introduced a new 'integers2' attribute to accommodate MongoDB limitations. Adjusted assertions to reflect these changes. 2025-08-27 16:32:13 +03:00
shimon 1274614d18 Update composer.json and composer.lock to reflect new versioning for utopia-php/database and other dependencies. Adjusted Docker configuration to set the database adapter to MariaDB. 2025-08-27 13:55:48 +03:00
shimon 4f937ac549 Update composer.json and composer.lock to reflect changes in utopia-php/database versioning, including the removal of the 'with-uuid7' suffix. Updated dependencies for composer/semver, symfony/polyfill packages, and phpunit/phpunit to their latest versions for improved compatibility and performance. 2025-08-27 10:28:17 +03:00
shimon 6910d669de Update composer.json to correct utopia-php/database version reference and modify test to use the correct endpoint for MongoDB operations. 2025-08-27 10:25:23 +03:00
shimon 4fb680350a Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb-pulls
# Conflicts:
#	Dockerfile
#	composer.json
#	composer.lock
#	tests/e2e/Services/Databases/TablesDB/DatabasesCustomClientTest.php
#	tests/e2e/Services/Databases/TablesDB/DatabasesCustomServerTest.php
2025-08-26 14:20:47 +03:00
Shimon NewmanandGitHub ff35dc6114 Merge pull request #10375 from appwrite/feat-mongodb-pulls
Feat mongodb pulls
2025-08-26 13:53:42 +03:00
shimon 38433b9ac1 Remove unnecessary var_dump statement from ProjectsConsoleClientTest for improved code clarity and consistency. 2025-08-26 13:52:05 +03:00
shimon b98be4756c Update composer.json and composer.lock to reflect changes in utopia-php/database versioning and dependencies. Adjusted MongoDB-related test conditions for improved compatibility and clarity. Removed unnecessary var_dump statements in account.php and enhanced logging in various test files for better debugging. 2025-08-26 13:48:01 +03:00
shimon ff8418ffb9 Update composer.lock to reflect new references and timestamps for dependencies, including updates for utopia-php/database and phpunit/phpunit. Removed duplicate entries and unnecessary lines for improved clarity. 2025-08-12 18:01:38 +03:00
shimon e151425b95 Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb-pulls 2025-08-12 17:54:55 +03:00
shimon b9f4815c3a Remove unnecessary var_dump statements from api.php and enhance logging in MessagingConsoleClientTest for improved clarity and debugging. This aligns with recent efforts to clean up debugging output across the codebase. 2025-08-12 17:47:11 +03:00
shimon c4fb7ecbc1 Refactor and clean up debugging output across multiple files, including mock.php, account.php, and OAuth2 classes. Removed unnecessary var_dump statements for improved code clarity and consistency. Updated MongoDB condition checks in test cases to ensure case-insensitive comparisons. 2025-08-12 15:20:30 +03:00
shimon 24b7c95514 Merge branch 'feat-mongodb' of github.com:appwrite/appwrite into feat-mongodb 2025-08-11 22:07:11 +03:00
shimon 870125ed84 Refactor MongoDB condition checks in DatabasesCustomClientTest and UsersBase. Improved formatting for consistency and clarity in test assertions, particularly for MongoDB-related tests. 2025-08-11 22:07:05 +03:00
shimon e1475619ba Refactor debugging output across various files, including http.php, mock.php, and OAuth2 classes. Adjusted var_dump statements for improved traceability and consistency. Updated test assertions in ProjectsConsoleClientTest and UsersBase to enhance clarity and functionality. 2025-08-11 22:05:18 +03:00
Matej Bačo ba058a89a3 Merge branch '1.8.x' into feat-mongodb 2025-08-11 13:22:27 +02:00
shimon 236fab4658 Update composer configuration for minimum stability and prefer stable packages. Adjust utopia-php/database version alias and update phpunit and sebastian packages to latest versions. Add debugging output in http.php and uncomment OAuth2 conversion test in AccountCustomClientTest for improved functionality. 2025-08-11 11:56:38 +03:00
shimon 6cfd6d9ac9 Remove debugging var_dump statements from projects API and enhance error handling in XList class. Update test cases to comment out a specific OAuth2 conversion test for clarity. 2025-08-11 11:28:11 +03:00
shimon da706378ea Enhance debugging output in OAuth2 mock endpoints and update MongoDB version in tests. Added var_dump statements for better traceability during OAuth2 flows and adjusted test assertions for user search functionality. 2025-08-11 11:27:45 +03:00
shimon 40e36e23dd Merge remote-tracking branch 'origin/1.8.x' into feat-mongodb 2025-08-10 18:16:03 +03:00
shimon 603980759a composer.lock 2025-08-06 15:49:05 +03:00
shimon d63a218bd8 Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb
# Conflicts:
#	composer.json
#	composer.lock
2025-08-06 15:48:19 +03:00
shimon def3628205 feat: rename tables to grids and update documentation 2025-08-06 15:37:59 +03:00
shimon 779fb7bd20 Update composer dependencies and Docker Compose configuration for MongoDB integration. Change database alias in composer.json, update package versions in composer.lock, and adjust volume mappings in Docker Compose to include MongoDB source directories. Refactor application code to set tenant to null for database connections. 2025-08-06 15:07:04 +03:00
shimon ea1aef1d70 composer.lock 2025-08-03 18:40:45 +03:00
shimon b1cc203eba composer 2025-08-03 18:38:38 +03:00
shimon b3e1a3415a Merge branch '1.8.x' of github.com:appwrite/appwrite into feat-mongodb
# Conflicts:
#	app/init/resources.php
#	composer.json
#	composer.lock
2025-08-03 18:33:27 +03:00
shimon d48248c9a2 Refactor Docker Compose and application code for MongoDB integration. Remove health check configuration and simplify service dependencies in Docker Compose. Update error logging in application to include context for MongoDB connection issues. 2025-08-03 18:13:16 +03:00
shimon eafca0c431 Add support for multiple database adapters in GitHub Actions workflow, including MARIADB and MONGODB. Set environment variables based on selected adapter for testing. 2025-07-31 18:22:42 +03:00
shimon a92bdfaed0 Update Docker Compose to clarify MongoDB readiness check comment, addressing implications of template substitution limitations. 2025-07-31 17:49:04 +03:00
shimon 46965135a4 Update Docker Compose and application configuration to support MongoDB as the primary database, replacing all instances of MariaDB. Introduce a new environment variable for database selection and adjust database schemes in the application initialization. 2025-07-31 17:17:08 +03:00
shimon 33c5ac83b2 Enhance Docker Compose configuration for MongoDB, add health checks, and update user creation logic in mongo-init.js. Improve database connection handling and fix conditional checks in application code. 2025-07-31 12:47:12 +03:00
shimon 57c08a41f1 Update environment configuration to support MongoDB, enhance Docker Compose for MongoDB integration, and improve database connection handling in the application. 2025-07-29 23:08:00 +03:00
shimon 36e7452f78 Update environment variables, Dockerfile version, and enhance MongoDB connection handling in the application. Add console document creation logic and improve code formatting for better readability. 2025-07-28 17:34:11 +03:00
shimon 469801b862 Update database configuration to use MongoDB, add MongoDB and Mongo Express services to Docker Compose, and adjust related code for MongoDB integration. 2025-07-27 20:38:49 +03:00
348 changed files with 37001 additions and 2898 deletions
+4
View File
@@ -37,10 +37,14 @@ _APP_REDIS_PASS=
_APP_REDIS_USER=
_APP_DB_HOST=mariadb
_APP_DB_PORT=3306
_APP_DB_ADAPTER=mariadb
_APP_DB_SCHEMA=appwrite
_APP_DB_USER=user
_APP_DB_PASS=password
_APP_DB_ROOT_PASS=rootsecretpassword
_APP_DB_ADAPTER_DOCUMENTSDB=mongodb
_APP_DB_HOST_DOCUMENTSDB=mongodb
_APP_DB_PORT_DOCUMENTSDB=27017
_APP_STORAGE_DEVICE=Local
_APP_STORAGE_S3_ACCESS_KEY=
_APP_STORAGE_S3_SECRET=
+19 -4
View File
@@ -151,10 +151,8 @@ jobs:
- name: Failure Logs
if: failure()
run: |
echo "=== Appwrite Worker Builds Logs ==="
docker compose logs appwrite-worker-builds
echo "=== OpenRuntimes Executor Logs ==="
docker compose logs openruntimes-executor
echo "=== Appwrite Logs ==="
docker compose logs
e2e_service_test:
name: E2E Service Test
@@ -163,12 +161,17 @@ jobs:
strategy:
fail-fast: false
matrix:
db_adapter: [
MARIADB,
MONGODB
]
service: [
Account,
Avatars,
Console,
Databases/Legacy,
Databases/TablesDB,
Databases/DocumentsDB,
Functions,
FunctionsSchedule,
GraphQL,
@@ -202,6 +205,18 @@ jobs:
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
sed -i 's|^_APP_BROWSER_HOST=.*|_APP_BROWSER_HOST=http://invalid-browser/v1|' .env
# Set DB Adapter in .env before starting services
if [ "${{ matrix.db_adapter }}" = "MARIADB" ]; then
sed -i 's|^_APP_DB_ADAPTER=.*|_APP_DB_ADAPTER=mariadb|' .env
sed -i 's|^_APP_DB_HOST=.*|_APP_DB_HOST=mariadb|' .env
sed -i 's|^_APP_DB_PORT=.*|_APP_DB_PORT=3306|' .env
elif [ "${{ matrix.db_adapter }}" = "MONGODB" ]; then
sed -i 's|^_APP_DB_ADAPTER=.*|_APP_DB_ADAPTER=mongodb|' .env
sed -i 's|^_APP_DB_HOST=.*|_APP_DB_HOST=mongodb|' .env
sed -i 's|^_APP_DB_PORT=.*|_APP_DB_PORT=27017|' .env
fi
docker compose up -d
sleep 30
+3
View File
@@ -37,6 +37,9 @@ COPY ./app /usr/src/code/app
COPY ./public /usr/src/code/public
COPY ./bin /usr/local/bin
COPY ./src /usr/src/code/src
COPY ./dev /usr/src/code/dev
COPY ./mongo-init.js /usr/src/code/mongo-init.js
COPY ./mongo-entrypoint.sh /usr/src/code/mongo-entrypoint.sh
# Set Volumes
RUN mkdir -p /storage/uploads && \
+1 -21
View File
@@ -1606,13 +1606,6 @@ return [
],
],
'indexes' => [
[
'$id' => ID::custom('_fulltext_name'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_search'),
'type' => Database::INDEX_FULLTEXT,
@@ -1842,13 +1835,6 @@ return [
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_type'),
'type' => Database::INDEX_KEY,
@@ -2116,14 +2102,8 @@ return [
'filters' => ['topicSearch'],
],
],
'indexes' => [
[
'$id' => ID::custom('_key_name'),
'type' => Database::INDEX_FULLTEXT,
'attributes' => ['name'],
'lengths' => [],
'orders' => [],
],
[
'$id' => ID::custom('_key_search'),
'type' => Database::INDEX_FULLTEXT,
+11
View File
@@ -78,6 +78,17 @@ $platformCollections = [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('documentsDatabase'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 256,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('logo'),
'type' => Database::VAR_STRING,
+9
View File
@@ -61,6 +61,15 @@ return [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('database'),
'type' => Database::VAR_STRING,
'size' => 128,
'required' => true,
'signed' => true,
'array' => false,
'filters' => [],
]
],
'indexes' => [
[
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
+9
View File
@@ -369,6 +369,15 @@ return [
]
],
],
[
'name' => '_APP_DB_ADAPTER',
'description' => 'Which database engine to use. Possible options are "mariadb" or "mongodb"',
'introduction' => '1.8.0',
'default' => 'mongoDB',
'required' => true,
'question' => 'Choose your database (mariadb|mongoDB)',
'filter' => ''
],
[
'category' => 'Redis',
'description' => 'Appwrite uses a Redis server for managing cache, queues and scheduled tasks. The Redis env vars are used to allow Appwrite server to connect to the Redis container.',
+22 -18
View File
@@ -367,7 +367,7 @@ App::post('/v1/account')
contentType: ContentType::JSON
))
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->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)
@@ -727,7 +727,7 @@ App::get('/v1/account/sessions/:sessionId')
],
contentType: ContentType::JSON
))
->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to get the current device session.')
->param('sessionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Session ID. Use the string \'current\' to get the current device session.', false, ['dbForProject'])
->inject('response')
->inject('user')
->inject('locale')
@@ -779,7 +779,7 @@ App::delete('/v1/account/sessions/:sessionId')
contentType: ContentType::NONE
))
->label('abuse-limit', 100)
->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to delete the current device session.')
->param('sessionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Session ID. Use the string \'current\' to delete the current device session.', false, ['dbForProject'])
->inject('requestTimestamp')
->inject('request')
->inject('response')
@@ -867,7 +867,7 @@ App::patch('/v1/account/sessions/:sessionId')
contentType: ContentType::JSON
))
->label('abuse-limit', 10)
->param('sessionId', '', new UID(), 'Session ID. Use the string \'current\' to update the current device session.')
->param('sessionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Session ID. Use the string \'current\' to update the current device session.', false, ['dbForProject'])
->inject('response')
->inject('user')
->inject('dbForProject')
@@ -1259,7 +1259,7 @@ App::post('/v1/account/sessions/token')
->label('abuse-limit', 10)
->label('abuse-key', 'ip:{ip},userId:{param-userId}')
->label('abuse-reset', [201])
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Secret of a token generated by login methods. For example, the `createMagicURLToken` or `createPhoneToken` methods.')
->inject('request')
->inject('response')
@@ -1479,6 +1479,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
@@ -1518,6 +1519,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
if (!empty($state['failure'])) {
$failure = URLParser::parse($state['failure']);
}
$failureRedirect = (function (string $type, ?string $message = null, ?int $code = null) use ($failure, $response) {
$exception = new Exception($type, $message, $code);
if (!empty($failure)) {
@@ -1563,7 +1565,9 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$accessToken = $oauth2->getAccessToken($code);
$refreshToken = $oauth2->getRefreshToken($code);
$accessTokenExpiry = $oauth2->getAccessTokenExpiry($code);
} catch (OAuth2Exception $ex) {
$failureRedirect(
$ex->getType(),
'Failed to obtain access token. The ' . $providerName . ' OAuth2 provider returned an error: ' . $ex->getMessage(),
@@ -2063,7 +2067,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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('email', '', new EmailValidator(), 'User email.')
->param('url', '', fn ($redirectValidator) => $redirectValidator, '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, ['redirectValidator'])
->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)
@@ -2342,7 +2346,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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->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')
@@ -2648,7 +2652,7 @@ App::put('/v1/account/sessions/magic-url')
->label('abuse-limit', 10)
->label('abuse-key', 'ip:{ip},userId:{param-userId}')
->label('abuse-reset', [201])
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Valid verification token.')
->inject('request')
->inject('response')
@@ -2696,7 +2700,7 @@ App::put('/v1/account/sessions/phone')
))
->label('abuse-limit', 10)
->label('abuse-key', 'ip:{ip},userId:{param-userId}')
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Valid verification token.')
->inject('request')
->inject('response')
@@ -2738,7 +2742,7 @@ App::post('/v1/account/tokens/phone')
))
->label('abuse-limit', 10)
->label('abuse-key', ['url:{url},phone:{param-phone}', '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 phone number has never been used, a new account is created using the provided userId. Otherwise, if the phone number is already attached to an account, the user ID is ignored.')
->param('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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 phone number has never been used, a new account is created using the provided userId. Otherwise, if the phone number is already attached to an account, the user ID is ignored.', false, ['dbForProject'])
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.')
->inject('request')
->inject('response')
@@ -3716,7 +3720,7 @@ App::put('/v1/account/recovery')
))
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{param-userId}')
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Valid reset token.')
->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'])
->inject('response')
@@ -4065,7 +4069,7 @@ App::put('/v1/account/verifications/email')
])
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},userId:{param-userId}')
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Valid verification token.')
->inject('response')
->inject('user')
@@ -4284,7 +4288,7 @@ App::put('/v1/account/verifications/phone')
))
->label('abuse-limit', 10)
->label('abuse-key', 'userId:{param-userId}')
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Valid verification token.')
->inject('response')
->inject('user')
@@ -4348,9 +4352,9 @@ App::post('/v1/account/targets/push')
],
contentType: ContentType::JSON
))
->param('targetId', '', new CustomId(), 'Target 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('targetId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Target 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.', false, ['dbForProject'])
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
->param('providerId', '', new UID(), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true)
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true, ['dbForProject'])
->inject('queueForEvents')
->inject('user')
->inject('request')
@@ -4431,7 +4435,7 @@ App::put('/v1/account/targets/:targetId/push')
],
contentType: ContentType::JSON
))
->param('targetId', '', new UID(), 'Target ID.')
->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID.', false, ['dbForProject'])
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
->inject('queueForEvents')
->inject('user')
@@ -4496,7 +4500,7 @@ App::delete('/v1/account/targets/:targetId/push')
],
contentType: ContentType::NONE
))
->param('targetId', '', new UID(), 'Target ID.')
->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID.', false, ['dbForProject'])
->inject('queueForEvents')
->inject('queueForDeletes')
->inject('user')
@@ -4623,7 +4627,7 @@ App::delete('/v1/account/identities/:identityId')
],
contentType: ContentType::NONE
))
->param('identityId', '', new UID(), 'Identity ID.')
->param('identityId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Identity ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
+69 -69
View File
@@ -77,7 +77,7 @@ App::post('/v1/messaging/providers/mailgun')
)
]
))
->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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('apiKey', '', new Text(0), 'Mailgun API Key.', true)
->param('domain', '', new Text(0), 'Mailgun Domain.', true)
@@ -171,7 +171,7 @@ App::post('/v1/messaging/providers/sendgrid')
)
]
))
->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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('apiKey', '', new Text(0), 'Sendgrid API key.', true)
->param('fromName', '', new Text(128, 0), 'Sender Name.', true)
@@ -355,7 +355,7 @@ App::post('/v1/messaging/providers/smtp')
]
)
])
->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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->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.')
->param('port', 587, new Range(1, 65535), 'The default SMTP server port.', true)
@@ -450,7 +450,7 @@ App::post('/v1/messaging/providers/msg91')
)
]
))
->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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('templateId', '', new Text(0), 'Msg91 template ID', true)
->param('senderId', '', new Text(0), 'Msg91 sender ID.', true)
@@ -533,7 +533,7 @@ App::post('/v1/messaging/providers/telesign')
)
]
))
->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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->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)
@@ -617,7 +617,7 @@ App::post('/v1/messaging/providers/textmagic')
)
]
))
->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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->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)
@@ -701,7 +701,7 @@ App::post('/v1/messaging/providers/twilio')
)
]
))
->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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->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)
@@ -785,7 +785,7 @@ App::post('/v1/messaging/providers/vonage')
)
]
))
->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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->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)
@@ -889,7 +889,7 @@ 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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('serviceAccountJSON', null, new Nullable(new JSON()), 'FCM service account JSON.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
@@ -979,7 +979,7 @@ App::post('/v1/messaging/providers/apns')
]
)
])
->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('providerId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.')
->param('authKey', '', new Text(0), 'APNS authentication key.', true)
->param('authKeyId', '', new Text(0), 'APNS authentication key ID.', true)
@@ -1138,7 +1138,7 @@ App::get('/v1/messaging/providers/:providerId/logs')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -1234,7 +1234,7 @@ App::get('/v1/messaging/providers/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->inject('dbForProject')
->inject('response')
->action(function (string $providerId, Database $dbForProject, Response $response) {
@@ -1268,7 +1268,7 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->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)
@@ -1381,7 +1381,7 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('apiKey', '', new Text(0), 'Sendgrid API key.', true)
@@ -1597,7 +1597,7 @@ App::patch('/v1/messaging/providers/smtp/:providerId')
]
)
])
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->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 Nullable(new Range(1, 65535)), 'SMTP port.', true)
@@ -1726,7 +1726,7 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('templateId', '', new Text(0), 'Msg91 template ID.', true)
@@ -1813,7 +1813,7 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('customerId', '', new Text(0), 'Telesign customer ID.', true)
@@ -1902,7 +1902,7 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('username', '', new Text(0), 'Textmagic username.', true)
@@ -1991,7 +1991,7 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('accountSid', '', new Text(0), 'Twilio account secret ID.', true)
@@ -2080,7 +2080,7 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
)
]
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('apiKey', '', new Text(0), 'Vonage API key.', true)
@@ -2189,7 +2189,7 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
]
)
])
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('serviceAccountJSON', null, new Nullable(new JSON()), 'FCM service account JSON.', true)
@@ -2285,7 +2285,7 @@ App::patch('/v1/messaging/providers/apns/:providerId')
]
)
])
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Provider name.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true)
->param('authKey', '', new Text(0), 'APNS authentication key.', true)
@@ -2388,7 +2388,7 @@ App::delete('/v1/messaging/providers/:providerId')
],
contentType: ContentType::NONE
))
->param('providerId', '', new UID(), 'Provider ID.')
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID.', false, ['dbForProject'])
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -2430,7 +2430,7 @@ App::post('/v1/messaging/topics')
)
]
))
->param('topicId', '', new CustomId(), 'Topic ID. Choose a custom Topic ID or a new Topic ID.')
->param('topicId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID. Choose a custom Topic ID or a new Topic ID.', false, ['dbForProject'])
->param('name', '', new Text(128), 'Topic Name.')
->param('subscribe', [Role::users()], 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')
@@ -2546,7 +2546,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
)
]
))
->param('topicId', '', new UID(), 'Topic ID.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID.', false, ['dbForProject'])
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -2643,7 +2643,7 @@ App::get('/v1/messaging/topics/:topicId')
)
]
))
->param('topicId', '', new UID(), 'Topic ID.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID.', false, ['dbForProject'])
->inject('dbForProject')
->inject('response')
->action(function (string $topicId, Database $dbForProject, Response $response) {
@@ -2678,7 +2678,7 @@ App::patch('/v1/messaging/topics/:topicId')
)
]
))
->param('topicId', '', new UID(), 'Topic ID.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID.', false, ['dbForProject'])
->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')
@@ -2730,7 +2730,7 @@ App::delete('/v1/messaging/topics/:topicId')
],
contentType: ContentType::NONE
))
->param('topicId', '', new UID(), 'Topic ID.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID.', false, ['dbForProject'])
->inject('queueForEvents')
->inject('dbForProject')
->inject('queueForDeletes')
@@ -2777,9 +2777,9 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
)
]
))
->param('subscriberId', '', new CustomId(), 'Subscriber ID. Choose a custom Subscriber ID or a new Subscriber ID.')
->param('topicId', '', new UID(), 'Topic ID. The topic ID to subscribe to.')
->param('targetId', '', new UID(), 'Target ID. The target ID to link to the specified Topic ID.')
->param('subscriberId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Subscriber ID. Choose a custom Subscriber ID or a new Subscriber ID.', false, ['dbForProject'])
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID. The topic ID to subscribe to.', false, ['dbForProject'])
->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID. The target ID to link to the specified Topic ID.', false, ['dbForProject'])
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -2877,7 +2877,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
)
]
))
->param('topicId', '', new UID(), 'Topic ID. The topic ID subscribed to.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID. The topic ID subscribed to.', false, ['dbForProject'])
->param('queries', [], new Subscribers(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Providers::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
@@ -2967,7 +2967,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
)
]
))
->param('subscriberId', '', new UID(), 'Subscriber ID.')
->param('subscriberId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Subscriber ID.', false, ['dbForProject'])
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -3064,8 +3064,8 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
)
]
))
->param('topicId', '', new UID(), 'Topic ID. The topic ID subscribed to.')
->param('subscriberId', '', new UID(), 'Subscriber ID.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID. The topic ID subscribed to.', false, ['dbForProject'])
->param('subscriberId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Subscriber ID.', false, ['dbForProject'])
->inject('dbForProject')
->inject('response')
->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response) {
@@ -3114,8 +3114,8 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
],
contentType: ContentType::NONE
))
->param('topicId', '', new UID(), 'Topic ID. The topic ID subscribed to.')
->param('subscriberId', '', new UID(), 'Subscriber ID.')
->param('topicId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Topic ID. The topic ID subscribed to.', false, ['dbForProject'])
->param('subscriberId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Subscriber ID.', false, ['dbForProject'])
->inject('queueForEvents')
->inject('dbForProject')
->inject('response')
@@ -3180,14 +3180,14 @@ App::post('/v1/messaging/messages/email')
)
]
))
->param('messageId', '', new CustomId(), 'Message 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('messageId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Message 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.', false, ['dbForProject'])
->param('subject', '', new Text(998), 'Email Subject.')
->param('content', '', new Text(64230), 'Email Content.')
->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('cc', [], new ArrayList(new UID()), 'Array of target IDs to be added as CC.', true)
->param('bcc', [], new ArrayList(new UID()), 'Array of target IDs to be added as BCC.', true)
->param('topics', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of Topic IDs.', true, ['dbForProject'])
->param('users', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of User IDs.', true, ['dbForProject'])
->param('targets', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of Targets IDs.', true, ['dbForProject'])
->param('cc', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'Array of target IDs to be added as CC.', true, ['dbForProject'])
->param('bcc', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'Array of target IDs to be added as BCC.', true, ['dbForProject'])
->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)
@@ -3359,11 +3359,11 @@ App::post('/v1/messaging/messages/sms')
]
)
])
->param('messageId', '', new CustomId(), 'Message 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('messageId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Message 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.', false, ['dbForProject'])
->param('content', '', new Text(64230), 'SMS Content.')
->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('topics', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of Topic IDs.', true, ['dbForProject'])
->param('users', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of User IDs.', true, ['dbForProject'])
->param('targets', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of Targets IDs.', true, ['dbForProject'])
->param('draft', false, 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')
@@ -3482,12 +3482,12 @@ App::post('/v1/messaging/messages/push')
)
]
))
->param('messageId', '', new CustomId(), 'Message 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('messageId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Message 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.', false, ['dbForProject'])
->param('title', '', new Text(256), 'Title for push notification.', true)
->param('body', '', new Text(64230), 'Body for push notification.', true)
->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('topics', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of Topic IDs.', true, ['dbForProject'])
->param('users', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of User IDs.', true, ['dbForProject'])
->param('targets', [], fn (Database $dbForProject) => new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength())), 'List of Targets IDs.', true, ['dbForProject'])
->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)
@@ -3767,7 +3767,7 @@ App::get('/v1/messaging/messages/:messageId/logs')
)
],
))
->param('messageId', '', new UID(), 'Message ID.')
->param('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -3864,7 +3864,7 @@ App::get('/v1/messaging/messages/:messageId/targets')
)
],
))
->param('messageId', '', new UID(), 'Message ID.')
->param('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->param('queries', [], new Targets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Targets::ALLOWED_ATTRIBUTES), true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -3947,7 +3947,7 @@ App::get('/v1/messaging/messages/:messageId')
)
]
))
->param('messageId', '', new UID(), 'Message ID.')
->param('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->inject('dbForProject')
->inject('response')
->action(function (string $messageId, Database $dbForProject, Response $response) {
@@ -3981,16 +3981,16 @@ App::patch('/v1/messaging/messages/email/:messageId')
)
]
))
->param('messageId', '', new UID(), 'Message ID.')
->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('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->param('topics', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of Topic IDs.', true, ['dbForProject'])
->param('users', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of User IDs.', true, ['dbForProject'])
->param('targets', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of Targets IDs.', true, ['dbForProject'])
->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('cc', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'Array of target IDs to be added as CC.', true, ['dbForProject'])
->param('bcc', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'Array of target IDs to be added as BCC.', true, ['dbForProject'])
->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')
@@ -4208,10 +4208,10 @@ App::patch('/v1/messaging/messages/sms/:messageId')
]
)
])
->param('messageId', '', new UID(), 'Message ID.')
->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('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->param('topics', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of Topic IDs.', true, ['dbForProject'])
->param('users', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of User IDs.', true, ['dbForProject'])
->param('targets', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of Targets IDs.', true, ['dbForProject'])
->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)
@@ -4370,10 +4370,10 @@ App::patch('/v1/messaging/messages/push/:messageId')
)
]
))
->param('messageId', '', new UID(), 'Message ID.')
->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('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->param('topics', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of Topic IDs.', true, ['dbForProject'])
->param('users', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of User IDs.', true, ['dbForProject'])
->param('targets', null, fn (Database $dbForProject) => new Nullable(new ArrayList(new UID($dbForProject->getAdapter()->getMaxUIDLength()))), 'List of Targets IDs.', true, ['dbForProject'])
->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)
@@ -4632,7 +4632,7 @@ App::delete('/v1/messaging/messages/:messageId')
],
contentType: ContentType::NONE
))
->param('messageId', '', new UID(), 'Message ID.')
->param('messageId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Message ID.', false, ['dbForProject'])
->inject('dbForProject')
->inject('dbForPlatform')
->inject('queueForEvents')
+37 -13
View File
@@ -43,6 +43,14 @@ use Utopia\Validator\WhiteList;
include_once __DIR__ . '/../shared/api.php';
function getDatabaseTransferResourceServices(string $databaseType)
{
return match($databaseType) {
DATABASE_TYPE_LEGACY,
DATABASE_TYPE_TABLESDB => Transfer::GROUP_DATABASES_TABLES_DB
};
}
App::post('/v1/migrations/appwrite')
->groups(['api', 'migrations'])
->desc('Create Appwrite migration')
@@ -64,7 +72,7 @@ App::post('/v1/migrations/appwrite')
))
->param('resources', [], new ArrayList(new WhiteList(Appwrite::getSupportedResources())), 'List of resources to migrate')
->param('endpoint', '', new URL(), 'Source Appwrite endpoint')
->param('projectId', '', new UID(), 'Source Project ID')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Source Project ID', false, ['dbForProject'])
->param('apiKey', '', new Text(512), 'Source API Key')
->inject('response')
->inject('dbForProject')
@@ -335,8 +343,8 @@ App::post('/v1/migrations/csv/imports')
)
]
))
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.')
->param('bucketId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).', false, ['dbForProject'])
->param('fileId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'File ID.', false, ['dbForProject'])
->param('resourceId', null, new CompoundUID(), 'Composite ID in the format {databaseId:collectionId}, identifying a collection within a database.')
->param('internalFile', false, new Boolean(), 'Is the file stored in an internal bucket?', true)
->inject('response')
@@ -425,8 +433,16 @@ App::post('/v1/migrations/csv/imports')
throw new \Exception('Unable to copy file');
}
// getting databasetype
$resources = explode(':', $resourceId);
$databaseId = $resources[0];
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$databaseType = $database->getAttribute('type');
if (!in_array($databaseType, CSV_ALLOWED_DATABASE_TYPES)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Database type not supported for csv');
}
$fileSize = $deviceForMigrations->getFileSize($newPath);
$resources = Transfer::extractServices([Transfer::GROUP_DATABASES]);
$resources = Transfer::extractServices([getDatabaseTransferResourceServices($databaseType)]);
$migration = $dbForProject->createDocument('migrations', new Document([
'$id' => $migrationId,
@@ -553,13 +569,23 @@ App::post('/v1/migrations/csv/exports')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
// getting databasetype
$resources = explode(':', $resourceId);
$databaseId = $resources[0];
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$databaseType = $database->getAttribute('type');
if (!in_array($databaseType, CSV_ALLOWED_DATABASE_TYPES)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Database type not supported for csv');
}
$resources = Transfer::extractServices([getDatabaseTransferResourceServices($databaseType)]);
$migration = $dbForProject->createDocument('migrations', new Document([
'$id' => ID::unique(),
'status' => 'pending',
'stage' => 'init',
'source' => Appwrite::getName(),
'destination' => CSV::getName(),
'resources' => Transfer::extractServices([Transfer::GROUP_DATABASES]),
'resources' => $resources,
'resourceId' => $resourceId,
'resourceType' => Resource::TYPE_DATABASE,
'statusCounters' => '{}',
@@ -680,7 +706,7 @@ App::get('/v1/migrations/:migrationId')
)
]
))
->param('migrationId', '', new UID(), 'Migration unique ID.')
->param('migrationId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Migration unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $migrationId, Response $response, Database $dbForProject) {
@@ -715,12 +741,10 @@ App::get('/v1/migrations/appwrite/report')
->param('projectID', '', new Text(512), "Source's Project ID")
->param('key', '', new Text(512), "Source's API Key")
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('user')
->action(function (array $resources, string $endpoint, string $projectID, string $key, Response $response) {
->inject('getDatabasesDB')
->action(function (array $resources, string $endpoint, string $projectID, string $key, Response $response, callable $getDatabasesDB) {
$appwrite = new Appwrite($projectID, $endpoint, $key);
$appwrite = new Appwrite($projectID, $endpoint, $key, $getDatabasesDB);
try {
$report = $appwrite->report($resources);
@@ -913,7 +937,7 @@ App::patch('/v1/migrations/:migrationId')
)
]
))
->param('migrationId', '', new UID(), 'Migration unique ID.')
->param('migrationId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Migration unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('project')
@@ -967,7 +991,7 @@ App::delete('/v1/migrations/:migrationId')
],
contentType: ContentType::NONE
))
->param('migrationId', '', new UID(), 'Migration ID.')
->param('migrationId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Migration ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
+19 -3
View File
@@ -61,15 +61,20 @@ App::get('/v1/project/usage')
METRIC_EXECUTIONS_MB_SECONDS,
METRIC_BUILDS_MB_SECONDS,
METRIC_DOCUMENTS,
METRIC_DOCUMENTS_DOCUMENTSDB,
METRIC_DATABASES,
METRIC_DATABASES_DOCUMENTSDB,
METRIC_USERS,
METRIC_BUCKETS,
METRIC_FILES_STORAGE,
METRIC_DATABASES_STORAGE,
METRIC_DATABASES_STORAGE_DOCUMENTSDB,
METRIC_DEPLOYMENTS_STORAGE,
METRIC_BUILDS_STORAGE,
METRIC_DATABASES_OPERATIONS_READS,
METRIC_DATABASES_OPERATIONS_READS_DOCUMENTSDB,
METRIC_DATABASES_OPERATIONS_WRITES,
METRIC_DATABASES_OPERATIONS_WRITES_DOCUMENTSDB,
METRIC_FILES_IMAGES_TRANSFORMED,
],
'period' => [
@@ -79,10 +84,13 @@ App::get('/v1/project/usage')
METRIC_USERS,
METRIC_EXECUTIONS,
METRIC_DATABASES_STORAGE,
METRIC_DATABASES_STORAGE_DOCUMENTSDB,
METRIC_EXECUTIONS_MB_SECONDS,
METRIC_BUILDS_MB_SECONDS,
METRIC_DATABASES_OPERATIONS_READS,
METRIC_DATABASES_OPERATIONS_READS_DOCUMENTSDB,
METRIC_DATABASES_OPERATIONS_WRITES,
METRIC_DATABASES_OPERATIONS_WRITES_DOCUMENTSDB,
METRIC_FILES_IMAGES_TRANSFORMED,
]
];
@@ -356,8 +364,11 @@ App::get('/v1/project/usage')
'buildsMbSecondsTotal' => $total[METRIC_BUILDS_MB_SECONDS],
'documentsTotal' => $total[METRIC_DOCUMENTS],
'rowsTotal' => $total[METRIC_DOCUMENTS],
'documentsdbDocumentsTotal' => $total[METRIC_DOCUMENTS_DOCUMENTSDB],
'databasesTotal' => $total[METRIC_DATABASES],
'documentsdbTotal' => $total[METRIC_DATABASES_DOCUMENTSDB],
'databasesStorageTotal' => $total[METRIC_DATABASES_STORAGE],
'documentsdbDatabasesStorageTotal' => $total[METRIC_DATABASES_STORAGE_DOCUMENTSDB],
'usersTotal' => $total[METRIC_USERS],
'bucketsTotal' => $total[METRIC_BUCKETS],
'filesStorageTotal' => $total[METRIC_FILES_STORAGE],
@@ -366,10 +377,15 @@ App::get('/v1/project/usage')
'deploymentsStorageTotal' => $total[METRIC_DEPLOYMENTS_STORAGE],
'databasesReadsTotal' => $total[METRIC_DATABASES_OPERATIONS_READS],
'databasesWritesTotal' => $total[METRIC_DATABASES_OPERATIONS_WRITES],
'documentsdbDatabasesReadsTotal' => $total[METRIC_DATABASES_OPERATIONS_READS_DOCUMENTSDB],
'documentsdbDatabasesWritesTotal' => $total[METRIC_DATABASES_OPERATIONS_WRITES_DOCUMENTSDB],
'executionsBreakdown' => $executionsBreakdown,
'bucketsBreakdown' => $bucketsBreakdown,
'databasesReads' => $usage[METRIC_DATABASES_OPERATIONS_READS],
'databasesWrites' => $usage[METRIC_DATABASES_OPERATIONS_WRITES],
'documentsdbDatabasesReads' => $usage[METRIC_DATABASES_OPERATIONS_READS_DOCUMENTSDB],
'documentsdbDatabasesWrites' => $usage[METRIC_DATABASES_OPERATIONS_WRITES_DOCUMENTSDB],
'documentsdbDatabasesStorage' => $usage[METRIC_DATABASES_STORAGE_DOCUMENTSDB],
'databasesStorageBreakdown' => $databasesStorageBreakdown,
'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown,
'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown,
@@ -495,7 +511,7 @@ App::get('/v1/project/variables/:variableId')
)
]
))
->param('variableId', '', new UID(), 'Variable unique ID.', false)
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->inject('response')
->inject('project')
->inject('dbForProject')
@@ -525,7 +541,7 @@ App::put('/v1/project/variables/:variableId')
)
]
))
->param('variableId', '', new UID(), 'Variable unique ID.', false)
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
->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)
@@ -584,7 +600,7 @@ App::delete('/v1/project/variables/:variableId')
],
contentType: ContentType::NONE
))
->param('variableId', '', new UID(), 'Variable unique ID.', false)
->param('variableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Variable unique ID.', false, ['dbForProject'])
->inject('project')
->inject('response')
->inject('dbForProject')
+150 -89
View File
@@ -16,7 +16,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Template\Template;
use Appwrite\Utopia\Database\Validator\ProjectId;
use Appwrite\Utopia\Database\Validator\Queries\Projects;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use PHPMailer\PHPMailer\PHPMailer;
@@ -82,7 +81,7 @@ App::post('/v1/projects')
))
->param('projectId', '', new ProjectId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, and hyphen. Can\'t start with a special char. Max length is 36 chars.')
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
->param('teamId', '', new UID(), 'Team unique ID.')
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team unique ID.', false, ['dbForProject'])
->param('region', System::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn ($config) => !$config['disabled']))), 'Project Region.', true)
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
->param('logo', '', new Text(1024), 'Project logo.', true)
@@ -101,6 +100,97 @@ App::post('/v1/projects')
->inject('hooks')
->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Request $request, Response $response, Database $dbForPlatform, Cache $cache, Group $pools, Hooks $hooks) {
// for returning dsn of multitype dbs with type optionally based on input dsn
$getDatabaseDSN = function (string $databasetype, $region, ?string $dsn = null): string {
$isSharedTablesV1 = false;
$isSharedTablesV2 = false;
if (!empty($dsn)) {
try {
$parsedDsn = new DSN($dsn);
$dsnHost = $parsedDsn->getHost();
} catch (\InvalidArgumentException) {
$dsnHost = $dsn;
}
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
$sharedTablesV2 = \array_diff($sharedTables, $sharedTablesV1);
$isSharedTablesV1 = \in_array($dsnHost, $sharedTablesV1);
$isSharedTablesV2 = \in_array($dsnHost, $sharedTablesV2);
}
$databases = [];
$databaseKeys = [];
/**
* @var string|null $databaseOverride
*/
$databaseOverride = '';
$dbScheme = '';
$sharedTables = [];
$sharedTablesV1 = [];
$sharedTablesV2 = [];
switch ($databasetype) {
case 'documentsDatabase':
$databases = Config::getParam('pools-documentsdb', []);
$databaseKeys = System::getEnv('_APP_DATABASE_DOCUMENTSDB_KEYS', '');
$databaseOverride = System::getEnv('_APP_DATABASE_DOCUMENTSDB_OVERRIDE');
$dbScheme = System::getEnv('_APP_DB_HOST_DOCUMENTSDB', 'mongodb');
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_DOCUMENTSDB_SHARED_TABLES', ''));
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_DOCUMENTSDB_SHARED_TABLES_V1', ''));
break;
default:
// legacy/tablesdb
$databases = Config::getParam('pools-database', []);
$databaseKeys = System::getEnv('_APP_DATABASE_KEYS', '');
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
$dbScheme = System::getEnv('_APP_DB_HOST', 'mysql');
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
break;
}
if ($region !== 'default') {
$keys = explode(',', $databaseKeys);
$databases = array_filter($keys, function ($value) use ($region) {
return str_contains($value, $region);
});
}
$sharedTablesV2 = \array_diff($sharedTables, $sharedTablesV1);
$index = \array_search($databaseOverride, $databases);
if ($index !== false) {
$selectedDsn = $databases[$index];
} else {
if (!empty($dsn)) {
if ($isSharedTablesV1) {
$databases = array_filter($databases, fn ($value) => \in_array($value, $sharedTablesV1));
} elseif ($isSharedTablesV2) {
$databases = array_filter($databases, fn ($value) => \in_array($value, $sharedTablesV2));
} else {
$databases = array_filter($databases, fn ($value) => !\in_array($value, $sharedTables));
}
}
$selectedDsn = !empty($databases) ? $databases[array_rand($databases)] : '';
}
if (\in_array($selectedDsn, $sharedTables)) {
$schema = 'appwrite';
$database = 'appwrite';
$namespace = System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '');
$selectedDsn = $schema . '://' . $selectedDsn . '?database=' . $database;
if (!empty($namespace)) {
$selectedDsn .= '&namespace=' . $namespace;
}
}
try {
new DSN($selectedDsn);
} catch (\InvalidArgumentException) {
$selectedDsn = $dbScheme.'://' . $selectedDsn;
}
return $selectedDsn;
};
$team = $dbForPlatform->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -139,37 +229,7 @@ App::post('/v1/projects')
throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project.");
}
$databases = Config::getParam('pools-database', []);
if ($region !== 'default') {
$databaseKeys = System::getEnv('_APP_DATABASE_KEYS', '');
$keys = explode(',', $databaseKeys);
$databases = array_filter($keys, function ($value) use ($region) {
return str_contains($value, $region);
});
}
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
$index = \array_search($databaseOverride, $databases);
if ($index !== false) {
$dsn = $databases[$index];
} else {
$dsn = $databases[array_rand($databases)];
}
// TODO: Temporary until all projects are using shared tables.
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn, $sharedTables)) {
$schema = 'appwrite';
$database = 'appwrite';
$namespace = System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '');
$dsn = $schema . '://' . $dsn . '?database=' . $database;
if (!empty($namespace)) {
$dsn .= '&namespace=' . $namespace;
}
}
$dsn = $getDatabaseDSN('databases', $region);
try {
$project = $dbForPlatform->createDocument('projects', new Document([
@@ -205,6 +265,7 @@ App::post('/v1/projects')
'search' => implode(' ', [$projectId, $name]),
'database' => $dsn,
'labels' => [],
'documentsDatabase' => $getDatabaseDSN('documentsDatabase', $region, $dsn),
]));
} catch (Duplicate) {
throw new Exception(Exception::PROJECT_ALREADY_EXISTS);
@@ -322,7 +383,7 @@ App::get('/v1/projects/:projectId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, Response $response, Database $dbForPlatform) {
@@ -355,7 +416,7 @@ App::patch('/v1/projects/:projectId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
->param('logo', '', new Text(1024), 'Project logo.', true)
@@ -409,8 +470,8 @@ App::patch('/v1/projects/:projectId/team')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('teamId', '', new UID(), 'Team ID of the team to transfer project to.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID of the team to transfer project to.', false, ['dbForProject'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $teamId, Response $response, Database $dbForPlatform) {
@@ -484,7 +545,7 @@ App::patch('/v1/projects/:projectId/service')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), fn ($element) => $element['optional'])), true), 'Service name.')
->param('status', null, new Boolean(), 'Service status.')
->inject('response')
@@ -522,7 +583,7 @@ App::patch('/v1/projects/:projectId/service/all')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('status', null, new Boolean(), 'Service status.')
->inject('response')
->inject('dbForPlatform')
@@ -583,7 +644,7 @@ App::patch('/v1/projects/:projectId/api')
]
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('api', '', new WhiteList(array_keys(Config::getParam('apis')), true), 'API name.')
->param('status', null, new Boolean(), 'API status.')
->inject('response')
@@ -641,7 +702,7 @@ App::patch('/v1/projects/:projectId/api/all')
]
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('status', null, new Boolean(), 'API status.')
->inject('response')
->inject('dbForPlatform')
@@ -682,7 +743,7 @@ App::patch('/v1/projects/:projectId/oauth2')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'Provider Name')
->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)
@@ -733,7 +794,7 @@ App::patch('/v1/projects/:projectId/auth/session-alerts')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('alerts', false, new Boolean(true), 'Set to true to enable session emails.')
->inject('response')
->inject('dbForPlatform')
@@ -771,7 +832,7 @@ App::patch('/v1/projects/:projectId/auth/memberships-privacy')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('userName', true, new Boolean(true), 'Set to true to show userName to members of a team.')
->param('userEmail', true, new Boolean(true), 'Set to true to show email to members of a team.')
->param('mfa', true, new Boolean(true), 'Set to true to show mfa to members of a team.')
@@ -813,7 +874,7 @@ App::patch('/v1/projects/:projectId/auth/limit')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('limit', false, new Range(0, APP_LIMIT_USERS), 'Set the max number of users allowed in this project. Use 0 for unlimited.')
->inject('response')
->inject('dbForPlatform')
@@ -851,7 +912,7 @@ App::patch('/v1/projects/:projectId/auth/duration')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('duration', 31536000, new Range(0, 31536000), 'Project session length in seconds. Max length: 31536000 seconds.')
->inject('response')
->inject('dbForPlatform')
@@ -889,7 +950,7 @@ App::patch('/v1/projects/:projectId/auth/:method')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('method', '', new WhiteList(\array_keys(Config::getParam('auth')), true), 'Auth Method. Possible values: ' . implode(',', \array_keys(Config::getParam('auth'))), false)
->param('status', false, new Boolean(true), 'Set the status of this auth method.')
->inject('response')
@@ -930,7 +991,7 @@ App::patch('/v1/projects/:projectId/auth/password-history')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('limit', 0, new Range(0, APP_LIMIT_USER_PASSWORD_HISTORY), 'Set the max number of passwords to store in user history. User can\'t choose a new password that is already stored in the password history list. Max number of passwords allowed in history is' . APP_LIMIT_USER_PASSWORD_HISTORY . '. Default value is 0')
->inject('response')
->inject('dbForPlatform')
@@ -968,7 +1029,7 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('enabled', false, new Boolean(false), 'Set whether or not to enable checking user\'s password against most commonly used passwords. Default is false.')
->inject('response')
->inject('dbForPlatform')
@@ -1006,7 +1067,7 @@ App::patch('/v1/projects/:projectId/auth/personal-data')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('enabled', false, new Boolean(false), 'Set whether or not to check a password for similarity with personal data. Default is false.')
->inject('response')
->inject('dbForPlatform')
@@ -1044,7 +1105,7 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('limit', false, new Range(1, APP_LIMIT_USER_SESSIONS_MAX), 'Set the max number of users allowed in this project. Value allowed is between 1-' . APP_LIMIT_USER_SESSIONS_MAX . '. Default is ' . APP_LIMIT_USER_SESSIONS_DEFAULT)
->inject('response')
->inject('dbForPlatform')
@@ -1082,7 +1143,7 @@ App::patch('/v1/projects/:projectId/auth/mock-numbers')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('numbers', '', new ArrayList(new MockNumber(), 10), 'An array of mock numbers and their corresponding verification codes (OTPs). Each number should be a valid E.164 formatted phone number. Maximum of 10 numbers are allowed.')
->inject('response')
->inject('dbForPlatform')
@@ -1131,7 +1192,7 @@ App::delete('/v1/projects/:projectId')
],
contentType: ContentType::NONE
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->inject('response')
->inject('user')
->inject('dbForPlatform')
@@ -1174,7 +1235,7 @@ App::post('/v1/projects/:projectId/webhooks')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.')
->param('enabled', true, new Boolean(true), 'Enable or disable a webhook.', true)
->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
@@ -1239,7 +1300,7 @@ App::get('/v1/projects/:projectId/webhooks')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
->inject('dbForPlatform')
@@ -1279,8 +1340,8 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', '', new UID(), 'Webhook unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('webhookId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Webhook unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForPlatform) {
@@ -1320,8 +1381,8 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', '', new UID(), 'Webhook unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('webhookId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Webhook unique ID.', false, ['dbForProject'])
->param('name', null, new Text(128), 'Webhook name. Max length: 128 chars.')
->param('enabled', true, new Boolean(true), 'Enable or disable a webhook.', true)
->param('events', null, new ArrayList(new Event(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
@@ -1386,8 +1447,8 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', '', new UID(), 'Webhook unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('webhookId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Webhook unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForPlatform) {
@@ -1433,8 +1494,8 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
],
contentType: ContentType::NONE
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('webhookId', '', new UID(), 'Webhook unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('webhookId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Webhook unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $webhookId, Response $response, Database $dbForPlatform) {
@@ -1480,7 +1541,7 @@ App::post('/v1/projects/:projectId/keys')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->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)
@@ -1541,7 +1602,7 @@ App::get('/v1/projects/:projectId/keys')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
->inject('dbForPlatform')
@@ -1582,8 +1643,8 @@ App::get('/v1/projects/:projectId/keys/:keyId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('keyId', '', new UID(), 'Key unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('keyId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Key unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $keyId, Response $response, Database $dbForPlatform) {
@@ -1624,8 +1685,8 @@ App::put('/v1/projects/:projectId/keys/:keyId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('keyId', '', new UID(), 'Key unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('keyId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Key unique ID.', false, ['dbForProject'])
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->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)
@@ -1679,8 +1740,8 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
],
contentType: ContentType::NONE
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('keyId', '', new UID(), 'Key unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('keyId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Key unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $keyId, Response $response, Database $dbForPlatform) {
@@ -1727,7 +1788,7 @@ App::post('/v1/projects/:projectId/jwts')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for JWT key. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
->inject('response')
@@ -1771,7 +1832,7 @@ App::post('/v1/projects/:projectId/platforms')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param(
'type',
null,
@@ -1849,7 +1910,7 @@ App::get('/v1/projects/:projectId/platforms')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
->inject('dbForPlatform')
@@ -1889,8 +1950,8 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('platformId', '', new UID(), 'Platform unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('platformId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Platform unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $platformId, Response $response, Database $dbForPlatform) {
@@ -1930,8 +1991,8 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('platformId', '', new UID(), 'Platform unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('platformId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Platform unique ID.', false, ['dbForProject'])
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
->param('key', '', new Text(256), 'Package name for android or bundle ID for iOS. Max length: 256 chars.', true)
->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true)
@@ -1987,8 +2048,8 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
],
contentType: ContentType::NONE
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('platformId', '', new UID(), 'Platform unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('platformId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Platform unique ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $platformId, Response $response, Database $dbForPlatform) {
@@ -2054,7 +2115,7 @@ App::patch('/v1/projects/:projectId/smtp')
]
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('enabled', false, new Boolean(), 'Enable custom SMTP service')
->param('senderName', '', new Text(255, 0), 'Name of the email sender', true)
->param('senderEmail', '', new Email(), 'Email of the sender', true)
@@ -2172,7 +2233,7 @@ App::post('/v1/projects/:projectId/smtp/tests')
]
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('emails', [], new ArrayList(new Email(), 10), 'Array of emails to send test email to. Maximum of 10 emails are allowed.')
->param('senderName', System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'), new Text(255, 0), 'Name of the email sender')
->param('senderEmail', System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), new Email(), 'Email of the sender')
@@ -2267,7 +2328,7 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
]
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? []), 'Template type')
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->inject('response')
@@ -2315,7 +2376,7 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? []), 'Template type')
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->inject('response')
@@ -2434,7 +2495,7 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
]
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? []), 'Template type')
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->param('message', '', new Text(0), 'Template message')
@@ -2481,7 +2542,7 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? []), 'Template type')
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->param('subject', '', new Text(255), 'Email Subject')
@@ -2560,7 +2621,7 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
contentType: ContentType::JSON
)
])
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? []), 'Template type')
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->inject('response')
@@ -2611,7 +2672,7 @@ App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
],
contentType: ContentType::JSON
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? []), 'Template type')
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
->inject('response')
@@ -2663,7 +2724,7 @@ App::patch('/v1/projects/:projectId/auth/session-invalidation')
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->param('projectId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForProject'])
->param('enabled', false, new Boolean(), 'Update authentication session invalidation status. Use this endpoint to enable or disable session invalidation on password change')
->inject('response')
->inject('dbForPlatform')
View File
+26 -26
View File
@@ -80,9 +80,9 @@ App::post('/v1/teams')
)
]
))
->param('teamId', '', new CustomId(), 'Team 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('teamId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Team 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.', false, ['dbForProject'])
->param('name', null, new Text(128), 'Team name. Max length: 128 chars.')
->param('roles', ['owner'], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the roles in the team for the user who created it. The default role is **owner**. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', true)
->param('roles', ['owner'], fn (Database $dbForProject) => new ArrayList(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the roles in the team for the user who created it. The default role is **owner**. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', true, ['dbForProject'])
->inject('response')
->inject('user')
->inject('dbForProject')
@@ -245,7 +245,7 @@ App::get('/v1/teams/:teamId')
)
]
))
->param('teamId', '', new UID(), 'Team ID.')
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $teamId, Response $response, Database $dbForProject) {
@@ -276,7 +276,7 @@ App::get('/v1/teams/:teamId/prefs')
)
]
))
->param('teamId', '', new UID(), 'Team ID.')
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $teamId, Response $response, Database $dbForProject) {
@@ -318,7 +318,7 @@ App::put('/v1/teams/:teamId')
)
]
))
->param('teamId', '', new UID(), 'Team ID.')
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID.', false, ['dbForProject'])
->param('name', null, new Text(128), 'New team name. Max length: 128 chars.')
->inject('requestTimestamp')
->inject('response')
@@ -364,7 +364,7 @@ App::put('/v1/teams/:teamId/prefs')
)
]
))
->param('teamId', '', new UID(), 'Team ID.')
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID.', false, ['dbForProject'])
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
->inject('response')
->inject('dbForProject')
@@ -412,7 +412,7 @@ App::delete('/v1/teams/:teamId')
],
contentType: ContentType::NONE
))
->param('teamId', '', new UID(), 'Team ID.')
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID.', false, ['dbForProject'])
->inject('response')
->inject('getProjectDB')
->inject('dbForProject')
@@ -471,11 +471,11 @@ App::post('/v1/teams/:teamId/memberships')
]
))
->label('abuse-limit', 10)
->param('teamId', '', new UID(), 'Team ID.')
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID.', false, ['dbForProject'])
->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('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'ID of the user to be added to a team.', true, ['dbForProject'])
->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) {
->param('roles', [], function (Document $project, Database $dbForProject) {
if ($project->getId() === 'console') {
$roles = array_keys(Config::getParam('roles', []));
$roles = array_filter($roles, function ($role) {
@@ -483,8 +483,8 @@ App::post('/v1/teams/:teamId/memberships')
});
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
}
return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE);
}, 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project'])
return new ArrayList(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), APP_LIMIT_ARRAY_PARAMS_SIZE);
}, 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project', 'dbForProject'])
->param('url', '', fn ($redirectValidator) => $redirectValidator, 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['redirectValidator']) // TODO add our own built-in confirm page
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
->inject('response')
@@ -856,7 +856,7 @@ App::get('/v1/teams/:teamId/memberships')
)
]
))
->param('teamId', '', new UID(), 'Team ID.')
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID.', false, ['dbForProject'])
->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Memberships::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
@@ -999,8 +999,8 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
)
]
))
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID.', false, ['dbForProject'])
->param('membershipId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Membership ID.', false, ['dbForProject'])
->inject('response')
->inject('project')
->inject('dbForProject')
@@ -1086,9 +1086,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
)
]
))
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->param('roles', [], function (Document $project) {
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID.', false, ['dbForProject'])
->param('membershipId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Membership ID.', false, ['dbForProject'])
->param('roles', [], function (Document $project, Database $dbForProject) {
if ($project->getId() === 'console') {
$roles = array_keys(Config::getParam('roles', []));
$roles = array_filter($roles, function ($role) {
@@ -1096,8 +1096,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
});
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
}
return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE);
}, 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project'])
return new ArrayList(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), APP_LIMIT_ARRAY_PARAMS_SIZE);
}, 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project', 'dbForProject'])
->inject('request')
->inject('response')
->inject('user')
@@ -1196,9 +1196,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
)
]
))
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->param('userId', '', new UID(), 'User ID.')
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID.', false, ['dbForProject'])
->param('membershipId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Membership ID.', false, ['dbForProject'])
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('secret', '', new Text(256), 'Secret key.')
->inject('request')
->inject('response')
@@ -1362,8 +1362,8 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
],
contentType: ContentType::NONE
))
->param('teamId', '', new UID(), 'Team ID.')
->param('membershipId', '', new UID(), 'Membership ID.')
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID.', false, ['dbForProject'])
->param('membershipId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Membership ID.', false, ['dbForProject'])
->inject('user')
->inject('project')
->inject('response')
@@ -1457,7 +1457,7 @@ App::get('/v1/teams/:teamId/logs')
)
]
))
->param('teamId', '', new UID(), 'Team ID.')
->param('teamId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Team ID.', false, ['dbForProject'])
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
+48 -48
View File
@@ -246,7 +246,7 @@ 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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->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'])
@@ -283,7 +283,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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->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)
@@ -321,7 +321,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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->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)
@@ -358,7 +358,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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->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)
@@ -395,7 +395,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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->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)
@@ -436,7 +436,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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->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)
@@ -473,7 +473,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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->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.')
@@ -521,7 +521,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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->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.')
@@ -566,11 +566,11 @@ App::post('/v1/users/:userId/targets')
)
]
))
->param('targetId', '', new CustomId(), 'Target 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('userId', '', new UID(), 'User ID.')
->param('targetId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Target 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.', false, ['dbForProject'])
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('providerType', '', new WhiteList([MESSAGE_TYPE_EMAIL, MESSAGE_TYPE_SMS, MESSAGE_TYPE_PUSH]), 'The target provider type. Can be one of the following: `email`, `sms` or `push`.')
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)')
->param('providerId', '', new UID(), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true)
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true, ['dbForProject'])
->param('name', '', new Text(128), 'Target name. Max length: 128 chars. For example: My Awesome App Galaxy S23.', true)
->inject('queueForEvents')
->inject('response')
@@ -737,7 +737,7 @@ App::get('/v1/users/:userId')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
@@ -768,7 +768,7 @@ App::get('/v1/users/:userId/prefs')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
@@ -801,8 +801,8 @@ App::get('/v1/users/:userId/targets/:targetId')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('targetId', '', new UID(), 'Target ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $userId, string $targetId, Response $response, Database $dbForProject) {
@@ -839,7 +839,7 @@ App::get('/v1/users/:userId/sessions')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
->inject('dbForProject')
@@ -883,7 +883,7 @@ App::get('/v1/users/:userId/memberships')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Memberships::ALLOWED_ATTRIBUTES), true)
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
@@ -938,7 +938,7 @@ App::get('/v1/users/:userId/logs')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -1025,7 +1025,7 @@ App::get('/v1/users/:userId/targets')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('queries', [], new Targets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Targets::ALLOWED_ATTRIBUTES), true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
@@ -1160,7 +1160,7 @@ App::patch('/v1/users/:userId/status')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('status', null, new Boolean(true), 'User Status. To activate the user pass `true` and to block the user pass `false`.')
->inject('response')
->inject('dbForProject')
@@ -1201,7 +1201,7 @@ App::put('/v1/users/:userId/labels')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('labels', [], new ArrayList(new Text(36, allowList: [...Text::NUMBERS, ...Text::ALPHABET_UPPER, ...Text::ALPHABET_LOWER]), APP_LIMIT_ARRAY_LABELS_SIZE), 'Array of user labels. Replaces the previous labels. Maximum of ' . APP_LIMIT_ARRAY_LABELS_SIZE . ' labels are allowed, each up to 36 alphanumeric characters long.')
->inject('response')
->inject('dbForProject')
@@ -1244,7 +1244,7 @@ App::patch('/v1/users/:userId/verification/phone')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('phoneVerification', false, new Boolean(), 'User phone verification status.')
->inject('response')
->inject('dbForProject')
@@ -1286,7 +1286,7 @@ App::patch('/v1/users/:userId/name')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('name', '', new Text(128, 0), 'User name. Max length: 128 chars.')
->inject('response')
->inject('dbForProject')
@@ -1329,7 +1329,7 @@ App::patch('/v1/users/:userId/password')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, enabled: $project->getAttribute('auths', [])['passwordDictionary'] ?? false, allowEmpty: true), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary'])
->inject('response')
->inject('project')
@@ -1428,7 +1428,7 @@ App::patch('/v1/users/:userId/email')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('email', '', new EmailValidator(allowEmpty: true), 'User email.')
->inject('response')
->inject('dbForProject')
@@ -1539,7 +1539,7 @@ App::patch('/v1/users/:userId/phone')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('number', '', new Phone(allowEmpty: true), 'User phone number.')
->inject('response')
->inject('dbForProject')
@@ -1629,7 +1629,7 @@ App::patch('/v1/users/:userId/verification')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('emailVerification', false, new Boolean(), 'User email verification status.')
->inject('response')
->inject('dbForProject')
@@ -1667,7 +1667,7 @@ App::patch('/v1/users/:userId/prefs')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
->inject('response')
->inject('dbForProject')
@@ -1708,10 +1708,10 @@ App::patch('/v1/users/:userId/targets/:targetId')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('targetId', '', new UID(), 'Target ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID.', false, ['dbForProject'])
->param('identifier', '', new Text(Database::LENGTH_KEY), 'The target identifier (token, email, phone etc.)', true)
->param('providerId', '', new UID(), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true)
->param('providerId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Provider ID. Message will be sent to this target from the specified provider ID. If no provider ID is set the first setup provider will be used.', true, ['dbForProject'])
->param('name', '', new Text(128), 'Target name. Max length: 128 chars. For example: My Awesome App Galaxy S23.', true)
->inject('queueForEvents')
->inject('response')
@@ -1833,7 +1833,7 @@ App::patch('/v1/users/:userId/mfa')
]
)
])
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('mfa', null, new Boolean(), 'Enable or disable MFA.')
->inject('response')
->inject('dbForProject')
@@ -1893,7 +1893,7 @@ App::get('/v1/users/:userId/mfa/factors')
]
)
])
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
@@ -1952,7 +1952,7 @@ App::get('/v1/users/:userId/mfa/recovery-codes')
]
)
])
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->action(function (string $userId, Response $response, Database $dbForProject) {
@@ -2017,7 +2017,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
]
)
])
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2090,7 +2090,7 @@ App::put('/v1/users/:userId/mfa/recovery-codes')
public: false,
)
])
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2163,7 +2163,7 @@ App::delete('/v1/users/:userId/mfa/authenticators/:type')
contentType: ContentType::NONE
)
])
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator.')
->inject('response')
->inject('dbForProject')
@@ -2210,7 +2210,7 @@ App::post('/v1/users/:userId/sessions')
)
]
))
->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('userId', '', fn (Database $dbForProject) => new CustomId(false, $dbForProject->getAdapter()->getMaxUIDLength()), '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.', false, ['dbForProject'])
->inject('request')
->inject('response')
->inject('dbForProject')
@@ -2302,7 +2302,7 @@ App::post('/v1/users/:userId/tokens')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('length', 6, new Range(4, 128), 'Token length in characters. The default length is 6 characters', true)
->param('expire', TOKEN_EXPIRATION_GENERIC, new Range(60, TOKEN_EXPIRATION_LOGIN_LONG), 'Token expiration period in seconds. The default expiration is 15 minutes.', true)
->inject('request')
@@ -2368,8 +2368,8 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
],
contentType: ContentType::NONE
))
->param('userId', '', new UID(), 'User ID.')
->param('sessionId', '', new UID(), 'Session ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('sessionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Session ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2419,7 +2419,7 @@ App::delete('/v1/users/:userId/sessions')
],
contentType: ContentType::NONE
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2469,7 +2469,7 @@ App::delete('/v1/users/:userId')
],
contentType: ContentType::NONE
))
->param('userId', '', new UID(), 'User ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2521,8 +2521,8 @@ App::delete('/v1/users/:userId/targets/:targetId')
],
contentType: ContentType::NONE
))
->param('userId', '', new UID(), 'User ID.')
->param('targetId', '', new UID(), 'Target ID.')
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('targetId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Target ID.', false, ['dbForProject'])
->inject('queueForEvents')
->inject('queueForDeletes')
->inject('response')
@@ -2579,7 +2579,7 @@ App::delete('/v1/users/identities/:identityId')
],
contentType: ContentType::NONE,
))
->param('identityId', '', new UID(), 'Identity ID.')
->param('identityId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Identity ID.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -2618,8 +2618,8 @@ App::post('/v1/users/:userId/jwts')
)
]
))
->param('userId', '', new UID(), 'User ID.')
->param('sessionId', '', new UID(), 'Session ID. Use the string \'recent\' to use the most recent session. Defaults to the most recent session.', true)
->param('userId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'User ID.', false, ['dbForProject'])
->param('sessionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Session ID. Use the string \'recent\' to use the most recent session. Defaults to the most recent session.', true, ['dbForProject'])
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
->inject('response')
->inject('dbForProject')
-1
View File
@@ -31,7 +31,6 @@ App::get('/v1/mock/tests/general/oauth2')
->param('state', '', new Text(1024), 'OAuth2 state.')
->inject('response')
->action(function (string $client_id, string $redirectURI, string $scope, string $state, Response $response) {
$response->redirect($redirectURI . '?' . \http_build_query(['code' => 'abcdef', 'state' => $state]));
});
+41 -12
View File
@@ -74,6 +74,18 @@ $parseLabel = function (string $label, array $responsePayload, array $requestPar
return $label;
};
/**
* Get database type prefixed metric
* Prefixes the metric with database type if it's not legacy or tablesdb
*
* @param string $databaseType Database type (e.g., 'documentsdb', 'tablesdb', 'legacy', '')
* @param string $metric Base metric constant (e.g., METRIC_DATABASES, METRIC_COLLECTIONS)
* @return string Prefixed metric or original metric if no prefix needed
*/
$getDatabaseTypePrefixedMetric = function (string $databaseType, string $metric): string {
return implode('.', array_filter([$databaseType, $metric]));
};
/**
* This isolated event handling for `users.*.create` which is based on a `Database::EVENT_DOCUMENT_CREATE` listener may look odd, but it is **intentional**.
*
@@ -112,7 +124,7 @@ $eventDatabaseListener = function (Document $project, Document $document, Respon
}
};
$usageDatabaseListener = function (string $event, Document $document, StatsUsage $queueForStatsUsage) {
$usageDatabaseListener = function (string $event, Document $document, StatsUsage $queueForStatsUsage, string $databaseType) use ($getDatabaseTypePrefixedMetric) {
$value = 1;
switch ($event) {
@@ -144,7 +156,8 @@ $usageDatabaseListener = function (string $event, Document $document, StatsUsage
$queueForStatsUsage->addMetric(METRIC_SESSIONS, $value); //per project
break;
case $document->getCollection() === 'databases': // databases
$queueForStatsUsage->addMetric(METRIC_DATABASES, $value); // per project
$metric = $getDatabaseTypePrefixedMetric($databaseType, METRIC_DATABASES);
$queueForStatsUsage->addMetric($metric, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForStatsUsage->addReduce($document);
@@ -153,22 +166,29 @@ $usageDatabaseListener = function (string $event, Document $document, StatsUsage
case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections
$parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0;
$collectionMetric = $getDatabaseTypePrefixedMetric($databaseType, METRIC_COLLECTIONS);
$databaseIdCollectionMetric = $getDatabaseTypePrefixedMetric($databaseType, METRIC_DATABASE_ID_COLLECTIONS);
$queueForStatsUsage
->addMetric(METRIC_COLLECTIONS, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value);
->addMetric($collectionMetric, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, $databaseIdCollectionMetric), $value);
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForStatsUsage->addReduce($document);
}
break;
// for databases route documents are getting created via getDatabasesDB and stats are maintained in the resources
// but keeping it since other routes might created documents still directly via dbForProject
case str_starts_with($document->getCollection(), 'database_') && str_contains($document->getCollection(), '_collection_'): //documents
$parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0;
$collectionInternalId = $parts[3] ?? 0;
$documentsMetric = $getDatabaseTypePrefixedMetric($databaseType, METRIC_DOCUMENTS);
$databaseIdDocumentsMetric = $getDatabaseTypePrefixedMetric($databaseType, METRIC_DATABASE_ID_DOCUMENTS);
$databaseIdCollectionIdDocumentsMetric = $getDatabaseTypePrefixedMetric($databaseType, METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS);
$queueForStatsUsage
->addMetric(METRIC_DOCUMENTS, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_DOCUMENTS), $value) // per database
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $collectionInternalId], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), $value); // per collection
->addMetric($documentsMetric, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, $databaseIdDocumentsMetric), $value) // per database
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $collectionInternalId], $databaseIdCollectionIdDocumentsMetric), $value); // per collection
break;
case $document->getCollection() === 'buckets': //buckets
$queueForStatsUsage
@@ -512,6 +532,11 @@ App::init()
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Mail $queueForMails, Migration $queueForMigrations, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform) use ($usageDatabaseListener, $eventDatabaseListener) {
$route = $utopia->getRoute();
$path = $route->getMatchedPath();
$databaseType = match (true) {
str_contains($path, '/documentsdb') => DATABASE_TYPE_DOCUMENTSDB,
default => '',
};
if (
array_key_exists('rest', $project->getAttribute('apis', []))
@@ -524,6 +549,7 @@ App::init()
/*
* Abuse Check
*/
$abuseKeyLabel = $route->getLabel('abuse-key', 'url:{url},ip:{ip}');
$timeLimitArray = [];
@@ -559,6 +585,7 @@ App::init()
$abuse = new Abuse($timeLimit);
$remaining = $timeLimit->remaining();
$limit = $timeLimit->limit();
$time = $timeLimit->time() + $route->getLabel('abuse-time', 3600);
@@ -636,12 +663,14 @@ App::init()
$queueForWebhooks = new Webhook($publisherWebhooks);
$queueForRealtime = new Realtime();
// to get consumed in the stats worker without calling to database
$queueForStatsUsage->setContext('database', new Document(['type' => $databaseType]));
$dbForProject
->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENTS_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENTS_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENTS_UPSERT, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage, $databaseType))
->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage, $databaseType))
->on(Database::EVENT_DOCUMENTS_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage, $databaseType))
->on(Database::EVENT_DOCUMENTS_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage, $databaseType))
->on(Database::EVENT_DOCUMENTS_UPSERT, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage, $databaseType))
->on(Database::EVENT_DOCUMENT_CREATE, 'create-trigger-events', fn ($event, $document) => $eventDatabaseListener(
$project,
$document,
+45 -16
View File
@@ -176,8 +176,8 @@ include __DIR__ . '/controllers/general.php';
function createDatabase(App $app, string $resourceKey, string $dbName, array $collections, mixed $pools, callable $extraSetup = null): void
{
$max = 10;
$sleep = 1;
$max = 15;
$sleep = 2;
$attempts = 0;
while (true) {
@@ -187,7 +187,7 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co
/* @var $database Database */
$database = is_callable($resource) ? $resource() : $resource;
break; // exit loop on success
} catch (\Exception $e) {
} catch (\Throwable $e) {
Console::warning(" └── Database not ready. Retrying connection ({$attempts})...");
if ($attempts >= $max) {
throw new \Exception(' └── Failed to connect to database: ' . $e->getMessage());
@@ -198,12 +198,26 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co
Console::success("[Setup] - $dbName database init started...");
// Attempt to create the database
try {
Console::info(" └── Creating database: $dbName...");
$database->create();
} catch (\Exception $e) {
Console::info(" └── Skip: metadata table already exists");
$attempts = 0;
while (true) {
try {
$attempts++;
Console::info(" └── Creating database: $dbName...");
$database->create();
break; // exit loop on success
} catch (\Exception $e) {
if ($e instanceof DuplicateException) {
Console::info(" └── Skip: metadata table already exists");
break;
}
Console::warning(" └── Database create failed. Retrying ({$attempts})...");
if ($attempts >= $max) {
throw new \Exception(' └── Failed to create database: ' . $e->getMessage());
}
\sleep($sleep);
}
}
// Process collections
@@ -385,11 +399,25 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
->setTenant(null)
->setNamespace(System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', ''));
try {
Console::success('[Setup] - Creating project database: ' . $hostname . '...');
$dbForProject->create();
} catch (DuplicateException) {
Console::success('[Setup] - Skip: metadata table already exists');
$max = 15;
$sleep = 2;
$attempts = 0;
while (true) {
try {
$attempts++;
Console::success('[Setup] - Creating project database: ' . $hostname . '...');
$dbForProject->create();
break; // exit loop on success
} catch (DuplicateException) {
Console::success('[Setup] - Skip: metadata table already exists');
break;
} catch (\Throwable $e) {
Console::warning(" └── Project database create failed. Retrying ({$attempts})...");
if ($attempts >= $max) {
throw new \Exception(' └── Failed to create project database: ' . $e->getMessage());
}
sleep($sleep);
}
}
if ($dbForProject->getCollection(AuditAdapterSQL::COLLECTION)->isEmpty()) {
@@ -432,6 +460,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
});
$http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register) {
App::setResource('swooleRequest', fn () => $swooleRequest);
App::setResource('swooleResponse', fn () => $swooleResponse);
@@ -572,14 +601,14 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
if ($latestDocument !== null) {
$queries[] = Query::cursorAfter($latestDocument);
}
if ($lastSyncUpdate != null) {
if ($lastSyncUpdate !== null) {
$queries[] = Query::greaterThanEqual('$updatedAt', $lastSyncUpdate);
}
$results = [];
try {
$results = Authorization::skip(fn () => $dbForPlatform->find('rules', $queries));
} catch (Throwable $th) {
Console::error($th->getMessage());
Console::error('rules ' . $th->getMessage());
}
$sum = count($results);
+24
View File
@@ -275,6 +275,22 @@ const METRIC_DATABASES_OPERATIONS_READS = 'databases.operations.reads';
const METRIC_DATABASE_ID_OPERATIONS_READS = '{databaseInternalId}.databases.operations.reads';
const METRIC_DATABASES_OPERATIONS_WRITES = 'databases.operations.writes';
const METRIC_DATABASE_ID_OPERATIONS_WRITES = '{databaseInternalId}.databases.operations.writes';
// documentsdb
const METRIC_DATABASES_DOCUMENTSDB = 'documentsdb.databases';
const METRIC_COLLECTIONS_DOCUMENTSDB = 'documentsdb.collections';
const METRIC_DATABASES_STORAGE_DOCUMENTSDB = 'documentsdb.databases.storage';
const METRIC_DATABASE_ID_COLLECTIONS_DOCUMENTSDB = 'documentsdb.{databaseInternalId}.collections';
const METRIC_DATABASE_ID_STORAGE_DOCUMENTSDB = 'documentsdb.{databaseInternalId}.databases.storage';
const METRIC_DOCUMENTS_DOCUMENTSDB = 'documentsdb.documents';
const METRIC_DATABASE_ID_DOCUMENTS_DOCUMENTSDB = 'documentsdb.{databaseInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS_DOCUMENTSDB = 'documentsdb.{databaseInternalId}.{collectionInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE_DOCUMENTSDB = 'documentsdb.{databaseInternalId}.{collectionInternalId}.databases.storage';
const METRIC_DATABASES_OPERATIONS_READS_DOCUMENTSDB = 'documentsdb.databases.operations.reads';
const METRIC_DATABASE_ID_OPERATIONS_READS_DOCUMENTSDB = 'documentsdb.{databaseInternalId}.databases.operations.reads';
const METRIC_DATABASES_OPERATIONS_WRITES_DOCUMENTSDB = 'documentsdb.databases.operations.writes';
const METRIC_DATABASE_ID_OPERATIONS_WRITES_DOCUMENTSDB = 'documentsdb.{databaseInternalId}.databases.operations.writes';
const METRIC_BUCKETS = 'buckets';
const METRIC_FILES = 'files';
const METRIC_FILES_STORAGE = 'files.storage';
@@ -374,3 +390,11 @@ const SCHEDULE_RESOURCE_TYPE_MESSAGE = 'message';
/** Preview cookie */
const COOKIE_NAME_PREVIEW = 'a_jwt_console';
// Database types
const DATABASE_TYPE_LEGACY = 'legacy';
const DATABASE_TYPE_TABLESDB = 'tablesdb';
const DATABASE_TYPE_DOCUMENTSDB = 'documentsdb';
// CSV import/export allowed database types
const CSV_ALLOWED_DATABASE_TYPES = [DATABASE_TYPE_LEGACY, DATABASE_TYPE_LEGACY];
+1 -1
View File
@@ -73,7 +73,7 @@ Database::addFilter(
$attributes = $database->find('attributes', [
Query::equal('collectionInternalId', [$document->getSequence()]),
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
Query::limit($database->getLimitForAttributes()),
Query::limit(PHP_INT_MAX),
]);
foreach ($attributes as $attribute) {
+5
View File
@@ -127,6 +127,8 @@ use Appwrite\Utopia\Response\Model\UsageBuckets;
use Appwrite\Utopia\Response\Model\UsageCollection;
use Appwrite\Utopia\Response\Model\UsageDatabase;
use Appwrite\Utopia\Response\Model\UsageDatabases;
use Appwrite\Utopia\Response\Model\UsageDocumentsDB;
use Appwrite\Utopia\Response\Model\UsageDocumentsDBs;
use Appwrite\Utopia\Response\Model\UsageFunction;
use Appwrite\Utopia\Response\Model\UsageFunctions;
use Appwrite\Utopia\Response\Model\UsageProject;
@@ -310,6 +312,9 @@ Response::setModel(new Metric());
Response::setModel(new MetricBreakdown());
Response::setModel(new UsageDatabases());
Response::setModel(new UsageDatabase());
Response::setModel(new UsageDocumentsDB());
Response::setModel(new UsageDocumentsDBs());
Response::setModel(new UsageTable());
Response::setModel(new UsageCollection());
Response::setModel(new UsageUsers());
+58 -13
View File
@@ -13,6 +13,7 @@ use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Adapter\Mongo;
use Utopia\Database\Adapter\MySQL;
use Utopia\Database\Adapter\SQL;
use Utopia\Database\PDO;
@@ -23,6 +24,7 @@ use Utopia\Logger\Adapter\LogOwl;
use Utopia\Logger\Adapter\Raygun;
use Utopia\Logger\Adapter\Sentry;
use Utopia\Logger\Logger;
use Utopia\Mongo\Client as MongoClient;
use Utopia\Pools\Group;
use Utopia\Pools\Pool;
use Utopia\Queue;
@@ -147,9 +149,17 @@ $register->set('pools', function () {
$group = new Group();
$fallbackForDB = 'db_main=' . AppwriteURL::unparse([
'scheme' => 'mariadb',
'host' => System::getEnv('_APP_DB_HOST', 'mariadb'),
'port' => System::getEnv('_APP_DB_PORT', '3306'),
'scheme' => System::getEnv('_APP_DB_ADAPTER', 'mongodb'),
'host' => System::getEnv('_APP_DB_HOST', 'mongodb'),
'port' => System::getEnv('_APP_DB_PORT', '27017'),
'user' => System::getEnv('_APP_DB_USER', ''),
'pass' => System::getEnv('_APP_DB_PASS', ''),
'path' => System::getEnv('_APP_DB_SCHEMA', ''),
]);
$fallbackForDocumentsDB = 'db_main=' . AppwriteURL::unparse([
'scheme' => System::getEnv('_APP_DB_ADAPTER_DOCUMENTSDB', 'mongodb'),
'host' => System::getEnv('_APP_DB_HOST_DOCUMENTSDB', 'mongodb'),
'port' => System::getEnv('_APP_DB_PORT_DOCUMENTSDB', '27017'),
'user' => System::getEnv('_APP_DB_USER', ''),
'pass' => System::getEnv('_APP_DB_PASS', ''),
'path' => System::getEnv('_APP_DB_SCHEMA', ''),
@@ -167,19 +177,25 @@ $register->set('pools', function () {
'type' => 'database',
'dsns' => $fallbackForDB,
'multiple' => false,
'schemes' => ['mariadb', 'mysql'],
'schemes' => ['mongodb','mariadb', 'mysql'],
],
'database' => [
'type' => 'database',
'dsns' => $fallbackForDB,
'multiple' => true,
'schemes' => ['mariadb', 'mysql'],
'schemes' => ['mongodb','mariadb', 'mysql'],
],
'documentsdb' => [
'type' => 'database',
'dsns' => System::getEnv('_APP_CONNECTIONS_DATABASE_DOCUMENTSDB', $fallbackForDocumentsDB),
'multiple' => true,
'schemes' => ['mongodb'],
],
'logs' => [
'type' => 'database',
'dsns' => System::getEnv('_APP_CONNECTIONS_DB_LOGS', $fallbackForDB),
'multiple' => false,
'schemes' => ['mariadb', 'mysql'],
'schemes' => ['mongodb','mariadb', 'mysql'],
],
'publisher' => [
'type' => 'publisher',
@@ -259,6 +275,7 @@ $register->set('pools', function () {
*
* Resource assignment to an adapter will happen below.
*/
$resource = match ($dsnScheme) {
'mysql',
'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
@@ -272,6 +289,16 @@ $register->set('pools', function () {
]);
});
},
'mongodb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase, $dsn) {
try {
$mongo = new MongoClient($dsnDatabase, $dsnHost, (int)$dsnPort, $dsnUser, $dsnPass, false);
@$mongo->connect();
return $mongo;
} catch (\Throwable $e) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, "MongoDB connection failed: " . $e->getMessage());
}
},
'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) {
$redis = new \Redis();
@$redis->pconnect($dsnHost, (int)$dsnPort);
@@ -285,17 +312,21 @@ $register->set('pools', function () {
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'),
};
$pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) {
$pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn, $key) {
// Get Adapter
switch ($type) {
case 'database':
$adapter = match ($dsn->getScheme()) {
'mariadb' => new MariaDB($resource()),
'mysql' => new MySQL($resource()),
'mongodb' => new Mongo($resource()),
default => null
};
$adapter->setDatabase($dsn->getPath());
if ($key === 'documentsdb') {
$adapter->setSupportForAttributes(false);
}
return $adapter;
case 'pubsub':
return match ($dsn->getScheme()) {
@@ -334,13 +365,27 @@ $register->set('db', function () {
$dbUser = System::getEnv('_APP_DB_USER', '');
$dbPass = System::getEnv('_APP_DB_PASS', '');
$dbScheme = System::getEnv('_APP_DB_SCHEMA', '');
$dbAdapter = System::getEnv('_APP_DB_ADAPTER', 'mongodb');
$dsn = '';
return new PDO(
"mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4",
$dbUser,
$dbPass,
SQL::getPDOAttributes()
);
switch ($dbAdapter) {
case 'mongodb':
try {
$mongo = new MongoClient($dbScheme, $dbHost, (int)$dbPort, $dbUser, $dbPass, false);
@$mongo->connect();
return $mongo;
} catch (\Throwable $e) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, "MongoDB connection failed: " . $e->getMessage());
}
case 'mysql':
case 'mariadb':
default:
$dsn = "mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4";
return new PDO($dsn, $dbUser, $dbPass, SQL::getPDOAttributes());
}
});
$register->set('smtp', function () {
+165 -3
View File
@@ -568,6 +568,135 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
return $database;
}, ['pools', 'cache']);
App::setResource('getDatabasesDB', function (Group $pools, Cache $cache, Document $project, Request $request, StatsUsage $queueForStatsUsage) {
return function (Document $database) use ($pools, $cache, $project, $request, $queueForStatsUsage): Database {
$databaseDSN = $database->getAttribute('database', '');
$databaseType = $database->getAttribute('type', '');
try {
$databaseDSN = new DSN($databaseDSN);
} catch (\InvalidArgumentException) {
// for old databases migrated through patch script
// databaseDSN determines the adapter
$databaseDSN = new DSN('mysql://'.$databaseDSN);
}
try {
$dsn = new DSN($project->getAttribute('database'));
} catch (\InvalidArgumentException) {
// TODO: Temporary until all projects are using shared tables
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
$pool = $pools->get($databaseDSN->getHost());
$adapter = new DatabasePool($pool);
$database = new Database($adapter, $cache);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
$database
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setSharedTables(true)
->setTenant((int)$project->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getSequence());
}
$timeout = \intval($request->getHeader('x-appwrite-timeout'));
if (!empty($timeout) && App::isDevelopment()) {
$database->setTimeout($timeout);
}
// Register database event listeners for usage stats collection
$documentsMetric = METRIC_DOCUMENTS;
$databaseIdDocumentsMetric = METRIC_DATABASE_ID_DOCUMENTS;
$databaseIdCollectionIdDocumentsMetric = METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS;
if ($databaseType !== DATABASE_TYPE_LEGACY && $databaseType !== DATABASE_TYPE_TABLESDB) {
$documentsMetric = $databaseType. '.' .$documentsMetric;
$databaseIdDocumentsMetric = $databaseType. '.' .$databaseIdDocumentsMetric;
$databaseIdCollectionIdDocumentsMetric = $databaseType . '.' .$databaseIdCollectionIdDocumentsMetric;
}
$database
->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', function ($event, $document) use ($queueForStatsUsage, $documentsMetric, $databaseIdDocumentsMetric, $databaseIdCollectionIdDocumentsMetric) {
$value = 1;
if (str_starts_with($document->getCollection(), 'database_') && str_contains($document->getCollection(), '_collection_')) {
$parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0;
$collectionInternalId = $parts[3] ?? 0;
$queueForStatsUsage
->addMetric($documentsMetric, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, $databaseIdDocumentsMetric), $value) // per database
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $collectionInternalId], $databaseIdCollectionIdDocumentsMetric), $value); // per collection
}
})
->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', function ($event, $document) use ($queueForStatsUsage, $documentsMetric, $databaseIdDocumentsMetric, $databaseIdCollectionIdDocumentsMetric) {
$value = -1;
if (str_starts_with($document->getCollection(), 'database_') && str_contains($document->getCollection(), '_collection_')) {
$parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0;
$collectionInternalId = $parts[3] ?? 0;
$queueForStatsUsage
->addMetric($documentsMetric, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, $databaseIdDocumentsMetric), $value) // per database
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $collectionInternalId], $databaseIdCollectionIdDocumentsMetric), $value); // per collection
}
})
->on(Database::EVENT_DOCUMENTS_CREATE, 'calculate-usage', function ($event, $document) use ($queueForStatsUsage, $documentsMetric, $databaseIdDocumentsMetric, $databaseIdCollectionIdDocumentsMetric) {
$value = $document->getAttribute('modified', 0);
if (str_starts_with($document->getCollection(), 'database_') && str_contains($document->getCollection(), '_collection_')) {
$parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0;
$collectionInternalId = $parts[3] ?? 0;
$queueForStatsUsage
->addMetric($documentsMetric, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, $databaseIdDocumentsMetric), $value) // per database
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $collectionInternalId], $databaseIdCollectionIdDocumentsMetric), $value); // per collection
}
})
->on(Database::EVENT_DOCUMENTS_DELETE, 'calculate-usage', function ($event, $document) use ($queueForStatsUsage, $documentsMetric, $databaseIdDocumentsMetric, $databaseIdCollectionIdDocumentsMetric) {
$value = -1 * $document->getAttribute('modified', 0);
if (str_starts_with($document->getCollection(), 'database_') && str_contains($document->getCollection(), '_collection_')) {
$parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0;
$collectionInternalId = $parts[3] ?? 0;
$queueForStatsUsage
->addMetric($documentsMetric, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, $databaseIdDocumentsMetric), $value) // per database
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $collectionInternalId], $databaseIdCollectionIdDocumentsMetric), $value); // per collection
}
})
->on(Database::EVENT_DOCUMENTS_UPSERT, 'calculate-usage', function ($event, $document) use ($queueForStatsUsage, $documentsMetric, $databaseIdDocumentsMetric, $databaseIdCollectionIdDocumentsMetric) {
$value = $document->getAttribute('created', 0);
if (str_starts_with($document->getCollection(), 'database_') && str_contains($document->getCollection(), '_collection_')) {
$parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0;
$collectionInternalId = $parts[3] ?? 0;
$queueForStatsUsage
->addMetric($documentsMetric, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, $databaseIdDocumentsMetric), $value) // per database
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $collectionInternalId], $databaseIdCollectionIdDocumentsMetric), $value); // per collection
}
});
return $database;
};
}, ['pools','cache','project','request','queueForStatsUsage']);
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
$databases = [];
@@ -1026,6 +1155,8 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
}
}
// if teamInternalId is empty, return an empty document
if (empty($teamInternalId)) {
return new Document([]);
}
@@ -1139,6 +1270,37 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
return new Document([]);
}, ['project', 'dbForProject', 'request']);
App::setResource('transactionState', function (Database $dbForProject) {
return new TransactionState($dbForProject);
}, ['dbForProject']);
App::setResource('httpReferrer', function (Request $request): string {
$referrer = $request->getReferer();
return $referrer;
}, ['request']);
App::setResource('httpReferrerSafe', function (Request $request, string $httpReferrer, array $platforms, Database $dbForPlatform, Document $project, App $utopia): string {
$origin = \parse_url($request->getOrigin($httpReferrer), PHP_URL_HOST);
$protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME);
$port = \parse_url($request->getOrigin($httpReferrer), PHP_URL_PORT);
$referrer = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
// Safe if route is publicly accessible
$route = $utopia->getRoute();
if ($route->getLabel('origin', false)) {
return $referrer;
}
// Safe if added as web platform
$originValidator = new Origin($platforms);
if ($originValidator->isValid($request->getOrigin($httpReferrer))) {
return $referrer;
}
// Unsafe; Localhost is always safe for ease of local development
$origin = 'localhost';
$protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME);
$port = \parse_url($request->getOrigin($httpReferrer), PHP_URL_PORT);
$referrer = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
return $referrer;
}, ['request', 'httpReferrer', 'platforms', 'dbForPlatform', 'project', 'utopia']);
App::setResource('transactionState', function (Database $dbForProject, callable $getDatabasesDB) {
return new TransactionState($dbForProject, $getDatabasesDB);
}, ['dbForProject', 'getDatabasesDB']);
+93 -27
View File
@@ -11,6 +11,7 @@ $httpsPort = $this->getParam('httpsPort', '');
$version = $this->getParam('version', '');
$organization = $this->getParam('organization', '');
$image = $this->getParam('image', '');
$dbService = $this->getParam('database');
$enableAssistant = $this->getParam('enableAssistant', false);
?>services:
traefik:
@@ -70,7 +71,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- appwrite-sites:/storage/sites:rw
- appwrite-builds:/storage/builds:rw
depends_on:
- mariadb
- <?= $dbService . "\n" ?>
- redis
# - clamav
environment:
@@ -103,6 +104,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -222,7 +224,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
networks:
- appwrite
depends_on:
- mariadb
- <?= $dbService . "\n" ?>
- redis
environment:
- _APP_ENV
@@ -234,6 +236,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -252,7 +255,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- appwrite
depends_on:
- redis
- mariadb
- <?= $dbService . "\n" ?>
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -261,6 +264,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -278,13 +282,14 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- appwrite
depends_on:
- redis
- mariadb
- <?= $dbService . "\n" ?>
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_EMAIL_SECURITY
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -306,7 +311,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- appwrite
depends_on:
- redis
- mariadb
- <?= $dbService . "\n" ?>
volumes:
- appwrite-uploads:/storage/uploads:rw
- appwrite-cache:/storage/cache:rw
@@ -322,6 +327,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -369,7 +375,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- appwrite
depends_on:
- redis
- mariadb
- <?= $dbService . "\n" ?>
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -378,6 +384,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -395,7 +402,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- appwrite
depends_on:
- redis
- mariadb
- <?= $dbService . "\n" ?>
volumes:
- appwrite-functions:/storage/functions:rw
- appwrite-sites:/storage/sites:rw
@@ -411,6 +418,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -463,7 +471,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- appwrite
depends_on:
- redis
- mariadb
- <?= $dbService . "\n" ?>
volumes:
- appwrite-config:/storage/config:rw
- appwrite-certificates:/storage/certificates:rw
@@ -483,6 +491,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -500,7 +509,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- appwrite
depends_on:
- redis
- mariadb
- <?= $dbService . "\n" ?>
- openruntimes-executor
environment:
- _APP_ENV
@@ -512,6 +521,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -545,6 +555,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_OPENSSL_KEY_V1
- _APP_SYSTEM_EMAIL_NAME
- _APP_SYSTEM_EMAIL_ADDRESS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -583,6 +594,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -625,7 +637,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
volumes:
- appwrite-imports:/storage/imports:rw
depends_on:
- mariadb
- <?= $dbService . "\n" ?>
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -641,6 +653,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -675,6 +688,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -699,11 +713,12 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- appwrite
depends_on:
- redis
- mariadb
- <?= $dbService . "\n" ?>
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -728,11 +743,12 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- appwrite
depends_on:
- redis
- mariadb
- <?= $dbService . "\n" ?>
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -756,11 +772,12 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- appwrite
depends_on:
- redis
- mariadb
- <?= $dbService . "\n" ?>
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -783,7 +800,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
networks:
- appwrite
depends_on:
- mariadb
- <?= $dbService . "\n" ?>
- redis
environment:
- _APP_ENV
@@ -793,6 +810,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -808,7 +826,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
networks:
- appwrite
depends_on:
- mariadb
- <?= $dbService . "\n" ?>
- redis
environment:
- _APP_ENV
@@ -818,6 +836,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -833,7 +852,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
networks:
- appwrite
depends_on:
- mariadb
- <?= $dbService . "\n" ?>
- redis
environment:
- _APP_ENV
@@ -843,6 +862,7 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -921,22 +941,63 @@ $enableAssistant = $this->getParam('enableAssistant', false);
- OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION
- OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET
mariadb:
image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p
container_name: appwrite-mariadb
<?php if ($dbService === 'mariadb'): ?>
mariadb:
image: mariadb:10.11
container_name: appwrite-mariadb
restart: unless-stopped
networks:
- appwrite
volumes:
- appwrite-mariadb:/var/lib/mysql:rw
environment:
- MYSQL_ROOT_PASSWORD=${_APP_DB_ROOT_PASS}
- MYSQL_DATABASE=${_APP_DB_SCHEMA}
- MYSQL_USER=${_APP_DB_USER}
- MYSQL_PASSWORD=${_APP_DB_PASS}
- MARIADB_AUTO_UPGRADE=1
command: 'mysqld --innodb-flush-method=fsync'
<?php elseif ($dbService === 'mongodb'): ?>
mongodb:
image: mongo:8.0.10
container_name: appwrite-mongodb
<<: *x-logging
restart: unless-stopped
networks:
- appwrite
volumes:
- appwrite-mariadb:/var/lib/mysql:rw
- appwrite-mongodb:/data/db
- appwrite-mongodb-keyfile:/data/keyfile
- ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
- ./mongo-entrypoint.sh:/mongo-entrypoint.sh:ro
ports:
- "27017:27017"
environment:
- MYSQL_ROOT_PASSWORD=${_APP_DB_ROOT_PASS}
- MYSQL_DATABASE=${_APP_DB_SCHEMA}
- MYSQL_USER=${_APP_DB_USER}
- MYSQL_PASSWORD=${_APP_DB_PASS}
- MARIADB_AUTO_UPGRADE=1
command: 'mysqld --innodb-flush-method=fsync'
- MONGO_INITDB_ROOT_USERNAME=root
- MONGO_INITDB_ROOT_PASSWORD=${_APP_DB_ROOT_PASS}
- MONGO_INITDB_DATABASE=${_APP_DB_SCHEMA}
- MONGO_INITDB_USERNAME=${_APP_DB_USER}
- MONGO_INITDB_PASSWORD=${_APP_DB_PASS}
entrypoint: ["/bin/bash", "/mongo-entrypoint.sh"]
healthcheck:
test: |
bash -c "
if mongosh -u root -p ${_APP_DB_ROOT_PASS} --authenticationDatabase admin --quiet --eval 'rs.status().ok' 2>/dev/null | grep -q 1; then
exit 0
else
mongosh -u root -p ${_APP_DB_ROOT_PASS} --authenticationDatabase admin --quiet --eval \"
rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'mongodb:27017'}]})
\" 2>/dev/null || exit 1
fi
"
interval: 10s
timeout: 10s
retries: 10
start_period: 30s
<?php endif; ?>
redis:
image: redis:7.2.4-alpine
@@ -971,7 +1032,12 @@ networks:
name: runtimes
volumes:
<?php if ($dbService === 'mariadb'): ?>
appwrite-mariadb:
<?php elseif ($dbService === 'mongodb'): ?>
appwrite-mongodb:
appwrite-mongodb-keyfile:
<?php endif; ?>
appwrite-redis:
appwrite-cache:
appwrite-uploads:
+43
View File
@@ -197,6 +197,49 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
};
}, ['pools', 'cache']);
Server::setResource('getDatabasesDB', function (Cache $cache, Registry $register, Document $project) {
return function (Document $database, ?Document $projectDocument = null) use ($cache, $register, $project): Database {
$projectDocument ??= $project;
$databaseType = $database->getAttribute('database', '');
try {
$databaseDSN = new DSN($databaseType);
} catch (\InvalidArgumentException) {
$databaseDSN = new DSN('mysql://'.$databaseType);
}
try {
$dsn = new DSN($projectDocument->getAttribute('database'));
} catch (\InvalidArgumentException) {
// Temporary fallback until all projects use shared tables
$dsn = new DSN('mysql://' . $projectDocument->getAttribute('database'));
}
$pools = $register->get('pools');
$pool = $pools->get($databaseDSN->getHost());
$adapter = new DatabasePool($pool);
$database = new Database($adapter, $cache);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables, true)) {
$database
->setSharedTables(true)
->setTenant((int) $projectDocument->getSequence())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $projectDocument->getSequence());
}
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
return $database;
};
}, ['cache', 'register', 'project']);
Server::setResource('abuseRetention', function () {
return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400); // 1 day
});
+14 -6
View File
@@ -29,6 +29,8 @@
"Appwrite\\Tests\\": "tests/extensions"
}
},
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": ">=8.3.0",
"ext-curl": "*",
@@ -64,27 +66,27 @@
"utopia-php/locale": "0.8.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.20.*",
"utopia-php/migration": "1.3.*",
"utopia-php/migration": "1.4.0",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.8.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/queue": "0.15.*",
"utopia-php/registry": "0.5.*",
"utopia-php/storage": "0.18.*",
"utopia-php/storage": "0.18.*",
"utopia-php/swoole": "0.8.*",
"utopia-php/system": "0.9.*",
"utopia-php/telemetry": "0.1.*",
"utopia-php/vcs": "0.13.*",
"utopia-php/websocket": "0.3.*",
"matomo/device-detector": "6.4.*",
"dragonmantank/cron-expression": "3.4.*",
"dragonmantank/cron-expression": "3.3.*",
"phpmailer/phpmailer": "6.9.*",
"chillerlan/php-qrcode": "4.4.*",
"chillerlan/php-qrcode": "4.3.*",
"adhocore/jwt": "1.1.*",
"spomky-labs/otphp": "10.0.*",
"spomky-labs/otphp": "^10.0",
"webonyx/graphql-php": "14.11.*",
"league/csv": "9.24.*",
"league/csv": "9.14.*",
"enshrined/svg-sanitize": "0.22.*"
},
"require-dev": {
@@ -100,6 +102,12 @@
"provide": {
"ext-phpiredis": "*"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/utopia-php/migration.git"
}
],
"config": {
"platform": {
"php": "8.3"
Generated
+79 -67
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": "ff3172688b600aa3c560131c9d9c5588",
"content-hash": "cd2578268f3230a027e1b28675bb7b81",
"packages": [
{
"name": "adhocore/jwt",
@@ -343,34 +343,31 @@
},
{
"name": "chillerlan/php-qrcode",
"version": "4.4.2",
"version": "4.3.4",
"source": {
"type": "git",
"url": "https://github.com/chillerlan/php-qrcode.git",
"reference": "345ed8e4ffb56e6b3fcd9f42e3970b9026fa6ce4"
"reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/345ed8e4ffb56e6b3fcd9f42e3970b9026fa6ce4",
"reference": "345ed8e4ffb56e6b3fcd9f42e3970b9026fa6ce4",
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d",
"reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d",
"shasum": ""
},
"require": {
"chillerlan/php-settings-container": "^2.1.6 || ^3.2.1",
"chillerlan/php-settings-container": "^2.1.4",
"ext-mbstring": "*",
"php": "^7.4 || ^8.0"
},
"require-dev": {
"phan/phan": "^5.4.5",
"phpmd/phpmd": "^2.15",
"phpunit/phpunit": "^9.6",
"setasign/fpdf": "^1.8.2",
"squizlabs/php_codesniffer": "^3.11"
"phan/phan": "^5.3",
"phpunit/phpunit": "^9.5",
"setasign/fpdf": "^1.8.2"
},
"suggest": {
"chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.",
"setasign/fpdf": "Required to use the QR FPDF output.",
"simple-icons/simple-icons": "SVG icons that you can use to embed as logos in the QR Code"
"setasign/fpdf": "Required to use the QR FPDF output."
},
"type": "library",
"autoload": {
@@ -397,7 +394,7 @@
"homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors"
}
],
"description": "A QR code generator with a user friendly API. PHP 7.4+",
"description": "A QR code generator. PHP 7.4+",
"homepage": "https://github.com/chillerlan/php-qrcode",
"keywords": [
"phpqrcode",
@@ -408,39 +405,43 @@
],
"support": {
"issues": "https://github.com/chillerlan/php-qrcode/issues",
"source": "https://github.com/chillerlan/php-qrcode/tree/4.4.2"
"source": "https://github.com/chillerlan/php-qrcode/tree/4.3.4"
},
"funding": [
{
"url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4",
"type": "custom"
},
{
"url": "https://ko-fi.com/codemasher",
"type": "ko_fi"
}
],
"time": "2024-11-15T15:36:24+00:00"
"time": "2022-07-25T09:12:45+00:00"
},
{
"name": "chillerlan/php-settings-container",
"version": "3.2.1",
"version": "2.1.6",
"source": {
"type": "git",
"url": "https://github.com/chillerlan/php-settings-container.git",
"reference": "95ed3e9676a1d47cab2e3174d19b43f5dbf52681"
"reference": "5553558bd381fce5108c6d0343c12e488cfec6bb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/95ed3e9676a1d47cab2e3174d19b43f5dbf52681",
"reference": "95ed3e9676a1d47cab2e3174d19b43f5dbf52681",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/5553558bd381fce5108c6d0343c12e488cfec6bb",
"reference": "5553558bd381fce5108c6d0343c12e488cfec6bb",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^8.1"
"php": "^7.4 || ^8.0"
},
"require-dev": {
"phpmd/phpmd": "^2.15",
"phpstan/phpstan": "^1.11",
"phpstan/phpstan-deprecation-rules": "^1.2",
"phpunit/phpunit": "^10.5",
"phpunit/phpunit": "^9.6",
"squizlabs/php_codesniffer": "^3.10"
},
"type": "library",
@@ -460,9 +461,10 @@
"homepage": "https://github.com/codemasher"
}
],
"description": "A container class for immutable settings objects. Not a DI container.",
"description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+",
"homepage": "https://github.com/chillerlan/php-settings-container",
"keywords": [
"PHP7",
"Settings",
"configuration",
"container",
@@ -482,7 +484,7 @@
"type": "ko_fi"
}
],
"time": "2024-07-16T11:13:48+00:00"
"time": "2024-07-17T01:04:28+00:00"
},
{
"name": "composer/semver",
@@ -563,16 +565,16 @@
},
{
"name": "dragonmantank/cron-expression",
"version": "v3.4.0",
"version": "v3.3.3",
"source": {
"type": "git",
"url": "https://github.com/dragonmantank/cron-expression.git",
"reference": "8c784d071debd117328803d86b2097615b457500"
"reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500",
"reference": "8c784d071debd117328803d86b2097615b457500",
"url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a",
"reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a",
"shasum": ""
},
"require": {
@@ -585,14 +587,10 @@
"require-dev": {
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^1.0",
"phpstan/phpstan-webmozart-assert": "^1.0",
"phpunit/phpunit": "^7.0|^8.0|^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Cron\\": "src/Cron/"
@@ -616,7 +614,7 @@
],
"support": {
"issues": "https://github.com/dragonmantank/cron-expression/issues",
"source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0"
"source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3"
},
"funding": [
{
@@ -624,7 +622,7 @@
"type": "github"
}
],
"time": "2024-10-09T13:47:03+00:00"
"time": "2023-08-10T19:36:49+00:00"
},
{
"name": "enshrined/svg-sanitize",
@@ -800,42 +798,40 @@
},
{
"name": "league/csv",
"version": "9.24.1",
"version": "9.14.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/csv.git",
"reference": "e0221a3f16aa2a823047d59fab5809d552e29bc8"
"reference": "34bf0df7340b60824b9449b5c526fcc3325070d5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/e0221a3f16aa2a823047d59fab5809d552e29bc8",
"reference": "e0221a3f16aa2a823047d59fab5809d552e29bc8",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/34bf0df7340b60824b9449b5c526fcc3325070d5",
"reference": "34bf0df7340b60824b9449b5c526fcc3325070d5",
"shasum": ""
},
"require": {
"ext-filter": "*",
"ext-json": "*",
"ext-mbstring": "*",
"php": "^8.1.2"
},
"require-dev": {
"doctrine/collections": "^2.1.4",
"ext-dom": "*",
"ext-xdebug": "*",
"friendsofphp/php-cs-fixer": "^3.75.0",
"phpbench/phpbench": "^1.4.1",
"phpstan/phpstan": "^1.12.27",
"phpstan/phpstan-deprecation-rules": "^1.2.1",
"phpstan/phpstan-phpunit": "^1.4.2",
"phpstan/phpstan-strict-rules": "^1.6.2",
"phpunit/phpunit": "^10.5.16 || ^11.5.22",
"symfony/var-dumper": "^6.4.8 || ^7.3.0"
"friendsofphp/php-cs-fixer": "^v3.22.0",
"phpbench/phpbench": "^1.2.15",
"phpstan/phpstan": "^1.10.50",
"phpstan/phpstan-deprecation-rules": "^1.1.4",
"phpstan/phpstan-phpunit": "^1.3.15",
"phpstan/phpstan-strict-rules": "^1.5.2",
"phpunit/phpunit": "^10.5.3",
"symfony/var-dumper": "^6.4.0"
},
"suggest": {
"ext-dom": "Required to use the XMLConverter and the HTMLConverter classes",
"ext-iconv": "Needed to ease transcoding CSV using iconv stream filters",
"ext-mbstring": "Needed to ease transcoding CSV using mb stream filters",
"ext-mysqli": "Requiered to use the package with the MySQLi extension",
"ext-pdo": "Required to use the package with the PDO extension",
"ext-pgsql": "Requiered to use the package with the PgSQL extension",
"ext-sqlite3": "Required to use the package with the SQLite3 extension"
"ext-iconv": "Needed to ease transcoding CSV using iconv stream filters"
},
"type": "library",
"extra": {
@@ -887,7 +883,7 @@
"type": "github"
}
],
"time": "2025-06-25T14:53:51+00:00"
"time": "2023-12-29T07:34:53+00:00"
},
{
"name": "matomo/device-detector",
@@ -4516,16 +4512,16 @@
},
{
"name": "utopia-php/migration",
"version": "1.3.13",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "c5e3f5e970e62e8f7db97b5b90baae2af800a715"
"reference": "18bd7d39dcee09280f40edb12879c727ecec98d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/c5e3f5e970e62e8f7db97b5b90baae2af800a715",
"reference": "c5e3f5e970e62e8f7db97b5b90baae2af800a715",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/18bd7d39dcee09280f40edb12879c727ecec98d3",
"reference": "18bd7d39dcee09280f40edb12879c727ecec98d3",
"shasum": ""
},
"require": {
@@ -4551,7 +4547,25 @@
"Utopia\\Migration\\": "src/Migration"
}
},
"notification-url": "https://packagist.org/downloads/",
"autoload-dev": {
"psr-4": {
"Utopia\\Tests\\": "tests/Migration"
}
},
"scripts": {
"test": [
"./vendor/bin/phpunit"
],
"lint": [
"./vendor/bin/pint --test"
],
"format": [
"./vendor/bin/pint"
],
"check": [
"./vendor/bin/phpstan analyse --level 3 src tests --memory-limit 2G"
]
},
"license": [
"MIT"
],
@@ -4564,10 +4578,10 @@
"utopia"
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/1.3.13"
"source": "https://github.com/utopia-php/migration/tree/1.4.0",
"issues": "https://github.com/utopia-php/migration/issues"
},
"time": "2026-01-07T14:48:05+00:00"
"time": "2025-11-21T06:08:59+00:00"
},
{
"name": "utopia-php/mongo",
@@ -8944,11 +8958,9 @@
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"utopia-php/audit": 5
},
"prefer-stable": false,
"minimum-stability": "dev",
"stability-flags": {},
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
"php": ">=8.3.0",
+89 -17
View File
@@ -98,8 +98,9 @@ services:
- ./public:/usr/src/code/public
- ./src:/usr/src/code/src
- ./dev:/usr/src/code/dev
depends_on:
- mariadb
- ${_APP_DB_HOST:-mongodb}
- redis
- coredns
# - clamav
@@ -140,6 +141,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -277,7 +279,7 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- mariadb
- ${_APP_DB_HOST:-mongodb}
- redis
environment:
- _APP_ENV
@@ -289,6 +291,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -311,7 +314,7 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -320,6 +323,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -340,7 +344,7 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
- ${_APP_DB_HOST:-mongodb}
- request-catcher-sms
- request-catcher-webhook
environment:
@@ -348,6 +352,7 @@ services:
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_EMAIL_SECURITY
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -370,7 +375,7 @@ services:
- appwrite
depends_on:
- redis
- mariadb
- ${_APP_DB_HOST:-mongodb}
volumes:
- appwrite-uploads:/storage/uploads:rw
- appwrite-cache:/storage/cache:rw
@@ -388,6 +393,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -436,7 +442,7 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -445,6 +451,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -470,7 +477,7 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -481,6 +488,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -595,7 +603,7 @@ services:
- appwrite
depends_on:
- redis
- mariadb
- ${_APP_DB_HOST:-mongodb}
volumes:
- appwrite-config:/storage/config:rw
- appwrite-certificates:/storage/certificates:rw
@@ -618,6 +626,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -638,7 +647,7 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
- ${_APP_DB_HOST:-mongodb}
- openruntimes-executor
environment:
- _APP_ENV
@@ -650,6 +659,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -725,6 +735,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -773,7 +784,7 @@ services:
- ./src:/usr/src/code/src
- ./tests:/usr/src/code/tests
depends_on:
- mariadb
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
@@ -789,6 +800,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -828,6 +840,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -893,11 +906,12 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -924,11 +938,12 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -955,11 +970,12 @@ services:
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
- ${_APP_DB_HOST:-mongodb}
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -985,7 +1001,7 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- mariadb
- ${_APP_DB_HOST:-mongodb}
- redis
environment:
- _APP_ENV
@@ -995,6 +1011,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -1013,7 +1030,7 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- mariadb
- ${_APP_DB_HOST:-mongodb}
- redis
environment:
- _APP_ENV
@@ -1023,6 +1040,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -1040,7 +1058,7 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- mariadb
- ${_APP_DB_HOST:-mongodb}
- redis
environment:
- _APP_ENV
@@ -1050,6 +1068,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_ADAPTER
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
@@ -1142,6 +1161,57 @@ services:
- MARIADB_AUTO_UPGRADE=1
command: "mysqld --innodb-flush-method=fsync"
mongodb:
image: mongo:8.0.10
container_name: appwrite-mongodb
<<: *x-logging
networks:
- appwrite
volumes:
- appwrite-mongodb:/data/db
- appwrite-mongodb-keyfile:/data/keyfile
- ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
- ./mongo-entrypoint.sh:/mongo-entrypoint.sh:ro
ports:
- "27017:27017"
environment:
- MONGO_INITDB_ROOT_USERNAME=root
- MONGO_INITDB_ROOT_PASSWORD=${_APP_DB_ROOT_PASS}
- MONGO_INITDB_DATABASE=${_APP_DB_SCHEMA}
- MONGO_INITDB_USERNAME=${_APP_DB_USER}
- MONGO_INITDB_PASSWORD=${_APP_DB_PASS}
entrypoint: ["/bin/bash", "/mongo-entrypoint.sh"]
healthcheck:
test: |
bash -c "
if mongosh -u root -p ${_APP_DB_ROOT_PASS} --authenticationDatabase admin --quiet --eval 'rs.status().ok' 2>/dev/null | grep -q 1; then
exit 0
else
mongosh -u root -p ${_APP_DB_ROOT_PASS} --authenticationDatabase admin --quiet --eval \"
rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'appwrite-mongodb:27017'}]})
\" 2>/dev/null || exit 1
fi
"
interval: 10s
timeout: 10s
retries: 10
start_period: 30s
appwrite-mongo-express:
image: mongo-express
container_name: appwrite-mongo-express
networks:
- appwrite
ports:
- "8082:8081"
environment:
ME_CONFIG_MONGODB_URL: "mongodb://root:${_APP_DB_ROOT_PASS}@appwrite-mongodb:27017/?replicaSet=rs0&directConnection=true"
ME_CONFIG_BASICAUTH_USERNAME: ${_APP_DB_USER}
ME_CONFIG_BASICAUTH_PASSWORD: ${_APP_DB_PASS}
depends_on:
- mongodb
redis:
image: redis:7.2.4-alpine
<<: *x-logging
@@ -1369,6 +1439,8 @@ configs:
volumes:
appwrite-mariadb:
appwrite-mongodb:
appwrite-mongodb-keyfile:
appwrite-redis:
appwrite-cache:
appwrite-uploads:
@@ -1377,4 +1449,4 @@ volumes:
appwrite-functions:
appwrite-sites:
appwrite-builds:
appwrite-config:
appwrite-config:
@@ -0,0 +1 @@
Create a new Collection. Before using this route, you should create a new database resource using either a [server integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) API or directly from your database console.
@@ -0,0 +1 @@
Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) API or directly from your database console.
@@ -0,0 +1 @@
Create new Documents. Before using this route, you should create a new collection resource using either a [server integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) API or directly from your database console.
@@ -0,0 +1,2 @@
Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request.
Attributes can be `key`, `fulltext`, and `unique`.
+1
View File
@@ -0,0 +1 @@
Create a new Database.
@@ -0,0 +1 @@
Decrement a specific column of a row by a given value.
@@ -0,0 +1 @@
Delete a collection by its unique ID. Only users with write permissions have access to delete this resource.
@@ -0,0 +1 @@
Delete a document by its unique ID.
@@ -0,0 +1 @@
Bulk delete documents using queries, if no queries are passed then all documents are deleted.
@@ -0,0 +1 @@
Delete an index.
+1
View File
@@ -0,0 +1 @@
Delete a database by its unique ID. Only API keys with with databases.write scope can delete a database.
@@ -0,0 +1 @@
Get the collection activity logs list by its unique ID.
@@ -0,0 +1 @@
Get usage metrics and statistics for a collection. Returning the total number of documents. The response includes both current totals and historical data over time. Use the optional range parameter to specify the time window for historical data: 24h (last 24 hours), 30d (last 30 days), or 90d (last 90 days). If not specified, range defaults to 30 days.
@@ -0,0 +1 @@
Get a collection by its unique ID. This endpoint response returns a JSON object with the collection metadata.
@@ -0,0 +1 @@
Get usage metrics and statistics for a database. You can view the total number of collections, documents, and storage usage. The response includes both current totals and historical data over time. Use the optional range parameter to specify the time window for historical data: 24h (last 24 hours), 30d (last 30 days), or 90d (last 90 days). If not specified, range defaults to 30 days.
@@ -0,0 +1 @@
Get the document activity logs list by its unique ID.
@@ -0,0 +1 @@
Get a document by its unique ID. This endpoint response returns a JSON object with the document data.
+1
View File
@@ -0,0 +1 @@
Get index by ID.
+1
View File
@@ -0,0 +1 @@
Get the database activity logs list by its unique ID.
+1
View File
@@ -0,0 +1 @@
Get a database by its unique ID. This endpoint response returns a JSON object with the database metadata.
@@ -0,0 +1 @@
Increment a specific column of a row by a given value.
@@ -0,0 +1 @@
List attributes in the collection.
@@ -0,0 +1 @@
Get a list of all collections that belong to the provided databaseId. You can use the search parameter to filter your results.
@@ -0,0 +1 @@
Get a list of all the user's documents in a given collection. You can use the query params to filter your results.
@@ -0,0 +1 @@
List indexes in the collection.
@@ -0,0 +1 @@
List usage metrics and statistics for all databases in the project. You can view the total number of databases, collections, documents, and storage usage. The response includes both current totals and historical data over time. Use the optional range parameter to specify the time window for historical data: 24h (last 24 hours), 30d (last 30 days), or 90d (last 90 days). If not specified, range defaults to 30 days.
+1
View File
@@ -0,0 +1 @@
Get a list of all databases from the current Appwrite project. You can use the search parameter to filter your results.
@@ -0,0 +1 @@
Update a collection by its unique ID.
@@ -0,0 +1 @@
Update a document by its unique ID. Using the patch method you can pass only specific fields that will get updated.
@@ -0,0 +1 @@
Update all documents that match your queries, if no queries are submitted then all documents are updated. You can pass only specific fields to be updated.
+1
View File
@@ -0,0 +1 @@
Update a database by its unique ID.
@@ -0,0 +1 @@
Create or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) API or directly from your database console.
@@ -0,0 +1 @@
Create or update Documents. Before using this route, you should create a new collection resource using either a [server integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) API or directly from your database console.
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
set -e
# Fix keyfile permissions if mounted from volume
KEYFILE_PATH="/data/keyfile/mongo-keyfile"
if [ ! -f "$KEYFILE_PATH" ]; then
echo "Generating random MongoDB keyfile..."
mkdir -p /data/keyfile
openssl rand -base64 756 > "$KEYFILE_PATH"
fi
chmod 400 "$KEYFILE_PATH"
chown mongodb:mongodb "$KEYFILE_PATH" 2>/dev/null || chown 999:999 "$KEYFILE_PATH"
# Use MongoDB's standard entrypoint with our command
exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --auth --keyFile "$KEYFILE_PATH"
+51
View File
@@ -0,0 +1,51 @@
#!/bin/bash
# MongoDB Replica Set Initialization Script
# Runs after MongoDB starts as part of docker-entrypoint-initdb.d
# Check if replica set is already initialized
RS_STATUS=$(mongosh --username root --password "${MONGO_INITDB_ROOT_PASSWORD}" --authenticationDatabase admin --eval "try { rs.status().ok } catch(e) { 0 }" --quiet 2>/dev/null || echo "0")
if [ "$RS_STATUS" = "1" ]; then
echo "Replica set already initialized"
exit 0
fi
# Initialize replica set
echo "Initializing replica set 'rs0'..."
# For single-node replica set, we can use localhost since clients connect directly
# The MongoDB driver will handle connection to this node
mongosh --username root --password "${MONGO_INITDB_ROOT_PASSWORD}" --authenticationDatabase admin --eval "rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: '127.0.0.1:27017' }] })"
# Wait for replica set to elect a primary and become stable
echo "Waiting for PRIMARY to be ready..."
MAX_WAIT=15
COUNTER=0
while [ $COUNTER -lt $MAX_WAIT ]; do
# Use try/catch to handle cases where rs.status() fails during initialization
PRIMARY_STATE=$(mongosh --username root --password "${MONGO_INITDB_ROOT_PASSWORD}" --authenticationDatabase admin --eval "try { rs.status().members[0].stateStr } catch(e) { 'WAITING' }" --quiet 2>/dev/null || echo "WAITING")
if [ "$PRIMARY_STATE" = "PRIMARY" ]; then
echo "PRIMARY is ready!"
break
fi
# Give it more time between checks
sleep 2
COUNTER=$((COUNTER+1))
# Only show status every few attempts to reduce log noise
if [ $((COUNTER % 3)) -eq 0 ]; then
echo "Still waiting for PRIMARY (attempt $COUNTER/$MAX_WAIT, current state: $PRIMARY_STATE)..."
fi
done
# Don't fail the init even if PRIMARY isn't ready immediately
# MongoDB will continue to elect a primary in the background
# Clients will wait for it to be ready via their connection retry logic
if [ $COUNTER -eq $MAX_WAIT ]; then
echo "NOTE: PRIMARY not confirmed within $((MAX_WAIT * 2)) seconds, but replica set is configured"
echo "MongoDB will continue initializing the replica set in the background"
fi
echo "Replica set initialization complete!"
+18
View File
@@ -0,0 +1,18 @@
// mongo-init.js
// Switch to the admin database
const adminDb = db.getSiblingDB('admin');
// Get username and password from environment variables
const username = process.env.MONGO_INITDB_USERNAME;
const password = process.env.MONGO_INITDB_PASSWORD;
const database = process.env.MONGO_INITDB_DATABASE;
// Create the user
adminDb.createUser({
user: username,
pwd: password,
roles: [
{ role: 'readWrite', db: database }
]
});
+16
View File
@@ -0,0 +1,16 @@
i4Ge9ZbzXgpP4ZLLX/9/pkfhZYlEpSCfhl3Gqr0iwx1UAf4z3egza5J5tjj+S1zl
flopL/FlHaBLQAbY9E6kQyaIFQHJ4Wd6bOkL5hajIizvBBLtKxgpKMPQdvnx6mwe
aeK866TFRx4rWWQs69Gz3nZb822YRmdC4m2H+1rx635/ICWMWv26EdmYayafe010
zKJW/Ty75MpAk9yxc3PFEooDZtmb9PQKkkA4c+P7hfg1HBnrkcqfOejmlwvr/tWE
P8JIQTtcruo8A/5BFq0x0BToLJzY/eOiGcNe1Qf8CO4/8ZKWnvk00FvxXuU1KQ1G
uDlhaCakwitUGEjkKSeIH7V1Qk96JlY8kR5qBhavmROEd/o3VceLfdbbZzd+bitH
PRIocDb06SuWWvJGnwLRoyQmpeXu8AEd5YD+yimhGMQqzCfRFhxlT9rIJbWc8Z+Y
M8uDgd3bHaVfQCQDxbOgo9xB0zqOx77HUBXqeU4xqMeiOkKJxDrt6Cz0osQyybZO
NL9lsJ2y+2S6RyUWNMWJTrVGhgKnse/Z0dgrHpShBMI02G/Q8MKTDXyJmeX1PToe
CCdDmVMKhQRkrvZqeNTvOiNGb/hUDqU/Px/lCmhZYzIKsOK7nhaH6U5FiIAzxeNi
g03gwyrwRfmJWRyTcTSuQKIprBoobl2xJG2CYjdc2UhtY2I3bSlOue1DOKYnOFjA
x58DNwUQdegadSsyd2IPlWpUGIY1GJjTV+J9vEf79TQtnzYSFH5lmg3J08Cq64wg
UAedRHUEnRHFlVfg4ZdkGTGL/2Q7l2Dk0jJQCFFq+tEFGncLgGlAJaWXkczaFaZy
r9h12ulCUq6trZ1RGS1DlDxwQU2J6bSz0ZFwux/3W0YyIQyq4rWcoDjEG98DSE5n
P3Qht2DsBjFkh55nfC0EgwXwnWzBTJiJo1GYf5RdEIVCmmWxmAfTfPYaKWOVcgGG
6QGX5TCPyI6+SZUT/qOfGxnIE/1R3N1wD+J1rORXwWucLOiR
+1
View File
@@ -56,6 +56,7 @@ class Mock extends OAuth2
*/
protected function getTokens(string $code): array
{
if (empty($this->tokens)) {
$this->tokens = \json_decode($this->request(
'GET',
+26 -13
View File
@@ -20,16 +20,22 @@ use Utopia\Database\Validator\Authorization;
class TransactionState
{
private Database $dbForProject;
/**
* @var callable(Document $database): Database
*/
private mixed $getDatabasesDB;
public function __construct(Database $dbForProject)
public function __construct(Database $dbForProject, callable $getDatabasesDB)
{
$this->dbForProject = $dbForProject;
$this->getDatabasesDB = $getDatabasesDB;
}
/**
* Get a document with transaction-aware logic
*
* @param Document $database Target database document
* @param string $collectionId Collection ID
* @param string $documentId Document ID
* @param string|null $transactionId Optional transaction ID
@@ -40,13 +46,15 @@ class TransactionState
* @throws Timeout
*/
public function getDocument(
Document $database,
string $collectionId,
string $documentId,
?string $transactionId = null,
array $queries = []
): Document {
$dbForDatabases = ($this->getDatabasesDB)($database);
if ($transactionId === null) {
return $this->dbForProject->getDocument($collectionId, $documentId, $queries);
return $dbForDatabases->getDocument($collectionId, $documentId, $queries);
}
$state = $this->getTransactionState($transactionId);
@@ -64,7 +72,7 @@ class TransactionState
if ($docState['action'] === 'update' || $docState['action'] === 'upsert') {
// Merge with committed version
$committedDoc = $this->dbForProject->getDocument($collectionId, $documentId, $queries);
$committedDoc = $dbForDatabases->getDocument($collectionId, $documentId, $queries);
if (!$committedDoc->isEmpty()) {
foreach ($docState['document']->getAttributes() as $key => $value) {
if ($key !== '$id') {
@@ -78,13 +86,13 @@ class TransactionState
}
}
}
return $this->dbForProject->getDocument($collectionId, $documentId, $queries);
return $dbForDatabases->getDocument($collectionId, $documentId, $queries);
}
/**
* List documents with transaction-aware logic
*
* @param Document $database Target database document
* @param string $collectionId Collection ID
* @param string|null $transactionId Optional transaction ID
* @param array $queries Optional query filters
@@ -94,17 +102,19 @@ class TransactionState
* @throws Timeout
*/
public function listDocuments(
Document $database,
string $collectionId,
?string $transactionId = null,
array $queries = []
): array {
$dbForDatabases = ($this->getDatabasesDB)($database);
// If no transaction, use normal database retrieval
if ($transactionId === null) {
return $this->dbForProject->find($collectionId, $queries);
return $dbForDatabases->find($collectionId, $queries);
}
$state = $this->getTransactionState($transactionId);
$committedDocs = $this->dbForProject->find($collectionId, $queries);
$committedDocs = $dbForDatabases->find($collectionId, $queries);
$documentMap = [];
// Build map of committed documents
@@ -145,6 +155,7 @@ class TransactionState
/**
* Count documents with transaction-aware logic
*
* @param Document $database Target database document
* @param string $collectionId Collection ID
* @param string|null $transactionId Optional transaction ID
* @param array $queries Optional query filters
@@ -154,23 +165,23 @@ class TransactionState
* @throws Timeout
*/
public function countDocuments(
Document $database,
string $collectionId,
?string $transactionId = null,
array $queries = []
): int {
$dbForDatabases = ($this->getDatabasesDB)($database);
if ($transactionId === null) {
return $this->dbForProject->count($collectionId, $queries, APP_LIMIT_COUNT);
return $dbForDatabases->count($collectionId, $queries, APP_LIMIT_COUNT);
}
$state = $this->getTransactionState($transactionId);
$baseCount = $this->dbForProject->count($collectionId, $queries, APP_LIMIT_COUNT);
$baseCount = $dbForDatabases->count($collectionId, $queries, APP_LIMIT_COUNT);
if (!isset($state[$collectionId])) {
return $baseCount;
}
$committedDocs = $this->dbForProject->find($collectionId, $queries);
$committedDocs = $dbForDatabases->find($collectionId, $queries);
$committedDocIds = [];
foreach ($committedDocs as $doc) {
$committedDocIds[$doc->getId()] = true;
@@ -212,17 +223,19 @@ class TransactionState
/**
* Check if a document exists with transaction-aware logic
*
* @param Document $database Target database document
* @param string $collectionId Collection ID
* @param string $documentId Document ID
* @param string|null $transactionId Optional transaction ID
* @return bool True if document exists
*/
public function documentExists(
Document $database,
string $collectionId,
string $documentId,
?string $transactionId = null
): bool {
$doc = $this->getDocument($collectionId, $documentId, $transactionId);
$doc = $this->getDocument($database, $collectionId, $documentId, $transactionId);
return !$doc->isEmpty();
}
+69 -9
View File
@@ -489,10 +489,11 @@ class Event
*
* @param string $pattern
* @param array $params
* @param ?Document $database
* @return array
* @throws \InvalidArgumentException
*/
public static function generateEvents(string $pattern, array $params = []): array
public static function generateEvents(string $pattern, array $params = [], ?Document $database = null): array
{
// $params = \array_filter($params, fn($param) => !\is_array($param));
$paramKeys = \array_keys($params);
@@ -501,6 +502,12 @@ class Event
$patterns = [];
$parsed = self::parseEventPattern($pattern);
// to switch the resource types from databases to the required prefix
// eg; all databases events get fired with databases. prefix which mainly depicts legacy type
// so a projection from databases to the actual prefix(documentsdb, vectordb,etc)
if ((str_contains($pattern, 'databases.') && $database && $database->getAttribute('type') !== 'legacy')) {
$parsed = self::getDatabaseTypeEvents($database, $parsed);
}
$type = $parsed['type'];
$resource = $parsed['resource'];
$subType = $parsed['subType'];
@@ -601,8 +608,8 @@ class Event
$eventValues = \array_values($events);
/**
* Return a combined list of table, collection events.
*/
* Return a combined list of table, collection events and if tablesdb present then include all for backward compatibility
*/
return Event::mirrorCollectionEvents($pattern, $eventValues[0], $eventValues);
}
@@ -642,16 +649,40 @@ class Event
'attributes' => 'columns',
];
$databasesEventMap = [
'tablesdb' => 'databases',
'tables' => 'collections',
'rows' => 'documents',
'columns' => 'attributes'
];
if (
str_contains($pattern, 'databases.') &&
str_contains($firstEvent, 'collections')
(
str_contains($pattern, 'databases.') &&
str_contains($firstEvent, 'collections')
) ||
(
str_contains($firstEvent, 'tablesdb.')
)
) {
$pairedEvents = [];
foreach ($events as $event) {
$pairedEvents[] = $event;
if (str_contains($event, 'collections')) {
// tablesdb needs databases event with tables and collections
if (str_contains($event, 'tablesdb')) {
$databasesSideEvent = str_replace(
array_keys($databasesEventMap),
array_values($databasesEventMap),
$event
);
$pairedEvents[] = $databasesSideEvent;
$tableSideEvent = str_replace(
array_keys($tableEventMap),
array_values($tableEventMap),
$databasesSideEvent
);
$pairedEvents[] = $tableSideEvent;
} elseif (str_contains($event, 'collections')) {
$tableSideEvent = str_replace(
array_keys($tableEventMap),
array_values($tableEventMap),
@@ -663,8 +694,37 @@ class Event
$events = $pairedEvents;
}
// mirrored events can have duplicates in case of smaller events
return array_unique($events);
}
return $events;
/**
* Maps event terminology based on database type
*/
private static function getDatabaseTypeEvents(Document $database, array $event): array
{
$eventMap = [];
switch ($database->getAttribute('type')) {
case 'tablesdb':
$eventMap = [
'databases' => 'tablesdb',
'documents' => 'rows',
'collections' => 'tables',
'attributes' => 'columns',
];
break;
case 'documentsdb':
$eventMap = [
'databases' => 'documentsdb'
];
break;
}
foreach ($event as $eventKey => $eventValue) {
if (isset($eventMap[$eventValue])) {
$event[$eventKey] = $eventMap[$eventValue];
}
}
return $event;
}
/**
+1 -1
View File
@@ -72,7 +72,7 @@ class Realtime extends Event
return false;
}
$allEvents = Event::generateEvents($this->getEvent(), $this->getParams());
$allEvents = Event::generateEvents($this->getEvent(), $this->getParams(), $this->getContext('database'));
$payload = new Document($this->getPayload());
+1
View File
@@ -84,6 +84,7 @@ class StatsUsage extends Event
}
return true;
}),
'context' => $this->context,
];
}
}
+78 -7
View File
@@ -178,7 +178,6 @@ class Realtime extends MessagingAdapter
*/
public function getSubscribers(array $event): array
{
$receivers = [];
/**
* Check if project has subscriber.
@@ -298,6 +297,8 @@ class Realtime extends MessagingAdapter
$roles = [Role::team(ID::custom($parts[1]))->toString()];
break;
case 'databases':
case 'tablesdb':
case 'documentsdb':
$resource = $parts[4] ?? '';
if (in_array($resource, ['columns', 'attributes', 'indexes'])) {
$channels[] = 'console';
@@ -315,14 +316,25 @@ class Realtime extends MessagingAdapter
$tableId = $payload->getAttribute('$tableId', '');
$collectionId = $payload->getAttribute('$collectionId', '');
$resourceId = $tableId ?: $collectionId;
$channels = [];
// backward compat(tablesdb will have databases channels + tablesdb prefixed channels)
if ($parts[0] === 'databases' || $parts[0] === 'tablesdb') {
$prefix = 'databases';
$channels[] = 'rows';
$channels[] = 'databases.' . $database->getId() . '.tables.' . $resourceId . '.rows';
$channels[] = 'databases.' . $database->getId() . '.tables.' . $resourceId . '.rows.' . $payload->getId();
$channels = self::getDatabaseChannels('legacy', $database->getId(), $resourceId, $payload->getId(), $prefix);
$channels[] = 'documents';
$channels[] = 'databases.' . $database->getId() . '.collections.' . $resourceId . '.documents';
$channels[] = 'databases.' . $database->getId() . '.collections.' . $resourceId . '.documents.' . $payload->getId();
$channels = array_unique([
...$channels,
...self::getDatabaseChannels('tablesdb', $database->getId(), $resourceId, $payload->getId(), $prefix)
]);
}
// prefixed channels -> tablesdb, documentsdb,etc
if ($parts[0] !== 'databases') {
$channels = array_unique([
...$channels,
...self::getDatabaseChannels($parts[0], $database->getId(), $resourceId, $payload->getId()),
]);
}
$roles = $collection->getAttribute('documentSecurity', false)
? \array_merge($collection->getRead(), $payload->getRead())
@@ -379,4 +391,63 @@ class Realtime extends MessagingAdapter
'projectId' => $projectId
];
}
/**
* Generate realtime channels for database events
*
* @param string $type The database API type
* @param string $databaseId The database ID
* @param string $resourceId The collection/table ID
* @param string $payloadId The document/row ID
* @param string $prefixOverride Override the channel prefix when different API types share the same terminology but need different prefixes
* (e.g., 'databases' and 'documentsdb' use same terminology but need different prefixes)
* @return array Array of channel names
*/
private static function getDatabaseChannels(
string $type = 'databases',
string $databaseId = '',
string $resourceId = '',
string $payloadId = '',
string $prefixOverride = '',
): array {
$basePrefix = $prefixOverride ?: $type;
if (!$databaseId || !$resourceId || !$payloadId) {
return [];
}
$channels = [];
switch ($type) {
case 'legacy':
if (empty($prefixOverride)) {
$basePrefix = 'databases';
}
$channels[] = 'documents';
$channels[] = "{$basePrefix}.{$databaseId}.collections.{$resourceId}.documents";
$channels[] = "{$basePrefix}.{$databaseId}.collections.{$resourceId}.documents.{$payloadId}";
break;
case 'tablesdb':
$channels[] = 'rows';
$channels[] = "{$basePrefix}.{$databaseId}.tables.{$resourceId}.rows";
$channels[] = "{$basePrefix}.{$databaseId}.tables.{$resourceId}.rows.{$payloadId}";
break;
case 'documentsdb':
$channels[] = 'documents';
$channels[] = "{$basePrefix}.{$databaseId}.collections.{$resourceId}.documents";
$channels[] = "{$basePrefix}.{$databaseId}.collections.{$resourceId}.documents.{$payloadId}";
break;
default:
$basePrefix = 'databases';
$channels[] = 'documents';
$channels[] = "{$basePrefix}.{$databaseId}.collections.{$resourceId}.documents";
$channels[] = "{$basePrefix}.{$databaseId}.collections.{$resourceId}.documents.{$payloadId}";
break;
}
return $channels;
}
}
@@ -22,3 +22,6 @@ const INDEX = 'index';
const DOCUMENTS = 'document';
const ATTRIBUTES = 'attribute';
const COLLECTIONS = 'collection';
const TABLESDB = 'tablesdb';
const DOCUMENTSDB = 'documentsdb';
@@ -10,7 +10,7 @@ use Utopia\Database\Operator;
class Action extends AppwriteAction
{
private string $context = 'legacy';
private string $context = DATABASE_TYPE_LEGACY;
public function getDatabaseType(): string
{
@@ -20,7 +20,10 @@ class Action extends AppwriteAction
public function setHttpPath(string $path): AppwriteAction
{
if (\str_contains($path, '/tablesdb')) {
$this->context = 'tablesdb';
$this->context = DATABASE_TYPE_TABLESDB;
}
if (\str_contains($path, '/documentsdb')) {
$this->context = DATABASE_TYPE_DOCUMENTSDB;
}
return parent::setHttpPath($path);
}
@@ -59,9 +59,9 @@ class Create extends Action
replaceWith: 'tablesDB.createBooleanColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Boolean()), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
@@ -59,12 +59,12 @@ class Update extends Action
replaceWith: 'tablesDB.updateBooleanColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Boolean()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Nullable(new Key()), 'New attribute key.', true)
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New attribute key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -60,9 +60,9 @@ class Create extends Action
replaceWith: 'tablesDB.createDatetimeColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, fn (Database $dbForProject) => new Nullable(new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime())), 'Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when attribute is required.', true, ['dbForProject'])
->param('array', false, new Boolean(), 'Is attribute an array?', true)
@@ -60,12 +60,12 @@ class Update extends Action
replaceWith: 'tablesDB.updateDatetimeColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, fn (Database $dbForProject) => new Nullable(new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime())), 'Default value for attribute when not provided. Cannot be set when attribute is required.', injections: ['dbForProject'])
->param('newKey', null, new Nullable(new Key()), 'New attribute key.', true)
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New attribute key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -60,9 +60,9 @@ class Delete extends Action
replaceWith: 'tablesDB.deleteColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
@@ -60,9 +60,9 @@ class Create extends Action
replaceWith: 'tablesDB.createEmailColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Email()), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
@@ -60,12 +60,12 @@ class Update extends Action
replaceWith: 'tablesDB.updateEmailColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Email()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true)
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Attribute Key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -62,9 +62,9 @@ class Create extends Action
replaceWith: 'tablesDB.createEnumColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('elements', [], new ArrayList(new Text(Database::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of enum values.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Text(0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true)
@@ -61,13 +61,13 @@ class Update extends Action
replaceWith: 'tablesDB.updateEnumColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('elements', null, new ArrayList(new Text(Database::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Updated list of enum values.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Text(0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true)
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Attribute Key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -62,9 +62,9 @@ class Create extends Action
replaceWith: 'tablesDB.createFloatColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Nullable(new FloatValidator()), 'Minimum value.', true)
->param('max', null, new Nullable(new FloatValidator()), 'Maximum value.', true)
@@ -60,14 +60,14 @@ class Update extends Action
replaceWith: 'tablesDB.updateFloatColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Nullable(new FloatValidator()), 'Minimum value.', true)
->param('max', null, new Nullable(new FloatValidator()), 'Maximum value.', true)
->param('default', null, new Nullable(new FloatValidator()), 'Default value. Cannot be set when required.')
->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true)
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Attribute Key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -63,9 +63,9 @@ class Get extends Action
replaceWith: 'tablesDB.getColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->callback($this->action(...));
@@ -60,9 +60,9 @@ class Create extends Action
replaceWith: 'tablesDB.createIpColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new IP()), 'Default value. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
@@ -60,12 +60,12 @@ class Update extends Action
replaceWith: 'tablesDB.updateIpColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new IP()), 'Default value. Cannot be set when attribute is required.')
->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true)
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Attribute Key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -62,9 +62,9 @@ class Create extends Action
replaceWith: 'tablesDB.createIntegerColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Nullable(new Integer()), 'Minimum value', true)
->param('max', null, new Nullable(new Integer()), 'Maximum value', true)
@@ -60,14 +60,14 @@ class Update extends Action
replaceWith: 'tablesDB.updateIntegerColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Nullable(new Integer()), 'Minimum value', true)
->param('max', null, new Nullable(new Integer()), 'Maximum value', true)
->param('default', null, new Nullable(new Integer()), 'Default value. Cannot be set when attribute is required.')
->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true)
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Attribute Key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -60,9 +60,9 @@ class Create extends Action
replaceWith: 'tablesDB.createLineColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Spatial(Database::VAR_LINESTRING)), 'Default value for attribute when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when attribute is required.', true)
->inject('response')
@@ -60,12 +60,12 @@ class Update extends Action
replaceWith: 'tablesDB.updateLineColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Spatial(Database::VAR_LINESTRING)), 'Default value for attribute when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when attribute is required.', true)
->param('newKey', null, new Nullable(new Key()), 'New attribute key.', true)
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New attribute key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -60,9 +60,9 @@ class Create extends Action
replaceWith: 'tablesDB.createPointColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Spatial(Database::VAR_POINT)), 'Default value for attribute when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when attribute is required.', true)
->inject('response')
@@ -60,12 +60,12 @@ class Update extends Action
replaceWith: 'tablesDB.updatePointColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Spatial(Database::VAR_POINT)), 'Default value for attribute when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when attribute is required.', true)
->param('newKey', null, new Nullable(new Key()), 'New attribute key.', true)
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New attribute key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
@@ -60,9 +60,9 @@ class Create extends Action
replaceWith: 'tablesDB.createPolygonColumn',
),
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('key', '', new Key(), 'Attribute Key.')
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('default', null, new Nullable(new Spatial(Database::VAR_POLYGON)), 'Default value for attribute when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when attribute is required.', true)
->inject('response')

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