Compare commits

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

This PR updates the migration to create the missing attributes.
2025-04-10 16:21:12 -07:00
Christy JacobandGitHub 2d0885ed70 Merge pull request #9632 from appwrite/revert-8667-multi-region-support
Revert "Multi region support"
2025-04-10 20:55:05 +04:00
Christy JacobandGitHub ffe153630d Revert "Multi region support" 2025-04-10 22:19:23 +05:30
Christy JacobandGitHub 6cd9fc72d9 Merge pull request #8667 from appwrite/multi-region-support
Multi region support
2025-04-10 20:05:21 +04:00
shimon 312d1029c0 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-04-10 18:36:05 +03:00
shimon fb802c7fe4 addressing comments 2025-04-10 18:35:45 +03:00
Christy JacobandGitHub 942775c352 Merge pull request #9631 from appwrite/chore-bump-console-5.2.56
Bump console to version 5.2.56
2025-04-10 19:01:06 +04:00
shimon f5852a52e2 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-04-10 17:20:23 +03:00
shimon ff89da513f addressing comments 2025-04-10 17:20:05 +03:00
Steven Nguyen 49ab765ee8 Bump console to version 5.2.56 2025-04-09 17:02:24 -07:00
Christy JacobandGitHub 2495101dfd Merge pull request #9624 from appwrite/feat-cache-telemetry
feat: cache telemetry
2025-04-10 00:31:06 +04:00
shimon 07cdd66296 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-04-09 10:02:40 +03:00
Jake BarnbyandGitHub e1b4917acf Merge pull request #9628 from appwrite/chore-update-migrations
Update migrations lib
2025-04-09 05:45:43 +00:00
Jake Barnby 796a1b5bd2 Update migrations lib 2025-04-09 17:26:41 +12:00
Christy JacobandGitHub 881525f349 Merge pull request #9623 from appwrite/feat-figma-oauth
feat: add figma oauth provider
2025-04-08 16:57:37 +04:00
Christy JacobandGitHub 8086d6eef0 Merge pull request #9625 from appwrite/update-composer
chore: update dependencies
2025-04-08 16:28:51 +04:00
Chirag Aggarwal badbcaf1a8 chore: update dependencies 2025-04-08 11:33:38 +00:00
loks0n e6d9c5c5fd chore: fmt 2025-04-08 12:24:05 +01:00
loks0n 86c26f2dc7 feat: cache telemetry 2025-04-08 12:12:28 +01:00
Chirag Aggarwal 2c453d5fd9 fix: request issues and permissions 2025-04-08 08:51:04 +00:00
Chirag Aggarwal 3bd8405deb feat: add figma oauth provider 2025-04-08 07:56:35 +00:00
Steven NguyenandGitHub a20ff31ba3 Merge pull request #9561 from appwrite/improve-gitpod-config
chore: update gitpod.yml config
2025-04-07 10:40:33 -07:00
shimon fed98ffdfe Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	docker-compose.yml
2025-04-07 13:06:54 +03:00
Christy JacobandGitHub 31a5a175b3 Merge pull request #9420 from appwrite/PLA-1968
feat: refactor executor setup
2025-04-07 12:49:25 +04:00
Fabian Gruber 0ae70f8321 feat: inject executor instead of creating a new instance 2025-04-07 09:53:38 +02:00
Fabian Gruber 954ed45a36 feat: update appwrite to use generic S3 adapter 2025-04-07 09:53:38 +02:00
Damodar LohaniandGitHub 57bc76edc7 Merge branch '1.6.x' into feat-usage-dump-multi-tenant-batch 2025-04-07 09:21:32 +05:45
Steven NguyenandGitHub fcd31f4cff Merge pull request #9616 from appwrite/update-dependencies
chore: update php image to 0.8.1
2025-04-06 07:25:34 -07:00
Damodar LohaniandGitHub 2d7b01aa35 Merge pull request #9615 from appwrite/fix-owner-downgrade
Restrict role change for sole org owner
2025-04-06 13:59:02 +05:45
Darshan 2243f0c72e fix: test for only console db <> orgs. 2025-04-06 10:29:18 +05:30
Darshan c3c568653f add: roles check test. 2025-04-06 10:21:55 +05:30
Chirag AggarwalandGitHub 403083b0ce Update .gitpod.yml 2025-04-06 08:31:46 +05:30
Chirag Aggarwal e5ef825ac4 chore: update php image to 0.8.1 2025-04-05 15:47:22 +00:00
Chirag Aggarwal abd953974d chore: give read write permission to vendor for gitpod config 2025-04-05 14:43:07 +00:00
Darshan 7297188c1b update: roles check. 2025-04-05 19:42:20 +05:30
Darshan 2a672f5a63 fix: don't allow the only owner in an org to change their role to anything else. 2025-04-05 19:20:24 +05:30
Eldad A. FuxandGitHub a160b0f987 Merge pull request #9559 from appwrite/change-sample-rate
Sample rate from DSN
2025-04-04 11:08:21 +02:00
DarshanandGitHub 01e2eb5025 Merge branch '1.6.x' into change-sample-rate 2025-04-04 13:44:28 +05:30
Jake BarnbyandGitHub baffa615e6 Merge pull request #9610 from appwrite/remove-audit-payload
Remove audit payload
2025-04-04 04:26:43 +00:00
fogelito bd87672607 Remove audit payload 2025-04-03 18:31:50 +03:00
shimon 31c326fe87 sync with 1.6.x 2025-04-03 11:31:45 +03:00
shimon 0dc319d244 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-04-03 11:30:14 +03:00
Jake BarnbyandGitHub 2e784c0e8b Merge pull request #9608 from appwrite/fix-image-transformations-cache
Fix: image transformation cache
2025-04-03 04:21:49 +00:00
Damodar Lohani 1660c0b00c Fix: image transformation cache 2025-04-03 02:44:29 +00:00
Jake BarnbyandGitHub 96327a9ebc Merge pull request #9603 from appwrite/feat-tenant-type-fixes
Update database
2025-04-02 02:49:10 +00:00
Jake Barnby 32a5ff4c8c Update release 2025-04-02 13:37:26 +13:00
Jake Barnby 31b1663f6b Merge remote-tracking branch 'origin/1.6.x' into feat-tenant-type-fixes
# Conflicts:
#	composer.lock
2025-04-02 13:30:51 +13:00
Jake Barnby c152d8e001 Update database 2025-04-02 13:02:35 +13:00
Eldad A. FuxandGitHub 8fd6208502 Merge pull request #9602 from appwrite/feat-update-ws
Feat update ws
2025-04-01 21:52:14 +02:00
Eldad Fux 938505da51 Updated websocket version 2025-04-01 21:23:23 +02:00
Damodar LohaniandGitHub 30462b1951 Merge pull request #9598 from appwrite/cast-tenant
Fix tenant casting
2025-04-01 11:54:31 +05:45
fogelito b46de29735 database 0.63.2 2025-04-01 08:15:03 +03:00
fogelito d92413fd6b Revert database lib 2025-04-01 08:13:56 +03:00
Damodar Lohani ae0cdaff63 Merge remote-tracking branch 'origin/feat-usage-dump-multi-tenant-batch' into fix-merge-stats-usage-and-dump 2025-04-01 04:38:40 +00:00
fogelito 4698323f38 lock 2025-03-31 16:19:17 +03:00
fogelito 612b064d2a 0.63.2 2025-03-31 15:16:59 +03:00
Chirag Aggarwal 2a7038bb8e chore: remove logging 2025-03-31 09:33:37 +00:00
Chirag Aggarwal 2c434924de chore: update openapi specs 2025-03-31 09:32:47 +00:00
Chirag Aggarwal d9b7a018ea chore: update specs 2025-03-31 06:23:31 +00:00
Chirag Aggarwal abcaf6cd73 feat: sdk group attribute 2025-03-31 05:48:17 +00:00
Damodar Lohani 81ee68e219 Merge remote-tracking branch 'origin/1.6.x' into feat-usage-dump-multi-tenant-batch 2025-03-31 04:07:10 +00:00
Damodar Lohani e14ae7ea4a fix 2025-03-31 03:59:37 +00:00
Damodar Lohani ec21effd2c Fix type error 2025-03-31 03:55:06 +00:00
Damodar Lohani 7399362c20 Merge remote-tracking branch 'origin/1.6.x' into fix-merge-stats-usage-and-dump 2025-03-31 03:45:46 +00:00
Damodar Lohani d5ad714f5a fix missing queue name 2025-03-30 06:09:03 +00:00
Damodar Lohani 1389bab3d1 remove db storage check 2025-03-30 05:56:14 +00:00
Damodar Lohani 100189c0fb Merge remote-tracking branch 'origin/feat-usage-dump-multi-tenant-batch' into fix-merge-stats-usage-and-dump 2025-03-30 05:55:28 +00:00
Damodar Lohani d36a09eec6 remove database storage triggers 2025-03-30 05:54:48 +00:00
Christy JacobandGitHub 1a5e9748f3 Merge pull request #9592 from appwrite/feat-update-fetch
Feat update fetch
2025-03-29 00:37:29 +04:00
Eldad Fux 22939831f2 Updated fetch version 2025-03-28 20:58:25 +01:00
Jake BarnbyandGitHub 2069c9fdad Merge pull request #9588 from appwrite/fix-select-cursor-pagination
Add order by for cursor
2025-03-28 06:33:27 +00:00
fogelito a4c18c4b77 Remove array merge 2025-03-28 09:16:25 +03:00
fogelito cba700402b Add order by for cursor 2025-03-28 09:04:50 +03:00
Christy JacobandGitHub 60399aaa0a Merge pull request #9573 from appwrite/feat-multi-tenant-insert-2
Feat multi tenant insert
2025-03-27 15:54:30 +04:00
Jake Barnby ad7552e676 Merge remote-tracking branch 'origin/1.6.x' into feat-multi-tenant-insert-2 2025-03-27 21:39:57 +13:00
Christy JacobandGitHub 93839ec36d Merge pull request #9586 from appwrite/revert-9533-PLA-2683
Revert "chore(audits): return queue pre-fetch results"
2025-03-27 12:38:00 +04:00
Jake BarnbyandGitHub 17dc8e3f8a Revert "chore(audits): return queue pre-fetch results" 2025-03-27 21:37:10 +13:00
Damodar Lohani f4d01f4c37 fix: remove stats usage dump worker from docker 2025-03-27 08:36:52 +00:00
Damodar Lohani cfb1f4fcdb fix: remove stats usage dump 2025-03-27 08:29:12 +00:00
Damodar Lohani 05067302d2 Fix: merge the working of StatsUsage and StatsUsageDump 2025-03-27 08:25:48 +00:00
Damodar Lohani 1a54bd58a6 check empty project id 2025-03-27 07:01:44 +00:00
Damodar Lohani c443a2f999 Fix dual writing 2025-03-27 05:50:01 +00:00
Jake Barnby c79e8e5b95 Update database 2025-03-27 18:04:17 +13:00
Damodar Lohani 9f537a5bee Feat: multi tenant dual writing 2025-03-27 04:59:34 +00:00
Jake Barnby a60d19fb42 Merge remote-tracking branch 'origin/1.6.x' into feat-multi-tenant-insert-2 2025-03-27 17:23:15 +13:00
Christy JacobandGitHub ef7cf41a61 Merge pull request #9533 from appwrite/PLA-2683
chore(audits): return queue pre-fetch results
2025-03-26 22:16:52 +05:30
Chirag Aggarwal 8b1e97bae4 chore: remove console import 2025-03-26 14:38:31 +00:00
Chirag Aggarwal 7b8f25696c chore: added showSensitve wrapper instead of param 2025-03-26 14:37:35 +00:00
Jake BarnbyandGitHub 91ad4097ff Merge branch '1.6.x' into feat-multi-tenant-insert-2 2025-03-26 11:16:27 +00:00
Jake Barnby d9d38197dd Update libs 2025-03-26 23:47:59 +13:00
Jake Barnby a8b2ebb356 Merge remote-tracking branch 'origin/1.6.x' into feat-multi-tenant-insert-2 2025-03-26 23:36:18 +13:00
Jake BarnbyandGitHub c53262a44c Merge pull request #9579 from appwrite/fix-select-cursor-pagination
Add $permissions for delete documents selects
2025-03-26 08:52:51 +00:00
fogelito 8339435fb5 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into fix-select-cursor-pagination 2025-03-26 10:02:50 +02:00
fogelito 424a357503 Add $permissions 2025-03-26 10:02:30 +02:00
shimon 35cd2b5295 adding custom domain validation against _APP_CUSTOM_DOMAIN_DENY_LIST 2025-03-26 09:39:53 +02:00
Jake BarnbyandGitHub 9a7512d1fd Merge pull request #9578 from appwrite/fix-select-cursor-pagination
Deletes worker fix selects for pagination
2025-03-26 07:35:41 +00:00
shimon 989fa2b8a0 adding custom domain validation against _APP_CUSTOM_DOMAIN_DENY_LIST 2025-03-26 09:25:49 +02:00
fogelito a461d5bf72 Deletes fix selects 2025-03-26 09:20:44 +02:00
shimon cb2d0dc592 adding custom domain validation against _APP_CUSTOM_DOMAIN_DENY_LIST 2025-03-26 09:07:38 +02:00
shimon a9d1f6668f adding custom domain validation against _APP_CUSTOM_DOMAIN_DENY_LIST 2025-03-26 09:05:36 +02:00
Christy JacobandGitHub 9d020fdef5 Merge pull request #9577 from appwrite/fix-region-scheduler
Fix: schedules region filtering
2025-03-26 02:24:13 +05:30
Matej Bačo f25143b240 Support fra-region on cloud; temporary fix 2025-03-25 21:52:02 +01:00
Matej Bačo d89cdbcf5a Fix schedules region filtering 2025-03-25 21:34:09 +01:00
shimon 8447732614 adding region query to deleteProjectsByTeam 2025-03-25 21:11:41 +02:00
shimon 9f446da0a5 adding region query to deleteProjectsByTeam 2025-03-25 20:28:29 +02:00
shimon 62effa7bc4 adding region query to deleteProjectsByTeam 2025-03-25 20:23:08 +02:00
shimon dedcdc2e0e adding region query to deleteProjectsByTeam 2025-03-25 19:13:11 +02:00
shimon 854256e8a6 debug 2025-03-25 10:47:05 +02:00
shimon d72f69f792 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.lock
2025-03-25 10:46:41 +02:00
Chirag AggarwalandGitHub 7fdde1a518 Update Response.php 2025-03-25 14:09:47 +05:30
Jake Barnby 6195674611 Update database 2025-03-25 21:20:26 +13:00
Jake BarnbyandGitHub 4804bdf110 Merge pull request #9568 from appwrite/db-0-62-1
Bump database 0.62.x
2025-03-25 03:44:18 +00:00
Jake BarnbyandGitHub cb356bc410 Merge pull request #9523 from appwrite/delete-query-fixes
Delete worker queries fixes
2025-03-25 03:19:39 +00:00
Jake Barnby 74d9a3c181 Fix syntax 2025-03-25 16:19:18 +13:00
Jake BarnbyandGitHub 6bcdc8c769 Update src/Appwrite/Utopia/Database/Validator/Queries/Base.php 2025-03-25 16:01:27 +13:00
Jake BarnbyandGitHub c9c9db5640 Update src/Appwrite/Platform/Workers/Deletes.php 2025-03-25 15:52:59 +13:00
Jake BarnbyandGitHub a20140eb36 Update src/Appwrite/Platform/Workers/Deletes.php 2025-03-25 15:52:54 +13:00
Jake BarnbyandGitHub c0cb4b26a3 Apply suggestions from code review 2025-03-25 15:51:47 +13:00
Jake BarnbyandGitHub b842e03f38 Merge branch '1.6.x' into delete-query-fixes 2025-03-25 02:43:08 +00:00
shimon 16aca35bd4 debug 2025-03-24 20:33:51 +02:00
fogelito 2795505e82 line 2025-03-24 17:38:01 +02:00
fogelito 5799ca1041 remove var_dump 2025-03-24 17:37:34 +02:00
fogelito e11e7e6780 Add internalId 2025-03-24 17:33:02 +02:00
Chirag Aggarwal e93010c5fe chore: add showSensitive attribute 2025-03-24 14:26:56 +00:00
Chirag Aggarwal d677b8ed88 fix: webhook tests 2025-03-24 14:06:36 +00:00
fogelito 0648de2046 Revert Base 2025-03-24 14:32:57 +02:00
Chirag Aggarwal 3399308ee3 Merge branch '1.6.x' into chore-sensitive-check 2025-03-24 12:28:54 +00:00
fogelito 42f72b4477 Add internalId to orders 2025-03-24 13:33:36 +02:00
fogelito 25b6d59e30 Add internalId 2025-03-24 12:54:19 +02:00
Fabian Gruber 5479e6f8f1 chore(audits): return queue pre-fetch results 2025-03-24 10:18:16 +01:00
fogelito 586e676299 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into db-0-62-1 2025-03-24 11:10:21 +02:00
fogelito 26821c8372 Bump database 0.62 2025-03-24 11:08:39 +02:00
shimon 56d78b1712 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	app/worker.php
2025-03-24 10:56:47 +02:00
shimon e4c4ffdb3c pull resources changes 2025-03-24 10:54:18 +02:00
Jake BarnbyandGitHub 6e146889bf Merge pull request #9563 from appwrite/refactor-init-auditretention
refactor: initialization of audit retention
2025-03-24 08:53:19 +00:00
shimon ab1683070b debug 2025-03-23 19:59:51 +02:00
shimon 9f3d15bd1c Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-03-23 19:58:34 +02:00
shimon a7199ff980 debug 2025-03-23 10:44:00 +02:00
Chirag Aggarwal 37dfe0d719 refactor: initialization of audit retention 2025-03-22 14:05:39 +00:00
Chirag Aggarwal 86856bf87d chore: update gitpod.yml config 2025-03-22 13:55:23 +00:00
Darshan 7c2ab4fcf6 update: change sample rate for 4xx via env. 2025-03-22 13:47:09 +05:30
Steven NguyenandGitHub 29ea03b7bb Merge pull request #9554 from appwrite/fix-team-count
chore: only update total count for privileged users
2025-03-21 10:09:22 -07:00
shimon 434fcdc508 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-03-21 19:08:53 +02:00
shimon ed7c2fa451 debug 2025-03-21 19:08:37 +02:00
Chirag Aggarwal d661195a5f chore: only update total count for prviledged users 2025-03-21 15:59:30 +00:00
Christy JacobandGitHub 50e7986f84 Merge pull request #9547 from appwrite/fix-audit-deletion
chore: fix console audits delete
2025-03-21 15:23:06 +05:30
Christy JacobandGitHub c7301af18a Merge pull request #9525 from appwrite/pla-2676
chore: stop tests on failure
2025-03-21 15:15:09 +05:30
Chirag Aggarwal c0c0c921dc chore: fix naming 2025-03-21 08:08:30 +00:00
Chirag Aggarwal 7c795da72f chore: fix type 2025-03-21 07:48:50 +00:00
Chirag Aggarwal 13750f7dbd feat: introduce new env 2025-03-21 07:42:10 +00:00
shimon 69bdf50c85 debug 2025-03-20 18:27:23 +02:00
shimon ea02639767 debug 2025-03-20 18:15:51 +02:00
shimon bd19da2e7a debug 2025-03-20 17:12:59 +02:00
Chirag Aggarwal b0b57935da chore: add comments 2025-03-20 13:42:19 +00:00
Chirag Aggarwal 7c3f8504bb chore: fix initialization 2025-03-20 13:15:14 +00:00
shimon 6c0ce5cc41 debug 2025-03-20 15:05:48 +02:00
shimon 403e5773a2 cleanup 2025-03-20 14:26:11 +02:00
Chirag Aggarwal 9d82892219 chore: update logic 2025-03-20 11:32:06 +00:00
Chirag Aggarwal 5eb7c36f50 chore: add console audit retention 2025-03-20 10:32:36 +00:00
shimon 68e8b7ca2b Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-03-20 10:47:07 +02:00
fogelito 9c54cce5bd Queries fixes 2025-03-20 09:07:32 +02:00
Jake BarnbyandGitHub 9345025ade Merge pull request #9546 from appwrite/feat-improve-maintenance
Use cursor pagination with bigger limit for maintenance project loop
2025-03-20 02:51:31 +00:00
Jake Barnby 31ca35782b Use cursor pagination with bigger limit for maintenance project loop 2025-03-20 15:21:24 +13:00
shimon 438d5185a9 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-03-19 19:47:44 +02:00
Christy JacobandGitHub 2505e25da8 Merge pull request #9430 from appwrite/updates-projects-regions
Remove default region
2025-03-19 22:36:30 +05:30
fogelito cc0a97b10e Fixes 2025-03-19 16:37:15 +02:00
shimon d3cabcd5ac composer 2025-03-19 11:15:25 +02:00
shimon 84133ef631 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.lock
2025-03-19 11:13:58 +02:00
shimon 2be59a94da update 2025-03-19 11:13:30 +02:00
shimon 88d6150db2 revert 2025-03-19 10:29:18 +02:00
shimon faed019d92 revert 2025-03-19 09:32:48 +02:00
shimon b318c2cc08 Merge branch '1.6.x' of github.com:appwrite/appwrite into updates-projects-regions 2025-03-18 21:54:55 +02:00
shimon eb4340cf4c revert 2025-03-18 21:49:28 +02:00
shimon ee09e2c91b revert 2025-03-18 21:47:54 +02:00
shimon e6e7c5c816 revert 2025-03-18 21:44:34 +02:00
shimon e9841e53b9 revert 2025-03-18 21:40:46 +02:00
shimon fca2e95fdd debug 2025-03-18 19:29:40 +02:00
shimon 5683c310db debug 2025-03-18 18:36:46 +02:00
Eldad A. FuxandGitHub 7d86ce76d8 Merge pull request #9536 from appwrite/pla-2687
chore: set min operations to 1 for reads and writes
2025-03-18 16:30:52 +01:00
fogelito 795928a037 Fix $id 2025-03-18 16:59:05 +02:00
Chirag Aggarwal aab8bca91e fix: logic 2025-03-18 14:20:25 +00:00
Chirag Aggarwal 70e27748be chore: set min operations to 1 for reads and writes 2025-03-18 12:52:41 +00:00
Jake BarnbyandGitHub 556e00b62b Merge pull request #9526 from appwrite/feat-disable-pdo-persist
Disable PDO persistence since we manage our own pool
2025-03-18 12:19:58 +00:00
Christy JacobandGitHub 370a8e231c Merge pull request #9530 from appwrite/PLA-2662
feat: add pool telemetry
2025-03-18 14:44:52 +05:30
Fabian Gruber aa5ea7f3a1 feat: add pool telemetry 2025-03-18 10:01:06 +01:00
Jake BarnbyandGitHub 8a77eaf2bb Merge pull request #9529 from appwrite/feat-migration-read-db
Migration read from db
2025-03-18 08:01:07 +00:00
Jake Barnby fe308b4f1f Update lib 2025-03-18 20:49:19 +13:00
Jake Barnby e43a3109eb Update migration lib 2025-03-18 20:05:12 +13:00
Jake Barnby d9fbe9658d Merge branch '1.6.x' into feat-migration-read-db 2025-03-18 20:04:23 +13:00
Jake BarnbyandGitHub 1e2f5bcb84 Merge branch '1.6.x' into feat-disable-pdo-persist 2025-03-18 07:02:54 +00:00
Christy JacobandGitHub 088d0998cd Merge pull request #9514 from appwrite/feat-domains-count
Feat: domains count
2025-03-18 11:03:45 +05:30
Jake Barnby a7165e6a32 Disable PDO persistence since we manage our own pool 2025-03-18 16:51:00 +13:00
Chirag Aggarwal 5f7ceecb30 chore: stop tests on failure 2025-03-18 03:46:14 +00:00
shimon e5261e7d70 _app_region 2025-03-17 18:31:14 +02:00
shimon 60912cb084 _app_region 2025-03-17 18:14:58 +02:00
shimon 7149deeacb _app_region 2025-03-17 18:11:49 +02:00
shimon 59f613f9bb _app_region 2025-03-17 17:33:48 +02:00
shimon 8184bc8c78 _app_region 2025-03-17 17:31:24 +02:00
fogelito bbfd22ce2e Index check for delete worker 2025-03-17 17:05:40 +02:00
shimon 9cb860d446 _app_region 2025-03-17 13:52:17 +02:00
shimon 6eff011100 Merge branch '1.6.x' of github.com:appwrite/appwrite into updates-projects-regions
# Conflicts:
#	app/views/install/compose.phtml
2025-03-17 13:29:22 +02:00
Damodar LohaniandGitHub 8fd877810d Merge branch '1.6.x' into feat-domains-count 2025-03-17 12:49:59 +05:45
Jake Barnby 04711d8c02 Add doc 2025-03-17 19:21:31 +13:00
Eldad A. FuxandGitHub 5a5d43f3d0 Merge pull request #9515 from appwrite/feat-env-to-system
Feat env vars to system lib
2025-03-16 17:09:47 +01:00
Eldad A. FuxandGitHub 3e462ded0a Merge pull request #9408 from appwrite/feat-dynamic-regions
Feat dynamic regions
2025-03-16 17:05:04 +01:00
Eldad Fux de96b2511a Merge remote-tracking branch 'origin/1.6.x' into feat-dynamic-regions 2025-03-16 16:31:09 +01:00
shimon ab790f6c02 debug 2025-03-16 15:24:29 +02:00
shimon ff618a0565 debug 2025-03-16 14:42:22 +02:00
shimon b3db296f13 sync with main 2025-03-16 14:05:03 +02:00
shimon e019e426f1 debug 2025-03-16 13:47:54 +02:00
Eldad Fux 90899b92e3 Change envs to system lib 2025-03-16 10:36:32 +01:00
shimon adacec4ad1 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	app/init.php
#	composer.lock
2025-03-16 10:55:24 +02:00
Damodar Lohani 4eece99768 Feat: domains count 2025-03-16 02:35:10 +00:00
Eldad A. FuxandGitHub 971a4b8f30 Merge pull request #9511 from appwrite/feat-eldad-cleanups
Cleanups
2025-03-15 19:15:32 +01:00
Eldad Fux 418185b914 fixed format 2025-03-15 14:43:51 +01:00
Eldad Fux 167011ce87 Fixed timelimit 2025-03-15 14:38:01 +01:00
Eldad Fux 3d69a35d5a Fixed tests 2025-03-15 14:32:18 +01:00
Eldad Fux aac90ea8b8 Fixed format 2025-03-15 14:17:47 +01:00
Eldad Fux 56aa503146 Fixed paths 2025-03-15 13:57:12 +01:00
Eldad Fux 875a60fe07 fixed paths 2025-03-15 13:53:00 +01:00
Eldad Fux 3427755695 Merge remote-tracking branch 'origin/1.6.x' into feat-eldad-cleanups 2025-03-15 13:45:31 +01:00
Jake BarnbyandGitHub 68238193d2 Merge pull request #9512 from appwrite/feat-improve-ordering
Improve delete ordering
2025-03-16 01:34:02 +13:00
Eldad Fux 07926534bf Fixed namespace 2025-03-15 13:24:56 +01:00
Eldad Fux a2a0a1405f plurals 2025-03-15 13:23:02 +01:00
Eldad Fux 195edd19ac wip 2025-03-15 13:18:34 +01:00
Eldad Fux 6cf333141c Merge remote-tracking branch 'origin/1.6.x' into feat-eldad-cleanups 2025-03-15 13:18:30 +01:00
Jake Barnby 12dbb1d50d Reapply "Improve deletes queries"
This reverts commit 9dff192508.
2025-03-16 01:02:44 +13:00
Jake Barnby 9dff192508 Revert "Improve deletes queries"
This reverts commit 650627bfb4.
2025-03-16 01:02:41 +13:00
Jake Barnby 650627bfb4 Improve deletes queries 2025-03-16 01:01:13 +13:00
Eldad Fux 4016895b8f Fixed DB test 2025-03-15 12:58:41 +01:00
Eldad Fux 3b7aea22c2 Fixed test 2025-03-15 12:42:34 +01:00
Eldad Fux 50fe0bf98d Code review fix 2025-03-15 12:39:15 +01:00
Eldad Fux 4f0a7de868 Fixes 2025-03-15 12:34:49 +01:00
Eldad Fux 1933e48d9c Cleanups 2025-03-15 12:00:36 +01:00
Steven NguyenandGitHub 90228a5ffb Merge pull request #9294 from appwrite/feat-1.6.1-release-prep
Prepare 1.6.1 release
2025-03-14 16:38:55 -07:00
Steven NguyenandGitHub 063c2be38f Merge pull request #9495 from appwrite/chore-bump-console-5.2.53
Bump console to version 5.2.53
2025-03-14 16:35:22 -07:00
Steven NguyenandGitHub ff9845c9b9 Merge pull request #9496 from appwrite/main
Sync main into 1.6.x
2025-03-14 16:35:01 -07:00
Steven NguyenandGitHub bd5bfc04ef Merge pull request #9466 from appwrite/fix-certificates-in-deletes
Fix certificates in deletes worker
2025-03-14 16:34:21 -07:00
Matej BačoandGitHub c55030387f Merge pull request #9508 from appwrite/feat-rules-improvements
Feat: Add rule attributes
2025-03-14 13:35:05 +01:00
Matej Bačo 5f2512dc4f code quality improvement 2025-03-14 12:07:38 +01:00
Matej Bačo f6fc425c98 Add rule attributes 2025-03-14 09:37:44 +01:00
Jake BarnbyandGitHub 810d650aba Merge pull request #9507 from appwrite/feat-improve-delete-queries
Feat improve delete queries
2025-03-14 18:37:56 +13:00
Jake Barnby 817a3808e5 Add 5 min timeout for workers and tasks DBs 2025-03-14 16:40:57 +13:00
Jake Barnby d96910138f Update lock 2025-03-14 15:40:05 +13:00
Jake Barnby b8673ac07f Merge remote-tracking branch 'origin/1.6.x' into feat-improve-delete-queries
# Conflicts:
#	composer.lock
2025-03-14 15:34:18 +13:00
Jake Barnby 246c8ce939 Add order on inequality queries 2025-03-14 15:29:52 +13:00
Jake Barnby 49d89a4206 Force clear timeout to ensure it's not set 2025-03-14 15:29:23 +13:00
Christy JacobandGitHub 2c803b262c Merge pull request #9505 from appwrite/PLA-2640
update queue to 0.9.*
2025-03-13 18:51:41 +05:30
Fabian Gruber fcf15689e8 update queue to 0.9.* 2025-03-13 14:02:32 +01:00
Christy JacobandGitHub aef38aa114 Merge pull request #9414 from appwrite/bump-sdk
chore: bump sdk
2025-03-13 17:29:56 +05:30
Chirag AggarwalandGitHub d81f2f8b5c Merge branch '1.6.x' into bump-sdk 2025-03-13 11:01:14 +05:30
Chirag Aggarwal e119bf6777 chore: update changelogs 2025-03-13 05:30:09 +00:00
Steven NguyenandGitHub 6e0c8724ce Merge pull request #9498 from appwrite/chore-regenerate-examples
Regenerate examples
2025-03-12 22:05:52 -07:00
Steven NguyenandGitHub 8f6d45b207 Merge pull request #9497 from appwrite/chore-regenerate-specs
Regenerate specs
2025-03-12 21:49:16 -07:00
Chirag AggarwalandGitHub 1d53cb41de Merge branch '1.6.x' into chore-regenerate-examples 2025-03-13 10:07:50 +05:30
Chirag AggarwalandGitHub dc073b8298 Merge branch '1.6.x' into chore-regenerate-specs 2025-03-13 10:07:13 +05:30
Steven NguyenandGitHub 8c3aebf735 Merge pull request #9499 from appwrite/feat-1.6.2-release-prep
Prepare 1.6.2 release
2025-03-12 21:26:09 -07:00
Jake BarnbyandGitHub b0d6ffa582 Merge pull request #9487 from appwrite/feat-bulk-audit-delete
Feat bulk audit delete
2025-03-13 12:53:57 +13:00
Steven Nguyen 556f7338c1 Bump appwrite version to 1.6.2 2025-03-12 15:52:29 -07:00
Steven Nguyen 301471354e chore: regenerated 1.6.x examples
To ensure we didn't have anything extra, I deleted the 1.6.x folder and
then regenerated the examples via the sdk command.
2025-03-12 15:45:40 -07:00
Steven Nguyen 5bce1e17cb chore: regenerate latest and 1.6.x specs 2025-03-12 15:34:38 -07:00
Steven Nguyen 7402145c9d Bump console to version 5.2.53 2025-03-12 15:22:43 -07:00
Damodar LohaniandGitHub 4b1e4008a9 Merge pull request #9494 from appwrite/fix-delete-usage-stats
chore: update initializing dbForLogs
2025-03-12 18:24:15 +05:45
Chirag Aggarwal 46693faa67 chore: prevent console project to queue for delete stats 2025-03-12 12:35:56 +00:00
Chirag Aggarwal ae00a69592 chore: update initializing dbForLogs 2025-03-12 11:58:06 +00:00
Damodar LohaniandGitHub 7f76bfed11 Merge pull request #9491 from appwrite/feat-add-timings-to-stats-resources
Feat: calculate and log time taken for each project
2025-03-12 16:28:11 +05:45
Damodar Lohani 81159e82d5 show time in seconds 2025-03-12 10:29:07 +00:00
Chirag Aggarwal 74f4ebf90e chore: update sdk generator 2025-03-12 10:26:01 +00:00
Damodar Lohani 5c25b58351 Fix date format 2025-03-12 10:19:55 +00:00
Damodar Lohani d77940d22f Feat: calculate and log time taken for each project 2025-03-12 10:00:07 +00:00
Jake Barnby 2052fb975b Use bulk deletes for audit cleanup 2025-03-12 16:53:08 +13:00
shimon a60d916c51 debug 2025-03-11 19:18:05 +02:00
shimon d522b1a04c debug 2025-03-11 18:56:24 +02:00
shimon 4b740dce86 sync with 1.6.x 2025-03-11 18:23:35 +02:00
shimon 6a05bd4e0d Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	app/init.php
2025-03-11 18:19:44 +02:00
shimon d6e53d8e2e Merge branch '1.6.x' of github.com:appwrite/appwrite into updates-projects-regions 2025-03-11 11:17:18 +02:00
Christy JacobandGitHub d4dd7fb757 Merge pull request #9462 from appwrite/pla-2550
chore: added logsdb for deletes worker
2025-03-11 14:30:40 +05:30
Christy JacobandGitHub 1971126e7b Merge pull request #9481 from appwrite/audits-timestamp-update
Update audit timestamp origin
2025-03-11 14:16:53 +05:30
shimon 141f0fc376 linter 2025-03-11 10:27:23 +02:00
shimon b387823d63 Update audit timestamp origin 2025-03-11 09:17:28 +02:00
Chirag Aggarwal e3f59c696d bump: python version 2025-03-11 05:43:30 +00:00
Chirag Aggarwal 107a185953 Merge branch '1.6.x' into bump-sdk 2025-03-11 05:30:47 +00:00
Christy JacobandGitHub acf86e47e2 Merge pull request #9479 from appwrite/pla-2567
chore: queue console project for maintenance delete
2025-03-10 22:40:26 +05:30
Chirag Aggarwal 61377ceb6e chore: shift initialization of console project to config 2025-03-10 10:48:44 +00:00
Chirag Aggarwal 6a6d99a9d0 chore: queue console project for maintenance delete 2025-03-10 08:54:20 +00:00
Jake BarnbyandGitHub 078f953e32 Merge pull request #9467 from appwrite/fix-logging-config
Fix logging config
2025-03-10 14:10:41 +13:00
shimon 0570567b2e sync with 1.6.x 2025-03-09 21:24:24 +02:00
shimon 3a29bcb41b Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.lock
2025-03-09 21:23:33 +02:00
Steven Nguyen 59a8172e96 fix: prevent "Failed to initialize logging provider" errors
If the _APP_EXPERIMENT_LOGGING_CONFIG env var isn't set, every 4XX error
outputs this warning:

Failed to initialize logging provider: Unable to parse DSN: scheme is
required

To reduce this unnecessary clutter when _APP_EXPERIMENT_LOGGING_CONFIG
isn't set, this PR adds a check to only attempt to initialize the
logging provider if the env var is set.
2025-03-07 12:59:11 -08:00
Steven Nguyen 0fbd2c6425 fix: prevent warning if no logging config is set
If no logging config is set, there's a warning saying:

> Using deprecated logging configuration.

However, if they didn't set any config, it's not deprecated.
2025-03-07 12:54:53 -08:00
Steven Nguyen f6b5deda6d fix: add missing _APP_EMAIL_CERTIFICATES env var to deletes worker
The deletes worker uses the certificates resource to delete
certificates, but the certificates resource requires the
_APP_EMAIL_CERTIFICATES env var to be set or it will throw an exception
and not process any delete jobs. This commit adds the missing env var
to the deletes worker.

Also add _APP_SYSTEM_SECURITY_EMAIL_ADDRESS as a fallback.
2025-03-07 12:46:14 -08:00
Chirag Aggarwal 493f536a6e chore: added logsdb for deletes worker 2025-03-07 14:04:52 +00:00
Chirag Aggarwal d9fc6b70ad chore: bump react native 2025-03-07 13:18:20 +00:00
Christy JacobandGitHub 3af7f0aa3d Merge pull request #9412 from appwrite/chore-bump-cache-0.12.x
chore: bump cache 0.12.x
2025-03-07 17:31:20 +05:30
loks0n b791e59672 chore: bump composer 2025-03-07 10:59:16 +00:00
loks0n c50a0de627 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into chore-bump-cache-0.12.x 2025-03-07 10:58:31 +00:00
Chirag Aggarwal a0605b14cb chore: regen specs and update vers 2025-03-07 04:14:45 +00:00
Chirag Aggarwal 6c652ba3ae Merge branch '1.6.x' into bump-sdk 2025-03-06 17:04:05 +00:00
shimon 1b3fe3046d sync with 1.6.x 2025-03-06 13:12:03 +02:00
shimon 7c91157dc8 sync with 1.6.x 2025-03-06 12:10:10 +02:00
shimon 584e4c0d75 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-03-06 12:03:10 +02:00
Christy JacobandGitHub 205024657a Merge pull request #9438 from appwrite/activities-multi-writes
Activities batch writes
2025-03-06 11:40:34 +05:30
Steven NguyenandGitHub fab43a5f83 Merge pull request #9447 from appwrite/pla-2607
Fix blocking of phone sessions when disabled on console
2025-03-05 11:13:40 -08:00
Chirag Aggarwal 36b0475298 chore: update tests 2025-03-05 18:40:41 +00:00
Chirag Aggarwal 0ce4555f70 chore: added auth group to create phone token 2025-03-05 17:42:43 +00:00
shimon f0477f8940 sync with 1.6.x 2025-03-05 17:30:59 +02:00
shimon 3bc81c82ec Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	src/Appwrite/Platform/Workers/Usage.php
2025-03-05 17:29:57 +02:00
Christy JacobandGitHub a734cadba1 Merge pull request #9443 from appwrite/feat-database-storage-in-stats-resources
Feat: calculate database storage in stats-resources
2025-03-05 12:06:56 +05:30
Damodar Lohani 2afb8a14ac Skip in dual writing 2025-03-05 05:27:56 +00:00
Damodar Lohani 6024d01e7b Feat: calculate database storage in stats-resources
- Calculate database storage in stats-resources worker
2025-03-05 05:25:50 +00:00
Christy JacobandGitHub f250f454f7 Merge pull request #9442 from appwrite/fix-model-image-trans
fix: model for image transformations in usage project
2025-03-05 10:21:05 +05:30
Chirag Aggarwal 2c6f731a38 fix: description 2025-03-05 04:49:34 +00:00
Chirag Aggarwal 1d2883f472 chore: fix model for image transformations in usage project 2025-03-05 04:46:06 +00:00
Jake BarnbyandGitHub 8c1a1604a8 Merge pull request #9387 from appwrite/pla-2526
chore: make min/max params optional for attribute update
2025-03-05 16:13:59 +13:00
Jake BarnbyandGitHub ea2fd4b995 Merge pull request #9426 from appwrite/pla-2584
chore: added timeout to deployment builds in tests
2025-03-05 16:11:47 +13:00
shimon f2ad3a501e updating audits workers var scope 2025-03-04 20:34:56 +02:00
shimon c443c8fdab updating audits workers var scope 2025-03-04 19:49:37 +02:00
shimon 1838b9c4e8 updating audits workers var scope 2025-03-04 19:49:10 +02:00
shimon 09aa383cf3 updating audits workers var scope 2025-03-04 19:15:00 +02:00
shimon ce054e7088 updating audits workers var scope 2025-03-04 19:12:20 +02:00
shimon 76dc8c29d5 updating audits workers var scope 2025-03-04 19:07:45 +02:00
shimon f761829d60 updating audits workers var scope 2025-03-04 18:49:31 +02:00
shimon 8b4bc7e4a6 updating audits workers var scope 2025-03-04 18:19:23 +02:00
shimon 2a5b3317bf update region 2025-03-04 11:36:45 +02:00
Christy JacobandGitHub 52ba87e77e Merge pull request #9393 from appwrite/add-file-transformation-endpoints
chore: add image transformation stats to usage endpoint
2025-03-04 14:46:14 +05:30
shimon 19ad2088a0 update region 2025-03-04 10:08:07 +02:00
Chirag Aggarwal 239bebfe2e Merge branch '1.6.x' into add-file-transformation-endpoints 2025-03-04 07:34:43 +00:00
Christy JacobandGitHub eff58459ef Merge pull request #9425 from appwrite/disable-admin-transformedAt
Disable transformedAt update for console users
2025-03-04 12:21:28 +05:30
Christy JacobandGitHub 85456696b8 Merge pull request #9429 from appwrite/fix-dual-writing
Fix: disable dual writing
2025-03-04 11:55:00 +05:30
Damodar Lohani 0659dce935 Fix: disable dual writing 2025-03-04 06:22:58 +00:00
Christy JacobandGitHub d5ab989891 Merge pull request #9339 from appwrite/feat-remove-deprecated-usage-stuff
Remove usage and usage dump in favor of stats-usage and stats-usage-dump
2025-03-04 11:23:29 +05:30
Christy JacobandGitHub 9b18a031d9 Merge pull request #9428 from appwrite/fix-stats-date
Fix: use receivedAt date when available
2025-03-04 11:22:12 +05:30
Damodar Lohani 4d5d30ba7b Fix default value 2025-03-04 05:50:21 +00:00
Damodar Lohani dd5dd4cc43 Same for database metrics 2025-03-04 05:34:02 +00:00
Damodar Lohani 5d61544444 Fix: use receivedAt date when available 2025-03-04 05:31:35 +00:00
Chirag Aggarwal 9a6de87f7d chore: usagetest and migrationsbase 2025-03-03 16:31:41 +00:00
Chirag Aggarwal fe7aeefabf chore: remove redundant param 2025-03-03 16:20:32 +00:00
Chirag Aggarwal fc05a4f1e3 chore: updated to use assertEventually 2025-03-03 16:07:33 +00:00
Chirag Aggarwal e865ca74d7 chore: added timeout to webhooks base test 2025-03-03 14:41:11 +00:00
Chirag Aggarwal c6b8e649fe chore: added maxtimeout to testChannelExecutions 2025-03-03 14:34:10 +00:00
shimon b314ae8208 disable transformedAt update for console users 2025-03-03 15:19:49 +02:00
Christy JacobandGitHub 32329ef3f9 Merge pull request #9424 from appwrite/revert-9411-lohanidamodar-patch-2
Revert "Fix: stats resources only queue projects accessed in last 3 hours"
2025-03-03 16:25:34 +05:30
Damodar LohaniandGitHub 5becd0a5dc Revert "Fix: stats resources only queue projects accessed in last 3 hours" 2025-03-03 16:04:31 +05:45
shimon 12eb3b4eea debug 2025-03-03 10:47:28 +02:00
Jake BarnbyandGitHub d85b28aabc Merge pull request #9325 from appwrite/pla-1883
refactor: migrate Realtime::send calls to queueForRealtime
2025-03-03 21:23:55 +13:00
Chirag AggarwalandGitHub 88c8d2aa4c Merge branch '1.6.x' into pla-1883 2025-03-03 13:37:05 +05:30
shimon adb451234b debug 2025-03-02 22:27:13 +02:00
shimon 52a73856bc debug 2025-03-02 21:19:45 +02:00
shimon 921b9154eb debug 2025-03-02 20:56:28 +02:00
Chirag Aggarwal 9849e7b232 Merge branch '1.6.x' into add-file-transformation-endpoints 2025-03-01 19:41:25 +00:00
Chirag Aggarwal 6900b717da chore: updated to use logsdb 2025-03-01 17:45:20 +00:00
Chirag Aggarwal aaee99e08b chore: generate specs 2025-03-01 17:19:55 +00:00
Chirag Aggarwal 712c33064a chore: fix sending of transformation stats 2025-03-01 17:18:44 +00:00
Chirag Aggarwal 02de0d2432 chore: fix stats count 2025-02-28 13:29:32 +00:00
Chirag Aggarwal df37462716 chore: fix typing and tests 2025-02-28 13:20:55 +00:00
Chirag Aggarwal d5cfdc570f chore: updated specs 2025-02-28 08:41:53 +00:00
Chirag Aggarwal e9d8d13fbd chore: added imageTransformations to project usage 2025-02-28 08:28:21 +00:00
Chirag Aggarwal f31acd214e chore: remove plan blocking 2025-02-27 09:52:27 +00:00
shimon 5ed1ca617f cleanup 2025-02-27 11:02:27 +02:00
shimon 12f274223f sync against 1.6.x 2025-02-27 10:31:47 +02:00
shimon 368b61493f Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.lock
2025-02-27 10:29:06 +02:00
Chirag Aggarwal 388affa3d0 chore: updated naming for subscribers 2025-02-27 07:23:13 +00:00
Chirag Aggarwal 6ad34a7bf7 chore: sdk versions and corresponding docs 2025-02-27 05:05:31 +00:00
loks0n 0bbcdf8268 chore: bump cache 0.12.x 2025-02-26 22:05:09 +00:00
loks0n 02e5b935fe Merge branch 'chore-bump-cache-0.12.x' of https://github.com/appwrite/appwrite into chore-bump-cache-0.12.x 2025-02-26 22:03:04 +00:00
loks0n 611f2cad6a chore: bump cache 0.12.x 2025-02-26 22:02:55 +00:00
Luke B. SilverandGitHub b9d5621bde Merge branch '1.6.x' into chore-bump-cache-0.12.x 2025-02-26 21:57:58 +00:00
Chirag Aggarwal 57c7ebb53d chore: bump sdk 2025-02-26 17:51:00 +00:00
Chirag Aggarwal c79ed286b1 chore: update tests 2025-02-26 17:35:06 +00:00
Chirag Aggarwal 28896081d7 chore: fix forloop 2025-02-26 16:50:12 +00:00
Chirag Aggarwal 7687784515 chore: added fallback to old values in update 2025-02-26 16:41:26 +00:00
loks0n 31d6fe2423 chore: bump cache 0.12.x 2025-02-26 16:40:19 +00:00
loks0n f5947de508 chore: bump cache 0.12.x 2025-02-26 16:38:05 +00:00
Chirag Aggarwal 0ddcb9b14c chore: refactor adapter 2025-02-26 16:24:05 +00:00
shimon 458df23797 debug cert 2025-02-26 17:41:44 +02:00
shimon 369caae87d debug cert 2025-02-26 17:40:06 +02:00
shimon f48791fba6 debug cert 2025-02-26 15:53:04 +02:00
Christy JacobandGitHub 1c8ec7ba1f Merge pull request #9406 from appwrite/bump-base-image
chore: bump docker-base
2025-02-26 18:49:25 +05:30
Christy JacobandGitHub e7eedcdeb7 Merge pull request #9397 from appwrite/track-options-requests
Track options requests
2025-02-26 18:48:50 +05:30
Chirag Aggarwal bb5ed1ccc6 chore: bump base to 0.10.1 2025-02-26 10:14:32 +00:00
Eldad Fux ad56782b49 Fixed tests 2025-02-26 10:19:51 +01:00
Chirag Aggarwal ae78e793dd chore: added blocking to file preview endpoint 2025-02-26 08:52:06 +00:00
Christy JacobandGitHub 24613c2e9f Merge pull request #9243 from appwrite/fix-smtp
Fix: email (smtp) to multiple recipients
2025-02-26 12:57:23 +05:30
Eldad Fux ce5a04a37d Merge remote-tracking branch 'origin/1.6.x' into feat-dynamic-regions 2025-02-26 07:52:40 +01:00
Christy JacobandGitHub 718e44adce Merge branch '1.6.x' into track-options-requests 2025-02-26 10:40:10 +05:30
Damodar LohaniandGitHub 1816fae5a3 Merge pull request #9411 from appwrite/lohanidamodar-patch-2
Fix: stats resources only queue projects accessed in last 3 hours
2025-02-26 09:05:22 +05:45
Damodar LohaniandGitHub 7ea108f015 Fix: stats resources only queue projects accessed in last 3 hours 2025-02-26 08:37:36 +05:45
Jake BarnbyandGitHub 69c00d428a Merge pull request #9410 from appwrite/batch-per-project
Batch per project
2025-02-26 14:02:24 +13:00
Jake BarnbyandGitHub e998474b97 Merge branch '1.6.x' into batch-per-project 2025-02-26 14:02:01 +13:00
Jake Barnby 6e82bfe8e7 Update date 2025-02-26 14:00:16 +13:00
Jake Barnby 34d91a6518 Revert "Test increase batch"
This reverts commit 7dfa3193c9.
2025-02-26 13:51:41 +13:00
Jake Barnby 7dfa3193c9 Test increase batch 2025-02-26 13:45:49 +13:00
Eldad Fux e8fb2f8ae1 Changed config format 2025-02-25 20:25:54 +01:00
shimon e2b9c64f91 debug cert 2025-02-25 18:01:30 +02:00
shimon 69e9a1bd8c debug cert 2025-02-25 17:42:36 +02:00
Chirag Aggarwal 542e34bc94 chore: bump docker-base 2025-02-25 12:28:05 +00:00
Chirag Aggarwal 9f7ba3d4ca Merge branch '1.6.x' into fix-smtp 2025-02-25 09:48:21 +00:00
Jake BarnbyandGitHub 66dc3adb98 Merge pull request #9403 from appwrite/batch-per-project
Log batches per project
2025-02-25 21:46:33 +13:00
Jake Barnby 3a2babc906 Update date 2025-02-25 21:34:46 +13:00
Eldad Fux 697c30f559 Init 2025-02-25 09:31:24 +01:00
Jake Barnby 03f0e382f4 Fix dates 2025-02-25 21:03:37 +13:00
Jake Barnby b272a8b1e7 Pass queries 2025-02-25 20:26:00 +13:00
Chirag Aggarwal 676431c355 chore: shifted to logsDb 2025-02-25 07:16:42 +00:00
Jake Barnby 2924f408b3 Reapply "Remove blocks"
This reverts commit f49346485a.
2025-02-25 19:21:35 +13:00
Jake Barnby b0547015a9 Add temp log date skip 2025-02-25 18:51:11 +13:00
Jake Barnby f49346485a Revert "Remove blocks"
This reverts commit 6f854b5eb6.
2025-02-25 18:39:12 +13:00
Jake Barnby 6f854b5eb6 Remove blocks 2025-02-25 18:18:25 +13:00
Chirag Aggarwal ab6c5c9f8d chore: bump utopia messaging 2025-02-25 04:47:35 +00:00
Chirag Aggarwal 7ac4a5bf27 chore: update naming 2025-02-25 04:43:44 +00:00
Jake Barnby 1f560d896f Fix plural 2025-02-25 16:58:30 +13:00
Jake Barnby 5efd231861 Allow validator to read logs collections from base 2025-02-25 16:56:13 +13:00
Jake Barnby 3f8e1cbedb Fix loop unset 2025-02-25 15:57:04 +13:00
Jake Barnby 08f4ed2b50 Log batches per project 2025-02-25 15:50:10 +13:00
shimon 1f606a591d debug cert 2025-02-24 14:13:18 +02:00
Eldad A. FuxandGitHub 392b33ef26 Merge pull request #9398 from appwrite/disable-logs-display
disable logs display
2025-02-23 20:08:55 +01:00
shimon 6bd14d548f composer 2025-02-23 20:58:02 +02:00
shimon 06d22f910c disable logs display 2025-02-23 20:51:38 +02:00
Christy Jacob 8958070327 chore: linter 2025-02-23 15:48:33 +05:30
Christy Jacob 3f50532c91 chore: remove sleep in functions tests 2025-02-23 15:42:53 +05:30
Christy Jacob 4c49272389 chore: enable assistant 2025-02-23 15:40:54 +05:30
Christy Jacob f25df67002 chore: remove sleep() for usage stats, since batch size is now 1 2025-02-23 15:39:37 +05:30
Christy Jacob 94108e6f3f chore: remove sleep() for usage stats, since batch size is now 1 2025-02-23 15:23:34 +05:30
Christy Jacob f682e4ac6f chore: remoove sleep() for usage stats, since batch size is now 1 2025-02-23 14:56:15 +05:30
Christy Jacob cf41d44de4 fix: general tests 2025-02-23 14:47:57 +05:30
Christy Jacob 30327f86fd feat: enable assistant 2025-02-23 11:39:18 +05:30
Christy Jacob 2359c1adb6 feat: enable assistant 2025-02-23 11:38:59 +05:30
Christy Jacob 12b494982b feat: compute usage for OPTIONS and 4XX errors 2025-02-23 11:37:54 +05:30
Christy Jacob 65dd5fe9b3 feat: compute usage for OPTIONS and 4XX errors 2025-02-23 11:34:28 +05:30
Steven NguyenandGitHub 34d587d7c5 Merge pull request #9122 from gurjeetsinghvirdee/152-sdk-cli-command-update
Fix outdated CLI commands in documentation
2025-02-21 13:59:50 -08:00
Chirag Aggarwal 0e0bab4c40 chore: updated test 2025-02-21 13:28:49 +00:00
Chirag Aggarwal 4ed4c660d5 chore: added tests for file transformations 2025-02-21 12:53:17 +00:00
Chirag Aggarwal b1629b24ad chore: added file transformation stats to usage endpoint for bucket 2025-02-21 11:44:04 +00:00
Christy JacobandGitHub 9fa13ddfd8 Merge pull request #9391 from appwrite/fix-null-error-on-invite
chore: fix null errors on team invite
2025-02-21 16:09:43 +05:30
Chirag Aggarwal d6c2dfb915 chore: updated name 2025-02-21 03:27:46 +00:00
Steven NguyenandGitHub f402262ffc Merge pull request #9390 from appwrite/pla-2142
chore: bump utopia-php/image to 0.8.0
2025-02-20 10:44:54 -08:00
Chirag Aggarwal 9d9e44a14f chore: remove redundant check 2025-02-20 17:36:42 +00:00
Chirag Aggarwal 397a6a34ed chore: fix null errors on team invite 2025-02-20 17:30:26 +00:00
Chirag Aggarwal c660b9d858 chore: fix tests 2025-02-20 14:56:23 +00:00
Chirag Aggarwal f9cc7882aa chore: fix tests 2025-02-20 14:43:21 +00:00
Chirag Aggarwal 4e71e85b5b chore: bump utopia-php/image version 2025-02-20 14:32:34 +00:00
Chirag Aggarwal 077fe10d80 chore: fix tests 2025-02-20 14:12:14 +00:00
Chirag Aggarwal b0ced9b351 chore: updated tests 2025-02-20 13:37:13 +00:00
Christy JacobandGitHub 6cbcd695d1 Merge pull request #9388 from appwrite/fix-audits-v2
Fix removed audits for shared v2
2025-02-20 18:05:25 +05:30
Jake Barnby b6166063db Add back remove shared v2 2025-02-21 00:51:29 +13:00
Chirag Aggarwal ccb7dfa839 chore: added test cases 2025-02-20 11:15:05 +00:00
Chirag Aggarwal 6235a1c729 chore: make min/max params optional for attribute update 2025-02-20 10:33:58 +00:00
shimon e972ccedd6 debug 2025-02-20 10:26:39 +02:00
shimon e4369e5e85 debug 2025-02-20 09:48:17 +02:00
Christy JacobandGitHub 7319b12b51 Merge pull request #9353 from appwrite/feat-messaging-metrics
Feat messaging metrics
2025-02-20 12:25:14 +05:30
shimon 45b2d58aa5 debug 2025-02-19 23:28:09 +02:00
shimon be1c12147f debug 2025-02-19 22:59:04 +02:00
shimon 4157e89ab7 debug 2025-02-19 22:33:13 +02:00
shimon c151d2aaca debug 2025-02-19 18:18:39 +02:00
shimon baa40e4007 debug 2025-02-19 18:15:26 +02:00
shimon 197e32f128 debug 2025-02-19 13:52:14 +02:00
shimon 39e5c5e734 debug 2025-02-19 10:11:47 +02:00
Chirag Aggarwal 55e7633bd1 chore: shift targets to realtime 2025-02-18 14:04:59 +00:00
Chirag Aggarwal 88a6616cf8 chore: fix naming 2025-02-18 13:05:50 +00:00
Chirag Aggarwal 9f7aaf7029 chore: fix warning in database worker 2025-02-18 12:51:19 +00:00
Chirag Aggarwal 235a357ca3 chore: initialized queueForWebhooks in worker init 2025-02-18 11:37:36 +00:00
Chirag AggarwalandGitHub b99d2c3c8c Merge branch '1.6.x' into pla-1883 2025-02-18 16:31:41 +05:30
Christy JacobandGitHub d59d6f70a5 Merge pull request #9303 from appwrite/pla-2428
Add support endpoint url for S3
2025-02-18 16:28:44 +05:30
Chirag Aggarwal ac84de7645 chore: updated variable descriptions 2025-02-18 10:38:13 +00:00
Christy JacobandGitHub 27d2cc9f00 Merge pull request #9348 from ChiragAgg5k/pla-2470
Fix resend team invite if already accepted
2025-02-18 15:10:11 +05:30
Chirag Aggarwal a74c6e0b10 chore: added targets to realtime, refactor webhook queues 2025-02-18 07:58:56 +00:00
Jake BarnbyandGitHub ae91869e76 Merge pull request #9336 from appwrite/feat-key-segmented-usage
Feat key segmented usage
2025-02-18 18:47:01 +13:00
Jake Barnby 2b29c792ad Merge remote-tracking branch 'origin/1.6.x' into feat-key-segmented-usage 2025-02-18 18:18:00 +13:00
Jake Barnby 3e02b830ac Fix inject 2025-02-18 18:17:52 +13:00
Christy JacobandGitHub f4e9b89195 Merge pull request #9373 from appwrite/pla-2503
Fix Self Hosting functions by adding missed config
2025-02-18 00:37:47 +05:30
Chirag Aggarwal 6ef4e76aa6 Merge branch '1.6.x' into pla-1883 2025-02-17 18:54:41 +00:00
Chirag Aggarwal 227f2c476d Merge branch '1.6.x' into pla-1883 2025-02-17 18:46:05 +00:00
Chirag Aggarwal c1b613e5bd chore: added migration script 2025-02-17 13:55:43 +00:00
Jake Barnby d6b9be8799 Merge remote-tracking branch 'origin/feat-key-segmented-usage' into feat-key-segmented-usage 2025-02-18 02:10:37 +13:00
Jake Barnby 0d79ae33ba Lint 2025-02-18 02:10:05 +13:00
Matej BačoandGitHub 685c141a59 Merge branch '1.6.x' into feat-key-segmented-usage 2025-02-17 14:06:40 +01:00
Jake Barnby ff09f1e62a Remove redundant check 2025-02-18 02:05:13 +13:00
Jake Barnby 07a6d60b01 Remove leftover 2025-02-18 02:02:29 +13:00
Jake Barnby 77e83d5cfb Remove unused injections 2025-02-18 01:49:51 +13:00
Jake Barnby fe251c443f Remove leftover 2025-02-18 01:32:48 +13:00
Christy JacobandGitHub d0630eae79 Merge pull request #9361 from appwrite/pla-2492
Fix Model Platform is missing response key: store
2025-02-17 17:08:17 +05:30
Jake BarnbyandGitHub d1cf833f15 Merge pull request #9375 from appwrite/feat-worker-batch-delete
Use batch delete in worker
2025-02-18 00:08:26 +13:00
Jake Barnby 1fbfbbf482 Also update database worker delete by group 2025-02-17 23:53:43 +13:00
Jake Barnby ef8520ad2d Format 2025-02-17 23:14:17 +13:00
Jake Barnby 36ce381325 Use batch delete in worker 2025-02-17 22:36:55 +13:00
shimon a8a8df602f sync against 1.6.x 2025-02-17 10:30:53 +02:00
Chirag Aggarwal 0dba22c606 chore: updated installations collection 2025-02-17 05:57:30 +00:00
Jake Barnby 6653972e11 Fix disabled metric check 2025-02-17 17:29:12 +13:00
Jake Barnby 390cdaa6a5 Merge remote-tracking branch 'origin/1.6.x' into feat-key-segmented-usage
# Conflicts:
#	composer.lock
2025-02-17 17:12:45 +13:00
shimon d641177618 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-02-16 20:13:11 +02:00
Damodar LohaniandGitHub b3067f562f Merge pull request #9370 from appwrite/fix-image-trnasformation-metrics
Fix image trnasformation metrics
2025-02-16 17:29:13 +05:45
Damodar Lohani 3e5e545304 Fix: image transformations metrics 2025-02-16 11:31:46 +00:00
shimon 463bc4aac3 sync against 1.6.x 2025-02-15 06:28:19 +02:00
shimon e597ee4493 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	app/http.php
2025-02-15 06:24:48 +02:00
Chirag Aggarwal 51438631da chore: added console check to ping 2025-02-14 15:40:08 +00:00
Steven NguyenandGitHub 993c07d7c6 Merge pull request #9360 from appwrite/fix-build-memory-limits
Fix build memory specifications
2025-02-14 07:36:34 -08:00
shimon 00478e8533 _APP_DOMAIN_FUNCTIONS_FALLBACK 2025-02-14 11:08:43 +02:00
Bradley Schofield 0f873792ba Fix build memory and add tests to prevent future regressions 2025-02-14 17:16:40 +09:00
Chirag Aggarwal 4c7917687d Merge branch '1.6.x' into pla-1883 2025-02-14 07:16:43 +00:00
Chirag AggarwalandGitHub 9aad6721cd Merge branch '1.6.x' into chore-sensitive-check 2025-02-14 09:04:56 +05:30
Chirag AggarwalandGitHub 9ebb4b9626 Merge branch '1.6.x' into pla-2428 2025-02-14 09:04:30 +05:30
Chirag Aggarwal 2457485230 chore: removed redundant exception 2025-02-13 13:16:29 +00:00
Jake BarnbyandGitHub 7143bef111 Merge pull request #9356 from appwrite/catch-invalid-index
Fix patch index validation
2025-02-13 22:56:28 +13:00
fogelito e4ae0d463d assertStringContainsString 2025-02-13 11:37:12 +02:00
fogelito 8778e2e666 lint 2025-02-13 11:35:16 +02:00
fogelito 8f212ccd30 in_array 2025-02-13 11:24:13 +02:00
fogelito e8a306253b Fix test 2025-02-13 11:22:40 +02:00
fogelito 4592a1cc36 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into catch-invalid-index 2025-02-13 11:20:52 +02:00
Christy JacobandGitHub 608a2cc67b Merge pull request #9354 from appwrite/fix-image-transformation-metric-time
Fix: metric time was not being written to DB
2025-02-13 14:41:52 +05:30
fogelito 6abf8ce727 Fix index length test 2025-02-13 10:44:27 +02:00
Jake BarnbyandGitHub ed51a059ba Merge pull request #9355 from appwrite/chore-update-migrations
Chore: Update migrations
2025-02-13 20:59:33 +13:00
Bradley Schofield 43d8374c5c Update migrations 2025-02-13 16:57:32 +09:00
Damodar Lohani d4599e6884 Fix, time was not being written to DB 2025-02-13 06:43:49 +00:00
Damodar Lohani 1e446948e8 Fix error 2025-02-13 05:24:00 +00:00
Damodar Lohani 503b8a0cde spacing 2025-02-13 04:58:14 +00:00
Damodar Lohani 745947621f Feat: count for targets 2025-02-13 04:54:29 +00:00
Damodar Lohani 721b9b9aea remove old events 2025-02-13 00:52:20 +00:00
Damodar LohaniandGitHub 29ffd703db Merge branch '1.6.x' into feat-remove-deprecated-usage-stuff 2025-02-13 06:28:48 +05:45
Christy JacobandGitHub 88c2fe2dd2 Merge pull request #9347 from appwrite/update-audit-logging
feat: batch create abuse logs
2025-02-12 22:35:34 +05:30
Christy Jacob a0482aca7a chore: revert package.json 2025-02-12 22:04:48 +05:30
Christy Jacob 9989137ed4 chore: use private variables for aggregation 2025-02-12 22:01:28 +05:30
Christy JacobandGitHub 933e762284 Merge pull request #9332 from appwrite/PLA-896
feat(builds): check if function is blocked before building
2025-02-12 21:23:10 +05:30
Jake BarnbyandGitHub 2e3a1cf82c Merge branch '1.6.x' into feat-key-segmented-usage 2025-02-13 01:30:51 +13:00
Jake Barnby 3fe1147194 Filter in prepare 2025-02-13 01:07:10 +13:00
Damodar LohaniandGitHub 8922d676c6 Merge pull request #9350 from appwrite/fix-image-metric
Fix: missing periodic metric
2025-02-12 17:25:15 +05:45
Jake Barnby 6dbc8c5464 Allow disabling specific metrics in hooks via keys 2025-02-13 00:19:51 +13:00
Damodar Lohani d2cc5ea932 Fix: missing periodic metric 2025-02-12 10:58:51 +00:00
Chirag Aggarwal d259f21c65 refactor: tests to match the new membership logic 2025-02-12 10:48:32 +00:00
Chirag Aggarwal e435e457de chore: added check for team membership exists 2025-02-12 09:50:13 +00:00
Jake BarnbyandGitHub 4c452417d1 Merge pull request #9338 from appwrite/feat-improve-tests
Only run all table mode tests on db update
2025-02-12 22:34:01 +13:00
Jake Barnby 2449b76676 Format 2025-02-12 21:25:53 +13:00
Jake Barnby 9427b07580 Add expired property directly to key 2025-02-12 21:22:08 +13:00
Christy Jacob 2dffcb29b0 chore: add comment about the unit 2025-02-12 13:09:31 +05:30
Jake BarnbyandGitHub dd03a04d58 Merge branch '1.6.x' into feat-improve-tests 2025-02-12 20:35:13 +13:00
Jake Barnby 775e6ec9cf Fix role setting 2025-02-12 20:34:38 +13:00
Christy Jacob 294f6818fa chore: review comments 2025-02-12 12:23:20 +05:30
Christy Jacob 48525ce755 chore: linter 2025-02-12 11:34:39 +05:30
Christy Jacob 978284b36f feat: use smaller batch size on development environments 2025-02-12 11:33:17 +05:30
Jake Barnby 5780ec9547 Make sure key has app role before continuing checks 2025-02-12 18:45:06 +13:00
Christy Jacob 7f706fce5d feat: linter & console logs 2025-02-12 11:09:24 +05:30
Christy Jacob df27e8950b feat: update composer 2025-02-12 11:02:29 +05:30
Christy Jacob 00473d4744 feat: batch create abuse logs 2025-02-12 10:57:43 +05:30
Jake Barnby da2e27650c Merge remote-tracking branch 'origin/1.6.x' into feat-key-segmented-usage 2025-02-12 18:20:28 +13:00
Jake Barnby d4c659f88d Fix key usage default 2025-02-12 18:17:47 +13:00
Jake BarnbyandGitHub c22ece068f Merge pull request #9346 from appwrite/fix-drop-abuse
Fix drop abuse on shared table project delete
2025-02-12 17:41:40 +13:00
Jake Barnby e394532bcd Fix drop abuse on shared table project delete 2025-02-12 17:03:37 +13:00
Christy JacobandGitHub e140603890 Merge pull request #9342 from appwrite/lohanidamodar-patch-2
Fix: missing call for image transformations counting
2025-02-11 22:13:15 +05:30
Christy Jacob 2189ca1469 chore: linter 2025-02-11 16:40:17 +00:00
Damodar LohaniandGitHub f6aff786f1 Fix: missing call for image transformations counting 2025-02-11 17:59:48 +05:45
Fabian Gruber 8324b789c5 chore(review): remove http status codes from exceptions 2025-02-11 11:00:28 +01:00
Damodar Lohani f2fc45da02 fix format 2025-02-11 09:46:00 +00:00
Fabian Gruber f2ae6e07cb feat(builds): check if function is blocked before building 2025-02-11 10:45:26 +01:00
Jake Barnby 61e65e1eda Skip shared jobs entirely 2025-02-11 22:37:57 +13:00
Damodar LohaniandGitHub 2b2be60e77 Merge branch '1.6.x' into feat-remove-deprecated-usage-stuff 2025-02-11 15:20:23 +05:45
Jake Barnby abaa5c7f48 Only run shared tables tests if database library version changed compared to base 2025-02-11 22:26:39 +13:00
Chirag Aggarwal ec5fd1d798 chore: remove showsensitive 2025-02-11 09:06:11 +00:00
Jake Barnby 704437b5e8 Format 2025-02-11 22:04:52 +13:00
Jake Barnby bed7ab321e Add unit test 2025-02-11 22:02:20 +13:00
Jake Barnby d04a5cab0d Merge remote-tracking branch 'origin/1.6.x' into feat-key-segmented-usage
# Conflicts:
#	composer.lock
2025-02-11 21:33:34 +13:00
Chirag Aggarwal f723948907 Merge branch '1.6.x' into chore-sensitive-check 2025-02-11 08:29:06 +00:00
Jake Barnby 0634a7eac3 Fix injections 2025-02-11 21:13:01 +13:00
Jake Barnby 1c91604c80 Only disable database reads/writes and network stats 2025-02-11 20:56:39 +13:00
Damodar Lohani 940f4d12af Remove usage and usage dump in favor of stats-usage and stats-usage-dump 2025-02-11 07:52:37 +00:00
Jake BarnbyandGitHub 2902b1bde8 Merge pull request #9329 from appwrite/catch-invalid-index
Catch invalid index
2025-02-11 20:34:10 +13:00
Chirag Aggarwal 5076303c33 chore: refactor realtime queueing in builds worker 2025-02-11 06:06:43 +00:00
Chirag Aggarwal 532160705f chore: refactor realtime queueing in functions worker 2025-02-11 05:50:30 +00:00
Jake Barnby 7490240397 Fix name shadowing 2025-02-11 18:29:42 +13:00
Chirag Aggarwal f666e18154 chore: fix databases worker realtime queueing 2025-02-11 05:00:45 +00:00
Jake Barnby e5a3866065 Fix imports 2025-02-11 17:53:40 +13:00
Jake Barnby 30bc70b9b1 Abstract key decoding and check if usage is enabled 2025-02-11 17:49:22 +13:00
Chirag Aggarwal bcdae7f4e3 chore: refactor certificate realtime, fix database 2025-02-11 04:21:36 +00:00
Chirag Aggarwal 47fbb777ed chore: refactor migrations realtime queue 2025-02-11 04:03:02 +00:00
Chirag Aggarwal 2c4c42de05 docs: fix database worker 2025-02-11 03:53:18 +00:00
Chirag Aggarwal 375f643214 refactor: triggering realtime events with queueForRealtime 2025-02-11 03:29:32 +00:00
Christy JacobandGitHub 2f95b0e2e0 Merge pull request #9272 from appwrite/feat-logs-db
Feat: logs DB
2025-02-10 23:49:48 +05:30
Christy Jacob bfc0b53163 chore: add simple toggle for dual writing 2025-02-10 23:05:34 +05:30
fogelito c77ca64fc0 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into catch-invalid-index 2025-02-10 15:41:50 +02:00
fogelito 8c3a243a5d Typo 2025-02-10 15:40:45 +02:00
fogelito 7a8688932c Add test 2025-02-10 15:40:21 +02:00
Steven NguyenandGitHub 06e857e5d9 Merge pull request #9321 from appwrite/pla-2453
Fix WEBP File Preview Rendering Issue
2025-02-10 06:33:40 -07:00
fogelito 7caab2f9ff IndexException 2025-02-10 10:55:37 +02:00
fogelito 33f55c73a7 Revert cast Index 2025-02-10 10:42:29 +02:00
fogelito edbe0fb72f Bump Database version 0.58.5 2025-02-10 10:40:12 +02:00
fogelito 23a9b519f6 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into catch-invalid-index 2025-02-10 10:37:05 +02:00
fogelito a032540b3c IndexException 2025-02-10 10:11:52 +02:00
fogelito 27709e7683 IndexException 2025-02-10 10:04:51 +02:00
fogelito 5b786e2a68 Catch index exception 2025-02-10 09:19:56 +02:00
Christy JacobandGitHub 42a4fe2efc Merge branch '1.6.x' into feat-logs-db 2025-02-10 10:18:09 +05:30
Christy JacobandGitHub 1c6bce0e79 Merge pull request #9330 from appwrite/update-queue-library
chore: update dependencies
2025-02-10 09:54:56 +05:30
Christy Jacob f6c28322c9 chore: update dependencies 2025-02-10 09:30:24 +05:30
fogelito 3092e765cc indexException 2025-02-09 18:34:41 +02:00
Damodar Lohani e8faf2a859 fix worker 2025-02-09 08:52:07 +00:00
shimon 2106022507 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.json
#	composer.lock
2025-02-09 10:06:56 +02:00
Damodar Lohani ec9045b488 add stats-resources health queue 2025-02-09 06:36:59 +00:00
55e30a0ef4 Update src/Appwrite/Platform/Workers/StatsUsageDump.php
Co-authored-by: Christy Jacob <christyjacob4@gmail.com>
2025-02-09 12:19:16 +05:45
Damodar Lohani abd899186d Merge remote-tracking branch 'origin/1.6.x' into feat-logs-db 2025-02-09 05:20:10 +00:00
Damodar Lohani 59516408af revert env update 2025-02-09 05:15:26 +00:00
Christy JacobandGitHub 859d556461 Merge pull request #9323 from appwrite/revert-9320-revert-9319-revert-9262-fix-redirect-validator-v2
Revert "Restore "feat: custom app schemes"""
2025-02-07 23:09:36 +05:30
Luke B. SilverandGitHub 975393ae48 Revert "Restore "feat: custom app schemes""" 2025-02-07 17:34:58 +00:00
Christy JacobandGitHub 0c096b7434 Merge pull request #9320 from appwrite/revert-9319-revert-9262-fix-redirect-validator-v2
Restore "feat: custom app schemes""
2025-02-07 21:40:13 +05:30
loks0n 3d74b651fe test: subdomain case 2025-02-07 15:49:15 +00:00
loks0n 111c28592d chore: fix warning 2025-02-07 15:22:02 +00:00
loks0n 10ab613f3b chore: fix empty schemes 2025-02-07 15:15:54 +00:00
loks0n 10b88ba194 chore: small improvements 2025-02-07 10:38:36 +00:00
Chirag Aggarwal 768e4c4988 chore: added webp to allowed file inputs 2025-02-07 10:36:41 +00:00
Luke B. SilverandGitHub 9788a126a8 Revert "Revert "feat: custom app schemes"" 2025-02-07 09:53:26 +00:00
Christy JacobandGitHub ad56994b83 Merge pull request #9319 from appwrite/revert-9262-fix-redirect-validator-v2
Revert "feat: custom app schemes"
2025-02-07 15:23:05 +05:30
Luke B. SilverandGitHub 2af026eabe Revert "feat: custom app schemes" 2025-02-07 09:51:27 +00:00
Chirag Aggarwal 20c01d0e73 chore: added env variable to compose.phtml 2025-02-07 07:57:29 +00:00
Chirag AggarwalandGitHub 989bbbc261 Merge branch '1.6.x' into pla-2428 2025-02-07 13:25:15 +05:30
Steven NguyenandGitHub a7b1363644 Merge pull request #9262 from appwrite/fix-redirect-validator-v2
feat: custom app schemes
2025-02-06 18:13:03 -08:00
Steven NguyenandGitHub 1d55bbc0d7 Merge pull request #9271 from appwrite/pla-2009
chore: override getUserAgent function to set forwarded user agent
2025-02-06 17:51:49 -08:00
Steven NguyenandGitHub 1f8448a514 Merge pull request #9228 from ChiragAgg5k/fix-slack-oauth
fix: slack oauth scopes, and updated to v2
2025-02-06 17:22:54 -08:00
Luke B. SilverandGitHub e6efe5b27e Merge branch '1.6.x' into pla-2428 2025-02-06 13:58:58 +00:00
Christy JacobandGitHub 7220abe9a4 Merge pull request #9313 from appwrite/queue-0.8.1
fix(schedule-tasks): revert back to direct pool usage
2025-02-06 18:08:42 +05:30
Fabian Gruber 273ea0abcc fix(schedule-tasks): revert back to direct pool usage 2025-02-06 13:18:18 +01:00
loks0n a4c8b74501 chore: revert storage 2025-02-06 11:24:29 +00:00
loks0n b69e78e5bf Merge branch '1.6.x' of https://github.com/appwrite/appwrite into fix-redirect-validator-v2 2025-02-06 11:22:49 +00:00
Damodar LohaniandGitHub 9e988747d8 Merge branch '1.6.x' into feat-logs-db 2025-02-06 16:02:41 +05:45
Matej Bačo e992ed11c1 Fix unwanted redirects in functions 2025-02-06 11:08:28 +01:00
Damodar Lohani cd45177558 fix pool empty 2025-02-06 08:06:15 +00:00
Damodar Lohani f6b2d9d937 remove old collections - we only need it in production temporarily 2025-02-06 07:02:07 +00:00
Damodar Lohani c48146b699 fix builds worker 2025-02-06 06:05:53 +00:00
Damodar Lohani c27c3499aa fix endpoint and add test 2025-02-06 04:48:40 +00:00
Damodar Lohani 0a6ebe6661 Merge remote-tracking branch 'origin/1.6.x' into feat-logs-db 2025-02-06 04:40:05 +00:00
Damodar Lohani ae9d3f4703 fix dual writing 2025-02-06 04:40:01 +00:00
Damodar Lohani 2efe1ed9dc new health endpoint 2025-02-06 04:24:04 +00:00
Damodar Lohani c3495dde35 fix key 2025-02-06 04:17:49 +00:00
Damodar Lohani 8da5bfccb6 fix format and test 2025-02-06 03:58:46 +00:00
Damodar Lohani 89aa61b17c fix duplicate constant 2025-02-06 03:37:01 +00:00
Damodar Lohani 87b3f535bd fix renaming 2025-02-06 03:36:53 +00:00
shimon 50400c4b43 _APP_DOMAIN_FUNCTIONS_FALLBACK 2025-02-05 13:14:03 +02:00
shimon 3cfb013f1e _APP_DOMAIN_FUNCTIONS_FALLBACK 2025-02-05 12:49:04 +02:00
Damodar Lohani 68c3aa3078 update to use publisher 2025-02-05 09:53:29 +00:00
Damodar Lohani 3e778c0094 update environment variable 2025-02-05 09:53:23 +00:00
Damodar Lohani cf66bee925 Merge remote-tracking branch 'origin/1.6.x' into feat-logs-db 2025-02-05 09:49:11 +00:00
shimon be27912a6e _APP_DOMAIN_FUNCTIONS_FALLBACK 2025-02-05 11:35:19 +02:00
Damodar Lohani 966fc47e25 fix classname 2025-02-05 09:28:44 +00:00
Damodar Lohani 99f23ff25a refactor 2025-02-05 09:27:36 +00:00
Damodar Lohani 6b930129e5 add DAU and WAU metric 2025-02-05 09:26:00 +00:00
Damodar Lohani 292e859133 rename metric 2025-02-05 09:17:27 +00:00
Damodar Lohani 5052fd8528 get latest usageDump and dual write 2025-02-05 09:11:03 +00:00
Damodar Lohani 5b91b5cec1 env to configure dual write 2025-02-05 09:10:45 +00:00
Damodar Lohani 4d43b41482 Merge remote-tracking branch 'origin/1.6.x' into feat-logs-db 2025-02-05 08:17:46 +00:00
shimon b73b450a2e updated against 1.6.x 2025-02-04 21:36:06 +02:00
shimon 4dc7206e04 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.json
#	composer.lock
2025-02-04 21:12:11 +02:00
Chirag Aggarwal bfb4cb4a04 chore: added empty check for s3 creds 2025-02-04 09:58:12 +00:00
Chirag Aggarwal deb556242e chore: update test 2025-02-04 08:35:26 +00:00
Chirag Aggarwal 6be805fe6e Merge branch '1.6.x' into pla-2428 2025-02-04 08:15:44 +00:00
Chirag Aggarwal fcfef4d474 Merge branch '1.6.x' into pla-2428 2025-02-04 08:12:42 +00:00
Chirag AggarwalandGitHub deeabc2d86 Merge branch '1.6.x' into pla-2009 2025-02-04 10:44:52 +05:30
Chirag Aggarwal a226e2ce79 chore: remove console user access 2025-02-04 05:14:37 +00:00
Chirag Aggarwal e4122e236c chore: added null coalescing operator 2025-02-04 04:59:18 +00:00
Steven NguyenandGitHub 0547dbc454 fix: use github var instead of secret for DOCKERHUB_USERNAME
DOCKERHUB_USERNAME was moved to be a variable since it doesn't need to
be secret.
2025-02-03 22:34:08 +00:00
Damodar LohaniandGitHub 63c07a456f Merge branch '1.6.x' into feat-logs-db 2025-02-02 07:08:25 +05:45
loks0n 58086fbc74 fix: leftover 2025-01-31 22:15:22 +00:00
Gurjeet Singh VirdeeandGitHub a3324b7453 Merge branch 'main' into 152-sdk-cli-command-update 2025-02-01 03:43:11 +05:30
loks0n 2e8b8367ed fix: localhost case 2025-01-31 22:01:41 +00:00
Steven NguyenandGitHub df017bccb2 chore: bump appwrite version to 1.6.1 2025-01-31 22:00:32 +00:00
loks0n ade25ec845 fix: validator edge case 2025-01-30 18:16:07 +00:00
loks0n 0be400da19 fix: validator description 2025-01-30 17:13:09 +00:00
loks0n 8ab06d053f chore: refactor, dbless approach 2025-01-30 16:58:42 +00:00
loks0n dada8f9bc1 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into fix-redirect-validator-v2 2025-01-30 16:00:50 +00:00
Damodar Lohani 978f22b56d rename transformed metric 2025-01-30 07:15:24 +00:00
Damodar Lohani 71c265d4e8 fix wrong comment 2025-01-30 07:12:15 +00:00
Damodar Lohani d31a358392 Temp: everything to be removed 2025-01-30 07:07:10 +00:00
Damodar Lohani 95845f8c0e update env name 2025-01-30 06:42:57 +00:00
Damodar Lohani 6eb4d3f021 reset dockerfile 2025-01-30 06:40:49 +00:00
Damodar Lohani 074f4417ff image transformations metrics and batch writes 2025-01-30 06:24:10 +00:00
Damodar Lohani da7a234038 update 2025-01-30 05:28:45 +00:00
Damodar Lohani f94d1a5d82 update compose.phtml 2025-01-30 05:24:30 +00:00
Damodar Lohani d0724812d7 fix 2025-01-30 05:05:05 +00:00
Damodar Lohani 6fedefdeda fix 2025-01-30 05:03:41 +00:00
Damodar Lohani cb31e7954e refactor rename 2025-01-30 04:53:53 +00:00
Damodar Lohani 0da3f870f9 refactor: rename 2025-01-30 04:43:16 +00:00
Damodar Lohani 469dbf0a31 reset usage 2025-01-30 04:33:42 +00:00
Damodar Lohani 3fde465dfa Update stats resources task container 2025-01-30 04:15:03 +00:00
Damodar Lohani c2ce273065 update stats resources task to use new queue 2025-01-30 04:10:35 +00:00
Damodar Lohani a05d774370 queue resources to cli 2025-01-30 04:07:44 +00:00
Damodar Lohani e78020d45a stats resources evenet 2025-01-30 04:06:57 +00:00
Damodar Lohani 22ab23d009 feat: Stats resources worker 2025-01-30 04:05:51 +00:00
Damodar Lohani 31ddb6e48f rename collection 2025-01-30 03:51:17 +00:00
Damodar Lohani 21097041f0 refactor: rename usage count to stats resources 2025-01-30 03:50:27 +00:00
loks0n 850bed904a chore: improve validator 2025-01-29 17:52:06 +00:00
loks0n 0457d4d052 Merge branch 'fix-redirect-validator-v2' of https://github.com/appwrite/appwrite into fix-redirect-validator-v2 2025-01-29 17:34:53 +00:00
loks0n 84f3e62908 chore: review changes 2025-01-29 17:34:45 +00:00
Eldad A. FuxandGitHub c4ab6f5aff Merge pull request #9285 from appwrite/LauraDuRy-patch-1
replaced init for cloud link
2025-01-29 12:06:46 +01:00
Laura Du RyandGitHub 891c1c4093 Update README.md 2025-01-29 10:04:15 +01:00
Laura Du RyandGitHub 507c8bd3ec Update README.md 2025-01-29 10:02:40 +01:00
Laura Du RyandGitHub 2de4e71662 Update README.md 2025-01-29 09:58:04 +01:00
Chirag AggarwalandGitHub 2930c32184 Merge branch '1.6.x' into fix-slack-oauth 2025-01-29 09:39:14 +05:30
Luke B. SilverandGitHub f95504f829 Merge branch '1.6.x' into fix-redirect-validator-v2 2025-01-28 13:34:18 +00:00
Damodar Lohani fb5aeb1476 skip some metrics override 2025-01-28 07:19:27 +00:00
Damodar Lohani d84f6e4ee0 reset database listeners 2025-01-28 06:40:20 +00:00
Damodar Lohani a85499a4ec Merge remote-tracking branch 'origin/1.6.x' into feat-logs-db 2025-01-28 02:11:01 +00:00
loks0n 46532671d6 chore: specs 2025-01-27 20:44:14 +00:00
loks0n 4320e5bef5 chore: specs 2025-01-27 19:35:09 +00:00
loks0n b070e56226 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into fix-redirect-validator-v2 2025-01-27 19:33:44 +00:00
Chirag Aggarwal 70c119b9cc chore: added test cases for forwarded user agent 2025-01-27 13:58:46 +00:00
loks0n f369e9100f fix: tests 2025-01-27 13:25:27 +00:00
loks0n c184938a2a fix: tests 2025-01-27 13:02:39 +00:00
loks0n dc33c26781 fix: tests 2025-01-27 12:01:18 +00:00
loks0n 3f59c087c8 fix: tests 2025-01-27 11:50:03 +00:00
loks0n d8848e10af fix: build 2025-01-27 11:12:11 +00:00
Luke B. SilverandGitHub c30689d664 Merge branch '1.6.x' into fix-redirect-validator-v2 2025-01-27 11:00:03 +00:00
Luke B. SilverandGitHub bce71d6177 Merge branch '1.6.x' into pla-2009 2025-01-27 10:40:23 +00:00
Damodar Lohani 7b588219f9 fix dual writing internal ID conflict 2025-01-27 08:19:03 +00:00
Damodar Lohani 05f99dcdf0 fix wrong arg 2025-01-27 07:46:54 +00:00
Damodar Lohani 3124554403 remove db listeners as counters are now done separately by usage worker 2025-01-27 07:39:33 +00:00
Damodar Lohani 336b0a7c02 rename collection 2025-01-27 07:22:50 +00:00
Damodar Lohani f593498c07 dual writing 2025-01-27 07:15:06 +00:00
Damodar Lohani 55cbab6270 fix format 2025-01-27 06:15:59 +00:00
Damodar Lohani 5d5547c6b9 periodic metric 2025-01-27 06:11:58 +00:00
Damodar Lohani 066273d0db update usage worker for usage count 2025-01-27 04:33:41 +00:00
Damodar Lohani 9955b6b61c usage count container and events 2025-01-27 04:33:33 +00:00
Chirag Aggarwal eea088556d chore: added check for empty forwarded user agent before 2025-01-27 04:15:32 +00:00
Damodar Lohani 5685c8ac25 refactor create collection and create logs db 2025-01-27 02:51:01 +00:00
Damodar Lohani c6360cce06 logs collection config 2025-01-27 02:50:50 +00:00
Damodar Lohani 187ea93f81 Get logs db resource 2025-01-27 02:26:06 +00:00
Chirag Aggarwal f74fe4438f chore: override getUserAgent function to set forwarded user agent 2025-01-26 07:11:00 +00:00
loks0n b34d64b4d4 fix: tests 2025-01-24 17:58:46 +00:00
shimon b18d3bc678 payload debug 2025-01-24 18:36:32 +02:00
loks0n 42cf1fefa5 feat: custom scheme 2025-01-24 16:21:12 +00:00
shimon 0cc5088e8e payload debug 2025-01-24 18:03:24 +02:00
shimon 2c4f5d3daa payload debug 2025-01-24 17:52:07 +02:00
shimon 1675683e76 payload debug 2025-01-24 17:38:29 +02:00
shimon 78dcfd2770 payload debug 2025-01-24 17:25:57 +02:00
shimon 87de071f53 payload debug 2025-01-24 16:53:26 +02:00
shimon 39585d0438 payload debug 2025-01-24 16:46:02 +02:00
shimon 270dfd685d payload debug 2025-01-24 16:39:16 +02:00
shimon de29879793 payload debug 2025-01-24 16:22:11 +02:00
Gurjeet Singh VirdeeandGitHub 8508e6009b refactor: cleaned up syntax by removing unnecessary '=' 2025-01-24 19:13:56 +05:30
shimon a70e323a15 payload debug 2025-01-24 09:41:53 +02:00
shimon ff1bfb61f1 payload debug 2025-01-24 09:06:10 +02:00
loks0n 674f3d83b3 chore: remove comment 2025-01-23 17:22:33 +00:00
loks0n 507d210bcb fix: tests 2025-01-23 17:20:57 +00:00
loks0n f5efc53406 fix: tests 2025-01-23 15:44:03 +00:00
loks0n ff9fb9bb46 Merge branch 'fix-redirect-validator-v2' of https://github.com/appwrite/appwrite into fix-redirect-validator-v2 2025-01-23 15:27:11 +00:00
loks0n 49b71ff43a chore: fmt 2025-01-23 15:27:03 +00:00
Luke B. SilverandGitHub d2a4361e8d Merge branch '1.6.x' into fix-redirect-validator-v2 2025-01-23 15:20:52 +00:00
loks0n 170f3656e1 fix: redirect validator v2 2025-01-23 15:00:54 +00:00
shimon 54c8a890d3 removed comments 2025-01-23 09:23:19 +02:00
shimon 74b06cdd96 debug platforms 2025-01-23 08:51:13 +02:00
shimon 5113c9e50a debug platforms 2025-01-23 07:53:03 +02:00
shimon 0052487d49 debug platforms 2025-01-23 07:47:23 +02:00
shimon 9841f6ae87 debug platforms 2025-01-23 07:40:44 +02:00
Chirag Aggarwal 6d404ada74 chore: update scopes 2025-01-23 05:36:12 +00:00
shimon 9b58ae7ff5 debug platforms 2025-01-23 07:23:35 +02:00
shimon 59c41478ca debug platforms 2025-01-22 21:14:41 +02:00
shimon 37087c37ca debug platforms 2025-01-22 20:44:18 +02:00
shimon 066008293d debug 2025-01-22 18:49:57 +02:00
Marton SchneiderandGitHub 1683fe0a98 Merge branch 'main' into main 2025-01-21 22:57:53 +01:00
Chirag Aggarwal ead7a17645 chore: remove comma 2025-01-21 13:34:45 +00:00
shimon b9acae5a25 debug 2025-01-21 09:43:34 +02:00
shimon d0452e5d3b debug 2025-01-20 19:26:14 +02:00
shimon f94f6642ea debug 2025-01-20 17:45:42 +02:00
shimon 01dc8fa396 debug 2025-01-20 16:16:38 +02:00
shimon 430c883a88 debug 2025-01-20 15:29:33 +02:00
shimon dbec8c01a9 debug 2025-01-20 11:47:47 +02:00
shimon 0c37530974 debug 2025-01-20 11:34:25 +02:00
shimon 5fa4a1232f debug 2025-01-20 11:32:54 +02:00
shimon 645fbd4f1c debug 2025-01-20 10:33:06 +02:00
shimon b2f920d983 debug 2025-01-20 10:03:54 +02:00
shimon 3c152b8349 debug 2025-01-20 09:25:15 +02:00
shimon bad427071e debug 2025-01-19 22:41:00 +02:00
shimon 419d18bb1c debug 2025-01-19 22:25:03 +02:00
shimon 6e1675c574 debug 2025-01-19 22:10:52 +02:00
shimon 561af1d386 debug 2025-01-19 21:03:48 +02:00
shimon 286f7e6b86 added attr installations collection 2025-01-19 11:22:22 +02:00
shimon fe18afcfca Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2025-01-19 10:33:47 +02:00
Chirag Aggarwal be5c4b753a fix: syntax 2025-01-17 19:40:33 +00:00
Chirag AggarwalandGitHub 4d86096c20 Merge branch '1.6.x' into fix-slack-oauth 2025-01-18 00:12:38 +05:30
Chirag Aggarwal 0517ebfb4e fix: auth user 2025-01-17 16:19:14 +00:00
Chirag Aggarwal 55360c3af0 debugging 2025-01-17 07:43:50 +00:00
Chirag Aggarwal 983eee9266 chore: added error handling to user 2025-01-17 07:14:48 +00:00
Chirag Aggarwal 5d3071e084 fix: scopes 2025-01-17 07:05:31 +00:00
shimon 348199712a composer.lock 2025-01-16 15:10:07 +02:00
shimon 1ae7d730ba Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support 2025-01-16 15:04:52 +02:00
Chirag Aggarwal 779510a605 fix: scopes, and updated to v2 2025-01-15 14:49:26 +00:00
shimon 246c334127 debug region validation 2025-01-13 21:05:19 +02:00
ChiragAgg5k 11867bef50 chore: fix webhooks 2025-01-13 21:46:42 +05:30
Chirag Aggarwal 8b0465f129 fix: webhooks 2025-01-13 15:04:33 +00:00
Chirag Aggarwal 99f736aa52 chore: add sensitive to fn logs n errors 2025-01-11 13:46:44 +00:00
Chirag AggarwalandGitHub 48fd3b371e Merge branch '1.6.x' into chore-sensitive-check 2025-01-10 23:51:26 +05:30
Chirag Aggarwal acea8812da chore: add senstive attribute to session 2025-01-10 18:21:04 +00:00
Chirag Aggarwal 13597d7c54 chore: abstract token secret hiding to response model 2025-01-10 13:47:38 +00:00
shimon f4d2583865 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support 2025-01-08 15:54:46 +02:00
shimon 18f17ff9fb remove abuse from deletes worker 2025-01-08 15:54:31 +02:00
Gurjeet Singh VirdeeandGitHub a39341ada8 fix: add missing single quote 2025-01-08 14:48:54 +05:30
Gurjeet Singh VirdeeandGitHub c451074278 restored the permissions term 2025-01-06 01:29:06 +05:30
shimon 33a8da45bd rollback project usage changes 2024-12-25 12:34:02 +02:00
shimon a7d09118e4 composer 2024-12-24 18:53:28 +02:00
shimon 0e0009dd27 composer 2024-12-24 18:52:33 +02:00
shimon 3159e5de0c project usage updates 2024-12-24 18:22:17 +02:00
shimon e4bdbdd9ba Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.lock
2024-12-24 18:18:44 +02:00
Gurjeet Singh VirdeeandGitHub cce6222184 docs: update CLI commands in documentation 2024-12-20 22:24:39 +05:30
shimon e154407fc6 Merge remote-tracking branch 'origin/multi-region-support' into multi-region-support 2024-12-14 20:23:32 +02:00
shimon 06b69360f4 sync with 1.6.x 2024-12-14 20:23:07 +02:00
shimon aa2a45f142 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	app/controllers/api/functions.php
#	app/controllers/api/messaging.php
#	docker-compose.yml
2024-12-14 20:18:06 +02:00
Christy JacobandGitHub 7ecba8c0f2 Merge pull request #9105 from gewenyu99/docs-create-document
Docs-create-document
2024-12-13 15:52:31 +04:00
Vincent (Wen Yu) Ge 46038c9fef update PR fix quote 2024-12-12 21:27:04 +00:00
Vincent (Wen Yu) Ge bb3143128f Merge branch 'main' into docs-create-document 2024-12-12 21:26:23 +00:00
Jake Barnby 54d1ef257d Remove logs 2024-12-12 21:01:26 +13:00
Jake Barnby 39e4a04625 Fix default region 2024-12-12 20:31:50 +13:00
Jake Barnby 9a89cc1332 Remove logs 2024-12-12 19:06:35 +13:00
Jake Barnby f4637e1b7d Create audit/abuse for V2 2024-12-12 18:55:25 +13:00
Jake Barnby 8f390a70c7 Fix self-hosted region check default 2024-12-12 18:54:58 +13:00
Marton Schneider eb2e9eadd7 fix docker-compose.yml 2024-12-11 21:10:21 +01:00
Marton SchneiderandGitHub aaf5ab1948 Merge branch 'appwrite:main' into main 2024-12-11 21:06:52 +01:00
Christy JacobandGitHub f3d6eb09dd Merge pull request #5769 from appwrite/lohanidamodar-patch-2
fix doc comment for filter method
2024-12-11 23:25:51 +04:00
Christy JacobandGitHub a735e969ec Merge pull request #5445 from appwrite/cloud-update-install-link
Add Appwrite Cloud to read me.
2024-12-11 23:10:39 +04:00
Christy JacobandGitHub 3ca1f3065f Merge pull request #7042 from mishmanners/patch-4
Add accessibility features
2024-12-11 23:00:10 +04:00
Christy Jacob 2c9da8a1c4 chore: update PR 2024-12-11 18:59:46 +00:00
Christy Jacob f5f549b520 chore: update PR 2024-12-11 18:58:54 +00:00
Christy Jacob 5da1dd26bc chore: update PR 2024-12-11 18:55:46 +00:00
Christy JacobandGitHub fef9b6dfd3 Merge pull request #5278 from gofenix/readme-cn
update readme-cn some doc
2024-12-11 21:53:32 +04:00
Christy JacobandGitHub 1483e08754 Merge pull request #4848 from olitomas/patch-1
🇮🇸 Added missing Icelandic translations for email strings.
2024-12-11 21:53:13 +04:00
Christy JacobandGitHub 740baec56a Merge pull request #5046 from Yavnik/patch-4
Added RuPay Credit Card Icon in Avatars Service
2024-12-11 21:52:58 +04:00
Christy Jacob c92217afb9 chore: remove console 2024-12-11 17:25:12 +00:00
Christy Jacob db5c29742d Merge branch 'main' of https://github.com/appwrite/appwrite into readme-cn 2024-12-11 17:22:23 +00:00
Christy Jacob 816a203e73 chore: update Chinese readme 2024-12-11 17:21:02 +00:00
Christy JacobandGitHub 363011ee32 Merge branch 'main' into patch-4 2024-12-11 21:08:19 +04:00
Christy JacobandGitHub 51e5b50ae7 Merge branch 'main' into patch-1 2024-12-11 21:07:53 +04:00
Christy JacobandGitHub 342b9f2910 Merge pull request #4313 from tusharxoxoxo/patch-2
Update add-oauth2-provider.md
2024-12-11 21:07:19 +04:00
Christy Jacob 71c21a7bd8 chore: linter 2024-12-11 16:46:24 +00:00
Christy Jacob f8c22a6700 Merge branch 'main' of https://github.com/appwrite/appwrite into patch-4 2024-12-11 16:45:42 +00:00
Christy JacobandGitHub 01465f5c8d Merge branch 'main' into patch-1 2024-12-11 20:36:14 +04:00
Christy JacobandGitHub 7301704762 Merge branch 'main' into patch-2 2024-12-11 20:25:57 +04:00
Christy JacobandGitHub f8b89b2294 Merge pull request #3728 from sashashura/patch-1
GitHub Workflows security hardening
2024-12-11 20:25:22 +04:00
Jake Barnby c9e6bfbdb3 Update database 2024-12-09 16:48:41 +13:00
shimon 8e0bd8d18d composer 2024-12-08 15:47:29 +02:00
shimon d0be46be74 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.json
#	composer.lock
2024-12-08 15:46:49 +02:00
Jake Barnby dd6cde6ae5 Log trace 2024-12-05 21:30:26 +13:00
Jake Barnby 714c2f30d7 Update database 2024-12-05 17:50:24 +13:00
shimon 316b739a27 composer 2024-12-03 19:43:39 +02:00
shimon b04a85491b debug 2024-12-03 17:36:19 +02:00
shimon 635dbdb5cb debug 2024-12-03 16:38:44 +02:00
shimon d06e41f81e debug 2024-12-03 15:00:45 +02:00
shimon 1a6f23c3a1 debug 2024-12-02 10:13:10 +02:00
shimon ba510a5641 debug 2024-12-01 20:07:40 +02:00
shimon cb93b9e1e7 debug 2024-12-01 19:25:43 +02:00
shimon 61d030b2c6 sync with 1.6.x 2024-11-29 15:24:52 +02:00
shimon 1d49d8284c Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support 2024-11-29 15:02:56 +02:00
shimon 65474df642 debug 2024-11-29 13:30:04 +02:00
shimon ac27c77426 debug 2024-11-29 13:14:20 +02:00
Steven NguyenandGitHub 6e09bcf6c6 Bump appwrite version to 1.6.1-RC1 2024-11-27 18:10:28 +00:00
shimon 5e3e0b6683 debug 2024-11-26 12:43:31 +02:00
shimon 9468b08eb2 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support 2024-11-26 12:31:22 +02:00
shimon d2bf15b20b debug 2024-11-26 11:50:34 +02:00
shimon b1e5a5b987 Merge branch 'multi-region-support' of https://github.com/appwrite/appwrite into multi-region-support 2024-11-26 11:11:51 +02:00
Shimon NewmanandGitHub 9ca420a0e6 Merge pull request #9027 from appwrite/fix-no-pool-access
Fix no pool access
2024-11-26 11:08:45 +02:00
shimon 6f8c38dfa0 debug 2024-11-26 09:26:14 +02:00
Jake Barnby ddb86127b7 Fix lint 2024-11-26 18:29:10 +13:00
Jake Barnby ea0a5a186c Merge remote-tracking branch 'origin/multi-region-support' into fix-no-pool-access 2024-11-26 16:21:48 +13:00
Jake Barnby 928590b175 Only create databases for current region 2024-11-26 16:20:11 +13:00
shimon 7656ba11ff debug 2024-11-25 12:31:06 +02:00
shimon 7ae7ccbe49 debug 2024-11-25 12:02:00 +02:00
shimon 6d376df00a updates 2024-11-25 09:44:57 +02:00
Jake Barnby 0e03842173 Move pool access inside not V2 block 2024-11-25 17:20:27 +13:00
shimon aa8af5a213 env var 2024-11-24 23:09:22 +02:00
shimon f1a3cd13ff env var 2024-11-24 22:52:20 +02:00
shimon 00989fcdde env var 2024-11-24 20:40:06 +02:00
shimon c677374a11 env var 2024-11-24 19:39:44 +02:00
shimon 3f0a8fbe21 env var 2024-11-24 19:03:46 +02:00
shimon 7d5ae2f1c3 env var 2024-11-24 19:01:24 +02:00
shimon 0f8ad539ad composer.lock 2024-11-24 10:32:27 +02:00
shimon 9839a4ef36 sync with 1.6.x 2024-11-24 10:31:21 +02:00
shimon e91c762953 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support 2024-11-24 10:30:54 +02:00
Marton Schneider 3c1e422bb9 change the env var name from _APP_STORAGE_S3_ENDPOINT_URL to _APP_STORAGE_S3_ENDPOINT 2024-11-22 18:21:44 +01:00
Marton Schneider 4d90ff06fd Merge remote-tracking branch 'upstream/main' 2024-11-22 18:16:44 +01:00
shimon 3f6368f046 adding app region to console project 2024-11-21 23:35:45 +02:00
shimon d802639b0e Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	.github/workflows/tests.yml
#	app/controllers/api/projects.php
#	app/init.php
#	composer.lock
#	docker-compose.yml
#	src/Appwrite/Platform/Workers/Deletes.php
2024-11-21 22:11:35 +02:00
shimon 3d333633e4 Merge branch 'debug-whitelist-console' of https://github.com/appwrite/appwrite into multi-region-support 2024-11-21 22:00:17 +02:00
Christy Jacob 900f64bf8d feat: hack for multi region 2024-11-12 18:00:38 +01:00
loks0n dad8f597ca debug: whitelist console 2024-11-12 16:05:51 +01:00
Luke B. SilverandGitHub 90f18c57ac Merge pull request #8962 from appwrite/feat-fix-web-routes
feat fix web routes
2024-11-12 12:42:30 +01:00
loks0n 6e7dd26f75 feat: fix web routes 2024-11-12 12:37:15 +01:00
Christy JacobandGitHub 9151abc951 Merge pull request #8797 from appwrite/feat-migration
Feat migration
2024-11-12 10:27:24 +01:00
Jake Barnby 714f6b62ac Merge remote-tracking branch 'origin/feat-custom-cf-hostnames' into feat-migration 2024-11-12 21:45:04 +13:00
Jake Barnby 316693798e Merge remote-tracking branch 'origin/1.6.x' into feat-custom-cf-hostnames 2024-11-12 21:44:50 +13:00
Jake Barnby c7f92fbb02 Merge remote-tracking branch 'origin/feat-custom-cf-hostnames' into feat-migration 2024-11-12 21:43:52 +13:00
Jake Barnby e715f9e90c Remove redundant V1 tests 2024-11-12 20:52:25 +13:00
Jake Barnby f6ea6d2fc5 Fix matrix 2024-11-12 19:15:33 +13:00
Jake Barnby e626c2ad45 Run in all table modes in parallel 2024-11-12 19:14:46 +13:00
Jake Barnby 3b19ef6b1e Remove V2 tests 2024-11-12 15:00:23 +13:00
Jake Barnby c1a3b29a97 Temp test all shared tables versions 2024-11-12 14:37:03 +13:00
Jake Barnby aa2385ffb9 Merge branch 'revert-revert-8946-chore-update-database' into feat-migration
# Conflicts:
#	composer.json
#	composer.lock
2024-11-12 13:36:05 +13:00
Jake Barnby 8b99517dd0 Merge remote-tracking branch 'origin/1.6.x' into feat-migration 2024-11-12 13:34:54 +13:00
Jake Barnby 33037a3ae5 Update database 2024-11-12 13:34:40 +13:00
Jake Barnby b0852aeca3 Update database 2024-11-11 23:15:00 +13:00
Jake Barnby 09da4378c6 Merge remote-tracking branch 'origin/1.6.x' into feat-migration 2024-11-11 23:13:35 +13:00
Jake Barnby a85b43654c Reapply "Update database"
This reverts commit b1a1ad618a.
2024-11-11 15:22:57 +13:00
shimon ad8693c018 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	Dockerfile
2024-11-10 19:39:27 +02:00
shimon a0280e3c43 adding app region to console project 2024-11-10 12:26:34 +02:00
Christy Jacob 76216c3bf6 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-09 16:01:16 +00:00
loks0n c8c4576bbc Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-09 13:07:15 +01:00
loks0n dac8766eb4 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-08 22:02:31 +01:00
loks0n f6de256220 chore: composer update 2024-11-08 21:50:33 +01:00
loks0n 2f9b44930f Revert "Merge pull request #8946 from appwrite/chore-update-database"
This reverts commit c0bd1664be, reversing
changes made to b42dd86451.
2024-11-08 21:48:27 +01:00
loks0n 627df0d11f chore: remove redundant compression 2024-11-08 20:24:55 +01:00
loks0n fcd323baa4 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-08 20:24:35 +01:00
loks0n 0009b73b06 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-08 16:53:22 +01:00
shimon b5efbdbb0f updates 2024-11-08 14:43:30 +02:00
shimon a6e9d58f12 Merge branch 'feat-migration' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	app/cli.php
#	app/controllers/api/projects.php
2024-11-08 14:12:48 +02:00
shimon b496bcb6df updates 2024-11-08 14:06:02 +02:00
Jake Barnby 030e26b223 Merge remote-tracking branch 'origin/1.6.x' into feat-custom-cf-hostnames 2024-11-08 23:33:10 +13:00
Jake Barnby 9e69e41e02 Update lock 2024-11-08 17:04:29 +13:00
Jake Barnby 773fa6ffd6 Update lock 2024-11-08 16:34:07 +13:00
Jake Barnby 21bff77c50 Merge remote-tracking branch 'origin/feat-custom-cf-hostnames' into feat-migration
# Conflicts:
#	composer.json
#	composer.lock
2024-11-08 16:32:55 +13:00
loks0n 472f167b4a Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-07 20:31:41 +01:00
shimon f86bf6fdfd updates 2024-11-07 13:59:53 +02:00
shimon f3315c69a0 projects.php 2024-11-07 13:19:52 +02:00
shimon 76701b0cca composer 2024-11-07 13:03:45 +02:00
shimon 2cc73151d1 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.json
#	composer.lock
2024-11-07 13:02:45 +02:00
shimon 39e237dfb4 composer 2024-11-07 12:45:18 +02:00
shimon c624d94c45 composer 2024-11-07 12:44:51 +02:00
shimon b22188251d composer 2024-11-07 12:41:00 +02:00
shimon 68d017f452 composer 2024-11-07 12:36:53 +02:00
shimon 2ed854893d composer 2024-11-07 12:32:48 +02:00
shimon eb083a7e10 composer 2024-11-07 12:22:18 +02:00
shimon 2076a429c6 composer 2024-11-07 12:00:44 +02:00
shimon 59766b6ce6 composer 2024-11-07 12:00:26 +02:00
shimon 36bff12939 project creation fix 2024-11-07 11:56:09 +02:00
Fabian Gruber b4c986839e Merge branch '1.6.x' into feat-custom-cf-hostnames 2024-11-06 12:40:56 +01:00
Christy Jacob 4d81a901cb feat: update API endpoint in specs 2024-11-06 12:22:31 +01:00
Christy Jacob c11888c906 feat: update API endpoint in specs 2024-11-06 12:16:15 +01:00
Christy Jacob e6fb296666 feat: update API endpoint in the code examples 2024-11-06 12:07:14 +01:00
Damodar LohaniandGitHub d425c9f071 Merge pull request #8930 from appwrite/pathc-update-utopia-storage
upgrade utopia storage
2024-11-06 16:01:56 +05:45
Damodar Lohani e219ee2d84 upgrade utopia storage 2024-11-06 10:02:11 +00:00
Jake Barnby e66d0216c6 Merge remote-tracking branch 'origin/1.6.x' into feat-custom-cf-hostnames 2024-11-06 17:02:06 +13:00
Steven NguyenandGitHub e3e20d2923 Merge pull request #8922 from appwrite/lohanidamodar-patch-4
Improve compression param checks
2024-11-05 17:35:39 -08:00
loks0n e0c503e249 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-11-05 19:47:29 +01:00
shimon c3a8db0ea4 Rollback cross regions connections 2024-11-05 17:14:23 +02:00
Jake Barnby 228cabab3a Merge remote-tracking branch 'origin/1.6.x' into feat-custom-cf-hostnames 2024-11-05 23:38:24 +13:00
shimon 2b3e7f67a4 Rollback cross regions connections 2024-11-05 12:21:16 +02:00
shimon c2d5e8ec11 Rollback cross regions connections 2024-11-05 12:03:57 +02:00
Damodar Lohani 028c5366c2 make compression nullable and assign default in action
- also update the `update bucket` endpoint
2024-11-05 08:12:58 +00:00
Damodar LohaniandGitHub cca0c3ae45 Make compression param whitelist strict 2024-11-05 12:59:11 +05:45
Jake Barnby 0b85a64dab Merge remote-tracking branch 'origin/1.6.x' into feat-custom-cf-hostnames 2024-11-04 22:44:25 +13:00
shimon 4eafce998d Rollback cross regions connections 2024-11-03 14:58:09 +02:00
shimon 4963d98331 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	composer.lock
2024-11-03 14:45:04 +02:00
Jake Barnby 1633acbdc3 Merge remote-tracking branch 'origin/feat-custom-cf-hostnames' into feat-migration
# Conflicts:
#	composer.json
#	composer.lock
2024-11-01 16:16:06 +13:00
shimon 9e07579a52 Rollback cross regions connections 2024-10-31 21:38:27 +02:00
shimon 16a2d61874 Rollback cross regions connections 2024-10-31 17:43:49 +02:00
Jake BarnbyandGitHub 75e8635563 Merge pull request #8904 from appwrite/1.6.x
Merge 1.6.x into feat-custom-cf-hostnames
2024-10-31 22:12:38 +13:00
Jake Barnby 83e94629dc Merge remote-tracking branch 'origin/feat-custom-cf-hostnames' into feat-migration 2024-10-31 21:49:49 +13:00
shimon b00b609621 sync against 1.6.x 2024-10-31 10:22:08 +02:00
shimon cdbd72ec3d sync against 1.6.x 2024-10-31 10:06:52 +02:00
shimon a6104ad8cd Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	app/worker.php
#	composer.json
#	composer.lock
#	src/Appwrite/Platform/Workers/Functions.php
2024-10-29 18:00:54 +02:00
Fabian Gruber 6e3160c502 Merge branch '1.6.x' into feat-custom-cf-hostnames 2024-10-29 16:15:54 +01:00
Fabian Gruber 104ec3188f Merge branch '1.6.x' into feat-custom-cf-hostnames 2024-10-29 15:36:38 +01:00
shimon 4e37699e25 debug 2024-10-29 11:15:55 +02:00
shimon 118252d3af injecting poolForQueue to schedulers 2024-10-28 12:04:58 +02:00
shimon 509f06c2f0 injecting poolForQueue to schedulers 2024-10-28 11:41:59 +02:00
shimon 12458af84a injecting poolForQueue to schedulers 2024-10-28 11:34:02 +02:00
shimon 66bfc5748b injecting poolForQueue to schedulers 2024-10-28 11:15:41 +02:00
shimon cf32ba0351 debug 2024-10-27 18:10:30 +02:00
Fabian Gruber b47a4703c4 Merge branch '1.6.x' into feat-custom-cf-hostnames 2024-10-25 13:39:12 +02:00
Fabian Gruber 4ceff430f9 Merge branch '1.6.x' into feat-custom-cf-hostnames 2024-10-24 12:49:26 +02:00
Christy JacobandGitHub 5603516d88 Merge branch '1.6.x' into feat-custom-cf-hostnames 2024-10-23 23:56:43 +04:00
Jake Barnby 013d95ebf3 Update audit and abuse 2024-10-23 18:42:00 +13:00
Jake Barnby eca8d116c2 Fix attributes 2024-10-23 18:10:41 +13:00
Jake Barnby 8210808db0 Improve logic 2024-10-23 18:00:06 +13:00
Jake Barnby 4a003f2826 Force create audit/abuse metadata documents for shared tables v1 2024-10-23 17:41:44 +13:00
Christy Jacob 54f39494c0 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-custom-cf-hostnames 2024-10-22 19:51:03 +00:00
Christy Jacob ae4edf119f chore: linter 2024-10-22 19:49:10 +00:00
shimon 7be7b3389c debug 2024-10-22 21:31:32 +03:00
shimon 54555cb3cd updates 2024-10-22 19:39:57 +03:00
Fabian GruberandChristy Jacob a56293e018 chore: replace 'Expires' with 'Cache-Control: private' header to avoid CDN caching 2024-10-22 12:18:58 +00:00
shimon f8b2878943 sync with feat-migration 2024-10-22 11:09:27 +03:00
Jake Barnby a0c58317bd Merge remote-tracking branch 'origin/1.6.x' into feat-migration
# Conflicts:
#	composer.json
#	composer.lock
2024-10-22 15:13:45 +13:00
shimon e687c64ca0 sync with feat-migration 2024-10-21 18:58:11 +03:00
shimon 3ff5472543 sync with feat-migration 2024-10-21 18:57:11 +03:00
Jake Barnby d979c0c914 Create metadata document from outside if duplicate on shared tables V1 2024-10-21 22:43:47 +13:00
shimon a41b767400 debug 2024-10-21 10:32:53 +03:00
shimon ba0a495d3c debug 2024-10-21 10:01:32 +03:00
shimon 6435fa84af debug 2024-10-21 10:00:35 +03:00
shimon 8743330ad8 debug 2024-10-21 09:59:05 +03:00
shimon a37aa5551a debug 2024-10-20 17:59:39 +03:00
shimon b2521cf8a3 debug 2024-10-20 17:36:08 +03:00
shimon c853b6593a debug 2024-10-20 17:22:37 +03:00
shimon d980711c30 debug 2024-10-20 16:32:19 +03:00
shimon 7ab55e0694 debug 2024-10-20 16:21:39 +03:00
shimon db244937ff debug 2024-10-20 16:10:14 +03:00
shimon bcb593e6df debug 2024-10-20 15:56:43 +03:00
shimon 9bfab69062 debug 2024-10-20 15:33:10 +03:00
shimon 5451a9cd23 sync against feat-migration 2024-10-20 10:56:44 +03:00
Christy Jacob 5ea83b8827 chore: update cert generation 2024-10-18 23:45:42 +00:00
Christy Jacob 6fe7d92fea chore: update cert generation 2024-10-18 22:32:03 +00:00
Christy Jacob 28b02a4ba8 chore: fix bug 2024-10-18 21:18:20 +00:00
Christy Jacob f4d456fa4c chore: updat certs logic 2024-10-18 20:52:29 +00:00
Fabian Gruber 687252b15b fix adding custom hostname & error logging 2024-10-18 22:21:53 +02:00
Fabian Gruber f21b1c87a6 fix missing ssl object 2024-10-18 22:07:36 +02:00
Fabian Gruber ccbde6c470 fix zone id 2024-10-18 21:44:36 +02:00
Torsten Dittmann f189c41421 temp: custom cloudflare hostnames 2024-10-18 21:39:16 +02:00
Jake BarnbyandGitHub 5fcb990b1c Merge branch '1.6.x' into feat-migration 2024-10-15 21:17:15 +13:00
Jake Barnby 30bd25d134 Update lock 2024-10-15 20:19:19 +13:00
Jake Barnby 36d2b84301 Update lock 2024-10-15 18:09:54 +13:00
shimon a80b921927 debug 2024-10-14 16:21:23 +03:00
shimon 1373d62c52 debug 2024-10-14 16:18:01 +03:00
shimon d0481482ae debug 2024-10-14 10:36:52 +03:00
Jake Barnby 78b9be2e78 Lint 2024-10-14 14:55:18 +13:00
Jake Barnby a3b7cf84d5 Allow shared tables V1 and V2 2024-10-14 14:50:56 +13:00
Jake Barnby add6a60af4 Remove redundant files 2024-10-14 14:33:53 +13:00
Jake Barnby 4543c182b5 Merge remote-tracking branch 'origin/1.6.x' into feat-migration
# Conflicts:
#	Dockerfile
#	composer.json
#	composer.lock
#	src/Appwrite/Platform/Workers/Deletes.php
2024-10-14 14:29:04 +13:00
shimon 051d6e580c debug 2024-10-13 18:15:11 +03:00
shimon 36e4c8c399 debug 2024-10-10 12:53:45 +03:00
shimon 7921bd62d9 debug 2024-10-10 12:11:35 +03:00
shimon 74ecc20751 debug 2024-10-10 12:07:53 +03:00
shimon d477de256a update file preview cache save flow 2024-10-10 11:40:13 +03:00
shimon cffbc77d05 update file preview cache save flow 2024-10-10 11:05:54 +03:00
shimon 16fe23244b builds metric reduce 2024-10-09 20:13:28 +03:00
shimon 7d7b2c7544 debug 2024-10-08 21:51:43 +03:00
shimon d2c66894fd debug 2024-10-07 17:56:08 +03:00
shimon 1cd3e69161 debug 2024-10-07 11:42:14 +03:00
shimon 5850caff35 debug 2024-10-07 11:25:52 +03:00
shimon 0cce55565a updates 2024-10-06 20:16:00 +03:00
shimon 6b429c9da9 updates 2024-10-06 18:59:22 +03:00
shimon 8f6151a575 updates 2024-10-06 18:56:39 +03:00
shimon 3f19fac0b4 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into multi-region-support
# Conflicts:
#	app/init.php
#	app/realtime.php
#	composer.json
#	src/Appwrite/Platform/Tasks/ScheduleBase.php
#	src/Appwrite/Platform/Tasks/ScheduleExecutions.php
#	src/Appwrite/Platform/Tasks/ScheduleFunctions.php
#	src/Appwrite/Platform/Tasks/ScheduleMessages.php
#	src/Appwrite/Platform/Workers/Deletes.php
#	src/Appwrite/Platform/Workers/Migrations.php
2024-10-04 16:26:47 +03:00
shimon 08e5ed6a8a debug 2024-09-29 17:37:56 +03:00
shimon e4d554689f debug 2024-09-29 17:35:27 +03:00
shimon 19912c87c9 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2024-09-29 17:04:34 +03:00
shimon 0de1e64958 debug 2024-09-24 12:41:14 +03:00
shimon aa0a795ec6 Merge branch '1.6.x' of github.com:appwrite/appwrite into multi-region-support 2024-09-22 09:49:50 +03:00
shimon d1b062a418 Scheduled task fix 2024-09-19 20:03:15 +03:00
shimon 812d8a34cf updates 2024-09-18 13:45:09 +03:00
shimon ec02d881c9 updates 2024-09-18 11:52:59 +03:00
shimon b706571a21 _APP_OPTIONS_ROUTER_PROTECTION 2024-09-18 10:55:30 +03:00
shimon 134f90a464 fix 2024-09-17 23:32:36 +03:00
shimon eb553fa96c debug 2024-09-17 21:09:16 +03:00
shimon fd8b7eb6fc update 2024-09-17 15:35:48 +03:00
shimon 501411f5b5 1.6.x 2024-09-15 17:42:46 +02:00
shimon bee844fcdb sync against 1.6x 2024-09-15 17:40:55 +02:00
Jake Barnby d18a67e6d8 Update packages 2024-09-12 23:06:47 +12:00
Jake Barnby 25d44b5b4c Update generator 2024-09-12 23:04:44 +12:00
Jake Barnby ee7de0cb41 Update database 2024-09-12 23:02:12 +12:00
Jake Barnby 28642b9f04 Shared tables array 2024-09-12 17:06:16 +12:00
Jake Barnby c18308c024 Update database 2024-08-27 17:43:29 +12:00
Jake Barnby 1eafbf2ee2 Update database 2024-08-19 21:21:20 +12:00
Jake Barnby b98bcdd4a1 Update database 2024-08-19 18:27:45 +12:00
Jake Barnby 4c8cd5b6c6 Update audit, abuse, database 2024-08-19 14:52:15 +12:00
Jake Barnby 5b37a91b46 Merge remote-tracking branch 'origin/1.5.x' into feat-migration 2024-08-13 22:11:18 +12:00
Jake Barnby 52647cef23 Merge remote-tracking branch 'origin/feat-single-shared-metadata-docs' into feat-migration
# Conflicts:
#	composer.json
#	composer.lock
2024-08-13 22:10:57 +12:00
Jake Barnby 11ec31aefb Update database 2024-08-13 21:49:25 +12:00
Jake Barnby 73ca1efbeb Catch duplicate db on project create 2024-08-13 21:49:14 +12:00
Jake Barnby 340aa48d93 Catch duplicate on console db create 2024-08-13 21:14:52 +12:00
Jake Barnby 8ff72b341e Don't set tenant for project collections creation 2024-08-12 23:46:34 +12:00
Jake Barnby a23402075e Update database 2024-08-12 17:45:43 +12:00
Jake Barnby 6b86ed0117 Merge remote-tracking branch 'origin/1.5.x' into feat-migration
# Conflicts:
#	composer.lock
2024-08-07 22:10:04 +12:00
Jake Barnby e2a45cec8e Merge branch '1.5.x' into feat-migration
# Conflicts:
#	composer.json
#	composer.lock
2024-08-05 21:13:42 +12:00
Jake Barnby ec2eb2bfa3 Revert to utopia database instead of extension 2024-07-02 19:10:02 +12:00
Jake Barnby 10adfc5124 Add mirror override 2024-07-02 14:55:13 +12:00
Jake Barnby 5f7aae0e60 Add missing import 2024-06-21 13:43:01 +12:00
Jake Barnby 4952608b8d Merge remote-tracking branch 'origin/refactor-usage-sn' into feat-migration 2024-06-21 13:39:51 +12:00
Jake Barnby 42ebefef92 Merge remote-tracking branch 'origin/refactor-usage-sn' into feat-migration 2024-06-19 19:32:30 +12:00
Jake Barnby 03d33601b0 Override find 2024-06-14 13:08:30 +12:00
Marton Schneider 5a2f06fb83 add support endpoint URL for S3 2023-11-18 13:02:47 +01:00
Michelle "MishManners®™" ManneringandGitHub 3211867f6a Accessibility for README-CN
Add accessibility features based on: https://github.blog/2023-10-26-5-tips-for-making-your-github-profile-page-accessible/
2023-10-30 10:46:17 +11:00
Michelle "MishManners®™" ManneringandGitHub d1c5ec4585 Add accessibility to CONTRIBUTING
Add accessibility features based on: https://github.blog/2023-10-26-5-tips-for-making-your-github-profile-page-accessible/
2023-10-30 10:44:24 +11:00
Michelle "MishManners®™" ManneringandGitHub f90034a510 Add accessibility to README
Add accessibility features based on: https://github.blog/2023-10-26-5-tips-for-making-your-github-profile-page-accessible/
2023-10-30 10:39:46 +11:00
Damodar LohaniandGitHub 701f0bbd4d fix doc comment for filter method 2023-07-05 11:51:45 +05:45
Vincent (Wen Yu) Ge c0bea3d833 change links from install -> self-hosting 2023-04-27 19:08:12 +00:00
Vincent (Wen Yu) Ge 53a9c87f42 Update links in readme to include appwrite cloud 2023-04-27 19:01:01 +00:00
Vincent (Wen Yu) Ge 03bfed4e25 Update create-document description and improve error message for unknown attribute. 2023-04-24 22:54:01 +00:00
zhenfeng-zhu 96359b8f6a update readme-cn some doc 2023-03-25 18:03:01 +08:00
Tushar DahiyaandGitHub 16c7aa230d Merge branch 'appwrite:master' into patch-2 2023-03-09 22:27:09 +05:30
Yavnik SharmaandGitHub 5ea1268107 Added rupay.png 2023-02-03 18:24:17 +05:30
Yavnik SharmaandGitHub c856ffef45 Delete old rupay.png 2023-02-03 18:23:41 +05:30
Yavnik SharmaandGitHub 6509f19773 Merge branch 'appwrite:master' into patch-4 2023-02-03 18:08:06 +05:30
Yavnik SharmaandGitHub 8ec13eb221 Added Rupay in Get Credit Card Icon Documentation 2023-01-26 17:53:58 +05:30
Yavnik SharmaandGitHub 19f965befe Added Rupay in Get Credit Card Icon Documentation 2023-01-26 17:53:32 +05:30
Yavnik SharmaandGitHub 4077dc48b4 Added Rupay in Get Credit Card Icon Documentation 2023-01-26 17:53:04 +05:30
Yavnik SharmaandGitHub 14f38343f5 Added Rupay in Get Credit Card Icon Documentation 2023-01-26 17:52:36 +05:30
Yavnik SharmaandGitHub dd04c005aa Added Rupay in Get Credit Card Icon Documentation 2023-01-26 17:52:01 +05:30
Yavnik SharmaandGitHub 0328134cbc Added Rupay in Get Credit Card Icon Documentation 2023-01-26 17:51:33 +05:30
Yavnik SharmaandGitHub a65f3b1f4a Added rupay.png file 2023-01-26 17:50:49 +05:30
Yavnik SharmaandGitHub 11c93f800f Added rupay 2023-01-26 17:49:11 +05:30
Óli TómasandGitHub c673662a34 🇮🇸 Added missing Icelandic translations for email strings. 2022-12-12 19:19:36 +00:00
Tushar DahiyaandGitHub 092322ff4e Update add-oauth2-provider.md 2022-10-07 09:27:56 +05:30
AlexandGitHub e4d8d38c6e Update codeql-analysis.yml
Signed-off-by: sashashura <93376818+sashashura@users.noreply.github.com>
2022-08-29 11:34:32 +01:00
17248 changed files with 191991 additions and 33007 deletions
+5
View File
@@ -15,6 +15,7 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io
_APP_EMAIL_SECURITY=security@appwrite.io
_APP_EMAIL_CERTIFICATES=certificates@appwrite.io
_APP_SYSTEM_RESPONSE_FORMAT=
_APP_CUSTOM_DOMAIN_DENY_LIST=
_APP_OPTIONS_ABUSE=disabled
_APP_OPTIONS_ROUTER_PROTECTION=disabled
_APP_OPTIONS_FORCE_HTTPS=disabled
@@ -39,6 +40,7 @@ _APP_STORAGE_S3_ACCESS_KEY=
_APP_STORAGE_S3_SECRET=
_APP_STORAGE_S3_REGION=us-east-1
_APP_STORAGE_S3_BUCKET=
_APP_STORAGE_S3_ENDPOINT=
_APP_STORAGE_DO_SPACES_ACCESS_KEY=
_APP_STORAGE_DO_SPACES_SECRET=
_APP_STORAGE_DO_SPACES_REGION=us-east-1
@@ -85,7 +87,9 @@ _APP_MAINTENANCE_RETENTION_CACHE=2592000
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
_APP_MAINTENANCE_RETENTION_ABUSE=86400
_APP_MAINTENANCE_RETENTION_AUDIT=1209600
_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE=15778800
_APP_USAGE_AGGREGATION_INTERVAL=30
_APP_STATS_RESOURCES_INTERVAL=3600
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
_APP_USAGE_STATS=enabled
@@ -110,3 +114,4 @@ _APP_MESSAGE_PUSH_TEST_DSN=
_APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10
_APP_PROJECT_REGIONS=default
_APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000
_APP_STATS_USAGE_DUAL_WRITING_DBS=database_db_main
+5
View File
@@ -13,8 +13,13 @@ on:
schedule:
- cron: '0 16 * * 0'
permissions:
contents: read
jobs:
analyze:
permissions:
security-events: write
name: Analyze
runs-on: ubuntu-latest
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
+83 -8
View File
@@ -8,9 +8,33 @@ env:
IMAGE: appwrite-dev
CACHE_KEY: appwrite-dev-${{ github.event.pull_request.head.sha }}
on: [pull_request]
on: [ pull_request ]
jobs:
check_database_changes:
name: Check if utopia-php/database changed
runs-on: ubuntu-latest
outputs:
database_changed: ${{ steps.check.outputs.database_changed }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Fetch base branch
run: git fetch origin ${{ github.event.pull_request.base.ref }}
- name: Check for utopia-php/database changes
id: check
run: |
if git diff origin/${{ github.event.pull_request.base.ref }} HEAD -- composer.lock | grep -q '"name": "utopia-php/database"'; then
echo "Database version changed, going to run all mode tests."
echo "database_changed=true" >> "$GITHUB_ENV"
echo "database_changed=true" >> "$GITHUB_OUTPUT"
else
echo "database_changed=false" >> "$GITHUB_ENV"
echo "database_changed=false" >> "$GITHUB_OUTPUT"
fi
setup:
name: Setup & Build Appwrite Image
runs-on: ubuntu-latest
@@ -103,6 +127,62 @@ jobs:
name: E2E Service Test
runs-on: ubuntu-latest
needs: setup
strategy:
fail-fast: false
matrix:
service: [
Account,
Avatars,
Console,
Databases,
Functions,
FunctionsSchedule,
GraphQL,
Health,
Locale,
Projects,
Realtime,
Storage,
Teams,
Users,
Webhooks,
VCS,
Messaging,
Migrations
]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Load Cache
uses: actions/cache@v4
with:
key: ${{ env.CACHE_KEY }}
path: /tmp/${{ env.IMAGE }}.tar
fail-on-cache-miss: true
- name: Load and Start Appwrite
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose up -d
sleep 30
- name: Run ${{ matrix.service }} tests with Project table mode
run: |
echo "Using project tables"
export _APP_DATABASE_SHARED_TABLES=
export _APP_DATABASE_SHARED_TABLES_V1=
docker compose exec -T \
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug
e2e_shared_mode_test:
name: E2E Shared Mode Service Test
runs-on: ubuntu-latest
needs: [ setup, check_database_changes ]
if: needs.check_database_changes.outputs.database_changed == 'true'
strategy:
fail-fast: false
matrix:
@@ -128,7 +208,6 @@ jobs:
Migrations
]
tables-mode: [
'Project',
'Shared V1',
'Shared V2',
]
@@ -160,12 +239,8 @@ jobs:
echo "Using shared tables V2"
export _APP_DATABASE_SHARED_TABLES=database_db_main
export _APP_DATABASE_SHARED_TABLES_V1=
else
echo "Using project tables"
export _APP_DATABASE_SHARED_TABLES=
export _APP_DATABASE_SHARED_TABLES_V1=
fi
docker compose exec -T \
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
@@ -251,4 +326,4 @@ jobs:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body-path: benchmark.txt
edit-mode: replace
edit-mode: replace
+1 -8
View File
@@ -7,6 +7,7 @@ tasks:
docker pull composer
command: |
docker run --rm --interactive --tty \
--user "$(id -u):$(id -g)" \
--volume $PWD:/app \
composer install \
--ignore-platform-reqs \
@@ -23,11 +24,3 @@ vscode:
extensions:
- ms-azuretools.vscode-docker
- zobo.php-intellisense
github:
# https://www.gitpod.io/docs/prebuilds#github-specific-configuration
prebuilds:
# enable for pull requests coming from forks (defaults to false)
pullRequestsFromForks: true
# add a check to pull requests (defaults to true)
addCheck: false
+13 -13
View File
@@ -1,6 +1,6 @@
# Contributing
We would ❤️ you to contribute to Appwrite and help make it better! We want contributing to Appwrite to be fun, enjoyable, and educational for anyone and everyone. All contributions are welcome, including issues, and new docs, as well as updates and tweaks, blog posts, workshops, and more.
We would :heart: you to contribute to Appwrite and help make it better! We want contributing to Appwrite to be fun, enjoyable, and educational for anyone and everyone. All contributions are welcome, including issues, and new docs, as well as updates and tweaks, blog posts, workshops, and more.
## Here for Hacktoberfest?
If you're here to contribute during Hacktoberfest, we're so happy to see you here. Appwrite has been a long-time participant of Hacktoberfest and we welcome you, whatever your experience level. This year, we're **only taking contributions for issues tagged** `hacktoberfest`, so we can focus our resources to support your contributions.
@@ -9,13 +9,13 @@ You can [find issues using this query](https://github.com/search?q=org%3Aappwrit
## How to Start?
If you are worried or dont know where to start, check out the next section that explains what kind of help we could use and where you can get involved. You can send your questions to [@appwrite](https://twitter.com/appwrite) on Twitter or to anyone from the [Appwrite team on Discord](https://appwrite.io/discord). You can also submit an issue, and a maintainer can guide you!
If you are worried or dont know where to start, check out the next section that explains what kind of help we could use and where you can get involved. You can send your questions to [@appwrite on Twitter](https://twitter.com/appwrite) or to anyone from the [Appwrite team on Discord](https://appwrite.io/discord). You can also submit an issue, and a maintainer can guide you!
## Code of Conduct
Help us keep Appwrite open and inclusive. Please read and follow our [Code of Conduct](https://github.com/appwrite/.github/blob/main/CODE_OF_CONDUCT.md).
## Submit a Pull Request 🚀
## Submit a Pull Request :rocket:
Branch naming convention is as following
@@ -65,13 +65,13 @@ Now, go a step further by running the linter using the following command to manu
composer lint <your file path>
```
This will give you a list of errors to rectify. If you need more information on the errors, you can pass in additional command line arguments to get more verbose information. More lists of available arguments can be found [here](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Usage). A very useful command line argument is `--report=diff`. This will give you the expected changes by the linter for easy fixing of formatting issues.
This will give you a list of errors to rectify. If you need more information on the errors, you can pass in additional command line arguments to get more verbose information. More lists of available arguments can be found [on PHP_Codesniffer usage Wiki](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Usage). A very useful command line argument is `--report=diff`. This will give you the expected changes by the linter for easy fixing of formatting issues.
```bash
composer lint --report=diff <your file path>
```
5. Push changes to GitHub.
5. Push changes to GitHub
```
$ git push origin [name_of_your_new_branch]
@@ -301,7 +301,7 @@ Adding a new dependency should have vital value for the product with minimum pos
## Introducing New Features
We would 💖 you to contribute to Appwrite, but we also want to ensure Appwrite is loyal to its vision and mission statement 🙏.
We would :sparkling_heart: you to contribute to Appwrite, but we also want to ensure Appwrite is loyal to its vision and mission statement :pray:.
For us to find the right balance, please open an issue explaining your ideas before introducing a new pull request.
@@ -367,7 +367,7 @@ In file `app/controllers/shared/api.php` On the database listener, add to an exi
```php
case $document->getCollection() === 'teams':
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_TEAMS, $value); // per project
break;
```
@@ -379,10 +379,10 @@ In that case you need also to handle children removal using addReduce() method c
```php
case $document->getCollection() === 'buckets': //buckets
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_BUCKETS, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForUsage
$queueForStatsUsage
->addReduce($document);
}
break;
@@ -428,16 +428,16 @@ public function __construct()
->inject('dbForProject')
->inject('queueForFunctions')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('queueForStatsUsage')
->inject('log')
->callback(fn (Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage, $log));
->callback(fn (Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForStatsUsage, $log));
}
```
and then trigger the queue with the new metric like so:
```php
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_BUILDS, 1)
->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0))
->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000)
@@ -640,7 +640,7 @@ Pull requests are great, but there are many other ways you can help Appwrite.
### Blogging & Speaking
Blogging, speaking about, or creating tutorials about one of Appwrites many features are great ways to get the word out about Appwrite. Mention [@appwrite](https://twitter.com/appwrite) on Twitter and/or [email team@appwrite.io](mailto:team@appwrite.io) so we can give pointers and tips and help you spread the word by promoting your content on the different Appwrite communication channels. Please add your blog posts and videos of talks to our [Awesome Appwrite](https://github.com/appwrite/awesome-appwrite) repo on GitHub.
Blogging, speaking about, or creating tutorials about one of Appwrites many features are great ways to get the word out about Appwrite. Mention [@appwrite on Twitter](https://twitter.com/appwrite) and/or [email team@appwrite.io](mailto:team@appwrite.io) so we can give pointers and tips and help you spread the word by promoting your content on the different Appwrite communication channels. Please add your blog posts and videos of talks to our [Awesome Appwrite](https://github.com/appwrite/awesome-appwrite) repo on GitHub.
### Presenting at Meetups
+4 -3
View File
@@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \
--no-plugins --no-scripts --prefer-dist \
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
FROM appwrite/base:0.9.5 AS final
FROM appwrite/base:0.10.1 AS final
LABEL maintainer="team@appwrite.io"
@@ -85,8 +85,9 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/worker-messaging && \
chmod +x /usr/local/bin/worker-migrations && \
chmod +x /usr/local/bin/worker-webhooks && \
chmod +x /usr/local/bin/worker-usage && \
chmod +x /usr/local/bin/worker-usage-dump
chmod +x /usr/local/bin/worker-stats-usage && \
chmod +x /usr/local/bin/stats-resources && \
chmod +x /usr/local/bin/worker-stats-resources
# Letsencrypt Permissions
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
+61 -25
View File
@@ -1,8 +1,8 @@
> 好消息!Appwrite 云现已进入公开测试版!立即访问 cloud.appwrite.io 注册,体验无忧的托管服务。今天就加入我们的云端吧!☁️🎉
> 好消息!Appwrite 云现已进入公开测试版!立即访问 cloud.appwrite.io 注册,体验无忧的托管服务。今天就加入我们的云端吧!:cloud: :tada:
<br />
<p align="center">
<a href="https://appwrite.io" target="_blank"><img src="./public/images/banner.png" alt="Appwrite Logo"></a>
<a href="https://appwrite.io" target="_blank"><img src="./public/images/banner.png" alt="Appwrite banner with logo and slogan build like a team of hundreds""></a>
<br />
<br />
<b>适用于[Flutter/Vue/Angular/React/iOS/Android/* 等等平台 *]的完整后端服务</b>
@@ -36,6 +36,8 @@ Appwrite 可以提供给开发者用户验证,外部授权,用户数据读
内容:
- [开始](#开始)
- [安装](#安装)
- [Unix](#unix)
- [Windows](#windows)
@@ -54,6 +56,9 @@ Appwrite 可以提供给开发者用户验证,外部授权,用户数据读
- [订阅我们](#订阅我们)
- [版权说明](#版权说明)
## 开始
要轻松开始使用Appwrite,您可以[**免费注册Appwrite Cloud**](https://cloud.appwrite.io/)。在Appwrite Cloud公开测试版期间,您可以完全免费使用Appwrite,而且我们不会收集您的信用卡信息。
## 安装
Appwrite 的容器化服务器只需要一行指令就可以运行。您可以使用 docker-compose 在本地主机上运行 Appwrite,也可以在任何其他容器化工具(如 [Kubernetes](https://kubernetes.io/docs/home/)、[Docker Swarm](https://docs.docker.com/engine/swarm/) 或 [Rancher](https://rancher.com/docs/))上运行 Appwrite。
@@ -67,7 +72,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:1.6.1
appwrite/appwrite:1.6.2
```
### Windows
@@ -79,7 +84,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^
appwrite/appwrite:1.6.1
appwrite/appwrite:1.6.2
```
#### PowerShell
@@ -89,7 +94,7 @@ docker run -it --rm `
--volume /var/run/docker.sock:/var/run/docker.sock `
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
--entrypoint="install" `
appwrite/appwrite:1.6.1
appwrite/appwrite:1.6.2
```
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
@@ -98,7 +103,42 @@ docker run -it --rm `
### 从旧版本升级
如果您从旧版本升级 Appwrite 服务器,则应在设置完成后使用 Appwrite 迁移工具。有关这方面的更多信息,请查看 [安装文档](https://appwrite.io/docs/installation)。
如果您从旧版本升级 Appwrite 服务器,则应在设置完成后使用 Appwrite 迁移工具。有关这方面的更多信息,请查看 [安装文档](https://appwrite.io/docs/self-hosting)。
## 一键配置
除了在本地运行 Appwrite,您还可以使用预配置的设置启动 Appwrite。这样可以让您快速启动并运行 Appwrite,而无需在本地计算机上安装 Docker。
请从以下提供商中选择一个:
<table border="0">
<tr>
<td align="center" width="100" height="100">
<a href="https://marketplace.digitalocean.com/apps/appwrite">
<img width="50" height="39" src="public/images/integrations/digitalocean-logo.svg" alt="DigitalOcean Logo" />
<br /><sub><b>DigitalOcean</b></sub></a>
</a>
</td>
<td align="center" width="100" height="100">
<a href="https://gitpod.io/#https://github.com/appwrite/integration-for-gitpod">
<img width="50" height="39" src="public/images/integrations/gitpod-logo.svg" alt="Gitpod Logo" />
<br /><sub><b>Gitpod</b></sub></a>
</a>
</td>
<td align="center" width="100" height="100">
<a href="https://www.linode.com/marketplace/apps/appwrite/appwrite/">
<img width="50" height="39" src="public/images/integrations/akamai-logo.svg" alt="Akamai Logo" />
<br /><sub><b>Akamai Compute</b></sub></a>
</a>
</td>
<td align="center" width="100" height="100">
<a href="https://aws.amazon.com/marketplace/pp/prodview-2hiaeo2px4md6">
<img width="50" height="39" src="public/images/integrations/aws-logo.svg" alt="AWS Logo" />
<br /><sub><b>AWS Marketplace</b></sub></a>
</a>
</td>
</tr>
</table>
## 入门
@@ -146,29 +186,25 @@ docker run -it --rm `
以下是当前支持的平台和语言列表。如果您想帮助我们为您选择的平台添加支持,您可以访问我们的 [SDK 生成器](https://github.com/appwrite/sdk-generator) 项目并查看我们的 [贡献指南](https://github.com/appwrite/sdk-generator/blob/master/CONTRIBUTING.md)。
#### 客户端
- &nbsp; [Web](https://github.com/appwrite/sdk-for-web) (由 Appwrite 团队维护)
- &nbsp; [Flutter](https://github.com/appwrite/sdk-for-flutter) (由 Appwrite 团队维护)
- &nbsp; [Apple](https://github.com/appwrite/sdk-for-apple) (由 Appwrite 团队维护)
- ✅ &nbsp; [Android](https://github.com/appwrite/sdk-for-android) (由 Appwrite 团队维护)
- ✅ &nbsp; [React Native](https://github.com/appwrite/sdk-for-react-native) - **公测** (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [Web](https://github.com/appwrite/sdk-for-web) (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [Flutter](https://github.com/appwrite/sdk-for-flutter) (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [Apple](https://github.com/appwrite/sdk-for-apple) - **公测** (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [Android](https://github.com/appwrite/sdk-for-android) (由 Appwrite 团队维护)
#### 服务器
- &nbsp; [NodeJS](https://github.com/appwrite/sdk-for-node) (由 Appwrite 团队维护)
- &nbsp; [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwrite 团队维护)
- &nbsp; [Dart](https://github.com/appwrite/sdk-for-dart) (由 Appwrite 团队维护)
- &nbsp; [Deno](https://github.com/appwrite/sdk-for-deno) (由 Appwrite 团队维护)
- &nbsp; [Ruby](https://github.com/appwrite/sdk-for-ruby) (由 Appwrite 团队维护)
- &nbsp; [Python](https://github.com/appwrite/sdk-for-python) (由 Appwrite 团队维护)
- &nbsp; [Kotlin](https://github.com/appwrite/sdk-for-kotlin) (由 Appwrite 团队维护)
- &nbsp; [Swift](https://github.com/appwrite/sdk-for-swift) (由 Appwrite 团队维护)
- ✅ &nbsp; [.NET](https://github.com/appwrite/sdk-for-dotnet) - **公测** (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [NodeJS](https://github.com/appwrite/sdk-for-node) (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [Dart](https://github.com/appwrite/sdk-for-dart) - (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [Deno](https://github.com/appwrite/sdk-for-deno) - **公测** (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [Ruby](https://github.com/appwrite/sdk-for-ruby) (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [Python](https://github.com/appwrite/sdk-for-python) (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [Kotlin](https://github.com/appwrite/sdk-for-kotlin) - **公测** (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [Apple](https://github.com/appwrite/sdk-for-apple) - **公测** (由 Appwrite 团队维护)
* :white_check_mark: &nbsp; [.NET](https://github.com/appwrite/sdk-for-dotnet) - **公测** (由 Appwrite 团队维护)
#### 开发者社区
- ✅ &nbsp; [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (维护者 [Michael Gangolf](https://github.com/m1ga/))
- ✅ &nbsp; [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (维护者 [fenix-hub @GodotNuts](https://github.com/fenix-hub))
* :white_check_mark: &nbsp; [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (维护者 [Michael Gangolf](https://github.com/m1ga/))
* :white_check_mark: &nbsp; [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (维护者 [fenix-hub @GodotNuts](https://github.com/fenix-hub))
找不到需要的的 SDK? - 欢迎通过发起 PR 来帮助我们完善 Appwrite 的软件生态环境 [SDK 生成器](https://github.com/appwrite/sdk-generator)!
+40 -38
View File
@@ -1,8 +1,8 @@
> Appwrite Init has concluded! You can check out all the latest announcements [on our Init website](https://appwrite.io/init) 🚀
> [Get started with Appwrite](https://apwr.dev/appcloud)
<br />
<p align="center">
<a href="https://appwrite.io" target="_blank"><img src="./public/images/banner.png" alt="Appwrite Logo"></a>
<a href="https://appwrite.io" target="_blank"><img src="./public/images/banner.png" alt="Appwrite banner, with logo and text saying "Build Like a Team of Hundreds"></a>
<br />
<br />
<b>Appwrite is a backend platform for developing Web, Mobile, and Flutter applications. Built with the open source community and optimized for developer experience in the coding languages you love.</b>
@@ -12,11 +12,11 @@
<!-- [![Build Status](https://img.shields.io/travis/com/appwrite/appwrite?style=flat-square)](https://travis-ci.com/appwrite/appwrite) -->
[![We're Hiring](https://img.shields.io/static/v1?label=We're&message=Hiring&color=blue&style=flat-square)](https://appwrite.io/company/careers)
[![Hacktoberfest](https://img.shields.io/static/v1?label=hacktoberfest&message=ready&color=191120&style=flat-square)](https://hacktoberfest.appwrite.io)
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord?r=Github)
[![Build Status](https://img.shields.io/github/actions/workflow/status/appwrite/appwrite/tests.yml?branch=master&label=tests&style=flat-square)](https://github.com/appwrite/appwrite/actions)
[![X Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite)
[![We're Hiring label](https://img.shields.io/static/v1?label=We're&message=Hiring&color=blue&style=flat-square)](https://appwrite.io/company/careers)
[![Hacktoberfest label](https://img.shields.io/static/v1?label=hacktoberfest&message=ready&color=191120&style=flat-square)](https://hacktoberfest.appwrite.io)
[![Discord label](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord?r=Github)
[![Build Status label](https://img.shields.io/github/actions/workflow/status/appwrite/appwrite/tests.yml?branch=master&label=tests&style=flat-square)](https://github.com/appwrite/appwrite/actions)
[![X Account label](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite)
<!-- [![Docker Pulls](https://img.shields.io/docker/pulls/appwrite/appwrite?color=f02e65&style=flat-square)](https://hub.docker.com/r/appwrite/appwrite) -->
<!-- [![Translate](https://img.shields.io/badge/translate-f02e65?style=flat-square)](docs/tutorials/add-translations.md) -->
@@ -24,11 +24,9 @@
English | [简体中文](README-CN.md)
[**Announcing Appwrite Cloud Public Beta! Sign up today!**](https://cloud.appwrite.io)
Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps packaged as a set of Docker<nobr> microservices. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.
Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, and [more services](https://appwrite.io/docs).
Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and [more services](https://appwrite.io/docs).
<p align="center">
<br />
@@ -37,13 +35,14 @@ Using Appwrite, you can easily integrate your app with user authentication and m
<br />
</p>
![Appwrite](public/images/github.png)
![Appwrite project dashboard showing various Appwrite features](public/images/github.png)
Find out more at: [https://appwrite.io](https://appwrite.io)
Find out more at: [https://appwrite.io](https://appwrite.io).
Table of Contents:
- [Installation](#installation)
- [Getting Started](#getting-started)
- [Self-Hosting](#self-hosting)
- [Unix](#unix)
- [Windows](#windows)
- [CMD](#cmd)
@@ -51,7 +50,7 @@ Table of Contents:
- [Upgrade from an Older Version](#upgrade-from-an-older-version)
- [One-Click Setups](#one-click-setups)
- [Getting Started](#getting-started)
- [Services](#services)
- [Products](#products)
- [SDKs](#sdks)
- [Client](#client)
- [Server](#server)
@@ -62,11 +61,14 @@ Table of Contents:
- [Follow Us](#follow-us)
- [License](#license)
## Installation
## Getting Started
The easiest way to get started with Appwrite is by [signing up for Appwrite Cloud](https://cloud.appwrite.io/). While Appwrite Cloud is in public beta, you can build with Appwrite completely free, and we won't collect you credit card information.
## Self-Hosting
Appwrite is designed to run in a containerized environment. Running your server is as easy as running one command from your terminal. You can either run Appwrite on your localhost using docker-compose or on any other container orchestration tool, such as [Kubernetes](https://kubernetes.io/docs/home/), [Docker Swarm](https://docs.docker.com/engine/swarm/), or [Rancher](https://rancher.com/docs/).
The easiest way to start running your Appwrite server is by running our docker-compose file. Before running the installation command, make sure you have [Docker](https://www.docker.com/products/docker-desktop) installed on your machine:
Before running the installation command, make sure you have [Docker](https://www.docker.com/products/docker-desktop) installed on your machine:
### Unix
@@ -75,7 +77,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:1.6.1
appwrite/appwrite:1.6.2
```
### Windows
@@ -87,7 +89,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^
appwrite/appwrite:1.6.1
appwrite/appwrite:1.6.2
```
#### PowerShell
@@ -97,7 +99,7 @@ docker run -it --rm `
--volume /var/run/docker.sock:/var/run/docker.sock `
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
--entrypoint="install" `
appwrite/appwrite:1.6.1
appwrite/appwrite:1.6.2
```
Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation.
@@ -106,7 +108,7 @@ For advanced production and custom installation, check out our Docker [environme
### Upgrade from an Older Version
If you are upgrading your Appwrite server from an older version, you should use the Appwrite migration tool once your setup is completed. For more information regarding this, check out the [Installation Docs](https://appwrite.io/docs/installation).
If you are upgrading your Appwrite server from an older version, you should use the Appwrite migration tool once your setup is completed. For more information regarding this, check out the [Installation Docs](https://appwrite.io/docs/self-hosting).
## One-Click Setups
@@ -192,34 +194,34 @@ Below is a list of currently supported platforms and languages. If you would lik
#### Client
- &nbsp; [Web](https://github.com/appwrite/sdk-for-web) (Maintained by the Appwrite Team)
- &nbsp; [Flutter](https://github.com/appwrite/sdk-for-flutter) (Maintained by the Appwrite Team)
- &nbsp; [Apple](https://github.com/appwrite/sdk-for-apple) (Maintained by the Appwrite Team)
- &nbsp; [Android](https://github.com/appwrite/sdk-for-android) (Maintained by the Appwrite Team)
- &nbsp; [React Native](https://github.com/appwrite/sdk-for-react-native) - **Beta** (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [Web](https://github.com/appwrite/sdk-for-web) (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [Flutter](https://github.com/appwrite/sdk-for-flutter) (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [Apple](https://github.com/appwrite/sdk-for-apple) (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [Android](https://github.com/appwrite/sdk-for-android) (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [React Native](https://github.com/appwrite/sdk-for-react-native) - **Beta** (Maintained by the Appwrite Team)
#### Server
- &nbsp; [NodeJS](https://github.com/appwrite/sdk-for-node) (Maintained by the Appwrite Team)
- &nbsp; [PHP](https://github.com/appwrite/sdk-for-php) (Maintained by the Appwrite Team)
- &nbsp; [Dart](https://github.com/appwrite/sdk-for-dart) (Maintained by the Appwrite Team)
- &nbsp; [Deno](https://github.com/appwrite/sdk-for-deno) (Maintained by the Appwrite Team)
- &nbsp; [Ruby](https://github.com/appwrite/sdk-for-ruby) (Maintained by the Appwrite Team)
- &nbsp; [Python](https://github.com/appwrite/sdk-for-python) (Maintained by the Appwrite Team)
- &nbsp; [Kotlin](https://github.com/appwrite/sdk-for-kotlin) (Maintained by the Appwrite Team)
- &nbsp; [Swift](https://github.com/appwrite/sdk-for-swift) (Maintained by the Appwrite Team)
- &nbsp; [.NET](https://github.com/appwrite/sdk-for-dotnet) - **Beta** (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [NodeJS](https://github.com/appwrite/sdk-for-node) (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [PHP](https://github.com/appwrite/sdk-for-php) (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [Dart](https://github.com/appwrite/sdk-for-dart) (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [Deno](https://github.com/appwrite/sdk-for-deno) (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [Ruby](https://github.com/appwrite/sdk-for-ruby) (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [Python](https://github.com/appwrite/sdk-for-python) (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [Kotlin](https://github.com/appwrite/sdk-for-kotlin) (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [Swift](https://github.com/appwrite/sdk-for-swift) (Maintained by the Appwrite Team)
- :white_check_mark: &nbsp; [.NET](https://github.com/appwrite/sdk-for-dotnet) - **Beta** (Maintained by the Appwrite Team)
#### Community
- &nbsp; [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (Maintained by [Michael Gangolf](https://github.com/m1ga/))
- &nbsp; [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (Maintained by [fenix-hub @GodotNuts](https://github.com/fenix-hub))
- :white_check_mark: &nbsp; [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (Maintained by [Michael Gangolf](https://github.com/m1ga/))
- :white_check_mark: &nbsp; [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (Maintained by [fenix-hub @GodotNuts](https://github.com/fenix-hub))
Looking for more SDKs? - Help us by contributing a pull request to our [SDK Generator](https://github.com/appwrite/sdk-generator)!
## Architecture
![Appwrite Architecture](docs/specs/overview.drawio.svg)
![Appwrite Architecture showing how Appwrite is built and the services and tools it uses](docs/specs/overview.drawio.svg)
Appwrite uses a microservices architecture that was designed for easy scaling and delegation of responsibilities. In addition, Appwrite supports multiple APIs, such as REST, WebSocket, and GraphQL to allow you to interact with your resources by leveraging your existing knowledge and protocols of choice.
@@ -229,7 +231,7 @@ The Appwrite API layer was designed to be extremely fast by leveraging in-memory
All code contributions, including those of people having commit access, must go through a pull request and be approved by a core developer before being merged. This is to ensure a proper review of all the code.
We truly ❤️ pull requests! If you wish to help, you can learn more about how you can contribute to this project in the [contribution guide](CONTRIBUTING.md).
We truly :heart: pull requests! If you wish to help, you can learn more about how you can contribute to this project in the [contribution guide](CONTRIBUTING.md).
## Security
+75 -10
View File
@@ -5,8 +5,11 @@ require_once __DIR__ . '/init.php';
use Appwrite\Event\Certificate;
use Appwrite\Event\Delete;
use Appwrite\Event\Func;
use Appwrite\Event\StatsResources;
use Appwrite\Event\StatsUsage;
use Appwrite\Platform\Appwrite;
use Appwrite\Runtimes\Runtimes;
use Executor\Executor;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
use Utopia\CLI\CLI;
@@ -23,7 +26,7 @@ use Utopia\Queue\Publisher;
use Utopia\Registry\Registry;
use Utopia\System\System;
// overwriting runtimes to be architectur agnostic for CLI
// Overwriting runtimes to be architecture agnostic for CLI
Config::setParam('runtimes', (new Runtimes('v4'))->getAll(supported: false));
// require controllers after overwriting runtimes
@@ -41,8 +44,7 @@ CLI::setResource('cache', function ($pools) {
$adapters[] = $pools
->get($value)
->pop()
->getResource()
;
->getResource();
}
return new Cache(new Sharding($adapters));
@@ -97,6 +99,10 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) {
return $dbForPlatform;
}, ['pools', 'cache']);
CLI::setResource('console', function () {
return new Document(Config::getParam('console'));
}, []);
CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
@@ -160,6 +166,45 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
};
}, ['pools', 'dbForPlatform', 'cache']);
CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, $database) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant($project->getInternalId());
return $database;
}
$dbAdapter = $pools
->get('logs')
->pop()
->getResource();
$database = new Database(
$dbAdapter,
$cache
);
$database
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_TASK)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
// set tenant
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant($project->getInternalId());
}
return $database;
};
}, ['pools', 'cache']);
CLI::setResource('queueForStatsUsage', function (Connection $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
CLI::setResource('queueForStatsResources', function (Publisher $publisher) {
return new StatsResources($publisher);
}, ['publisher']);
CLI::setResource('publisher', function (Group $pools) {
return $pools->get('publisher')->pop()->getResource();
}, ['pools']);
@@ -174,6 +219,13 @@ CLI::setResource('queueForCertificates', function (Publisher $publisher) {
}, ['publisher']);
CLI::setResource('logError', function (Registry $register) {
return function (Throwable $error, string $namespace, string $action) use ($register) {
Console::error('[Error] Timestamp: ' . date('c', time()));
Console::error('[Error] Type: ' . get_class($error));
Console::error('[Error] Message: ' . $error->getMessage());
Console::error('[Error] File: ' . $error->getFile());
Console::error('[Error] Line: ' . $error->getLine());
Console::error('[Error] Trace: ' . $error->getTraceAsString());
$logger = $register->get('logger');
if ($logger) {
@@ -192,6 +244,7 @@ CLI::setResource('logError', function (Registry $register) {
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('detailedTrace', $error->getTrace());
$log->setAction($action);
@@ -205,22 +258,34 @@ CLI::setResource('logError', function (Registry $register) {
Console::error('Error pushing log: ' . $th->getMessage());
}
}
Console::warning("Failed: {$error->getMessage()}");
Console::warning($error->getTraceAsString());
};
}, ['register']);
$platform = new Appwrite();
$platform->init(Service::TYPE_TASK);
CLI::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
$platform = new Appwrite();
$args = $platform->getEnv('argv');
if (!isset($args[0])) {
Console::error('Missing task name');
Console::exit(1);
}
\array_shift($args);
$taskName = $args[0];
$platform->init(Service::TYPE_TASK);
$cli = $platform->getCli();
$cli
->error()
->inject('error')
->action(function (Throwable $error) {
Console::error($error->getMessage());
->inject('logError')
->action(function (Throwable $error, callable $logError) use ($taskName) {
call_user_func_array($logError, [
$error,
'Task',
$taskName,
]);
});
$cli->run();
+2 -1
View File
@@ -16,5 +16,6 @@ return [
'union-china-pay' => ['name' => 'Union China Pay', 'path' => __DIR__ . '/credit-cards/union-china-pay.png'],
'visa' => ['name' => 'Visa', 'path' => __DIR__ . '/credit-cards/visa.png'],
'mir' => ['name' => 'MIR', 'path' => __DIR__ . '/credit-cards/mir.png'],
'maestro' => ['name' => 'Maestro', 'path' => __DIR__ . '/credit-cards/maestro.png']
'maestro' => ['name' => 'Maestro', 'path' => __DIR__ . '/credit-cards/maestro.png'],
'rupay' => ['name' => 'Rupay', 'path' => __DIR__ . '/credit-cards/rupay.png']
];
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

+2
View File
@@ -5,6 +5,7 @@ $common = include __DIR__ . '/collections/common.php';
$projects = include __DIR__ . '/collections/projects.php';
$databases = include __DIR__ . '/collections/databases.php';
$platform = include __DIR__ . '/collections/platform.php';
$logs = include __DIR__ . '/collections/logs.php';
// see - http.php#245
// $collections['buckets']['files'];
@@ -27,6 +28,7 @@ $collections = [
'databases' => $databases,
'projects' => array_merge($projects, $common),
'console' => array_merge($platform, $common),
'logs' => $logs,
];
return $collections;
+3 -3
View File
@@ -1038,7 +1038,7 @@ return [
'$id' => ID::custom('providerUid'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 2048,
'size' => 2048, // Decrease to 128 as in index length?
'signed' => true,
'required' => false,
'default' => null,
@@ -1107,14 +1107,14 @@ return [
'$id' => ID::custom('_key_userInternalId_provider_providerUid'),
'type' => Database::INDEX_UNIQUE,
'attributes' => ['userInternalId', 'provider', 'providerUid'],
'lengths' => [11, 128, 128],
'lengths' => [11, 128, 128], // providerUid is length 2000!
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_provider_providerUid'),
'type' => Database::INDEX_UNIQUE,
'attributes' => ['provider', 'providerUid'],
'lengths' => [128, 128],
'lengths' => [128, 128], // providerUid is length 2000!
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
],
[
+94
View File
@@ -0,0 +1,94 @@
<?php
use Utopia\Database\Database;
use Utopia\Database\Helpers\ID;
$logsCollection = [];
$logsCollection['stats'] = [
'$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('stats'),
'name' => 'stats',
'attributes' => [
[
'$id' => ID::custom('metric'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 255,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('region'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 255,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('value'),
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 8,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('time'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('period'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 4,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
'$id' => ID::custom('_key_time'),
'type' => Database::INDEX_KEY,
'attributes' => ['time'],
'lengths' => [],
'orders' => [Database::ORDER_DESC],
],
[
'$id' => ID::custom('_key_period_time'),
'type' => Database::INDEX_KEY,
'attributes' => ['period', 'time'],
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_metric_period_time'),
'type' => Database::INDEX_UNIQUE,
'attributes' => ['metric', 'period', 'time'],
'lengths' => [],
'orders' => [Database::ORDER_DESC],
],
],
];
return $logsCollection;
+70 -1
View File
@@ -1066,7 +1066,29 @@ return [
'default' => null,
'array' => false,
'filters' => [],
]
],
[
'$id' => ID::custom('owner'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16,
'signed' => true,
'required' => false,
'default' => '', // "Appwrite" or empty string
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('region'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
@@ -1111,6 +1133,20 @@ return [
'lengths' => [],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_owner'),
'type' => Database::INDEX_KEY,
'attributes' => ['owner'],
'lengths' => [16],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_region'),
'type' => Database::INDEX_KEY,
'attributes' => ['region'],
'lengths' => [16],
'orders' => [Database::ORDER_ASC],
],
],
],
@@ -1185,6 +1221,39 @@ return [
'default' => false,
'array' => false,
],
[
'$id' => ID::custom('personalAccessToken'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 256,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['encrypt'],
],
[
'$id' => ID::custom('personalAccessTokenExpiry'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('personalRefreshToken'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 256,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['encrypt'],
],
],
'indexes' => [
+53
View File
@@ -0,0 +1,53 @@
<?php
/**
* Initializes console project document.
*/
use Appwrite\Auth\Auth;
use Appwrite\Network\Validator\Origin;
use Utopia\Database\Helpers\ID;
use Utopia\System\System;
$console = [
'$id' => ID::custom('console'),
'$internalId' => ID::custom('console'),
'name' => 'Appwrite',
'$collection' => ID::custom('projects'),
'description' => 'Appwrite core engine',
'logo' => '',
'teamId' => null,
'webhooks' => [],
'keys' => [],
'platforms' => [
[
'$collection' => ID::custom('platforms'),
'name' => 'Localhost',
'type' => Origin::CLIENT_TYPE_WEB,
'hostname' => 'localhost',
], // Current host is added on app init
],
'region' => 'fra',
'legalName' => '',
'legalCountry' => '',
'legalState' => '',
'legalCity' => '',
'legalAddress' => '',
'legalTaxId' => '',
'auths' => [
'mockNumbers' => [],
'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled',
'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled'
],
'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
'oAuthProviders' => [
'githubEnabled' => true,
'githubSecret' => System::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''),
'githubAppid' => System::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '')
],
];
return $console;
+6 -1
View File
@@ -356,7 +356,7 @@ return [
],
Exception::TEAM_INVALID_SECRET => [
'name' => Exception::TEAM_INVALID_SECRET,
'description' => 'The team invitation secret is invalid. Please request a new invitation and try again.',
'description' => 'The team invitation secret is invalid. Please request a new invitation and try again.',
'code' => 401,
],
Exception::TEAM_MEMBERSHIP_MISMATCH => [
@@ -593,6 +593,11 @@ return [
'description' => 'Database timed out. Try adjusting your queries or adding an index.',
'code' => 408
],
Exception::DATABASE_QUERY_ORDER_NULL => [
'name' => Exception::DATABASE_QUERY_ORDER_NULL,
'description' => 'The order attribute had a null value. Cursor pagination requires all documents order attribute values are non-null.',
'code' => 400,
],
/** Collections */
Exception::COLLECTION_NOT_FOUND => [
+42 -7
View File
@@ -1,11 +1,46 @@
<?php
/**
* Continent codes with names and approximate central coordinates
*
* Coordinates represent approximate geographical centers of each continent
* Note: These are simplified centroids and may not represent the exact geographical center
*/
return [
'AF',
'AN',
'AS',
'EU',
'NA',
'OC',
'SA',
'AF' => [
'name' => 'Africa',
'latitude' => 8.7832,
'longitude' => 34.5085
],
'AN' => [
'name' => 'Antarctica',
'latitude' => -82.8628,
'longitude' => 135.0000
],
'AS' => [
'name' => 'Asia',
'latitude' => 34.0479,
'longitude' => 100.6197
],
'EU' => [
'name' => 'Europe',
'latitude' => 54.5260,
'longitude' => 15.2551
],
'NA' => [
'name' => 'North America',
'latitude' => 54.5260,
'longitude' => -105.2551
],
'OC' => [
'name' => 'Oceania',
'latitude' => -22.7359,
'longitude' => 140.0188
],
'SA' => [
'name' => 'South America',
'latitude' => -8.7832,
'longitude' => -55.4915
],
];
+199 -198
View File
@@ -1,209 +1,210 @@
<?php
/**
* ISO 3166 standard country codes
* ISO 3166 standard country codes with country names and coordinates
* https://www.iso.org/iso-3166-country-codes.html
*
* Source:
* https://www.iso.org/obp/ui/#search/code/
* Coordinates source: Natural Earth Data (approximate country centroids)
*/
return [
'AF',
'AO',
'AL',
'AD',
'AE',
'AR',
'AM',
'AG',
'AU',
'AT',
'AZ',
'BI',
'BE',
'BJ',
'BF',
'BD',
'BG',
'BH',
'BS',
'BA',
'BY',
'BZ',
'BO',
'BR',
'BB',
'BN',
'BT',
'BW',
'CF',
'CA',
'CH',
'CL',
'CN',
'CI',
'CM',
'CD',
'CG',
'CO',
'KM',
'CV',
'CR',
'CU',
'CY',
'CZ',
'DE',
'DJ',
'DM',
'DK',
'DO',
'DZ',
'EC',
'EG',
'ER',
'ES',
'EE',
'ET',
'FI',
'FJ',
'FR',
'FM',
'GA',
'GB',
'GE',
'GH',
'GN',
'GM',
'GW',
'GQ',
'GR',
'GD',
'GT',
'GY',
'HK',
'HN',
'HR',
'HT',
'HU',
'ID',
'IN',
'IE',
'IR',
'IQ',
'IS',
'IL',
'IT',
'JM',
'JO',
'JP',
'KZ',
'KE',
'KG',
'KH',
'KI',
'KN',
'KR',
'KW',
'LA',
'LB',
'LR',
'LY',
'LC',
'LI',
'LK',
'LS',
'LT',
'LU',
'LV',
'MA',
'MC',
'MD',
'MG',
'MV',
'MX',
'MH',
'MK',
'ML',
'MT',
'MM',
'ME',
'MN',
'MZ',
'MR',
'MU',
'MW',
'MY',
'NA',
'NE',
'NG',
'NI',
'NL',
'NO',
'NP',
'NR',
'NZ',
'OM',
'PK',
'PS',
'PA',
'PE',
'PH',
'PW',
'PG',
'PL',
'KP',
'PT',
'PY',
'QA',
'RO',
'RU',
'RW',
'SA',
'SD',
'SN',
'SG',
'SB',
'SL',
'SV',
'SM',
'SO',
'RS',
'SS',
'ST',
'SR',
'SK',
'SI',
'SE',
'SZ',
'SC',
'SY',
'TD',
'TG',
'TH',
'TJ',
'TM',
'TL',
'TO',
'TT',
'TN',
'TR',
'TV',
'TZ',
'TW',
'UG',
'UA',
'UY',
'US',
'UZ',
'VA',
'VC',
'VE',
'VN',
'VU',
'WS',
'YE',
'ZA',
'ZM',
'ZW',
'AF' => ['name' => 'Afghanistan', 'latitude' => 33.0, 'longitude' => 66.0],
'AO' => ['name' => 'Angola', 'latitude' => -12.5, 'longitude' => 18.5],
'AL' => ['name' => 'Albania', 'latitude' => 41.0, 'longitude' => 20.0],
'AD' => ['name' => 'Andorra', 'latitude' => 42.5, 'longitude' => 1.6],
'AE' => ['name' => 'United Arab Emirates', 'latitude' => 24.0, 'longitude' => 54.0],
'AR' => ['name' => 'Argentina', 'latitude' => -34.0, 'longitude' => -64.0],
'AM' => ['name' => 'Armenia', 'latitude' => 40.0, 'longitude' => 45.0],
'AG' => ['name' => 'Antigua and Barbuda', 'latitude' => 17.05, 'longitude' => -61.8],
'AU' => ['name' => 'Australia', 'latitude' => -25.0, 'longitude' => 135.0],
'AT' => ['name' => 'Austria', 'latitude' => 47.3, 'longitude' => 13.3],
'AZ' => ['name' => 'Azerbaijan', 'latitude' => 40.5, 'longitude' => 47.5],
'BI' => ['name' => 'Burundi', 'latitude' => -3.5, 'longitude' => 30.0],
'BE' => ['name' => 'Belgium', 'latitude' => 50.8, 'longitude' => 4.0],
'BJ' => ['name' => 'Benin', 'latitude' => 9.5, 'longitude' => 2.25],
'BF' => ['name' => 'Burkina Faso', 'latitude' => 13.0, 'longitude' => -2.0],
'BD' => ['name' => 'Bangladesh', 'latitude' => 24.0, 'longitude' => 90.0],
'BG' => ['name' => 'Bulgaria', 'latitude' => 43.0, 'longitude' => 25.0],
'BH' => ['name' => 'Bahrain', 'latitude' => 26.0, 'longitude' => 50.5],
'BS' => ['name' => 'Bahamas', 'latitude' => 24.25, 'longitude' => -76.0],
'BA' => ['name' => 'Bosnia and Herzegovina', 'latitude' => 44.0, 'longitude' => 18.0],
'BY' => ['name' => 'Belarus', 'latitude' => 53.0, 'longitude' => 28.0],
'BZ' => ['name' => 'Belize', 'latitude' => 17.25, 'longitude' => -88.75],
'BO' => ['name' => 'Bolivia', 'latitude' => -17.0, 'longitude' => -65.0],
'BR' => ['name' => 'Brazil', 'latitude' => -10.0, 'longitude' => -55.0],
'BB' => ['name' => 'Barbados', 'latitude' => 13.17, 'longitude' => -59.53],
'BN' => ['name' => 'Brunei', 'latitude' => 4.5, 'longitude' => 114.67],
'BT' => ['name' => 'Bhutan', 'latitude' => 27.5, 'longitude' => 90.5],
'BW' => ['name' => 'Botswana', 'latitude' => -22.0, 'longitude' => 24.0],
'CF' => ['name' => 'Central African Republic', 'latitude' => 7.0, 'longitude' => 21.0],
'CA' => ['name' => 'Canada', 'latitude' => 60.0, 'longitude' => -95.0],
'CH' => ['name' => 'Switzerland', 'latitude' => 47.0, 'longitude' => 8.0],
'CL' => ['name' => 'Chile', 'latitude' => -30.0, 'longitude' => -71.0],
'CN' => ['name' => 'China', 'latitude' => 35.0, 'longitude' => 105.0],
'CI' => ['name' => 'Côte d\'Ivoire', 'latitude' => 8.0, 'longitude' => -5.0],
'CM' => ['name' => 'Cameroon', 'latitude' => 6.0, 'longitude' => 12.0],
'CD' => ['name' => 'Democratic Republic of the Congo', 'latitude' => -2.5, 'longitude' => 23.5],
'CG' => ['name' => 'Republic of the Congo', 'latitude' => -1.0, 'longitude' => 15.0],
'CO' => ['name' => 'Colombia', 'latitude' => 4.0, 'longitude' => -72.0],
'KM' => ['name' => 'Comoros', 'latitude' => -12.17, 'longitude' => 44.25],
'CV' => ['name' => 'Cape Verde', 'latitude' => 16.0, 'longitude' => -24.0],
'CR' => ['name' => 'Costa Rica', 'latitude' => 10.0, 'longitude' => -84.0],
'CU' => ['name' => 'Cuba', 'latitude' => 21.5, 'longitude' => -80.0],
'CY' => ['name' => 'Cyprus', 'latitude' => 35.0, 'longitude' => 33.0],
'CZ' => ['name' => 'Czech Republic', 'latitude' => 49.75, 'longitude' => 15.5],
'DE' => ['name' => 'Germany', 'latitude' => 51.0, 'longitude' => 9.0],
'DJ' => ['name' => 'Djibouti', 'latitude' => 11.5, 'longitude' => 43.0],
'DM' => ['name' => 'Dominica', 'latitude' => 15.42, 'longitude' => -61.33],
'DK' => ['name' => 'Denmark', 'latitude' => 56.0, 'longitude' => 10.0],
'DO' => ['name' => 'Dominican Republic', 'latitude' => 19.0, 'longitude' => -70.67],
'DZ' => ['name' => 'Algeria', 'latitude' => 28.0, 'longitude' => 3.0],
'EC' => ['name' => 'Ecuador', 'latitude' => -2.0, 'longitude' => -77.5],
'EG' => ['name' => 'Egypt', 'latitude' => 27.0, 'longitude' => 30.0],
'ER' => ['name' => 'Eritrea', 'latitude' => 15.0, 'longitude' => 39.0],
'ES' => ['name' => 'Spain', 'latitude' => 40.0, 'longitude' => -4.0],
'EE' => ['name' => 'Estonia', 'latitude' => 59.0, 'longitude' => 26.0],
'ET' => ['name' => 'Ethiopia', 'latitude' => 8.0, 'longitude' => 38.0],
'FI' => ['name' => 'Finland', 'latitude' => 64.0, 'longitude' => 26.0],
'FJ' => ['name' => 'Fiji', 'latitude' => -18.0, 'longitude' => 175.0],
'FR' => ['name' => 'France', 'latitude' => 46.0, 'longitude' => 2.0],
'FM' => ['name' => 'Micronesia', 'latitude' => 6.92, 'longitude' => 158.25],
'GA' => ['name' => 'Gabon', 'latitude' => -1.0, 'longitude' => 11.75],
'GB' => ['name' => 'United Kingdom', 'latitude' => 54.0, 'longitude' => -2.0],
'GE' => ['name' => 'Georgia', 'latitude' => 42.0, 'longitude' => 43.5],
'GH' => ['name' => 'Ghana', 'latitude' => 8.0, 'longitude' => -2.0],
'GN' => ['name' => 'Guinea', 'latitude' => 11.0, 'longitude' => -10.0],
'GM' => ['name' => 'Gambia', 'latitude' => 13.47, 'longitude' => -16.57],
'GW' => ['name' => 'Guinea-Bissau', 'latitude' => 12.0, 'longitude' => -15.0],
'GQ' => ['name' => 'Equatorial Guinea', 'latitude' => 2.0, 'longitude' => 10.0],
'GR' => ['name' => 'Greece', 'latitude' => 39.0, 'longitude' => 22.0],
'GD' => ['name' => 'Grenada', 'latitude' => 12.12, 'longitude' => -61.67],
'GT' => ['name' => 'Guatemala', 'latitude' => 15.5, 'longitude' => -90.25],
'GY' => ['name' => 'Guyana', 'latitude' => 5.0, 'longitude' => -59.0],
'HK' => ['name' => 'Hong Kong', 'latitude' => 22.25, 'longitude' => 114.17],
'HN' => ['name' => 'Honduras', 'latitude' => 15.0, 'longitude' => -86.5],
'HR' => ['name' => 'Croatia', 'latitude' => 45.17, 'longitude' => 15.5],
'HT' => ['name' => 'Haiti', 'latitude' => 19.0, 'longitude' => -72.42],
'HU' => ['name' => 'Hungary', 'latitude' => 47.0, 'longitude' => 20.0],
'ID' => ['name' => 'Indonesia', 'latitude' => -5.0, 'longitude' => 120.0],
'IN' => ['name' => 'India', 'latitude' => 20.0, 'longitude' => 77.0],
'IE' => ['name' => 'Ireland', 'latitude' => 53.0, 'longitude' => -8.0],
'IR' => ['name' => 'Iran', 'latitude' => 32.0, 'longitude' => 53.0],
'IQ' => ['name' => 'Iraq', 'latitude' => 33.0, 'longitude' => 44.0],
'IS' => ['name' => 'Iceland', 'latitude' => 65.0, 'longitude' => -18.0],
'IL' => ['name' => 'Israel', 'latitude' => 31.5, 'longitude' => 34.75],
'IT' => ['name' => 'Italy', 'latitude' => 42.83, 'longitude' => 12.83],
'JM' => ['name' => 'Jamaica', 'latitude' => 18.25, 'longitude' => -77.5],
'JO' => ['name' => 'Jordan', 'latitude' => 31.0, 'longitude' => 36.0],
'JP' => ['name' => 'Japan', 'latitude' => 36.0, 'longitude' => 138.0],
'KZ' => ['name' => 'Kazakhstan', 'latitude' => 48.0, 'longitude' => 68.0],
'KE' => ['name' => 'Kenya', 'latitude' => 1.0, 'longitude' => 38.0],
'KG' => ['name' => 'Kyrgyzstan', 'latitude' => 41.0, 'longitude' => 75.0],
'KH' => ['name' => 'Cambodia', 'latitude' => 13.0, 'longitude' => 105.0],
'KI' => ['name' => 'Kiribati', 'latitude' => 1.42, 'longitude' => 173.0],
'KN' => ['name' => 'Saint Kitts and Nevis', 'latitude' => 17.33, 'longitude' => -62.75],
'KR' => ['name' => 'South Korea', 'latitude' => 37.0, 'longitude' => 127.5],
'KW' => ['name' => 'Kuwait', 'latitude' => 29.34, 'longitude' => 47.66],
'LA' => ['name' => 'Laos', 'latitude' => 18.0, 'longitude' => 105.0],
'LB' => ['name' => 'Lebanon', 'latitude' => 33.83, 'longitude' => 35.83],
'LR' => ['name' => 'Liberia', 'latitude' => 6.5, 'longitude' => -9.5],
'LY' => ['name' => 'Libya', 'latitude' => 25.0, 'longitude' => 17.0],
'LC' => ['name' => 'Saint Lucia', 'latitude' => 13.88, 'longitude' => -61.13],
'LI' => ['name' => 'Liechtenstein', 'latitude' => 47.17, 'longitude' => 9.53],
'LK' => ['name' => 'Sri Lanka', 'latitude' => 7.0, 'longitude' => 81.0],
'LS' => ['name' => 'Lesotho', 'latitude' => -29.5, 'longitude' => 28.5],
'LT' => ['name' => 'Lithuania', 'latitude' => 56.0, 'longitude' => 24.0],
'LU' => ['name' => 'Luxembourg', 'latitude' => 49.75, 'longitude' => 6.17],
'LV' => ['name' => 'latitudevia', 'latitude' => 57.0, 'longitude' => 25.0],
'MA' => ['name' => 'Morocco', 'latitude' => 32.0, 'longitude' => -5.0],
'MC' => ['name' => 'Monaco', 'latitude' => 43.73, 'longitude' => 7.4],
'MD' => ['name' => 'Moldova', 'latitude' => 47.0, 'longitude' => 29.0],
'MG' => ['name' => 'Madagascar', 'latitude' => -20.0, 'longitude' => 47.0],
'MV' => ['name' => 'Maldives', 'latitude' => 3.25, 'longitude' => 73.0],
'MX' => ['name' => 'Mexico', 'latitude' => 23.0, 'longitude' => -102.0],
'MH' => ['name' => 'Marshall Islands', 'latitude' => 9.0, 'longitude' => 168.0],
'MK' => ['name' => 'North Macedonia', 'latitude' => 41.83, 'longitude' => 22.0],
'ML' => ['name' => 'Mali', 'latitude' => 17.0, 'longitude' => -4.0],
'MT' => ['name' => 'Malta', 'latitude' => 35.83, 'longitude' => 14.58],
'MM' => ['name' => 'Myanmar', 'latitude' => 22.0, 'longitude' => 98.0],
'ME' => ['name' => 'Montenegro', 'latitude' => 42.5, 'longitude' => 19.3],
'MN' => ['name' => 'Mongolia', 'latitude' => 46.0, 'longitude' => 105.0],
'MZ' => ['name' => 'Mozambique', 'latitude' => -18.25, 'longitude' => 35.0],
'MR' => ['name' => 'Mauritania', 'latitude' => 20.0, 'longitude' => -12.0],
'MU' => ['name' => 'Mauritius', 'latitude' => -20.28, 'longitude' => 57.55],
'MW' => ['name' => 'Malawi', 'latitude' => -13.5, 'longitude' => 34.0],
'MY' => ['name' => 'Malaysia', 'latitude' => 2.5, 'longitude' => 112.5],
'NA' => ['name' => 'Namibia', 'latitude' => -22.0, 'longitude' => 17.0],
'NE' => ['name' => 'Niger', 'latitude' => 16.0, 'longitude' => 8.0],
'NG' => ['name' => 'Nigeria', 'latitude' => 10.0, 'longitude' => 8.0],
'NI' => ['name' => 'Nicaragua', 'latitude' => 13.0, 'longitude' => -85.0],
'NL' => ['name' => 'Netherlands', 'latitude' => 52.5, 'longitude' => 5.75],
'NO' => ['name' => 'Norway', 'latitude' => 62.0, 'longitude' => 10.0],
'NP' => ['name' => 'Nepal', 'latitude' => 28.0, 'longitude' => 84.0],
'NR' => ['name' => 'Nauru', 'latitude' => -0.53, 'longitude' => 166.92],
'NZ' => ['name' => 'New Zealand', 'latitude' => -41.0, 'longitude' => 174.0],
'OM' => ['name' => 'Oman', 'latitude' => 21.0, 'longitude' => 57.0],
'PK' => ['name' => 'Pakistan', 'latitude' => 30.0, 'longitude' => 70.0],
'PS' => ['name' => 'Palestine', 'latitude' => 31.9, 'longitude' => 35.2],
'PA' => ['name' => 'Panama', 'latitude' => 9.0, 'longitude' => -80.0],
'PE' => ['name' => 'Peru', 'latitude' => -10.0, 'longitude' => -76.0],
'PH' => ['name' => 'Philippines', 'latitude' => 13.0, 'longitude' => 122.0],
'PW' => ['name' => 'Palau', 'latitude' => 7.5, 'longitude' => 134.5],
'PG' => ['name' => 'Papua New Guinea', 'latitude' => -6.0, 'longitude' => 147.0],
'PL' => ['name' => 'Poland', 'latitude' => 52.0, 'longitude' => 20.0],
'KP' => ['name' => 'North Korea', 'latitude' => 40.0, 'longitude' => 127.0],
'PT' => ['name' => 'Portugal', 'latitude' => 39.5, 'longitude' => -8.0],
'PY' => ['name' => 'Paraguay', 'latitude' => -23.0, 'longitude' => -58.0],
'QA' => ['name' => 'Qatar', 'latitude' => 25.5, 'longitude' => 51.25],
'RO' => ['name' => 'Romania', 'latitude' => 46.0, 'longitude' => 25.0],
'RU' => ['name' => 'Russia', 'latitude' => 60.0, 'longitude' => 100.0],
'RW' => ['name' => 'Rwanda', 'latitude' => -2.0, 'longitude' => 30.0],
'SA' => ['name' => 'Saudi Arabia', 'latitude' => 25.0, 'longitude' => 45.0],
'SD' => ['name' => 'Sudan', 'latitude' => 15.0, 'longitude' => 30.0],
'SN' => ['name' => 'Senegal', 'latitude' => 14.0, 'longitude' => -14.0],
'SG' => ['name' => 'Singapore', 'latitude' => 1.37, 'longitude' => 103.8],
'SB' => ['name' => 'Solomon Islands', 'latitude' => -8.0, 'longitude' => 159.0],
'SL' => ['name' => 'Sierra Leone', 'latitude' => 8.5, 'longitude' => -11.5],
'SV' => ['name' => 'El Salvador', 'latitude' => 13.83, 'longitude' => -88.92],
'SM' => ['name' => 'San Marino', 'latitude' => 43.77, 'longitude' => 12.42],
'SO' => ['name' => 'Somalia', 'latitude' => 10.0, 'longitude' => 49.0],
'RS' => ['name' => 'Serbia', 'latitude' => 44.0, 'longitude' => 21.0],
'SS' => ['name' => 'South Sudan', 'latitude' => 8.0, 'longitude' => 30.0],
'ST' => ['name' => 'São Tomé and Príncipe', 'latitude' => 1.0, 'longitude' => 7.0],
'SR' => ['name' => 'Suriname', 'latitude' => 4.0, 'longitude' => -56.0],
'SK' => ['name' => 'Slovakia', 'latitude' => 48.67, 'longitude' => 19.5],
'SI' => ['name' => 'Slovenia', 'latitude' => 46.0, 'longitude' => 15.0],
'SE' => ['name' => 'Sweden', 'latitude' => 62.0, 'longitude' => 15.0],
'SZ' => ['name' => 'Eswatini', 'latitude' => -26.5, 'longitude' => 31.5],
'SC' => ['name' => 'Seychelles', 'latitude' => -4.58, 'longitude' => 55.67],
'SY' => ['name' => 'Syria', 'latitude' => 35.0, 'longitude' => 38.0],
'TD' => ['name' => 'Chad', 'latitude' => 15.0, 'longitude' => 19.0],
'TG' => ['name' => 'Togo', 'latitude' => 8.0, 'longitude' => 1.17],
'TH' => ['name' => 'Thailand', 'latitude' => 15.0, 'longitude' => 100.0],
'TJ' => ['name' => 'Tajikistan', 'latitude' => 39.0, 'longitude' => 71.0],
'TM' => ['name' => 'Turkmenistan', 'latitude' => 40.0, 'longitude' => 60.0],
'TL' => ['name' => 'Timor-Leste', 'latitude' => -8.83, 'longitude' => 125.92],
'TO' => ['name' => 'Tonga', 'latitude' => -20.0, 'longitude' => -175.0],
'TT' => ['name' => 'Trinidad and Tobago', 'latitude' => 11.0, 'longitude' => -61.0],
'TN' => ['name' => 'Tunisia', 'latitude' => 34.0, 'longitude' => 9.0],
'TR' => ['name' => 'Turkey', 'latitude' => 39.0, 'longitude' => 35.0],
'TV' => ['name' => 'Tuvalu', 'latitude' => -8.0, 'longitude' => 178.0],
'TZ' => ['name' => 'Tanzania', 'latitude' => -6.0, 'longitude' => 35.0],
'TW' => ['name' => 'Taiwan', 'latitude' => 23.5, 'longitude' => 121.0],
'UG' => ['name' => 'Uganda', 'latitude' => 1.0, 'longitude' => 32.0],
'UA' => ['name' => 'Ukraine', 'latitude' => 49.0, 'longitude' => 32.0],
'UY' => ['name' => 'Uruguay', 'latitude' => -33.0, 'longitude' => -56.0],
'US' => ['name' => 'United States', 'latitude' => 38.0, 'longitude' => -97.0],
'UZ' => ['name' => 'Uzbekistan', 'latitude' => 41.0, 'longitude' => 64.0],
'VA' => ['name' => 'Vatican City', 'latitude' => 41.9, 'longitude' => 12.45],
'VC' => ['name' => 'Saint Vincent and the Grenadines', 'latitude' => 13.25, 'longitude' => -61.2],
'VE' => ['name' => 'Venezuela', 'latitude' => 8.0, 'longitude' => -66.0],
'VN' => ['name' => 'Vietnam', 'latitude' => 16.0, 'longitude' => 106.0],
'VU' => ['name' => 'Vanuatu', 'latitude' => -16.0, 'longitude' => 167.0],
'WS' => ['name' => 'Samoa', 'latitude' => -13.58, 'longitude' => -172.33],
'YE' => ['name' => 'Yemen', 'latitude' => 15.0, 'longitude' => 48.0],
'ZA' => ['name' => 'South Africa', 'latitude' => -29.0, 'longitude' => 24.0],
'ZM' => ['name' => 'Zambia', 'latitude' => -15.0, 'longitude' => 30.0],
'ZW' => ['name' => 'Zimbabwe', 'latitude' => -20.0, 'longitude' => 30.0],
];
+10
View File
@@ -142,6 +142,16 @@ return [
'beta' => false,
'mock' => false,
],
'figma' => [
'name' => 'Figma',
'developers' => 'https://www.figma.com/developers/api#oauth2',
'icon' => 'icon-figma',
'enabled' => true,
'sandbox' => false,
'form' => false,
'beta' => false,
'mock' => false,
],
'github' => [
'name' => 'GitHub',
'developers' => 'https://developer.github.com/',
+16 -16
View File
@@ -11,7 +11,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '16.1.0',
'version' => '17.0.2',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -59,7 +59,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '13.1.1',
'version' => '15.0.1',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -77,7 +77,7 @@ return [
[
'key' => 'apple',
'name' => 'Apple',
'version' => '7.1.0',
'version' => '9.0.1',
'url' => 'https://github.com/appwrite/sdk-for-apple',
'package' => 'https://github.com/appwrite/sdk-for-apple',
'enabled' => true,
@@ -112,7 +112,7 @@ return [
[
'key' => 'android',
'name' => 'Android',
'version' => '6.1.0',
'version' => '7.0.1',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
'enabled' => true,
@@ -134,7 +134,7 @@ return [
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.6.0',
'version' => '0.7.3',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
@@ -217,7 +217,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '6.2.0',
'version' => '6.2.3',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -245,7 +245,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '14.2.0',
'version' => '16.0.0',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -263,7 +263,7 @@ return [
[
'key' => 'deno',
'name' => 'Deno',
'version' => '12.2.0',
'version' => '14.0.0',
'url' => 'https://github.com/appwrite/sdk-for-deno',
'package' => 'https://deno.land/x/appwrite',
'enabled' => true,
@@ -281,7 +281,7 @@ return [
[
'key' => 'php',
'name' => 'PHP',
'version' => '12.2.0',
'version' => '14.0.0',
'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true,
@@ -299,7 +299,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '6.2.0',
'version' => '10.0.0',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -317,7 +317,7 @@ return [
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '12.2.0',
'version' => '15.0.0',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@@ -335,7 +335,7 @@ return [
[
'key' => 'go',
'name' => 'Go',
'version' => '0.3.0',
'version' => '0.5.0',
'url' => 'https://github.com/appwrite/sdk-for-go',
'package' => 'https://github.com/appwrite/sdk-for-go',
'enabled' => true,
@@ -353,7 +353,7 @@ return [
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.11.0',
'version' => '0.12.0',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@@ -371,7 +371,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '12.2.0',
'version' => '15.0.0',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@@ -389,7 +389,7 @@ return [
[
'key' => 'kotlin',
'name' => 'Kotlin',
'version' => '6.2.0',
'version' => '8.0.0',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,
@@ -411,7 +411,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '6.2.0',
'version' => '9.0.0',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,
+1 -65
View File
@@ -3,72 +3,8 @@
return [
'default' => [
'$id' => 'default',
'name' => 'Frankfurt',
'name' => 'default',
'disabled' => false,
'flag' => 'de',
'default' => true,
],
'fra' => [
'$id' => 'fra',
'name' => 'Frankfurt',
'disabled' => false,
'flag' => 'de',
'default' => true,
],
'nyc' => [
'$id' => 'nyc',
'name' => 'New York',
'disabled' => true,
'flag' => 'us',
'default' => true,
],
'sfo' => [
'$id' => 'sfo',
'name' => 'San Francisco',
'disabled' => true,
'flag' => 'us',
'default' => true,
],
'blr' => [
'$id' => 'blr',
'name' => 'Bengaluru',
'disabled' => true,
'flag' => 'in',
'default' => true,
],
'lon' => [
'$id' => 'lon',
'name' => 'London',
'disabled' => true,
'flag' => 'gb',
'default' => true,
],
'ams' => [
'$id' => 'ams',
'name' => 'Amsterdam',
'disabled' => true,
'flag' => 'nl',
'default' => true,
],
'sgp' => [
'$id' => 'sgp',
'name' => 'Singapore',
'disabled' => true,
'flag' => 'sg',
'default' => true,
],
'tor' => [
'$id' => 'tor',
'name' => 'Toronto',
'disabled' => true,
'flag' => 'ca',
'default' => true,
],
'syd' => [
'$id' => 'syd',
'name' => 'Sydney',
'disabled' => true,
'flag' => 'au',
'default' => true,
],
];
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+143 -41
View File
@@ -1,7 +1,7 @@
{
"openapi": "3.0.0",
"info": {
"version": "1.6.1",
"version": "1.6.2",
"title": "Appwrite",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
@@ -18,6 +18,9 @@
"servers": [
{
"url": "https:\/\/cloud.appwrite.io\/v1"
},
{
"url": "https:\/\/<REGION>.cloud.appwrite.io\/v1"
}
],
"paths": {
@@ -43,6 +46,7 @@
},
"x-appwrite": {
"method": "get",
"group": "account",
"weight": 9,
"cookies": false,
"type": "",
@@ -91,6 +95,7 @@
},
"x-appwrite": {
"method": "create",
"group": "account",
"weight": 8,
"cookies": false,
"type": "",
@@ -175,6 +180,7 @@
},
"x-appwrite": {
"method": "updateEmail",
"group": "account",
"weight": 34,
"cookies": false,
"type": "",
@@ -250,6 +256,7 @@
},
"x-appwrite": {
"method": "listIdentities",
"group": "identities",
"weight": 57,
"cookies": false,
"type": "",
@@ -308,6 +315,7 @@
},
"x-appwrite": {
"method": "deleteIdentity",
"group": "identities",
"weight": 58,
"cookies": false,
"type": "",
@@ -370,6 +378,7 @@
},
"x-appwrite": {
"method": "createJWT",
"group": "tokens",
"weight": 29,
"cookies": false,
"type": "",
@@ -418,6 +427,7 @@
},
"x-appwrite": {
"method": "listLogs",
"group": "logs",
"weight": 31,
"cookies": false,
"type": "",
@@ -483,6 +493,7 @@
},
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 44,
"cookies": false,
"type": "",
@@ -552,6 +563,7 @@
},
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 46,
"cookies": false,
"type": "",
@@ -597,7 +609,7 @@
]
},
"put": {
"summary": "Verify authenticator",
"summary": "Update authenticator (confirmation)",
"operationId": "accountUpdateMfaAuthenticator",
"tags": [
"account"
@@ -617,6 +629,7 @@
},
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 47,
"cookies": false,
"type": "",
@@ -694,6 +707,7 @@
},
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 51,
"cookies": false,
"type": "",
@@ -761,6 +775,7 @@
},
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 52,
"cookies": false,
"type": "",
@@ -814,7 +829,7 @@
}
},
"put": {
"summary": "Create MFA challenge (confirmation)",
"summary": "Update MFA challenge (confirmation)",
"operationId": "accountUpdateMfaChallenge",
"tags": [
"account"
@@ -834,6 +849,7 @@
},
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 53,
"cookies": false,
"type": "",
@@ -909,6 +925,7 @@
},
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 45,
"cookies": false,
"type": "",
@@ -939,7 +956,7 @@
},
"\/account\/mfa\/recovery-codes": {
"get": {
"summary": "Get MFA recovery codes",
"summary": "List MFA recovery codes",
"operationId": "accountGetMfaRecoveryCodes",
"tags": [
"account"
@@ -959,6 +976,7 @@
},
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 50,
"cookies": false,
"type": "",
@@ -1007,6 +1025,7 @@
},
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 48,
"cookies": false,
"type": "",
@@ -1035,7 +1054,7 @@
]
},
"patch": {
"summary": "Regenerate MFA recovery codes",
"summary": "Update MFA recovery codes (regenerate)",
"operationId": "accountUpdateMfaRecoveryCodes",
"tags": [
"account"
@@ -1055,6 +1074,7 @@
},
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 49,
"cookies": false,
"type": "",
@@ -1105,6 +1125,7 @@
},
"x-appwrite": {
"method": "updateName",
"group": "account",
"weight": 32,
"cookies": false,
"type": "",
@@ -1174,6 +1195,7 @@
},
"x-appwrite": {
"method": "updatePassword",
"group": "account",
"weight": 33,
"cookies": false,
"type": "",
@@ -1248,6 +1270,7 @@
},
"x-appwrite": {
"method": "updatePhone",
"group": "account",
"weight": 35,
"cookies": false,
"type": "",
@@ -1323,6 +1346,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"group": "account",
"weight": 30,
"cookies": false,
"type": "",
@@ -1371,6 +1395,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"group": "account",
"weight": 36,
"cookies": false,
"type": "",
@@ -1440,6 +1465,7 @@
},
"x-appwrite": {
"method": "createRecovery",
"group": "recovery",
"weight": 38,
"cookies": false,
"type": "",
@@ -1496,7 +1522,7 @@
}
},
"put": {
"summary": "Create password recovery (confirmation)",
"summary": "Update password recovery (confirmation)",
"operationId": "accountUpdateRecovery",
"tags": [
"account"
@@ -1516,6 +1542,7 @@
},
"x-appwrite": {
"method": "updateRecovery",
"group": "recovery",
"weight": 39,
"cookies": false,
"type": "",
@@ -1597,6 +1624,7 @@
},
"x-appwrite": {
"method": "listSessions",
"group": "sessions",
"weight": 11,
"cookies": false,
"type": "",
@@ -1638,6 +1666,7 @@
},
"x-appwrite": {
"method": "deleteSessions",
"group": "sessions",
"weight": 12,
"cookies": false,
"type": "",
@@ -1688,6 +1717,7 @@
},
"x-appwrite": {
"method": "createAnonymousSession",
"group": "sessions",
"weight": 17,
"cookies": false,
"type": "",
@@ -1736,6 +1766,7 @@
},
"x-appwrite": {
"method": "createEmailPasswordSession",
"group": "sessions",
"weight": 16,
"cookies": false,
"type": "",
@@ -1809,6 +1840,7 @@
},
"x-appwrite": {
"method": "updateMagicURLSession",
"group": "sessions",
"weight": 26,
"cookies": false,
"type": "",
@@ -1875,6 +1907,7 @@
},
"x-appwrite": {
"method": "createOAuth2Session",
"group": "sessions",
"weight": 19,
"cookies": false,
"type": "webAuth",
@@ -1902,7 +1935,7 @@
"parameters": [
{
"name": "provider",
"description": "OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoho, zoom.",
"description": "OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoho, zoom.",
"required": true,
"schema": {
"type": "string",
@@ -1922,6 +1955,7 @@
"dropbox",
"etsy",
"facebook",
"figma",
"github",
"gitlab",
"google",
@@ -2015,6 +2049,7 @@
},
"x-appwrite": {
"method": "updatePhoneSession",
"group": "sessions",
"weight": 27,
"cookies": false,
"type": "",
@@ -2088,6 +2123,7 @@
},
"x-appwrite": {
"method": "createSession",
"group": "sessions",
"weight": 18,
"cookies": false,
"type": "",
@@ -2161,6 +2197,7 @@
},
"x-appwrite": {
"method": "getSession",
"group": "sessions",
"weight": 13,
"cookies": false,
"type": "",
@@ -2221,6 +2258,7 @@
},
"x-appwrite": {
"method": "updateSession",
"group": "sessions",
"weight": 15,
"cookies": false,
"type": "",
@@ -2274,6 +2312,7 @@
},
"x-appwrite": {
"method": "deleteSession",
"group": "sessions",
"weight": 14,
"cookies": false,
"type": "",
@@ -2336,6 +2375,7 @@
},
"x-appwrite": {
"method": "updateStatus",
"group": "account",
"weight": 37,
"cookies": false,
"type": "",
@@ -2386,6 +2426,7 @@
},
"x-appwrite": {
"method": "createPushTarget",
"group": "pushTargets",
"weight": 54,
"cookies": false,
"type": "",
@@ -2464,6 +2505,7 @@
},
"x-appwrite": {
"method": "updatePushTarget",
"group": "pushTargets",
"weight": 55,
"cookies": false,
"type": "",
@@ -2534,6 +2576,7 @@
},
"x-appwrite": {
"method": "deletePushTarget",
"group": "pushTargets",
"weight": 56,
"cookies": false,
"type": "",
@@ -2594,6 +2637,7 @@
},
"x-appwrite": {
"method": "createEmailToken",
"group": "tokens",
"weight": 25,
"cookies": false,
"type": "",
@@ -2672,6 +2716,7 @@
},
"x-appwrite": {
"method": "createMagicURLToken",
"group": "tokens",
"weight": 24,
"cookies": false,
"type": "",
@@ -2751,6 +2796,7 @@
},
"x-appwrite": {
"method": "createOAuth2Token",
"group": "tokens",
"weight": 23,
"cookies": false,
"type": "webAuth",
@@ -2778,7 +2824,7 @@
"parameters": [
{
"name": "provider",
"description": "OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoho, zoom.",
"description": "OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoho, zoom.",
"required": true,
"schema": {
"type": "string",
@@ -2798,6 +2844,7 @@
"dropbox",
"etsy",
"facebook",
"figma",
"github",
"gitlab",
"google",
@@ -2891,6 +2938,7 @@
},
"x-appwrite": {
"method": "createPhoneToken",
"group": "tokens",
"weight": 28,
"cookies": false,
"type": "",
@@ -2967,6 +3015,7 @@
},
"x-appwrite": {
"method": "createVerification",
"group": "verification",
"weight": 40,
"cookies": false,
"type": "",
@@ -3014,7 +3063,7 @@
}
},
"put": {
"summary": "Create email verification (confirmation)",
"summary": "Update email verification (confirmation)",
"operationId": "accountUpdateVerification",
"tags": [
"account"
@@ -3034,6 +3083,7 @@
},
"x-appwrite": {
"method": "updateVerification",
"group": "verification",
"weight": 41,
"cookies": false,
"type": "",
@@ -3109,6 +3159,7 @@
},
"x-appwrite": {
"method": "createPhoneVerification",
"group": "verification",
"weight": 42,
"cookies": false,
"type": "",
@@ -3160,6 +3211,7 @@
},
"x-appwrite": {
"method": "updatePhoneVerification",
"group": "verification",
"weight": 43,
"cookies": false,
"type": "",
@@ -3228,6 +3280,7 @@
},
"x-appwrite": {
"method": "getBrowser",
"group": null,
"weight": 60,
"cookies": false,
"type": "location",
@@ -3353,6 +3406,7 @@
},
"x-appwrite": {
"method": "getCreditCard",
"group": null,
"weight": 59,
"cookies": false,
"type": "location",
@@ -3383,7 +3437,7 @@
"parameters": [
{
"name": "code",
"description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.",
"description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.",
"required": true,
"schema": {
"type": "string",
@@ -3404,7 +3458,8 @@
"union-china-pay",
"visa",
"mir",
"maestro"
"maestro",
"rupay"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3423,7 +3478,8 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro"
"Maestro",
"Rupay"
]
},
"in": "path"
@@ -3482,6 +3538,7 @@
},
"x-appwrite": {
"method": "getFavicon",
"group": null,
"weight": 63,
"cookies": false,
"type": "location",
@@ -3539,6 +3596,7 @@
},
"x-appwrite": {
"method": "getFlag",
"group": null,
"weight": 61,
"cookies": false,
"type": "location",
@@ -4026,6 +4084,7 @@
},
"x-appwrite": {
"method": "getImage",
"group": null,
"weight": 62,
"cookies": false,
"type": "location",
@@ -4107,6 +4166,7 @@
},
"x-appwrite": {
"method": "getInitials",
"group": null,
"weight": 65,
"cookies": false,
"type": "location",
@@ -4198,6 +4258,7 @@
},
"x-appwrite": {
"method": "getQR",
"group": null,
"weight": 64,
"cookies": false,
"type": "location",
@@ -4296,6 +4357,7 @@
},
"x-appwrite": {
"method": "listDocuments",
"group": "documents",
"weight": 109,
"cookies": false,
"type": "",
@@ -4365,7 +4427,7 @@
"tags": [
"databases"
],
"description": "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#databasesCreateCollection) API or directly from your database console.",
"description": "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#databasesCreateCollection) API or directly from your database console.\n",
"responses": {
"201": {
"description": "Document",
@@ -4380,6 +4442,7 @@
},
"x-appwrite": {
"method": "createDocument",
"group": "documents",
"weight": 108,
"cookies": false,
"type": "",
@@ -4486,6 +4549,7 @@
},
"x-appwrite": {
"method": "getDocument",
"group": "documents",
"weight": 110,
"cookies": false,
"type": "",
@@ -4580,6 +4644,7 @@
},
"x-appwrite": {
"method": "updateDocument",
"group": "documents",
"weight": 112,
"cookies": false,
"type": "",
@@ -4678,6 +4743,7 @@
},
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 113,
"cookies": false,
"type": "",
@@ -4761,7 +4827,8 @@
},
"x-appwrite": {
"method": "listExecutions",
"weight": 306,
"group": "executions",
"weight": 305,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4846,7 +4913,8 @@
},
"x-appwrite": {
"method": "createExecution",
"weight": 305,
"group": "executions",
"weight": 304,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4960,7 +5028,8 @@
},
"x-appwrite": {
"method": "getExecution",
"weight": 307,
"group": "executions",
"weight": 306,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5033,7 +5102,8 @@
},
"x-appwrite": {
"method": "query",
"weight": 331,
"group": "graphql",
"weight": 330,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5084,7 +5154,8 @@
},
"x-appwrite": {
"method": "mutation",
"weight": 330,
"group": "graphql",
"weight": 329,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5135,6 +5206,7 @@
},
"x-appwrite": {
"method": "get",
"group": null,
"weight": 117,
"cookies": false,
"type": "",
@@ -5186,6 +5258,7 @@
},
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 118,
"cookies": false,
"type": "",
@@ -5237,6 +5310,7 @@
},
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 122,
"cookies": false,
"type": "",
@@ -5288,6 +5362,7 @@
},
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 119,
"cookies": false,
"type": "",
@@ -5339,6 +5414,7 @@
},
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 120,
"cookies": false,
"type": "",
@@ -5390,6 +5466,7 @@
},
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 121,
"cookies": false,
"type": "",
@@ -5441,6 +5518,7 @@
},
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 123,
"cookies": false,
"type": "",
@@ -5492,6 +5570,7 @@
},
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 124,
"cookies": false,
"type": "",
@@ -5543,7 +5622,8 @@
},
"x-appwrite": {
"method": "createSubscriber",
"weight": 376,
"group": "subscribers",
"weight": 375,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5625,7 +5705,8 @@
},
"x-appwrite": {
"method": "deleteSubscriber",
"weight": 380,
"group": "subscribers",
"weight": 379,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5699,7 +5780,8 @@
},
"x-appwrite": {
"method": "listFiles",
"weight": 208,
"group": "files",
"weight": 207,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5784,7 +5866,8 @@
},
"x-appwrite": {
"method": "createFile",
"weight": 207,
"group": "files",
"weight": 206,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -5881,7 +5964,8 @@
},
"x-appwrite": {
"method": "getFile",
"weight": 209,
"group": "files",
"weight": 208,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5952,7 +6036,8 @@
},
"x-appwrite": {
"method": "updateFile",
"weight": 214,
"group": "files",
"weight": 213,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6040,7 +6125,8 @@
},
"x-appwrite": {
"method": "deleteFile",
"weight": 215,
"group": "files",
"weight": 214,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6106,7 +6192,8 @@
},
"x-appwrite": {
"method": "getFileDownload",
"weight": 211,
"group": "files",
"weight": 210,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6172,7 +6259,8 @@
},
"x-appwrite": {
"method": "getFilePreview",
"weight": 210,
"group": "files",
"weight": 209,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6388,7 +6476,8 @@
},
"x-appwrite": {
"method": "getFileView",
"weight": 212,
"group": "files",
"weight": 211,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6461,7 +6550,8 @@
},
"x-appwrite": {
"method": "list",
"weight": 219,
"group": "teams",
"weight": 218,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6536,7 +6626,8 @@
},
"x-appwrite": {
"method": "create",
"weight": 218,
"group": "teams",
"weight": 217,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6620,7 +6711,8 @@
},
"x-appwrite": {
"method": "get",
"weight": 220,
"group": "teams",
"weight": 219,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6681,7 +6773,8 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 222,
"group": "teams",
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6754,7 +6847,8 @@
},
"x-appwrite": {
"method": "delete",
"weight": 224,
"group": "teams",
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6817,7 +6911,8 @@
},
"x-appwrite": {
"method": "listMemberships",
"weight": 226,
"group": "memberships",
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6902,7 +6997,8 @@
},
"x-appwrite": {
"method": "createMembership",
"weight": 225,
"group": "memberships",
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7012,7 +7108,8 @@
},
"x-appwrite": {
"method": "getMembership",
"weight": 227,
"group": "memberships",
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7083,7 +7180,8 @@
},
"x-appwrite": {
"method": "updateMembership",
"weight": 228,
"group": "memberships",
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7169,7 +7267,8 @@
},
"x-appwrite": {
"method": "deleteMembership",
"weight": 230,
"group": "memberships",
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7242,7 +7341,8 @@
},
"x-appwrite": {
"method": "updateMembershipStatus",
"weight": 229,
"group": "memberships",
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7339,7 +7439,8 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 221,
"group": "teams",
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7399,7 +7500,8 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 223,
"group": "teams",
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+143 -41
View File
@@ -1,7 +1,7 @@
{
"openapi": "3.0.0",
"info": {
"version": "1.6.1",
"version": "1.6.2",
"title": "Appwrite",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
@@ -18,6 +18,9 @@
"servers": [
{
"url": "https:\/\/cloud.appwrite.io\/v1"
},
{
"url": "https:\/\/<REGION>.cloud.appwrite.io\/v1"
}
],
"paths": {
@@ -43,6 +46,7 @@
},
"x-appwrite": {
"method": "get",
"group": "account",
"weight": 9,
"cookies": false,
"type": "",
@@ -91,6 +95,7 @@
},
"x-appwrite": {
"method": "create",
"group": "account",
"weight": 8,
"cookies": false,
"type": "",
@@ -175,6 +180,7 @@
},
"x-appwrite": {
"method": "updateEmail",
"group": "account",
"weight": 34,
"cookies": false,
"type": "",
@@ -250,6 +256,7 @@
},
"x-appwrite": {
"method": "listIdentities",
"group": "identities",
"weight": 57,
"cookies": false,
"type": "",
@@ -308,6 +315,7 @@
},
"x-appwrite": {
"method": "deleteIdentity",
"group": "identities",
"weight": 58,
"cookies": false,
"type": "",
@@ -370,6 +378,7 @@
},
"x-appwrite": {
"method": "createJWT",
"group": "tokens",
"weight": 29,
"cookies": false,
"type": "",
@@ -418,6 +427,7 @@
},
"x-appwrite": {
"method": "listLogs",
"group": "logs",
"weight": 31,
"cookies": false,
"type": "",
@@ -483,6 +493,7 @@
},
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 44,
"cookies": false,
"type": "",
@@ -552,6 +563,7 @@
},
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 46,
"cookies": false,
"type": "",
@@ -597,7 +609,7 @@
]
},
"put": {
"summary": "Verify authenticator",
"summary": "Update authenticator (confirmation)",
"operationId": "accountUpdateMfaAuthenticator",
"tags": [
"account"
@@ -617,6 +629,7 @@
},
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 47,
"cookies": false,
"type": "",
@@ -694,6 +707,7 @@
},
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 51,
"cookies": false,
"type": "",
@@ -761,6 +775,7 @@
},
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 52,
"cookies": false,
"type": "",
@@ -814,7 +829,7 @@
}
},
"put": {
"summary": "Create MFA challenge (confirmation)",
"summary": "Update MFA challenge (confirmation)",
"operationId": "accountUpdateMfaChallenge",
"tags": [
"account"
@@ -834,6 +849,7 @@
},
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 53,
"cookies": false,
"type": "",
@@ -909,6 +925,7 @@
},
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 45,
"cookies": false,
"type": "",
@@ -939,7 +956,7 @@
},
"\/account\/mfa\/recovery-codes": {
"get": {
"summary": "Get MFA recovery codes",
"summary": "List MFA recovery codes",
"operationId": "accountGetMfaRecoveryCodes",
"tags": [
"account"
@@ -959,6 +976,7 @@
},
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 50,
"cookies": false,
"type": "",
@@ -1007,6 +1025,7 @@
},
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 48,
"cookies": false,
"type": "",
@@ -1035,7 +1054,7 @@
]
},
"patch": {
"summary": "Regenerate MFA recovery codes",
"summary": "Update MFA recovery codes (regenerate)",
"operationId": "accountUpdateMfaRecoveryCodes",
"tags": [
"account"
@@ -1055,6 +1074,7 @@
},
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 49,
"cookies": false,
"type": "",
@@ -1105,6 +1125,7 @@
},
"x-appwrite": {
"method": "updateName",
"group": "account",
"weight": 32,
"cookies": false,
"type": "",
@@ -1174,6 +1195,7 @@
},
"x-appwrite": {
"method": "updatePassword",
"group": "account",
"weight": 33,
"cookies": false,
"type": "",
@@ -1248,6 +1270,7 @@
},
"x-appwrite": {
"method": "updatePhone",
"group": "account",
"weight": 35,
"cookies": false,
"type": "",
@@ -1323,6 +1346,7 @@
},
"x-appwrite": {
"method": "getPrefs",
"group": "account",
"weight": 30,
"cookies": false,
"type": "",
@@ -1371,6 +1395,7 @@
},
"x-appwrite": {
"method": "updatePrefs",
"group": "account",
"weight": 36,
"cookies": false,
"type": "",
@@ -1440,6 +1465,7 @@
},
"x-appwrite": {
"method": "createRecovery",
"group": "recovery",
"weight": 38,
"cookies": false,
"type": "",
@@ -1496,7 +1522,7 @@
}
},
"put": {
"summary": "Create password recovery (confirmation)",
"summary": "Update password recovery (confirmation)",
"operationId": "accountUpdateRecovery",
"tags": [
"account"
@@ -1516,6 +1542,7 @@
},
"x-appwrite": {
"method": "updateRecovery",
"group": "recovery",
"weight": 39,
"cookies": false,
"type": "",
@@ -1597,6 +1624,7 @@
},
"x-appwrite": {
"method": "listSessions",
"group": "sessions",
"weight": 11,
"cookies": false,
"type": "",
@@ -1638,6 +1666,7 @@
},
"x-appwrite": {
"method": "deleteSessions",
"group": "sessions",
"weight": 12,
"cookies": false,
"type": "",
@@ -1688,6 +1717,7 @@
},
"x-appwrite": {
"method": "createAnonymousSession",
"group": "sessions",
"weight": 17,
"cookies": false,
"type": "",
@@ -1736,6 +1766,7 @@
},
"x-appwrite": {
"method": "createEmailPasswordSession",
"group": "sessions",
"weight": 16,
"cookies": false,
"type": "",
@@ -1809,6 +1840,7 @@
},
"x-appwrite": {
"method": "updateMagicURLSession",
"group": "sessions",
"weight": 26,
"cookies": false,
"type": "",
@@ -1875,6 +1907,7 @@
},
"x-appwrite": {
"method": "createOAuth2Session",
"group": "sessions",
"weight": 19,
"cookies": false,
"type": "webAuth",
@@ -1902,7 +1935,7 @@
"parameters": [
{
"name": "provider",
"description": "OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoho, zoom.",
"description": "OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoho, zoom.",
"required": true,
"schema": {
"type": "string",
@@ -1922,6 +1955,7 @@
"dropbox",
"etsy",
"facebook",
"figma",
"github",
"gitlab",
"google",
@@ -2015,6 +2049,7 @@
},
"x-appwrite": {
"method": "updatePhoneSession",
"group": "sessions",
"weight": 27,
"cookies": false,
"type": "",
@@ -2088,6 +2123,7 @@
},
"x-appwrite": {
"method": "createSession",
"group": "sessions",
"weight": 18,
"cookies": false,
"type": "",
@@ -2161,6 +2197,7 @@
},
"x-appwrite": {
"method": "getSession",
"group": "sessions",
"weight": 13,
"cookies": false,
"type": "",
@@ -2221,6 +2258,7 @@
},
"x-appwrite": {
"method": "updateSession",
"group": "sessions",
"weight": 15,
"cookies": false,
"type": "",
@@ -2274,6 +2312,7 @@
},
"x-appwrite": {
"method": "deleteSession",
"group": "sessions",
"weight": 14,
"cookies": false,
"type": "",
@@ -2336,6 +2375,7 @@
},
"x-appwrite": {
"method": "updateStatus",
"group": "account",
"weight": 37,
"cookies": false,
"type": "",
@@ -2386,6 +2426,7 @@
},
"x-appwrite": {
"method": "createPushTarget",
"group": "pushTargets",
"weight": 54,
"cookies": false,
"type": "",
@@ -2464,6 +2505,7 @@
},
"x-appwrite": {
"method": "updatePushTarget",
"group": "pushTargets",
"weight": 55,
"cookies": false,
"type": "",
@@ -2534,6 +2576,7 @@
},
"x-appwrite": {
"method": "deletePushTarget",
"group": "pushTargets",
"weight": 56,
"cookies": false,
"type": "",
@@ -2594,6 +2637,7 @@
},
"x-appwrite": {
"method": "createEmailToken",
"group": "tokens",
"weight": 25,
"cookies": false,
"type": "",
@@ -2672,6 +2716,7 @@
},
"x-appwrite": {
"method": "createMagicURLToken",
"group": "tokens",
"weight": 24,
"cookies": false,
"type": "",
@@ -2751,6 +2796,7 @@
},
"x-appwrite": {
"method": "createOAuth2Token",
"group": "tokens",
"weight": 23,
"cookies": false,
"type": "webAuth",
@@ -2778,7 +2824,7 @@
"parameters": [
{
"name": "provider",
"description": "OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoho, zoom.",
"description": "OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, github, gitlab, google, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, yahoo, yammer, yandex, zoho, zoom.",
"required": true,
"schema": {
"type": "string",
@@ -2798,6 +2844,7 @@
"dropbox",
"etsy",
"facebook",
"figma",
"github",
"gitlab",
"google",
@@ -2891,6 +2938,7 @@
},
"x-appwrite": {
"method": "createPhoneToken",
"group": "tokens",
"weight": 28,
"cookies": false,
"type": "",
@@ -2967,6 +3015,7 @@
},
"x-appwrite": {
"method": "createVerification",
"group": "verification",
"weight": 40,
"cookies": false,
"type": "",
@@ -3014,7 +3063,7 @@
}
},
"put": {
"summary": "Create email verification (confirmation)",
"summary": "Update email verification (confirmation)",
"operationId": "accountUpdateVerification",
"tags": [
"account"
@@ -3034,6 +3083,7 @@
},
"x-appwrite": {
"method": "updateVerification",
"group": "verification",
"weight": 41,
"cookies": false,
"type": "",
@@ -3109,6 +3159,7 @@
},
"x-appwrite": {
"method": "createPhoneVerification",
"group": "verification",
"weight": 42,
"cookies": false,
"type": "",
@@ -3160,6 +3211,7 @@
},
"x-appwrite": {
"method": "updatePhoneVerification",
"group": "verification",
"weight": 43,
"cookies": false,
"type": "",
@@ -3228,6 +3280,7 @@
},
"x-appwrite": {
"method": "getBrowser",
"group": null,
"weight": 60,
"cookies": false,
"type": "location",
@@ -3353,6 +3406,7 @@
},
"x-appwrite": {
"method": "getCreditCard",
"group": null,
"weight": 59,
"cookies": false,
"type": "location",
@@ -3383,7 +3437,7 @@
"parameters": [
{
"name": "code",
"description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.",
"description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.",
"required": true,
"schema": {
"type": "string",
@@ -3404,7 +3458,8 @@
"union-china-pay",
"visa",
"mir",
"maestro"
"maestro",
"rupay"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3423,7 +3478,8 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro"
"Maestro",
"Rupay"
]
},
"in": "path"
@@ -3482,6 +3538,7 @@
},
"x-appwrite": {
"method": "getFavicon",
"group": null,
"weight": 63,
"cookies": false,
"type": "location",
@@ -3539,6 +3596,7 @@
},
"x-appwrite": {
"method": "getFlag",
"group": null,
"weight": 61,
"cookies": false,
"type": "location",
@@ -4026,6 +4084,7 @@
},
"x-appwrite": {
"method": "getImage",
"group": null,
"weight": 62,
"cookies": false,
"type": "location",
@@ -4107,6 +4166,7 @@
},
"x-appwrite": {
"method": "getInitials",
"group": null,
"weight": 65,
"cookies": false,
"type": "location",
@@ -4198,6 +4258,7 @@
},
"x-appwrite": {
"method": "getQR",
"group": null,
"weight": 64,
"cookies": false,
"type": "location",
@@ -4296,6 +4357,7 @@
},
"x-appwrite": {
"method": "listDocuments",
"group": "documents",
"weight": 109,
"cookies": false,
"type": "",
@@ -4365,7 +4427,7 @@
"tags": [
"databases"
],
"description": "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#databasesCreateCollection) API or directly from your database console.",
"description": "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#databasesCreateCollection) API or directly from your database console.\n",
"responses": {
"201": {
"description": "Document",
@@ -4380,6 +4442,7 @@
},
"x-appwrite": {
"method": "createDocument",
"group": "documents",
"weight": 108,
"cookies": false,
"type": "",
@@ -4486,6 +4549,7 @@
},
"x-appwrite": {
"method": "getDocument",
"group": "documents",
"weight": 110,
"cookies": false,
"type": "",
@@ -4580,6 +4644,7 @@
},
"x-appwrite": {
"method": "updateDocument",
"group": "documents",
"weight": 112,
"cookies": false,
"type": "",
@@ -4678,6 +4743,7 @@
},
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 113,
"cookies": false,
"type": "",
@@ -4761,7 +4827,8 @@
},
"x-appwrite": {
"method": "listExecutions",
"weight": 306,
"group": "executions",
"weight": 305,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4846,7 +4913,8 @@
},
"x-appwrite": {
"method": "createExecution",
"weight": 305,
"group": "executions",
"weight": 304,
"cookies": false,
"type": "",
"deprecated": false,
@@ -4960,7 +5028,8 @@
},
"x-appwrite": {
"method": "getExecution",
"weight": 307,
"group": "executions",
"weight": 306,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5033,7 +5102,8 @@
},
"x-appwrite": {
"method": "query",
"weight": 331,
"group": "graphql",
"weight": 330,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5084,7 +5154,8 @@
},
"x-appwrite": {
"method": "mutation",
"weight": 330,
"group": "graphql",
"weight": 329,
"cookies": false,
"type": "graphql",
"deprecated": false,
@@ -5135,6 +5206,7 @@
},
"x-appwrite": {
"method": "get",
"group": null,
"weight": 117,
"cookies": false,
"type": "",
@@ -5186,6 +5258,7 @@
},
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 118,
"cookies": false,
"type": "",
@@ -5237,6 +5310,7 @@
},
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 122,
"cookies": false,
"type": "",
@@ -5288,6 +5362,7 @@
},
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 119,
"cookies": false,
"type": "",
@@ -5339,6 +5414,7 @@
},
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 120,
"cookies": false,
"type": "",
@@ -5390,6 +5466,7 @@
},
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 121,
"cookies": false,
"type": "",
@@ -5441,6 +5518,7 @@
},
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 123,
"cookies": false,
"type": "",
@@ -5492,6 +5570,7 @@
},
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 124,
"cookies": false,
"type": "",
@@ -5543,7 +5622,8 @@
},
"x-appwrite": {
"method": "createSubscriber",
"weight": 376,
"group": "subscribers",
"weight": 375,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5625,7 +5705,8 @@
},
"x-appwrite": {
"method": "deleteSubscriber",
"weight": 380,
"group": "subscribers",
"weight": 379,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5699,7 +5780,8 @@
},
"x-appwrite": {
"method": "listFiles",
"weight": 208,
"group": "files",
"weight": 207,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5784,7 +5866,8 @@
},
"x-appwrite": {
"method": "createFile",
"weight": 207,
"group": "files",
"weight": 206,
"cookies": false,
"type": "upload",
"deprecated": false,
@@ -5881,7 +5964,8 @@
},
"x-appwrite": {
"method": "getFile",
"weight": 209,
"group": "files",
"weight": 208,
"cookies": false,
"type": "",
"deprecated": false,
@@ -5952,7 +6036,8 @@
},
"x-appwrite": {
"method": "updateFile",
"weight": 214,
"group": "files",
"weight": 213,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6040,7 +6125,8 @@
},
"x-appwrite": {
"method": "deleteFile",
"weight": 215,
"group": "files",
"weight": 214,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6106,7 +6192,8 @@
},
"x-appwrite": {
"method": "getFileDownload",
"weight": 211,
"group": "files",
"weight": 210,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6172,7 +6259,8 @@
},
"x-appwrite": {
"method": "getFilePreview",
"weight": 210,
"group": "files",
"weight": 209,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6388,7 +6476,8 @@
},
"x-appwrite": {
"method": "getFileView",
"weight": 212,
"group": "files",
"weight": 211,
"cookies": false,
"type": "location",
"deprecated": false,
@@ -6461,7 +6550,8 @@
},
"x-appwrite": {
"method": "list",
"weight": 219,
"group": "teams",
"weight": 218,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6536,7 +6626,8 @@
},
"x-appwrite": {
"method": "create",
"weight": 218,
"group": "teams",
"weight": 217,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6620,7 +6711,8 @@
},
"x-appwrite": {
"method": "get",
"weight": 220,
"group": "teams",
"weight": 219,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6681,7 +6773,8 @@
},
"x-appwrite": {
"method": "updateName",
"weight": 222,
"group": "teams",
"weight": 221,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6754,7 +6847,8 @@
},
"x-appwrite": {
"method": "delete",
"weight": 224,
"group": "teams",
"weight": 223,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6817,7 +6911,8 @@
},
"x-appwrite": {
"method": "listMemberships",
"weight": 226,
"group": "memberships",
"weight": 225,
"cookies": false,
"type": "",
"deprecated": false,
@@ -6902,7 +6997,8 @@
},
"x-appwrite": {
"method": "createMembership",
"weight": 225,
"group": "memberships",
"weight": 224,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7012,7 +7108,8 @@
},
"x-appwrite": {
"method": "getMembership",
"weight": 227,
"group": "memberships",
"weight": 226,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7083,7 +7180,8 @@
},
"x-appwrite": {
"method": "updateMembership",
"weight": 228,
"group": "memberships",
"weight": 227,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7169,7 +7267,8 @@
},
"x-appwrite": {
"method": "deleteMembership",
"weight": 230,
"group": "memberships",
"weight": 229,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7242,7 +7341,8 @@
},
"x-appwrite": {
"method": "updateMembershipStatus",
"weight": 229,
"group": "memberships",
"weight": 228,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7339,7 +7439,8 @@
},
"x-appwrite": {
"method": "getPrefs",
"weight": 221,
"group": "teams",
"weight": 220,
"cookies": false,
"type": "",
"deprecated": false,
@@ -7399,7 +7500,8 @@
},
"x-appwrite": {
"method": "updatePrefs",
"weight": 223,
"group": "teams",
"weight": 222,
"cookies": false,
"type": "",
"deprecated": false,
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
@@ -7,4 +7,5 @@ return [
"gif" => "image/gif",
"png" => "image/png",
"heic" => "image/heic",
"webp" => "image/webp",
];
+31 -5
View File
@@ -79,6 +79,15 @@ return [
'question' => 'Enter your Appwrite hostname',
'filter' => ''
],
[
'name' => '_APP_CUSTOM_DOMAIN_DENY_LIST',
'description' => 'List of reserved or prohibited domains when configuring custom domains.',
'introduction' => '',
'default' => 'example.com,test.com,app.example.com',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_DOMAIN_FUNCTIONS',
'description' => 'A domain to use for function preview URLs. Setting to empty turns off function preview URLs.',
@@ -572,7 +581,7 @@ return [
],
[
'name' => '_APP_STORAGE_S3_ACCESS_KEY',
'description' => 'AWS S3 storage access key. Required when the storage adapter is set to S3. You can get your access key from your AWS console',
'description' => 'S3 storage access key. Required when the storage adapter is set to S3. You can get your access key from your S3 storage provider',
'introduction' => '0.13.0',
'default' => '',
'required' => false,
@@ -580,7 +589,7 @@ return [
],
[
'name' => '_APP_STORAGE_S3_SECRET',
'description' => 'AWS S3 storage secret key. Required when the storage adapter is set to S3. You can get your secret key from your AWS console.',
'description' => 'S3 storage secret key. Required when the storage adapter is set to S3. You can get your secret key from your S3 storage provider.',
'introduction' => '0.13.0',
'default' => '',
'required' => false,
@@ -588,7 +597,7 @@ return [
],
[
'name' => '_APP_STORAGE_S3_REGION',
'description' => 'AWS S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from AWS console.',
'description' => 'S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from your S3 storage provider.',
'introduction' => '0.13.0',
'default' => 'us-east-1',
'required' => false,
@@ -596,12 +605,20 @@ return [
],
[
'name' => '_APP_STORAGE_S3_BUCKET',
'description' => 'AWS S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your AWS console.',
'description' => 'S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your S3 storage provider.',
'introduction' => '0.13.0',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_STORAGE_S3_ENDPOINT',
'description' => 'S3 storage endpoint. Required when using S3 storage providers other than AWS.',
'introduction' => '0.16.2',
'default' => '',
'required' => false,
'question' => '',
],
[
'name' => '_APP_STORAGE_DO_SPACES_ACCESS_KEY',
'description' => 'DigitalOcean spaces access key. Required when the storage adapter is set to DOSpaces. You can get your access key from your DigitalOcean console.',
@@ -1040,13 +1057,22 @@ return [
],
[
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT',
'description' => 'IThe maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days).',
'description' => 'The maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days).',
'introduction' => '0.7.0',
'default' => '1209600',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE',
'description' => 'The maximum duration (in seconds) upto which to retain console audit logs. The default value is 15778800 seconds (6 months).',
'introduction' => '1.6.2',
'default' => '15778800',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_MAINTENANCE_RETENTION_ABUSE',
'description' => 'The maximum duration (in seconds) upto which to retain abuse logs. The default value is 86400 seconds (1 day).',
+101 -117
View File
@@ -17,7 +17,7 @@ use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
use Appwrite\Event\Usage;
use Appwrite\Event\StatsUsage;
use Appwrite\Extend\Exception;
use Appwrite\Hooks\Hooks;
use Appwrite\Network\Validator\Email;
@@ -43,6 +43,7 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -155,9 +156,6 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails) {
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
/** @var Utopia\Database\Document $user */
$userFromRequest = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
@@ -273,7 +271,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('expire', $expire)
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $sessionSecret) : '')
->setAttribute('secret', Auth::encodeSession($user->getId(), $sessionSecret))
;
$response->dynamic($session, Response::MODEL_SESSION);
@@ -289,6 +287,7 @@ App::post('/v1/account')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'create',
description: '/docs/references/account/create.md',
auth: [],
@@ -432,6 +431,7 @@ App::get('/v1/account')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'get',
description: '/docs/references/account/get.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -461,6 +461,7 @@ App::delete('/v1/account')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'delete',
description: '/docs/references/account/delete.md',
auth: [AuthType::ADMIN],
@@ -513,6 +514,7 @@ App::get('/v1/account/sessions')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'listSessions',
description: '/docs/references/account/list-sessions.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -530,9 +532,6 @@ App::get('/v1/account/sessions')
->inject('project')
->action(function (Response $response, Document $user, Locale $locale, Document $project) {
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$sessions = $user->getAttribute('sessions', []);
$current = Auth::sessionVerify($sessions, Auth::$secret);
@@ -542,7 +541,7 @@ App::get('/v1/account/sessions')
$session->setAttribute('countryName', $countryName);
$session->setAttribute('current', ($current == $session->getId()) ? true : false);
$session->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $session->getAttribute('secret', '') : '');
$session->setAttribute('secret', $session->getAttribute('secret', ''));
$sessions[$key] = $session;
}
@@ -562,6 +561,7 @@ App::delete('/v1/account/sessions')
->label('audits.resource', 'user/{user.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'deleteSessions',
description: '/docs/references/account/delete-sessions.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -631,6 +631,7 @@ App::get('/v1/account/sessions/:sessionId')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'getSession',
description: '/docs/references/account/get-session.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -649,10 +650,6 @@ App::get('/v1/account/sessions/:sessionId')
->inject('project')
->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Document $project) {
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$sessions = $user->getAttribute('sessions', []);
$sessionId = ($sessionId === 'current')
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
@@ -665,7 +662,7 @@ App::get('/v1/account/sessions/:sessionId')
$session
->setAttribute('current', ($session->getAttribute('secret') == Auth::hash(Auth::$secret)))
->setAttribute('countryName', $countryName)
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $session->getAttribute('secret', '') : '')
->setAttribute('secret', $session->getAttribute('secret', ''))
;
return $response->dynamic($session, Response::MODEL_SESSION);
@@ -684,6 +681,7 @@ App::delete('/v1/account/sessions/:sessionId')
->label('audits.resource', 'user/{user.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'deleteSession',
description: '/docs/references/account/delete-session.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -772,6 +770,7 @@ App::patch('/v1/account/sessions/:sessionId')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'updateSession',
description: '/docs/references/account/update-session.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -856,6 +855,7 @@ App::post('/v1/account/sessions/email')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'createEmailPasswordSession',
description: '/docs/references/account/create-session-email-password.md',
auth: [],
@@ -897,10 +897,6 @@ App::post('/v1/account/sessions/email')
throw new Exception(Exception::USER_BLOCKED); // User is in status blocked
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$user->setAttributes($profile->getArrayCopy());
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]);
@@ -966,7 +962,7 @@ App::post('/v1/account/sessions/email')
$session
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '')
->setAttribute('secret', Auth::encodeSession($user->getId(), $secret))
;
$queueForEvents
@@ -996,6 +992,7 @@ App::post('/v1/account/sessions/anonymous')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'createAnonymousSession',
description: '/docs/references/account/create-session-anonymous.md',
auth: [],
@@ -1019,9 +1016,6 @@ App::post('/v1/account/sessions/anonymous')
->inject('queueForEvents')
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) {
$protocol = $request->getProtocol();
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if ('console' === $project->getId()) {
throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED, 'Failed to create anonymous user');
@@ -1123,7 +1117,7 @@ App::post('/v1/account/sessions/anonymous')
$session
->setAttribute('current', true)
->setAttribute('countryName', $countryName)
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '')
->setAttribute('secret', Auth::encodeSession($user->getId(), $secret))
;
$response->dynamic($session, Response::MODEL_SESSION);
@@ -1139,6 +1133,7 @@ App::post('/v1/account/sessions/token')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'createSession',
description: '/docs/references/account/create-session.md',
auth: [],
@@ -1172,6 +1167,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
->label('scope', 'sessions.write')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'createOAuth2Session',
description: '/docs/references/account/create-session-oauth2.md',
type: MethodType::WEBAUTH,
@@ -1243,7 +1239,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
});
App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->desc('OAuth2 callback')
->desc('Get OAuth2 callback')
->groups(['account'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('scope', 'public')
@@ -1273,7 +1269,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
});
App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->desc('OAuth2 callback')
->desc('Create OAuth2 callback')
->groups(['account'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('scope', 'public')
@@ -1304,7 +1300,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
});
App::get('/v1/account/sessions/oauth2/:provider/redirect')
->desc('OAuth2 redirect')
->desc('Get OAuth2 redirect')
->groups(['api', 'account', 'session'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('event', 'users.[userId].sessions.[sessionId].create')
@@ -1449,7 +1445,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Query::notEqual('userInternalId', $user->getInternalId()),
]);
if (!$identityWithMatchingEmail->isEmpty()) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
$failureRedirect(Exception::USER_ALREADY_EXISTS);
}
$userWithMatchingEmail = $dbForProject->find('users', [
@@ -1457,7 +1453,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Query::notEqual('$id', $userId),
]);
if (!empty($userWithMatchingEmail)) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
$failureRedirect(Exception::USER_ALREADY_EXISTS);
}
$sessionUpgrade = true;
@@ -1486,7 +1482,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
if ($user === false || $user->isEmpty()) { // No user logged in or with OAuth2 provider ID, create new one or connect with account with same email
if (empty($email)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
$failureRedirect(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
}
/**
@@ -1529,7 +1525,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Query::equal('providerEmail', [$email]),
]);
if (!$identityWithMatchingEmail->isEmpty()) {
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
}
try {
@@ -1601,7 +1597,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
Query::notEqual('userInternalId', $user->getInternalId()),
]);
if (!empty($identitiesWithMatchingEmail)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
}
$dbForProject->createDocument('identities', new Document([
@@ -1769,6 +1765,7 @@ App::get('/v1/account/tokens/oauth2/:provider')
->label('scope', 'sessions.write')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createOAuth2Token',
description: '/docs/references/account/create-token-oauth2.md',
auth: [],
@@ -1849,6 +1846,7 @@ App::post('/v1/account/tokens/magic-url')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createMagicURLToken',
description: '/docs/references/account/create-token-magic-url.md',
auth: [],
@@ -1884,9 +1882,6 @@ App::post('/v1/account/tokens/magic-url')
$phrase = Phrase::generate();
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$result = $dbForProject->findOne('users', [Query::equal('email', [$email])]);
if (!$result->isEmpty()) {
@@ -2068,17 +2063,11 @@ App::post('/v1/account/tokens/magic-url')
->setRecipient($email)
->trigger();
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $tokenSecret);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$token->setAttribute('secret', '');
}
if (!empty($phrase)) {
$token->setAttribute('phrase', $phrase);
}
@@ -2098,6 +2087,7 @@ App::post('/v1/account/tokens/email')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createEmailToken',
description: '/docs/references/account/create-token-email.md',
auth: [],
@@ -2131,10 +2121,6 @@ App::post('/v1/account/tokens/email')
$phrase = Phrase::generate();
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$result = $dbForProject->findOne('users', [Query::equal('email', [$email])]);
if (!$result->isEmpty()) {
$user->setAttributes($result->getArrayCopy());
@@ -2303,17 +2289,11 @@ App::post('/v1/account/tokens/email')
->setRecipient($email)
->trigger();
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $tokenSecret);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$token->setAttribute('secret', '');
}
if (!empty($phrase)) {
$token->setAttribute('phrase', $phrase);
}
@@ -2333,6 +2313,7 @@ App::put('/v1/account/sessions/magic-url')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'updateMagicURLSession',
description: '/docs/references/account/create-session.md',
auth: [],
@@ -2370,6 +2351,7 @@ App::put('/v1/account/sessions/phone')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'sessions',
name: 'updatePhoneSession',
description: '/docs/references/account/create-session.md',
auth: [],
@@ -2400,7 +2382,7 @@ App::put('/v1/account/sessions/phone')
App::post('/v1/account/tokens/phone')
->alias('/v1/account/sessions/phone')
->desc('Create phone token')
->groups(['api', 'account'])
->groups(['api', 'account', 'auth'])
->label('scope', 'sessions.write')
->label('auth.type', 'phone')
->label('audits.event', 'session.create')
@@ -2408,6 +2390,7 @@ App::post('/v1/account/tokens/phone')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createPhoneToken',
description: '/docs/references/account/create-token-phone.md',
auth: [],
@@ -2432,17 +2415,13 @@ App::post('/v1/account/tokens/phone')
->inject('queueForMessaging')
->inject('locale')
->inject('timelimit')
->inject('queueForUsage')
->inject('queueForStatsUsage')
->inject('plan')
->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, Usage $queueForUsage, array $plan) {
->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$result = $dbForProject->findOne('users', [Query::equal('phone', [$phone])]);
if (!$result->isEmpty()) {
$user->setAttributes($result->getArrayCopy());
@@ -2583,25 +2562,24 @@ App::post('/v1/account/tokens/phone')
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForUsage
$queueForStatsUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
}
}
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $secret);
$queueForEvents
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '');
// Encode secret for clients
$token->setAttribute('secret', Auth::encodeSession($user->getId(), $secret));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -2616,6 +2594,7 @@ App::post('/v1/account/jwts')
->label('auth.type', 'jwt')
->label('sdk', new Method(
namespace: 'account',
group: 'tokens',
name: 'createJWT',
description: '/docs/references/account/create-jwt.md',
auth: [],
@@ -2664,6 +2643,7 @@ App::get('/v1/account/prefs')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'getPrefs',
description: '/docs/references/account/get-prefs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2690,6 +2670,7 @@ App::get('/v1/account/logs')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'logs',
name: 'listLogs',
description: '/docs/references/account/list-logs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2715,13 +2696,15 @@ App::get('/v1/account/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
// Temp fix for logs
$queries[] = Query::or([
Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))),
Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))),
]);
$audit = new EventAudit($dbForProject);
$logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset);
$logs = $audit->getLogsByUser($user->getInternalId(), $queries);
$output = [];
@@ -2750,7 +2733,7 @@ App::get('/v1/account/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByUser($user->getInternalId()),
'total' => $audit->countLogsByUser($user->getInternalId(), $queries),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -2764,6 +2747,7 @@ App::patch('/v1/account/name')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updateName',
description: '/docs/references/account/update-name.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2802,6 +2786,7 @@ App::patch('/v1/account/password')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updatePassword',
description: '/docs/references/account/update-password.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2875,6 +2860,7 @@ App::patch('/v1/account/email')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updateEmail',
description: '/docs/references/account/update-email.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -2971,6 +2957,7 @@ App::patch('/v1/account/phone')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updatePhone',
description: '/docs/references/account/update-phone.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3056,6 +3043,7 @@ App::patch('/v1/account/prefs')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updatePrefs',
description: '/docs/references/account/update-prefs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3093,6 +3081,7 @@ App::patch('/v1/account/status')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'account',
name: 'updateStatus',
description: '/docs/references/account/update-status.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3143,6 +3132,7 @@ App::post('/v1/account/recovery')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'recovery',
name: 'createRecovery',
description: '/docs/references/account/create-recovery.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3172,11 +3162,6 @@ App::post('/v1/account/recovery')
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
}
$url = htmlentities($url);
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$email = \strtolower($email);
$profile = $dbForProject->findOne('users', [
@@ -3300,19 +3285,13 @@ App::post('/v1/account/recovery')
->setSubject($subject)
->trigger();
// Set to unhashed secret for events and server responses
$recovery->setAttribute('secret', $secret);
$queueForEvents
->setParam('userId', $profile->getId())
->setParam('tokenId', $recovery->getId())
->setUser($profile)
->setPayload($response->output($recovery, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$recovery->setAttribute('secret', '');
}
->setPayload(Response::showSensitive(fn () => $response->output($recovery, Response::MODEL_TOKEN)), sensitive: ['secret']);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -3320,7 +3299,7 @@ App::post('/v1/account/recovery')
});
App::put('/v1/account/recovery')
->desc('Create password recovery (confirmation)')
->desc('Update password recovery (confirmation)')
->groups(['api', 'account'])
->label('scope', 'sessions.write')
->label('event', 'users.[userId].recovery.[tokenId].update')
@@ -3329,6 +3308,7 @@ App::put('/v1/account/recovery')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'recovery',
name: 'updateRecovery',
description: '/docs/references/account/update-recovery.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3405,7 +3385,7 @@ App::put('/v1/account/recovery')
$queueForEvents
->setParam('userId', $profile->getId())
->setParam('tokenId', $recoveryDocument->getId())
;
->setPayload(Response::showSensitive(fn () => $response->output($recoveryDocument, Response::MODEL_TOKEN)), sensitive: ['secret']);
$response->dynamic($recoveryDocument, Response::MODEL_TOKEN);
});
@@ -3419,6 +3399,7 @@ App::post('/v1/account/verification')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'createVerification',
description: '/docs/references/account/create-email-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3452,9 +3433,6 @@ App::post('/v1/account/verification')
throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED);
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION);
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
@@ -3563,18 +3541,12 @@ App::post('/v1/account/verification')
->setName($user->getAttribute('name') ?? '')
->trigger();
// Set to unhashed secret for events and server responses
$verification->setAttribute('secret', $verificationSecret);
$queueForEvents
->setParam('userId', $user->getId())
->setParam('tokenId', $verification->getId())
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$verification->setAttribute('secret', '');
}
->setPayload(Response::showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -3582,7 +3554,7 @@ App::post('/v1/account/verification')
});
App::put('/v1/account/verification')
->desc('Create email verification (confirmation)')
->desc('Update email verification (confirmation)')
->groups(['api', 'account'])
->label('scope', 'public')
->label('event', 'users.[userId].verification.[tokenId].update')
@@ -3590,6 +3562,7 @@ App::put('/v1/account/verification')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'updateVerification',
description: '/docs/references/account/update-email-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3641,7 +3614,8 @@ App::put('/v1/account/verification')
$queueForEvents
->setParam('userId', $userId)
->setParam('tokenId', $verification->getId());
->setParam('tokenId', $verification->getId())
->setPayload(Response::showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
$response->dynamic($verification, Response::MODEL_TOKEN);
});
@@ -3656,6 +3630,7 @@ App::post('/v1/account/verification/phone')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'createPhoneVerification',
description: '/docs/references/account/create-phone-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3678,9 +3653,9 @@ App::post('/v1/account/verification/phone')
->inject('project')
->inject('locale')
->inject('timelimit')
->inject('queueForUsage')
->inject('queueForStatsUsage')
->inject('plan')
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, Usage $queueForUsage, array $plan) {
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
@@ -3694,10 +3669,6 @@ App::post('/v1/account/verification/phone')
throw new Exception(Exception::USER_PHONE_ALREADY_VERIFIED);
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$secret = null;
$sendSMS = true;
$mockNumbers = $project->getAttribute('auths', [])['mockNumbers'] ?? [];
@@ -3775,18 +3746,17 @@ App::post('/v1/account/verification/phone')
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForUsage
$queueForStatsUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
}
}
// Set to unhashed secret for events and server responses
$verification->setAttribute('secret', $secret);
$queueForEvents
@@ -3794,11 +3764,6 @@ App::post('/v1/account/verification/phone')
->setParam('tokenId', $verification->getId())
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);
// Hide secret for clients
if (!$isPrivilegedUser && !$isAppUser) {
$verification->setAttribute('secret', '');
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($verification, Response::MODEL_TOKEN);
@@ -3813,6 +3778,7 @@ App::put('/v1/account/verification/phone')
->label('audits.resource', 'user/{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'verification',
name: 'updatePhoneVerification',
description: '/docs/references/account/update-phone-verification.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3878,6 +3844,7 @@ App::patch('/v1/account/mfa')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMFA',
description: '/docs/references/account/update-mfa.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3931,6 +3898,7 @@ App::get('/v1/account/mfa/factors')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'listMfaFactors',
description: '/docs/references/account/list-mfa-factors.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -3971,6 +3939,7 @@ App::post('/v1/account/mfa/authenticators/:type')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'createMfaAuthenticator',
description: '/docs/references/account/create-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4038,7 +4007,7 @@ App::post('/v1/account/mfa/authenticators/:type')
});
App::put('/v1/account/mfa/authenticators/:type')
->desc('Verify authenticator')
->desc('Update authenticator (confirmation)')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
@@ -4047,6 +4016,7 @@ App::put('/v1/account/mfa/authenticators/:type')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMfaAuthenticator',
description: '/docs/references/account/update-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4116,6 +4086,7 @@ App::post('/v1/account/mfa/recovery-codes')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'createMfaRecoveryCodes',
description: '/docs/references/account/create-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4153,7 +4124,7 @@ App::post('/v1/account/mfa/recovery-codes')
});
App::patch('/v1/account/mfa/recovery-codes')
->desc('Regenerate MFA recovery codes')
->desc('Update MFA recovery codes (regenerate)')
->groups(['api', 'account', 'mfaProtected'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
@@ -4162,6 +4133,7 @@ App::patch('/v1/account/mfa/recovery-codes')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMfaRecoveryCodes',
description: '/docs/references/account/update-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4198,11 +4170,12 @@ App::patch('/v1/account/mfa/recovery-codes')
});
App::get('/v1/account/mfa/recovery-codes')
->desc('Get MFA recovery codes')
->desc('List MFA recovery codes')
->groups(['api', 'account', 'mfaProtected'])
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'getMfaRecoveryCodes',
description: '/docs/references/account/get-mfa-recovery-codes.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4241,6 +4214,7 @@ App::delete('/v1/account/mfa/authenticators/:type')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'deleteMfaAuthenticator',
description: '/docs/references/account/delete-mfa-authenticator.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4286,6 +4260,7 @@ App::post('/v1/account/mfa/challenge')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'createMfaChallenge',
description: '/docs/references/account/create-mfa-challenge.md',
auth: [],
@@ -4310,9 +4285,9 @@ App::post('/v1/account/mfa/challenge')
->inject('queueForMessaging')
->inject('queueForMails')
->inject('timelimit')
->inject('queueForUsage')
->inject('queueForStatsUsage')
->inject('plan')
->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Locale $locale, Document $project, Request $request, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails, callable $timelimit, Usage $queueForUsage, array $plan) {
->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Locale $locale, Document $project, Request $request, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) {
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
$code = Auth::codeGenerator();
@@ -4383,11 +4358,11 @@ App::post('/v1/account/mfa/challenge')
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForUsage
$queueForStatsUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
@@ -4496,7 +4471,7 @@ App::post('/v1/account/mfa/challenge')
});
App::put('/v1/account/mfa/challenge')
->desc('Create MFA challenge (confirmation)')
->desc('Update MFA challenge (confirmation)')
->groups(['api', 'account', 'mfa'])
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[sessionId].create')
@@ -4505,6 +4480,7 @@ App::put('/v1/account/mfa/challenge')
->label('audits.userId', '{response.userId}')
->label('sdk', new Method(
namespace: 'account',
group: 'mfa',
name: 'updateMfaChallenge',
description: '/docs/references/account/update-mfa-challenge.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4598,6 +4574,7 @@ App::post('/v1/account/targets/push')
->label('event', 'users.[userId].targets.[targetId].create')
->label('sdk', new Method(
namespace: 'account',
group: 'pushTargets',
name: 'createPushTarget',
description: '/docs/references/account/create-push-target.md',
auth: [AuthType::SESSION],
@@ -4678,6 +4655,7 @@ App::put('/v1/account/targets/:targetId/push')
->label('event', 'users.[userId].targets.[targetId].update')
->label('sdk', new Method(
namespace: 'account',
group: 'pushTargets',
name: 'updatePushTarget',
description: '/docs/references/account/update-push-target.md',
auth: [AuthType::SESSION],
@@ -4742,6 +4720,7 @@ App::delete('/v1/account/targets/:targetId/push')
->label('event', 'users.[userId].targets.[targetId].delete')
->label('sdk', new Method(
namespace: 'account',
group: 'pushTargets',
name: 'deletePushTarget',
description: '/docs/references/account/delete-push-target.md',
auth: [AuthType::SESSION],
@@ -4792,6 +4771,7 @@ App::get('/v1/account/identities')
->label('scope', 'account')
->label('sdk', new Method(
namespace: 'account',
group: 'identities',
name: 'listIdentities',
description: '/docs/references/account/list-identities.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -4843,8 +4823,11 @@ App::get('/v1/account/identities')
}
$filterQueries = Query::groupByType($queries)['filters'];
$results = $dbForProject->find('identities', $queries);
try {
$results = $dbForProject->find('identities', $queries);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT);
$response->dynamic(new Document([
@@ -4863,6 +4846,7 @@ App::delete('/v1/account/identities/:identityId')
->label('audits.userId', '{user.$id}')
->label('sdk', new Method(
namespace: 'account',
group: 'identities',
name: 'deleteIdentity',
description: '/docs/references/account/delete-identity.md',
auth: [AuthType::SESSION, AuthType::JWT],
+7
View File
@@ -171,6 +171,7 @@ App::get('/v1/avatars/credit-cards/:code')
->label('cache.resource', 'avatar/credit-card')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getCreditCard',
description: '/docs/references/avatars/get-credit-card.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -198,6 +199,7 @@ App::get('/v1/avatars/browsers/:code')
->label('cache.resource', 'avatar/browser')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getBrowser',
description: '/docs/references/avatars/get-browser.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -225,6 +227,7 @@ App::get('/v1/avatars/flags/:code')
->label('cache.resource', 'avatar/flag')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getFlag',
description: '/docs/references/avatars/get-flag.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -252,6 +255,7 @@ App::get('/v1/avatars/image')
->label('cache.resource', 'avatar/image')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getImage',
description: '/docs/references/avatars/get-image.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -322,6 +326,7 @@ App::get('/v1/avatars/favicon')
->label('cache.resource', 'avatar/favicon')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getFavicon',
description: '/docs/references/avatars/get-favicon.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -472,6 +477,7 @@ App::get('/v1/avatars/qr')
->label('scope', 'avatars.read')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getQR',
description: '/docs/references/avatars/get-qr.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -520,6 +526,7 @@ App::get('/v1/avatars/initials')
->label('cache.resource', 'avatar/initials')
->label('sdk', new Method(
namespace: 'avatars',
group: null,
name: 'getInitials',
description: '/docs/references/avatars/get-initials.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
+3 -1
View File
@@ -27,6 +27,7 @@ App::get('/v1/console/variables')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'console',
group: 'console',
name: 'variables',
description: '/docs/references/console/variables.md',
auth: [AuthType::ADMIN],
@@ -67,11 +68,12 @@ App::get('/v1/console/variables')
});
App::post('/v1/console/assistant')
->desc('Ask query')
->desc('Create assistant query')
->groups(['api', 'assistant'])
->label('scope', 'assistant.read')
->label('sdk', new Method(
namespace: 'assistant',
group: 'console',
name: 'chat',
description: '/docs/references/assistant/chat.md',
auth: [AuthType::ADMIN],
+162 -87
View File
@@ -4,7 +4,7 @@ use Appwrite\Auth\Auth;
use Appwrite\Detector\Detector;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Event\Usage;
use Appwrite\Event\StatsUsage;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\SDK\AuthType;
@@ -23,12 +23,15 @@ use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Authorization as AuthorizationException;
use Utopia\Database\Exception\Conflict as ConflictException;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Index as IndexException;
use Utopia\Database\Exception\Limit as LimitException;
use Utopia\Database\Exception\NotFound as NotFoundException;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Exception\Restricted as RestrictedException;
use Utopia\Database\Exception\Structure as StructureException;
@@ -241,8 +244,8 @@ function updateAttribute(
string $filter = null,
string|bool|int|float $default = null,
bool $required = null,
int|float $min = null,
int|float $max = null,
int|float|null $min = null,
int|float|null $max = null,
array $elements = null,
array $options = [],
string $newKey = null,
@@ -298,6 +301,9 @@ function updateAttribute(
switch ($attribute->getAttribute('format')) {
case APP_DATABASE_ATTRIBUTE_INT_RANGE:
case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE:
$min ??= $attribute->getAttribute('formatOptions')['min'];
$max ??= $attribute->getAttribute('formatOptions')['max'];
if ($min > $max) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value');
}
@@ -393,6 +399,8 @@ function updateAttribute(
throw new Exception(Exception::ATTRIBUTE_NOT_FOUND);
} catch (LimitException) {
throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED);
} catch (IndexException $e) {
throw new Exception(Exception::INDEX_INVALID, $e->getMessage());
}
}
@@ -459,6 +467,7 @@ App::post('/v1/databases')
->label('audits.resource', 'database/{response.$id}')
->label('sdk', new Method(
namespace: 'databases',
group: 'databases',
name: 'create',
description: '/docs/references/databases/create.md',
auth: [AuthType::KEY],
@@ -476,8 +485,8 @@ App::post('/v1/databases')
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('queueForUsage')
->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents, Usage $queueForUsage) {
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
$databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId;
@@ -527,7 +536,6 @@ App::post('/v1/databases')
}
$queueForEvents->setParam('databaseId', $database->getId());
$queueForUsage->addMetric(str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_STORAGE), 1); // per database
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -541,6 +549,7 @@ App::get('/v1/databases')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'databases',
name: 'list',
description: '/docs/references/databases/list.md',
auth: [AuthType::KEY],
@@ -590,9 +599,15 @@ App::get('/v1/databases')
$filterQueries = Query::groupByType($queries)['filters'];
try {
$databases = $dbForProject->find('databases', $queries);
$total = $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'databases' => $dbForProject->find('databases', $queries),
'total' => $dbForProject->count('databases', $filterQueries, APP_LIMIT_COUNT),
'databases' => $databases,
'total' => $total,
]), Response::MODEL_DATABASE_LIST);
});
@@ -603,6 +618,7 @@ App::get('/v1/databases/:databaseId')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'databases',
name: 'get',
description: '/docs/references/databases/get.md',
auth: [AuthType::KEY],
@@ -635,6 +651,7 @@ App::get('/v1/databases/:databaseId/logs')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'logs',
name: 'listLogs',
description: '/docs/references/databases/get-logs.md',
auth: [AuthType::ADMIN],
@@ -666,13 +683,15 @@ App::get('/v1/databases/:databaseId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
// Temp fix for logs
$queries[] = Query::or([
Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))),
Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))),
]);
$audit = new Audit($dbForProject);
$resource = 'database/' . $databaseId;
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$logs = $audit->getLogsByResource($resource, $queries);
$output = [];
@@ -720,7 +739,7 @@ App::get('/v1/databases/:databaseId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource),
'total' => $audit->countLogsByResource($resource, $queries),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -736,6 +755,7 @@ App::put('/v1/databases/:databaseId')
->label('audits.resource', 'database/{response.$id}')
->label('sdk', new Method(
namespace: 'databases',
group: 'databases',
name: 'update',
description: '/docs/references/databases/update.md',
auth: [AuthType::KEY],
@@ -781,6 +801,7 @@ App::delete('/v1/databases/:databaseId')
->label('audits.resource', 'database/{request.databaseId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'databases',
name: 'delete',
description: '/docs/references/databases/delete.md',
auth: [AuthType::KEY],
@@ -797,8 +818,8 @@ App::delete('/v1/databases/:databaseId')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('queueForUsage')
->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) {
->inject('queueForStatsUsage')
->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
$database = $dbForProject->getDocument('databases', $databaseId);
@@ -821,9 +842,6 @@ App::delete('/v1/databases/:databaseId')
->setParam('databaseId', $database->getId())
->setPayload($response->output($database, Response::MODEL_DATABASE));
$queueForUsage
->addMetric(METRIC_DATABASES_STORAGE, 1); // Global, deletion forces full recalculation
$response->noContent();
});
@@ -837,6 +855,7 @@ App::post('/v1/databases/:databaseId/collections')
->label('audits.resource', 'database/{request.databaseId}/collection/{response.$id}')
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'createCollection',
description: '/docs/references/databases/create-collection.md',
auth: [AuthType::KEY],
@@ -908,6 +927,7 @@ App::get('/v1/databases/:databaseId/collections')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'listCollections',
description: '/docs/references/databases/list-collections.md',
auth: [AuthType::KEY],
@@ -966,9 +986,15 @@ App::get('/v1/databases/:databaseId/collections')
$filterQueries = Query::groupByType($queries)['filters'];
try {
$collections = $dbForProject->find('database_' . $database->getInternalId(), $queries);
$total = $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'collections' => $dbForProject->find('database_' . $database->getInternalId(), $queries),
'total' => $dbForProject->count('database_' . $database->getInternalId(), $filterQueries, APP_LIMIT_COUNT),
'collections' => $collections,
'total' => $total,
]), Response::MODEL_COLLECTION_LIST);
});
@@ -980,6 +1006,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'getCollection',
description: '/docs/references/databases/get-collection.md',
auth: [AuthType::KEY],
@@ -1021,6 +1048,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'listCollectionLogs',
description: '/docs/references/databases/get-collection-logs.md',
auth: [AuthType::ADMIN],
@@ -1054,14 +1082,21 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$queries = Query::parseQueries($queries);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
// Temp fix for logs
$queries[] = Query::or([
Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))),
Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))),
]);
$audit = new Audit($dbForProject);
$resource = 'database/' . $databaseId . '/collection/' . $collectionId;
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$logs = $audit->getLogsByResource($resource, $queries);
$output = [];
@@ -1109,7 +1144,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource),
'total' => $audit->countLogsByResource($resource, $queries),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -1126,6 +1161,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'updateCollection',
description: '/docs/references/databases/update-collection.md',
auth: [AuthType::KEY],
@@ -1200,6 +1236,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'deleteCollection',
description: '/docs/references/databases/delete-collection.md',
auth: [AuthType::KEY],
@@ -1263,6 +1300,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createStringAttribute',
description: '/docs/references/databases/create-string-attribute.md',
auth: [AuthType::KEY],
@@ -1325,6 +1363,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/email'
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createEmailAttribute',
description: '/docs/references/databases/create-email-attribute.md',
auth: [AuthType::KEY],
@@ -1373,6 +1412,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createEnumAttribute',
description: '/docs/references/databases/create-attribute-enum.md',
auth: [AuthType::KEY],
@@ -1426,6 +1466,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/ip')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createIpAttribute',
description: '/docs/references/databases/create-ip-attribute.md',
auth: [AuthType::KEY],
@@ -1474,6 +1515,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/url')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createUrlAttribute',
description: '/docs/references/databases/create-url-attribute.md',
auth: [AuthType::KEY],
@@ -1522,6 +1564,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createIntegerAttribute',
description: '/docs/references/databases/create-integer-attribute.md',
auth: [AuthType::KEY],
@@ -1547,8 +1590,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) {
// Ensure attribute default is within range
$min = \is_null($min) ? PHP_INT_MIN : $min;
$max = \is_null($max) ? PHP_INT_MAX : $max;
$min ??= PHP_INT_MIN;
$max ??= PHP_INT_MAX;
if ($min > $max) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value');
@@ -1599,6 +1642,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float'
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createFloatAttribute',
description: '/docs/references/databases/create-float-attribute.md',
auth: [AuthType::KEY],
@@ -1624,8 +1668,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float'
->action(function (string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) {
// Ensure attribute default is within range
$min = \is_null($min) ? -PHP_FLOAT_MAX : $min;
$max = \is_null($max) ? PHP_FLOAT_MAX : $max;
$min ??= -PHP_FLOAT_MAX;
$max ??= PHP_FLOAT_MAX;
if ($min > $max) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value');
@@ -1674,6 +1718,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/boolea
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createBooleanAttribute',
description: '/docs/references/databases/create-boolean-attribute.md',
auth: [AuthType::KEY],
@@ -1721,6 +1766,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/dateti
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createDatetimeAttribute',
description: '/docs/references/databases/create-datetime-attribute.md',
auth: [AuthType::KEY],
@@ -1771,6 +1817,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'createRelationshipAttribute',
description: '/docs/references/databases/create-relationship-attribute.md',
auth: [AuthType::KEY],
@@ -1902,6 +1949,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'listAttributes',
description: '/docs/references/databases/list-attributes.md',
auth: [AuthType::KEY],
@@ -1970,9 +2018,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes')
}
$filters = Query::groupByType($queries)['filters'];
$attributes = $dbForProject->find('attributes', $queries);
$total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT);
try {
$attributes = $dbForProject->find('attributes', $queries);
$total = $dbForProject->count('attributes', $filters, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'attributes' => $attributes,
@@ -1988,6 +2039,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'getAttribute',
description: '/docs/references/databases/get-attribute.md',
auth: [AuthType::KEY],
@@ -2072,6 +2124,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/strin
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateStringAttribute',
description: '/docs/references/databases/update-string-attribute.md',
auth: [AuthType::KEY],
@@ -2123,6 +2176,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/email
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateEmailAttribute',
description: '/docs/references/databases/update-email-attribute.md',
auth: [AuthType::KEY],
@@ -2172,6 +2226,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/enum/
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateEnumAttribute',
description: '/docs/references/databases/update-enum-attribute.md',
auth: [AuthType::KEY],
@@ -2223,6 +2278,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/ip/:k
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateIpAttribute',
description: '/docs/references/databases/update-ip-attribute.md',
auth: [AuthType::KEY],
@@ -2272,6 +2328,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/url/:
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateUrlAttribute',
description: '/docs/references/databases/update-url-attribute.md',
auth: [AuthType::KEY],
@@ -2321,6 +2378,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateIntegerAttribute',
description: '/docs/references/databases/update-integer-attribute.md',
auth: [AuthType::KEY],
@@ -2336,8 +2394,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/integ
->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('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Integer(), 'Minimum value to enforce on new documents')
->param('max', null, new Integer(), 'Maximum value to enforce on new documents')
->param('min', null, new Integer(), 'Minimum value to enforce on new documents', true)
->param('max', null, new Integer(), 'Maximum value to enforce on new documents', true)
->param('default', null, new Nullable(new Integer()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
@@ -2380,6 +2438,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateFloatAttribute',
description: '/docs/references/databases/update-float-attribute.md',
auth: [AuthType::KEY],
@@ -2395,8 +2454,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/float
->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('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents')
->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents')
->param('min', null, new FloatValidator(), 'Minimum value to enforce on new documents', true)
->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents', true)
->param('default', null, new Nullable(new FloatValidator()), 'Default value for attribute when not provided. Cannot be set when attribute is required.')
->param('newKey', null, new Key(), 'New attribute key.', true)
->inject('response')
@@ -2439,6 +2498,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/boole
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateBooleanAttribute',
description: '/docs/references/databases/update-boolean-attribute.md',
auth: [AuthType::KEY],
@@ -2487,6 +2547,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/datet
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateDatetimeAttribute',
description: '/docs/references/databases/update-datetime-attribute.md',
auth: [AuthType::KEY],
@@ -2535,6 +2596,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/attributes/:key/
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'updateRelationshipAttribute',
description: '/docs/references/databases/update-relationship-attribute.md',
auth: [AuthType::KEY],
@@ -2600,6 +2662,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'attributes',
name: 'deleteAttribute',
description: '/docs/references/databases/delete-attribute.md',
auth: [AuthType::KEY],
@@ -2618,8 +2681,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('queueForUsage')
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) {
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@@ -2716,9 +2779,6 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->setContext('database', $db)
->setPayload($response->output($attribute, $model));
$queueForUsage
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$db->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
$response->noContent();
});
@@ -2733,6 +2793,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'collections',
name: 'createIndex',
description: '/docs/references/databases/create-index.md',
auth: [AuthType::KEY],
@@ -2822,7 +2883,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes')
$attributeIndex = \array_search($attribute, array_column($oldAttributes, 'key'));
if ($attributeIndex === false) {
throw new Exception(Exception::ATTRIBUTE_UNKNOWN, 'Unknown attribute: ' . $attribute);
throw new Exception(Exception::ATTRIBUTE_UNKNOWN, 'Unknown attribute: ' . $attribute . '. Verify the attribute name or create the attribute.');
}
$attributeStatus = $oldAttributes[$attributeIndex]['status'];
@@ -2903,6 +2964,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'indexes',
name: 'listIndexes',
description: '/docs/references/databases/list-indexes.md',
auth: [AuthType::KEY],
@@ -2971,9 +3033,16 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$total = $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT);
$indexes = $dbForProject->find('indexes', $queries);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'total' => $dbForProject->count('indexes', $filterQueries, APP_LIMIT_COUNT),
'indexes' => $dbForProject->find('indexes', $queries),
'total' => $total,
'indexes' => $indexes,
]), Response::MODEL_INDEX_LIST);
});
@@ -2985,6 +3054,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'indexes',
name: 'getIndex',
description: '/docs/references/databases/get-index.md',
auth: [AuthType::KEY],
@@ -3034,6 +3104,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: 'databases',
group: 'indexes',
name: 'deleteIndex',
description: '/docs/references/databases/delete-index.md',
auth: [AuthType::KEY],
@@ -3112,6 +3183,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
[
new Method(
namespace: 'databases',
group: 'documents',
name: 'createDocument',
description: '/docs/references/databases/create-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -3134,9 +3206,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->inject('dbForProject')
->inject('user')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('mode')
->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode) {
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
@@ -3339,10 +3410,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
$processDocument($collection, $document);
$queueForUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, $operations)
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations)
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); // per collection
$response->addHeader('X-Debug-Operations', $operations);
@@ -3375,6 +3445,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'listDocuments',
description: '/docs/references/databases/list-documents.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -3391,9 +3462,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), '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.', true)
->inject('response')
->inject('dbForProject')
->inject('mode')
->inject('queueForUsage')
->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode, Usage $queueForUsage) {
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
@@ -3439,9 +3509,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
$cursor->setValue($cursorDocument);
}
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries);
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT);
try {
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries);
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$operations = 0;
@@ -3505,10 +3578,9 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
$processDocument($collection, $document);
}
$queueForUsage
->addMetric(METRIC_DATABASES_OPERATIONS_READS, $operations)
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations)
;
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations);
$response->addHeader('X-Debug-Operations', $operations);
@@ -3553,6 +3625,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'getDocument',
description: '/docs/references/databases/get-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -3570,9 +3643,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), '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.', true)
->inject('response')
->inject('dbForProject')
->inject('mode')
->inject('queueForUsage')
->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, string $mode, Usage $queueForUsage) {
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
@@ -3648,10 +3720,9 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
$processDocument($collection, $document);
$queueForUsage
->addMetric(METRIC_DATABASES_OPERATIONS_READS, $operations)
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations)
;
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations);
$response->addHeader('X-Debug-Operations', $operations);
@@ -3666,6 +3737,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: 'logs',
name: 'listDocumentLogs',
description: '/docs/references/databases/get-document-logs.md',
auth: [AuthType::ADMIN],
@@ -3711,13 +3783,15 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
// Temp fix for logs
$queries[] = Query::or([
Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))),
Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))),
]);
$audit = new Audit($dbForProject);
$resource = 'database/' . $databaseId . '/collection/' . $collectionId . '/document/' . $document->getId();
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$logs = $audit->getLogsByResource($resource, $queries);
$output = [];
@@ -3765,7 +3839,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource),
'total' => $audit->countLogsByResource($resource, $queries),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -3784,6 +3858,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'updateDocument',
description: '/docs/references/databases/update-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -3804,9 +3879,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('mode')
->inject('queueForUsage')
->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Usage $queueForUsage) {
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
@@ -3946,10 +4020,9 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
$setCollection($collection, $newDocument);
$queueForUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, $operations)
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations)
;
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations);
$response->addHeader('X-Debug-Operations', $operations);
@@ -4040,6 +4113,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'databases',
group: 'documents',
name: 'deleteDocument',
description: '/docs/references/databases/delete-document.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -4058,9 +4132,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('mode')
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, Usage $queueForUsage, string $mode) {
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
@@ -4129,10 +4202,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
$processDocument($collection, $document);
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1)
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1)
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1); // per collection
$response->addHeader('X-Debug-Operations', 1);
@@ -4162,6 +4234,7 @@ App::get('/v1/databases/usage')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: null,
name: 'getUsage',
description: '/docs/references/databases/get-usage.md',
auth: [AuthType::ADMIN],
@@ -4257,6 +4330,7 @@ App::get('/v1/databases/:databaseId/usage')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: null,
name: 'getDatabaseUsage',
description: '/docs/references/databases/get-database-usage.md',
auth: [AuthType::ADMIN],
@@ -4358,6 +4432,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
namespace: 'databases',
group: null,
name: 'getCollectionUsage',
description: '/docs/references/databases/get-collection-usage.md',
auth: [AuthType::ADMIN],
+84 -88
View File
@@ -6,13 +6,14 @@ use Appwrite\Event\Build;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Usage;
use Appwrite\Event\Realtime;
use Appwrite\Event\StatsUsage;
use Appwrite\Event\Validator\FunctionEvent;
use Appwrite\Event\Webhook;
use Appwrite\Extend\Exception;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Functions\Validator\Headers;
use Appwrite\Functions\Validator\RuntimeSpecification;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Platform\Tasks\ScheduleExecutions;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
@@ -36,6 +37,7 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -149,6 +151,7 @@ App::post('/v1/functions')
->label('audits.resource', 'function/{response.$id}')
->label('sdk', new Method(
namespace: 'functions',
group: 'functions',
name: 'create',
description: '/docs/references/functions/create-function.md',
auth: [AuthType::KEY],
@@ -194,9 +197,12 @@ App::post('/v1/functions')
->inject('user')
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('queueForWebhooks')
->inject('queueForFunctions')
->inject('queueForRealtime')
->inject('dbForPlatform')
->inject('gitHub')
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, callable $timelimit, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForPlatform, GitHub $github) use ($redeployVcs) {
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, callable $timelimit, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, Database $dbForPlatform, GitHub $github) use ($redeployVcs) {
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
// Temporary abuse check
@@ -291,7 +297,7 @@ App::post('/v1/functions')
$schedule = Authorization::skip(
fn () => $dbForPlatform->createDocument('schedules', new Document([
'region' => System::getEnv('_APP_REGION', 'default'), // Todo replace with projects region
'region' => $project->getAttribute('region'),
'resourceType' => 'function',
'resourceId' => $function->getId(),
'resourceInternalId' => $function->getInternalId(),
@@ -383,54 +389,34 @@ App::post('/v1/functions')
'resourceInternalId' => $function->getInternalId(),
'status' => 'verified',
'certificateId' => '',
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
/** Trigger Webhook */
$ruleModel = new Rule();
$ruleCreate =
$queueForEvents
->setClass(Event::WEBHOOK_CLASS_NAME)
->setQueue(Event::WEBHOOK_QUEUE_NAME);
->setProject($project)
->setEvent('rules.[ruleId].create')
->setParam('ruleId', $rule->getId())
->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules())));
$ruleCreate
->setProject($project)
->setEvent('rules.[ruleId].create')
->setParam('ruleId', $rule->getId())
->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules())))
/** Trigger Webhook */
$queueForWebhooks
->from($ruleCreate)
->trigger();
/** Trigger Functions */
$ruleCreate
->setClass(Event::FUNCTIONS_CLASS_NAME)
->setQueue(Event::FUNCTIONS_QUEUE_NAME)
$queueForFunctions
->from($ruleCreate)
->trigger();
/** Trigger realtime event */
$allEvents = Event::generateEvents('rules.[ruleId].create', [
'ruleId' => $rule->getId(),
]);
$target = Realtime::fromPayload(
// Pass first, most verbose event pattern
event: $allEvents[0],
payload: $rule,
project: $project
);
Realtime::send(
projectId: 'console',
payload: $rule->getArrayCopy(),
events: $allEvents,
channels: $target['channels'],
roles: $target['roles']
);
Realtime::send(
projectId: $project->getId(),
payload: $rule->getArrayCopy(),
events: $allEvents,
channels: $target['channels'],
roles: $target['roles']
);
/** Trigger Realtime Events */
$queueForRealtime
->from($ruleCreate)
->setSubscribers(['console', $project->getId()])
->trigger();
}
$queueForEvents->setParam('functionId', $function->getId());
@@ -447,6 +433,7 @@ App::get('/v1/functions')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'functions',
name: 'list',
description: '/docs/references/functions/list-functions.md',
auth: [AuthType::KEY],
@@ -499,10 +486,15 @@ App::get('/v1/functions')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$functions = $dbForProject->find('functions', $queries);
$total = $dbForProject->count('functions', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'functions' => $dbForProject->find('functions', $queries),
'total' => $dbForProject->count('functions', $filterQueries, APP_LIMIT_COUNT),
'functions' => $functions,
'total' => $total,
]), Response::MODEL_FUNCTION_LIST);
});
@@ -513,6 +505,7 @@ App::get('/v1/functions/runtimes')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'runtimes',
name: 'listRuntimes',
description: '/docs/references/functions/list-runtimes.md',
auth: [AuthType::KEY],
@@ -552,6 +545,7 @@ App::get('/v1/functions/specifications')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'runtimes',
name: 'listSpecifications',
description: '/docs/references/functions/list-specifications.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -594,6 +588,7 @@ App::get('/v1/functions/:functionId')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'functions',
name: 'get',
description: '/docs/references/functions/get-function.md',
auth: [AuthType::KEY],
@@ -624,6 +619,7 @@ App::get('/v1/functions/:functionId/usage')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: null,
name: 'getFunctionUsage',
description: '/docs/references/functions/get-function-usage.md',
auth: [AuthType::ADMIN],
@@ -735,6 +731,7 @@ App::get('/v1/functions/usage')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: null,
name: 'getUsage',
description: '/docs/references/functions/get-functions-usage.md',
auth: [AuthType::ADMIN],
@@ -844,6 +841,7 @@ App::put('/v1/functions/:functionId')
->label('audits.resource', 'function/{response.$id}')
->label('sdk', new Method(
namespace: 'functions',
group: 'functions',
name: 'update',
description: '/docs/references/functions/update-function.md',
auth: [AuthType::KEY],
@@ -885,7 +883,8 @@ App::put('/v1/functions/:functionId')
->inject('queueForBuilds')
->inject('dbForPlatform')
->inject('gitHub')
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, ?string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForPlatform, GitHub $github) use ($redeployVcs) {
->inject('executor')
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, ?string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForPlatform, GitHub $github, Executor $executor) use ($redeployVcs) {
// TODO: If only branch changes, re-deploy
$function = $dbForProject->getDocument('functions', $functionId);
@@ -988,7 +987,6 @@ App::put('/v1/functions/:functionId')
// Enforce Cold Start if spec limits change.
if ($function->getAttribute('specification') !== $specification && !empty($function->getAttribute('deployment'))) {
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
try {
$executor->deleteRuntime($project->getId(), $function->getAttribute('deployment'));
} catch (\Throwable $th) {
@@ -1044,11 +1042,12 @@ App::put('/v1/functions/:functionId')
App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
->groups(['api', 'functions'])
->desc('Download deployment')
->desc('Get deployment download')
->label('scope', 'functions.read')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'getDeploymentDownload',
description: '/docs/references/functions/get-deployment-download.md',
auth: [AuthType::KEY, AuthType::JWT],
@@ -1145,6 +1144,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'updateDeployment',
description: '/docs/references/functions/update-function-deployment.md',
auth: [AuthType::KEY],
@@ -1213,6 +1213,7 @@ App::delete('/v1/functions/:functionId')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'functions',
name: 'delete',
description: '/docs/references/functions/delete-function.md',
auth: [AuthType::KEY],
@@ -1268,6 +1269,7 @@ App::post('/v1/functions/:functionId/deployments')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'createDeployment',
description: '/docs/references/functions/create-deployment.md',
auth: [AuthType::KEY],
@@ -1277,9 +1279,9 @@ App::post('/v1/functions/:functionId/deployments')
model: Response::MODEL_DEPLOYMENT,
)
],
requestType: 'multipart/form-data',
type: MethodType::UPLOAD,
packaging: true,
requestType: 'multipart/form-data',
))
->param('functionId', '', new UID(), 'Function ID.')
->param('entrypoint', null, new Text(1028), 'Entrypoint File.', true)
@@ -1490,6 +1492,7 @@ App::get('/v1/functions/:functionId/deployments')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'listDeployments',
description: '/docs/references/functions/list-deployments.md',
auth: [AuthType::KEY],
@@ -1553,9 +1556,12 @@ App::get('/v1/functions/:functionId/deployments')
}
$filterQueries = Query::groupByType($queries)['filters'];
$results = $dbForProject->find('deployments', $queries);
$total = $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT);
try {
$results = $dbForProject->find('deployments', $queries);
$total = $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
foreach ($results as $result) {
$build = $dbForProject->getDocument('builds', $result->getAttribute('buildId', ''));
@@ -1579,6 +1585,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'getDeployment',
description: '/docs/references/functions/get-deployment.md',
auth: [AuthType::KEY],
@@ -1631,6 +1638,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'deleteDeployment',
description: '/docs/references/functions/delete-deployment.md',
auth: [AuthType::KEY],
@@ -1696,7 +1704,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
->alias('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
->groups(['api', 'functions'])
->desc('Rebuild deployment')
->desc('Create deployment build')
->label('scope', 'functions.write')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
@@ -1704,6 +1712,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'createBuild',
description: '/docs/references/functions/create-build.md',
auth: [AuthType::KEY],
@@ -1779,6 +1788,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'deployments',
name: 'updateDeploymentBuild',
description: '/docs/references/functions/update-deployment-build.md',
auth: [AuthType::KEY],
@@ -1795,7 +1805,8 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
->inject('dbForProject')
->inject('project')
->inject('queueForEvents')
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents) {
->inject('executor')
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Executor $executor) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
@@ -1850,7 +1861,6 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
$dbForProject->purgeCachedDocument('deployments', $deployment->getId());
try {
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
$executor->deleteRuntime($project->getId(), $deploymentId . "-build");
} catch (\Throwable $th) {
// Don't throw if the deployment doesn't exist
@@ -1874,6 +1884,7 @@ App::post('/v1/functions/:functionId/executions')
->label('event', 'functions.[functionId].executions.[executionId].create')
->label('sdk', new Method(
namespace: 'functions',
group: 'executions',
name: 'createExecution',
description: '/docs/references/functions/create-execution.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1900,10 +1911,11 @@ App::post('/v1/functions/:functionId/executions')
->inject('dbForPlatform')
->inject('user')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('queueForStatsUsage')
->inject('queueForFunctions')
->inject('executor')
->inject('geodb')
->action(function (string $functionId, string $body, mixed $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForPlatform, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) {
->action(function (string $functionId, string $body, mixed $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForPlatform, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb) {
$async = \strval($async) === 'true' || \strval($async) === '1';
if (!$async && !is_null($scheduledAt)) {
@@ -2095,7 +2107,7 @@ App::post('/v1/functions/:functionId/executions')
];
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => System::getEnv('_APP_REGION', 'default'),
'region' => $project->getAttribute('region'),
'resourceType' => ScheduleExecutions::getSupportedResource(),
'resourceId' => $execution->getId(),
'resourceInternalId' => $execution->getInternalId(),
@@ -2176,7 +2188,6 @@ App::post('/v1/functions/:functionId/executions')
]);
/** Execute function */
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
try {
$version = $function->getAttribute('version', 'v2');
$command = $runtime['startCommand'];
@@ -2230,7 +2241,7 @@ App::post('/v1/functions/:functionId/executions')
throw $th;
}
} finally {
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_EXECUTIONS, 1)
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1)
->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project
@@ -2242,15 +2253,6 @@ App::post('/v1/functions/:functionId/executions')
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if (!$isPrivilegedUser && !$isAppUser) {
$execution->setAttribute('logs', '');
$execution->setAttribute('errors', '');
}
$headers = [];
foreach (($executionResponse['headers'] ?? []) as $key => $value) {
$headers[] = ['name' => $key, 'value' => $value];
@@ -2282,6 +2284,7 @@ App::get('/v1/functions/:functionId/executions')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'executions',
name: 'listExecutions',
description: '/docs/references/functions/list-executions.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -2347,19 +2350,11 @@ App::get('/v1/functions/:functionId/executions')
}
$filterQueries = Query::groupByType($queries)['filters'];
$results = $dbForProject->find('executions', $queries);
$total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT);
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if (!$isPrivilegedUser && !$isAppUser) {
$results = array_map(function ($execution) {
$execution->setAttribute('logs', '');
$execution->setAttribute('errors', '');
return $execution;
}, $results);
try {
$results = $dbForProject->find('executions', $queries);
$total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
@@ -2375,6 +2370,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'executions',
name: 'getExecution',
description: '/docs/references/functions/get-execution.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -2410,14 +2406,6 @@ App::get('/v1/functions/:functionId/executions/:executionId')
throw new Exception(Exception::EXECUTION_NOT_FOUND);
}
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
if (!$isPrivilegedUser && !$isAppUser) {
$execution->setAttribute('logs', '');
$execution->setAttribute('errors', '');
}
$response->dynamic($execution, Response::MODEL_EXECUTION);
});
@@ -2431,6 +2419,7 @@ App::delete('/v1/functions/:functionId/executions/:executionId')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'executions',
name: 'deleteExecution',
description: '/docs/references/functions/delete-execution.md',
auth: [AuthType::KEY],
@@ -2508,6 +2497,7 @@ App::post('/v1/functions/:functionId/variables')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'variables',
name: 'createVariable',
description: '/docs/references/functions/create-variable.md',
auth: [AuthType::KEY],
@@ -2578,6 +2568,7 @@ App::get('/v1/functions/:functionId/variables')
'sdk',
new Method(
namespace: 'functions',
group: 'variables',
name: 'listVariables',
description: '/docs/references/functions/list-variables.md',
auth: [AuthType::KEY],
@@ -2614,6 +2605,7 @@ App::get('/v1/functions/:functionId/variables/:variableId')
'sdk',
new Method(
namespace: 'functions',
group: 'variables',
name: 'getVariable',
description: '/docs/references/functions/get-variable.md',
auth: [AuthType::KEY],
@@ -2662,6 +2654,7 @@ App::put('/v1/functions/:functionId/variables/:variableId')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'variables',
name: 'updateVariable',
description: '/docs/references/functions/update-variable.md',
auth: [AuthType::KEY],
@@ -2729,6 +2722,7 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
group: 'variables',
name: 'deleteVariable',
description: '/docs/references/functions/delete-variable.md',
auth: [AuthType::KEY],
@@ -2783,6 +2777,7 @@ App::get('/v1/functions/templates')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'templates',
name: 'listTemplates',
description: '/docs/references/functions/list-templates.md',
auth: [AuthType::ADMIN],
@@ -2826,6 +2821,7 @@ App::get('/v1/functions/templates/:templateId')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('sdk', new Method(
namespace: 'functions',
group: 'templates',
name: 'getTemplate',
description: '/docs/references/functions/get-template.md',
auth: [AuthType::ADMIN],
+3
View File
@@ -44,6 +44,7 @@ App::get('/v1/graphql')
->label('scope', 'graphql')
->label('sdk', new Method(
namespace: 'graphql',
group: 'graphql',
name: 'get',
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
hide: true,
@@ -90,6 +91,7 @@ App::post('/v1/graphql/mutation')
->label('scope', 'graphql')
->label('sdk', new Method(
namespace: 'graphql',
group: 'graphql',
name: 'mutation',
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
description: '/docs/references/graphql/post.md',
@@ -140,6 +142,7 @@ App::post('/v1/graphql')
->label('scope', 'graphql')
->label('sdk', new Method(
namespace: 'graphql',
group: 'graphql',
name: 'query',
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
description: '/docs/references/graphql/post.md',
+34 -15
View File
@@ -32,6 +32,7 @@ App::get('/v1/health')
->label('scope', 'health.read')
->label('sdk', new Method(
namespace: 'health',
group: 'health',
name: 'get',
auth: [AuthType::KEY],
description: '/docs/references/health/get.md',
@@ -71,6 +72,7 @@ App::get('/v1/health/db')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getDB',
description: '/docs/references/health/get-db.md',
responses: [
@@ -131,6 +133,7 @@ App::get('/v1/health/cache')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getCache',
description: '/docs/references/health/get-cache.md',
responses: [
@@ -195,6 +198,7 @@ App::get('/v1/health/pubsub')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getPubSub',
description: '/docs/references/health/get-pubsub.md',
responses: [
@@ -259,6 +263,7 @@ App::get('/v1/health/time')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getTime',
description: '/docs/references/health/get-time.md',
responses: [
@@ -322,6 +327,7 @@ App::get('/v1/health/queue/webhooks')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueWebhooks',
description: '/docs/references/health/get-queue-webhooks.md',
responses: [
@@ -354,6 +360,7 @@ App::get('/v1/health/queue/logs')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueLogs',
description: '/docs/references/health/get-queue-logs.md',
responses: [
@@ -386,6 +393,7 @@ App::get('/v1/health/certificate')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getCertificate',
description: '/docs/references/health/get-certificate.md',
responses: [
@@ -442,6 +450,7 @@ App::get('/v1/health/queue/certificates')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueCertificates',
description: '/docs/references/health/get-queue-certificates.md',
responses: [
@@ -474,6 +483,7 @@ App::get('/v1/health/queue/builds')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueBuilds',
description: '/docs/references/health/get-queue-builds.md',
responses: [
@@ -506,6 +516,7 @@ App::get('/v1/health/queue/databases')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueDatabases',
description: '/docs/references/health/get-queue-databases.md',
responses: [
@@ -539,6 +550,7 @@ App::get('/v1/health/queue/deletes')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueDeletes',
description: '/docs/references/health/get-queue-deletes.md',
responses: [
@@ -571,6 +583,7 @@ App::get('/v1/health/queue/mails')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueMails',
description: '/docs/references/health/get-queue-mails.md',
responses: [
@@ -603,6 +616,7 @@ App::get('/v1/health/queue/messaging')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueMessaging',
description: '/docs/references/health/get-queue-messaging.md',
responses: [
@@ -635,6 +649,7 @@ App::get('/v1/health/queue/migrations')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueMigrations',
description: '/docs/references/health/get-queue-migrations.md',
responses: [
@@ -667,6 +682,7 @@ App::get('/v1/health/queue/functions')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getQueueFunctions',
description: '/docs/references/health/get-queue-functions.md',
responses: [
@@ -692,15 +708,16 @@ App::get('/v1/health/queue/functions')
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
App::get('/v1/health/queue/usage')
->desc('Get usage queue')
App::get('/v1/health/queue/stats-resources')
->desc('Get stats resources queue')
->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
name: 'getQueueUsage',
description: '/docs/references/health/get-queue-usage.md',
group: 'queue',
name: 'getQueueStatsResources',
description: '/docs/references/health/get-queue-stats-resources.md',
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
@@ -715,7 +732,7 @@ App::get('/v1/health/queue/usage')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::USAGE_QUEUE_NAME));
$size = $publisher->getQueueSize(new Queue(Event::STATS_RESOURCES_QUEUE_NAME));
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -724,15 +741,16 @@ App::get('/v1/health/queue/usage')
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
});
App::get('/v1/health/queue/usage-dump')
->desc('Get usage dump queue')
App::get('/v1/health/queue/stats-usage')
->desc('Get stats usage queue')
->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
name: 'getQueueUsageDump',
description: '/docs/references/health/get-queue-usage-dump.md',
group: 'queue',
name: 'getQueueUsage',
description: '/docs/references/health/get-queue-stats-usage.md',
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
@@ -747,7 +765,7 @@ App::get('/v1/health/queue/usage-dump')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::USAGE_DUMP_QUEUE_NAME));
$size = $publisher->getQueueSize(new Queue(Event::STATS_USAGE_QUEUE_NAME));
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -763,6 +781,7 @@ App::get('/v1/health/storage/local')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'storage',
name: 'getStorageLocal',
description: '/docs/references/health/get-storage-local.md',
responses: [
@@ -812,6 +831,7 @@ App::get('/v1/health/storage')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'storage',
name: 'getStorage',
description: '/docs/references/health/get-storage.md',
responses: [
@@ -859,6 +879,7 @@ App::get('/v1/health/anti-virus')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'health',
name: 'getAntivirus',
description: '/docs/references/health/get-storage-anti-virus.md',
responses: [
@@ -904,6 +925,7 @@ App::get('/v1/health/queue/failed/:name')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
group: 'queue',
name: 'getFailedJobs',
description: '/docs/references/health/get-failed-queue-jobs.md',
responses: [
@@ -920,8 +942,8 @@ App::get('/v1/health/queue/failed/:name')
Event::AUDITS_QUEUE_NAME,
Event::MAILS_QUEUE_NAME,
Event::FUNCTIONS_QUEUE_NAME,
Event::USAGE_QUEUE_NAME,
Event::USAGE_DUMP_QUEUE_NAME,
Event::STATS_RESOURCES_QUEUE_NAME,
Event::STATS_USAGE_QUEUE_NAME,
Event::WEBHOOK_QUEUE_NAME,
Event::CERTIFICATES_QUEUE_NAME,
Event::BUILDS_QUEUE_NAME,
@@ -947,9 +969,6 @@ App::get('/v1/health/stats') // Currently only used internally
->desc('Get system stats')
->groups(['api', 'health'])
->label('scope', 'root')
// ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
// ->label('sdk.namespace', 'health')
// ->label('sdk.method', 'getStats')
->label('docs', false)
->inject('response')
->inject('register')
+10 -2
View File
@@ -17,6 +17,7 @@ App::get('/v1/locale')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'get',
description: '/docs/references/locale/get-locale.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -80,6 +81,7 @@ App::get('/v1/locale/codes')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCodes',
description: '/docs/references/locale/list-locale-codes.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -105,6 +107,7 @@ App::get('/v1/locale/countries')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCountries',
description: '/docs/references/locale/list-countries.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -118,7 +121,7 @@ App::get('/v1/locale/countries')
->inject('response')
->inject('locale')
->action(function (Response $response, Locale $locale) {
$list = Config::getParam('locale-countries'); /* @var $list array */
$list = array_keys(Config::getParam('locale-countries')); /* @var $list array */
$output = [];
foreach ($list as $value) {
@@ -141,6 +144,7 @@ App::get('/v1/locale/countries/eu')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCountriesEU',
description: '/docs/references/locale/list-countries-eu.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -179,6 +183,7 @@ App::get('/v1/locale/countries/phones')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCountriesPhones',
description: '/docs/references/locale/list-countries-phones.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -216,6 +221,7 @@ App::get('/v1/locale/continents')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listContinents',
description: '/docs/references/locale/list-continents.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -229,7 +235,7 @@ App::get('/v1/locale/continents')
->inject('response')
->inject('locale')
->action(function (Response $response, Locale $locale) {
$list = Config::getParam('locale-continents');
$list = array_keys(Config::getParam('locale-continents'));
foreach ($list as $value) {
$output[] = new Document([
@@ -251,6 +257,7 @@ App::get('/v1/locale/currencies')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listCurrencies',
description: '/docs/references/locale/list-currencies.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -277,6 +284,7 @@ App::get('/v1/locale/languages')
->label('scope', 'locale.read')
->label('sdk', new Method(
namespace: 'locale',
group: null,
name: 'listLanguages',
description: '/docs/references/locale/list-languages.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
+118 -40
View File
@@ -30,6 +30,7 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
@@ -63,6 +64,7 @@ App::post('/v1/messaging/providers/mailgun')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createMailgunProvider',
description: '/docs/references/messaging/create-mailgun-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -156,6 +158,7 @@ App::post('/v1/messaging/providers/sendgrid')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createSendgridProvider',
description: '/docs/references/messaging/create-sendgrid-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -237,6 +240,7 @@ App::post('/v1/messaging/providers/smtp')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createSmtpProvider',
description: '/docs/references/messaging/create-smtp-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -331,6 +335,7 @@ App::post('/v1/messaging/providers/msg91')
->label('event', 'providers.[providerId].create')
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createMsg91Provider',
description: '/docs/references/messaging/create-msg91-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -413,6 +418,7 @@ App::post('/v1/messaging/providers/telesign')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createTelesignProvider',
description: '/docs/references/messaging/create-telesign-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -496,6 +502,7 @@ App::post('/v1/messaging/providers/textmagic')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createTextmagicProvider',
description: '/docs/references/messaging/create-textmagic-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -579,6 +586,7 @@ App::post('/v1/messaging/providers/twilio')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createTwilioProvider',
description: '/docs/references/messaging/create-twilio-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -662,6 +670,7 @@ App::post('/v1/messaging/providers/vonage')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createVonageProvider',
description: '/docs/references/messaging/create-vonage-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -745,6 +754,7 @@ App::post('/v1/messaging/providers/fcm')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createFcmProvider',
description: '/docs/references/messaging/create-fcm-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -814,6 +824,7 @@ App::post('/v1/messaging/providers/apns')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'createApnsProvider',
description: '/docs/references/messaging/create-apns-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -903,6 +914,7 @@ App::get('/v1/messaging/providers')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'listProviders',
description: '/docs/references/messaging/list-providers.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -951,10 +963,15 @@ App::get('/v1/messaging/providers')
$cursor->setValue($cursorDocument);
}
try {
$providers = $dbForProject->find('providers', $queries);
$total = $dbForProject->count('providers', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'providers' => $dbForProject->find('providers', $queries),
'total' => $dbForProject->count('providers', $queries, APP_LIMIT_COUNT),
'providers' => $providers,
'total' => $total,
]), Response::MODEL_PROVIDER_LIST);
});
@@ -965,6 +982,7 @@ App::get('/v1/messaging/providers/:providerId/logs')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'listProviderLogs',
description: '/docs/references/messaging/list-provider-logs.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -994,13 +1012,15 @@ App::get('/v1/messaging/providers/:providerId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
// Temp fix for logs
$queries[] = Query::or([
Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))),
Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))),
]);
$audit = new Audit($dbForProject);
$resource = 'provider/' . $providerId;
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$logs = $audit->getLogsByResource($resource, $queries);
$output = [];
foreach ($logs as $i => &$log) {
@@ -1047,7 +1067,7 @@ App::get('/v1/messaging/providers/:providerId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource),
'total' => $audit->countLogsByResource($resource, $queries),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -1059,6 +1079,7 @@ App::get('/v1/messaging/providers/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'getProvider',
description: '/docs/references/messaging/get-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1092,6 +1113,7 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateMailgunProvider',
description: '/docs/references/messaging/update-mailgun-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1204,6 +1226,7 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateSendgridProvider',
description: '/docs/references/messaging/update-sendgrid-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1301,6 +1324,7 @@ App::patch('/v1/messaging/providers/smtp/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateSmtpProvider',
description: '/docs/references/messaging/update-smtp-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1429,6 +1453,7 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateMsg91Provider',
description: '/docs/references/messaging/update-msg91-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1515,6 +1540,7 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateTelesignProvider',
description: '/docs/references/messaging/update-telesign-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1603,6 +1629,7 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateTextmagicProvider',
description: '/docs/references/messaging/update-textmagic-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1691,6 +1718,7 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateTwilioProvider',
description: '/docs/references/messaging/update-twilio-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1779,6 +1807,7 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateVonageProvider',
description: '/docs/references/messaging/update-vonage-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1867,6 +1896,7 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateFcmProvider',
description: '/docs/references/messaging/update-fcm-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -1942,6 +1972,7 @@ App::patch('/v1/messaging/providers/apns/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'updateApnsProvider',
description: '/docs/references/messaging/update-apns-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2043,6 +2074,7 @@ App::delete('/v1/messaging/providers/:providerId')
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'providers',
name: 'deleteProvider',
description: '/docs/references/messaging/delete-provider.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2085,6 +2117,7 @@ App::post('/v1/messaging/topics')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'createTopic',
description: '/docs/references/messaging/create-topic.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2131,6 +2164,7 @@ App::get('/v1/messaging/topics')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'listTopics',
description: '/docs/references/messaging/list-topics.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2179,10 +2213,15 @@ App::get('/v1/messaging/topics')
$cursor->setValue($cursorDocument[0]);
}
try {
$topics = $dbForProject->find('topics', $queries);
$total = $dbForProject->count('topics', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'topics' => $dbForProject->find('topics', $queries),
'total' => $dbForProject->count('topics', $queries, APP_LIMIT_COUNT),
'topics' => $topics,
'total' => $total,
]), Response::MODEL_TOPIC_LIST);
});
@@ -2193,6 +2232,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'listTopicLogs',
description: '/docs/references/messaging/list-topic-logs.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2222,13 +2262,15 @@ App::get('/v1/messaging/topics/:topicId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
// Temp fix for logs
$queries[] = Query::or([
Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))),
Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))),
]);
$audit = new Audit($dbForProject);
$resource = 'topic/' . $topicId;
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$logs = $audit->getLogsByResource($resource, $queries);
$output = [];
@@ -2276,7 +2318,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource),
'total' => $audit->countLogsByResource($resource, $queries),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -2288,6 +2330,7 @@ App::get('/v1/messaging/topics/:topicId')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'getTopic',
description: '/docs/references/messaging/get-topic.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2322,6 +2365,7 @@ App::patch('/v1/messaging/topics/:topicId')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'updateTopic',
description: '/docs/references/messaging/update-topic.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2372,6 +2416,7 @@ App::delete('/v1/messaging/topics/:topicId')
->label('resourceType', RESOURCE_TYPE_TOPICS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'topics',
name: 'deleteTopic',
description: '/docs/references/messaging/delete-topic.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2419,6 +2464,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'createSubscriber',
description: '/docs/references/messaging/create-subscriber.md',
auth: [AuthType::JWT, AuthType::SESSION, AuthType::ADMIN, AuthType::KEY],
@@ -2518,6 +2564,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'listSubscribers',
description: '/docs/references/messaging/list-subscribers.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2575,8 +2622,11 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
$cursor->setValue($cursorDocument);
}
$subscribers = $dbForProject->find('subscribers', $queries);
try {
$subscribers = $dbForProject->find('subscribers', $queries);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) {
return function () use ($subscriber, $dbForProject) {
@@ -2603,6 +2653,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'listSubscriberLogs',
description: '/docs/references/messaging/list-subscriber-logs.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2632,13 +2683,15 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
// Temp fix for logs
$queries[] = Query::or([
Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))),
Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))),
]);
$audit = new Audit($dbForProject);
$resource = 'subscriber/' . $subscriberId;
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$logs = $audit->getLogsByResource($resource, $queries);
$output = [];
@@ -2686,7 +2739,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource),
'total' => $audit->countLogsByResource($resource, $queries),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -2698,6 +2751,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'getSubscriber',
description: '/docs/references/messaging/get-subscriber.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2746,6 +2800,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
->label('sdk', new Method(
namespace: 'messaging',
group: 'subscribers',
name: 'deleteSubscriber',
description: '/docs/references/messaging/delete-subscriber.md',
auth: [AuthType::JWT, AuthType::SESSION, AuthType::ADMIN, AuthType::KEY],
@@ -2812,6 +2867,7 @@ App::post('/v1/messaging/messages/email')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'createEmail',
description: '/docs/references/messaging/create-email.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -2930,7 +2986,7 @@ App::post('/v1/messaging/messages/email')
break;
case MessageStatus::SCHEDULED:
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => System::getEnv('_APP_REGION', 'default'),
'region' => $project->getAttribute('region'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@@ -2970,6 +3026,7 @@ App::post('/v1/messaging/messages/sms')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'createSms',
description: '/docs/references/messaging/create-sms.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3052,7 +3109,7 @@ App::post('/v1/messaging/messages/sms')
break;
case MessageStatus::SCHEDULED:
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => System::getEnv('_APP_REGION', 'default'),
'region' => $project->getAttribute('region'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@@ -3092,6 +3149,7 @@ App::post('/v1/messaging/messages/push')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'createPush',
description: '/docs/references/messaging/create-push.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3269,7 +3327,7 @@ App::post('/v1/messaging/messages/push')
break;
case MessageStatus::SCHEDULED:
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => System::getEnv('_APP_REGION', 'default'),
'region' => $project->getAttribute('region'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@@ -3306,6 +3364,7 @@ App::get('/v1/messaging/messages')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'listMessages',
description: '/docs/references/messaging/list-messages.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3354,10 +3413,15 @@ App::get('/v1/messaging/messages')
$cursor->setValue($cursorDocument);
}
try {
$messages = $dbForProject->find('messages', $queries);
$total = $dbForProject->count('messages', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'messages' => $dbForProject->find('messages', $queries),
'total' => $dbForProject->count('messages', $queries, APP_LIMIT_COUNT),
'messages' => $messages,
'total' => $total,
]), Response::MODEL_MESSAGE_LIST);
});
@@ -3368,6 +3432,7 @@ App::get('/v1/messaging/messages/:messageId/logs')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'logs',
name: 'listMessageLogs',
description: '/docs/references/messaging/list-message-logs.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3397,13 +3462,15 @@ App::get('/v1/messaging/messages/:messageId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
// Temp fix for logs
$queries[] = Query::or([
Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))),
Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))),
]);
$audit = new Audit($dbForProject);
$resource = 'message/' . $messageId;
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$logs = $audit->getLogsByResource($resource, $queries);
$output = [];
@@ -3451,7 +3518,7 @@ App::get('/v1/messaging/messages/:messageId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource),
'total' => $audit->countLogsByResource($resource, $queries),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -3463,6 +3530,7 @@ App::get('/v1/messaging/messages/:messageId/targets')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'listTargets',
description: '/docs/references/messaging/list-message-targets.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3525,10 +3593,15 @@ App::get('/v1/messaging/messages/:messageId/targets')
$cursor->setValue($cursorDocument);
}
try {
$targets = $dbForProject->find('targets', $queries);
$total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'targets' => $dbForProject->find('targets', $queries),
'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT),
'targets' => $targets,
'total' => $total,
]), Response::MODEL_TARGET_LIST);
});
@@ -3539,6 +3612,7 @@ App::get('/v1/messaging/messages/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'getMessage',
description: '/docs/references/messaging/get-message.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3572,6 +3646,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'updateEmail',
description: '/docs/references/messaging/update-email.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3653,7 +3728,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => System::getEnv('_APP_REGION', 'default'),
'region' => $project->getAttribute('region'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@@ -3778,6 +3853,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'updateSms',
description: '/docs/references/messaging/update-sms.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -3854,7 +3930,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => System::getEnv('_APP_REGION', 'default'),
'region' => $project->getAttribute('region'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@@ -3939,6 +4015,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'updatePush',
description: '/docs/references/messaging/update-push.md',
auth: [AuthType::ADMIN, AuthType::KEY],
@@ -4027,7 +4104,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => System::getEnv('_APP_REGION', 'default'),
'region' => $project->getAttribute('region'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@@ -4198,6 +4275,7 @@ App::delete('/v1/messaging/messages/:messageId')
->label('resourceType', RESOURCE_TYPE_MESSAGES)
->label('sdk', new Method(
namespace: 'messaging',
group: 'messages',
name: 'delete',
description: '/docs/references/messaging/delete-message.md',
auth: [AuthType::ADMIN, AuthType::KEY],
+34 -15
View File
@@ -12,6 +12,7 @@ use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
@@ -31,12 +32,13 @@ include_once __DIR__ . '/../shared/api.php';
App::post('/v1/migrations/appwrite')
->groups(['api', 'migrations'])
->desc('Migrate Appwrite data')
->desc('Create Appwrite migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createAppwriteMigration',
description: '/docs/references/migrations/migration-appwrite.md',
auth: [AuthType::ADMIN],
@@ -48,9 +50,9 @@ App::post('/v1/migrations/appwrite')
]
))
->param('resources', [], new ArrayList(new WhiteList(Appwrite::getSupportedResources())), 'List of resources to migrate')
->param('endpoint', '', new URL(), "Source's Appwrite Endpoint")
->param('projectId', '', new UID(), "Source's Project ID")
->param('apiKey', '', new Text(512), "Source's API Key")
->param('endpoint', '', new URL(), 'Source Appwrite endpoint')
->param('projectId', '', new UID(), 'Source Project ID')
->param('apiKey', '', new Text(512), 'Source API Key')
->inject('response')
->inject('dbForProject')
->inject('project')
@@ -92,12 +94,13 @@ App::post('/v1/migrations/appwrite')
App::post('/v1/migrations/firebase')
->groups(['api', 'migrations'])
->desc('Migrate Firebase data')
->desc('Create Firebase migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createFirebaseMigration',
description: '/docs/references/migrations/migration-firebase.md',
auth: [AuthType::ADMIN],
@@ -158,12 +161,13 @@ App::post('/v1/migrations/firebase')
App::post('/v1/migrations/supabase')
->groups(['api', 'migrations'])
->desc('Migrate Supabase data')
->desc('Create Supabase migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createSupabaseMigration',
description: '/docs/references/migrations/migration-supabase.md',
auth: [AuthType::ADMIN],
@@ -224,12 +228,13 @@ App::post('/v1/migrations/supabase')
App::post('/v1/migrations/nhost')
->groups(['api', 'migrations'])
->desc('Migrate NHost data')
->desc('Create NHost migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].create')
->label('audits.event', 'migration.create')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'createNHostMigration',
description: '/docs/references/migrations/migration-nhost.md',
auth: [AuthType::ADMIN],
@@ -296,6 +301,7 @@ App::get('/v1/migrations')
->label('scope', 'migrations.read')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'list',
description: '/docs/references/migrations/list-migrations.md',
auth: [AuthType::ADMIN],
@@ -347,10 +353,15 @@ App::get('/v1/migrations')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$migrations = $dbForProject->find('migrations', $queries);
$total = $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'migrations' => $dbForProject->find('migrations', $queries),
'total' => $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT),
'migrations' => $migrations,
'total' => $total,
]), Response::MODEL_MIGRATION_LIST);
});
@@ -360,6 +371,7 @@ App::get('/v1/migrations/:migrationId')
->label('scope', 'migrations.read')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'get',
description: '/docs/references/migrations/get-migration.md',
auth: [AuthType::ADMIN],
@@ -385,10 +397,11 @@ App::get('/v1/migrations/:migrationId')
App::get('/v1/migrations/appwrite/report')
->groups(['api', 'migrations'])
->desc('Generate a report on Appwrite data')
->desc('Get Appwrite migration report')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'getAppwriteReport',
description: '/docs/references/migrations/migration-appwrite-report.md',
auth: [AuthType::ADMIN],
@@ -408,6 +421,7 @@ App::get('/v1/migrations/appwrite/report')
->inject('project')
->inject('user')
->action(function (array $resources, string $endpoint, string $projectID, string $key, Response $response) {
$appwrite = new Appwrite($projectID, $endpoint, $key);
try {
@@ -432,10 +446,11 @@ App::get('/v1/migrations/appwrite/report')
App::get('/v1/migrations/firebase/report')
->groups(['api', 'migrations'])
->desc('Generate a report on Firebase data')
->desc('Get Firebase migration report')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'getFirebaseReport',
description: '/docs/references/migrations/migration-firebase-report.md',
auth: [AuthType::ADMIN],
@@ -484,10 +499,11 @@ App::get('/v1/migrations/firebase/report')
App::get('/v1/migrations/supabase/report')
->groups(['api', 'migrations'])
->desc('Generate a report on Supabase Data')
->desc('Get Supabase migration report')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'getSupabaseReport',
description: '/docs/references/migrations/migration-supabase-report.md',
auth: [AuthType::ADMIN],
@@ -532,10 +548,11 @@ App::get('/v1/migrations/supabase/report')
App::get('/v1/migrations/nhost/report')
->groups(['api', 'migrations'])
->desc('Generate a report on NHost Data')
->desc('Get NHost migration report')
->label('scope', 'migrations.write')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'getNHostReport',
description: '/docs/references/migrations/migration-nhost-report.md',
auth: [AuthType::ADMIN],
@@ -580,13 +597,14 @@ App::get('/v1/migrations/nhost/report')
App::patch('/v1/migrations/:migrationId')
->groups(['api', 'migrations'])
->desc('Retry migration')
->desc('Update retry migration')
->label('scope', 'migrations.write')
->label('event', 'migrations.[migrationId].retry')
->label('audits.event', 'migration.retry')
->label('audits.resource', 'migrations/{request.migrationId}')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'retry',
description: '/docs/references/migrations/retry-migration.md',
auth: [AuthType::ADMIN],
@@ -637,6 +655,7 @@ App::delete('/v1/migrations/:migrationId')
->label('audits.resource', 'migrations/{request.migrationId}')
->label('sdk', new Method(
namespace: 'migrations',
group: null,
name: 'delete',
description: '/docs/references/migrations/delete-migration.md',
auth: [AuthType::ADMIN],
+22 -4
View File
@@ -26,6 +26,7 @@ App::get('/v1/project/usage')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'getUsage',
description: '/docs/references/project/get-usage.md',
auth: [AuthType::ADMIN],
@@ -40,14 +41,18 @@ App::get('/v1/project/usage')
->param('endDate', '', new DateTimeValidator(), 'End date for the usage')
->param('period', '1d', new WhiteList(['1h', '1d']), 'Period used', true)
->inject('response')
->inject('project')
->inject('dbForProject')
->inject('getLogsDB')
->inject('smsRates')
->action(function (string $startDate, string $endDate, string $period, Response $response, Database $dbForProject, array $smsRates) {
->action(function (string $startDate, string $endDate, string $period, Response $response, Document $project, Database $dbForProject, callable $getLogsDB, array $smsRates) {
$stats = $total = $usage = [];
$format = 'Y-m-d 00:00:00';
$firstDay = (new DateTime($startDate))->format($format);
$lastDay = (new DateTime($endDate))->format($format);
$dbForLogs = call_user_func($getLogsDB, $project);
$metrics = [
'total' => [
METRIC_EXECUTIONS,
@@ -63,6 +68,7 @@ App::get('/v1/project/usage')
METRIC_BUILDS_STORAGE,
METRIC_DATABASES_OPERATIONS_READS,
METRIC_DATABASES_OPERATIONS_WRITES,
METRIC_FILES_IMAGES_TRANSFORMED,
],
'period' => [
METRIC_NETWORK_REQUESTS,
@@ -75,6 +81,7 @@ App::get('/v1/project/usage')
METRIC_BUILDS_MB_SECONDS,
METRIC_DATABASES_OPERATIONS_READS,
METRIC_DATABASES_OPERATIONS_WRITES,
METRIC_FILES_IMAGES_TRANSFORMED,
]
];
@@ -93,9 +100,11 @@ App::get('/v1/project/usage')
'1d' => 'Y-m-d\T00:00:00.000P',
};
Authorization::skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) {
Authorization::skip(function () use ($dbForProject, $dbForLogs, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) {
foreach ($metrics['total'] as $metric) {
$result = $dbForProject->findOne('stats', [
$db = ($metric === METRIC_FILES_IMAGES_TRANSFORMED) ? $dbForLogs : $dbForProject;
$result = $db->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
@@ -103,7 +112,9 @@ App::get('/v1/project/usage')
}
foreach ($metrics['period'] as $metric) {
$results = $dbForProject->find('stats', [
$db = ($metric === METRIC_FILES_IMAGES_TRANSFORMED) ? $dbForLogs : $dbForProject;
$results = $db->find('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', [$period]),
Query::greaterThanEqual('time', $firstDay),
@@ -363,6 +374,8 @@ App::get('/v1/project/usage')
'authPhoneTotal' => $authPhoneTotal,
'authPhoneEstimate' => $authPhoneEstimate,
'authPhoneCountryBreakdown' => $authPhoneCountryBreakdown,
'imageTransformations' => $usage[METRIC_FILES_IMAGES_TRANSFORMED],
'imageTransformationsTotal' => $total[METRIC_FILES_IMAGES_TRANSFORMED],
]), Response::MODEL_USAGE_PROJECT);
});
@@ -375,6 +388,7 @@ App::post('/v1/project/variables')
->label('audits.event', 'variable.create')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'createVariable',
description: '/docs/references/project/create-variable.md',
auth: [AuthType::ADMIN],
@@ -434,6 +448,7 @@ App::get('/v1/project/variables')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'listVariables',
description: '/docs/references/project/list-variables.md',
auth: [AuthType::ADMIN],
@@ -464,6 +479,7 @@ App::get('/v1/project/variables/:variableId')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'getVariable',
description: '/docs/references/project/get-variable.md',
auth: [AuthType::ADMIN],
@@ -493,6 +509,7 @@ App::put('/v1/project/variables/:variableId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'updateVariable',
description: '/docs/references/project/update-variable.md',
auth: [AuthType::ADMIN],
@@ -544,6 +561,7 @@ App::delete('/v1/project/variables/:variableId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'project',
group: null,
name: 'deleteVariable',
description: '/docs/references/project/delete-variable.md',
auth: [AuthType::ADMIN],
+68 -8
View File
@@ -28,6 +28,7 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -68,6 +69,7 @@ App::post('/v1/projects')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'create',
description: '/docs/references/projects/create.md',
auth: [AuthType::ADMIN],
@@ -138,6 +140,14 @@ App::post('/v1/projects')
$databases = Config::getParam('pools-database', []);
if ($region !== 'default') {
$databaseKeys = System::getEnv('_APP_DATABASE_KEYS', '');
$keys = explode(',', $databaseKeys);
$databases = array_filter($keys, function ($value) use ($region) {
return str_contains($value, $region);
});
}
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
$index = \array_search($databaseOverride, $databases);
if ($index !== false) {
@@ -205,17 +215,17 @@ App::post('/v1/projects')
$dsn = new DSN('mysql://' . $dsn);
}
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
$dbForProject = new Database($adapter, $cache);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
$projectTables = !\in_array($dsn->getHost(), $sharedTables);
$sharedTablesV1 = \in_array($dsn->getHost(), $sharedTablesV1);
$sharedTablesV2 = !$projectTables && !$sharedTablesV1;
$sharedTables = $sharedTablesV1 || $sharedTablesV2;
if (!$sharedTablesV2) {
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
$dbForProject = new Database($adapter, $cache);
if ($sharedTables) {
$dbForProject
->setSharedTables(true)
@@ -297,6 +307,7 @@ App::get('/v1/projects')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'list',
description: '/docs/references/projects/list.md',
auth: [AuthType::ADMIN],
@@ -349,10 +360,15 @@ App::get('/v1/projects')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$projects = $dbForPlatform->find('projects', $queries);
$total = $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'projects' => $dbForPlatform->find('projects', $queries),
'total' => $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT),
'projects' => $projects,
'total' => $total,
]), Response::MODEL_PROJECT_LIST);
});
@@ -362,6 +378,7 @@ App::get('/v1/projects/:projectId')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'get',
description: '/docs/references/projects/get.md',
auth: [AuthType::ADMIN],
@@ -394,6 +411,7 @@ App::patch('/v1/projects/:projectId')
->label('audits.resource', 'project/{request.projectId}')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'update',
description: '/docs/references/projects/update.md',
auth: [AuthType::ADMIN],
@@ -447,6 +465,7 @@ App::patch('/v1/projects/:projectId/team')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateTeam',
description: '/docs/references/projects/update-team.md',
auth: [AuthType::ADMIN],
@@ -521,6 +540,7 @@ App::patch('/v1/projects/:projectId/service')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateServiceStatus',
description: '/docs/references/projects/update-service-status.md',
auth: [AuthType::ADMIN],
@@ -558,6 +578,7 @@ App::patch('/v1/projects/:projectId/service/all')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateServiceStatusAll',
description: '/docs/references/projects/update-service-status-all.md',
auth: [AuthType::ADMIN],
@@ -598,6 +619,7 @@ App::patch('/v1/projects/:projectId/api')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateApiStatus',
description: '/docs/references/projects/update-api-status.md',
auth: [AuthType::ADMIN],
@@ -635,6 +657,7 @@ App::patch('/v1/projects/:projectId/api/all')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'updateApiStatusAll',
description: '/docs/references/projects/update-api-status-all.md',
auth: [AuthType::ADMIN],
@@ -675,6 +698,7 @@ App::patch('/v1/projects/:projectId/oauth2')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateOAuth2',
description: '/docs/references/projects/update-oauth2.md',
auth: [AuthType::ADMIN],
@@ -725,6 +749,7 @@ App::patch('/v1/projects/:projectId/auth/session-alerts')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateSessionAlerts',
description: '/docs/references/projects/update-session-alerts.md',
auth: [AuthType::ADMIN],
@@ -762,6 +787,7 @@ App::patch('/v1/projects/:projectId/auth/memberships-privacy')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateMembershipsPrivacy',
description: '/docs/references/projects/update-memberships-privacy.md',
auth: [AuthType::ADMIN],
@@ -803,6 +829,7 @@ App::patch('/v1/projects/:projectId/auth/limit')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthLimit',
description: '/docs/references/projects/update-auth-limit.md',
auth: [AuthType::ADMIN],
@@ -840,6 +867,7 @@ App::patch('/v1/projects/:projectId/auth/duration')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthDuration',
description: '/docs/references/projects/update-auth-duration.md',
auth: [AuthType::ADMIN],
@@ -877,6 +905,7 @@ App::patch('/v1/projects/:projectId/auth/:method')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthStatus',
description: '/docs/references/projects/update-auth-status.md',
auth: [AuthType::ADMIN],
@@ -917,6 +946,7 @@ App::patch('/v1/projects/:projectId/auth/password-history')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthPasswordHistory',
description: '/docs/references/projects/update-auth-password-history.md',
auth: [AuthType::ADMIN],
@@ -954,6 +984,7 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthPasswordDictionary',
description: '/docs/references/projects/update-auth-password-dictionary.md',
auth: [AuthType::ADMIN],
@@ -986,11 +1017,12 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
});
App::patch('/v1/projects/:projectId/auth/personal-data')
->desc('Enable or disable checking user passwords for similarity with their personal data.')
->desc('Update personal data check')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updatePersonalDataCheck',
description: '/docs/references/projects/update-personal-data-check.md',
auth: [AuthType::ADMIN],
@@ -1028,6 +1060,7 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateAuthSessionsLimit',
description: '/docs/references/projects/update-auth-sessions-limit.md',
auth: [AuthType::ADMIN],
@@ -1065,6 +1098,7 @@ App::patch('/v1/projects/:projectId/auth/mock-numbers')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateMockNumbers',
description: '/docs/references/projects/update-mock-numbers.md',
auth: [AuthType::ADMIN],
@@ -1112,6 +1146,7 @@ App::delete('/v1/projects/:projectId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'delete',
description: '/docs/references/projects/delete.md',
auth: [AuthType::ADMIN],
@@ -1155,6 +1190,7 @@ App::post('/v1/projects/:projectId/webhooks')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'createWebhook',
description: '/docs/references/projects/create-webhook.md',
auth: [AuthType::ADMIN],
@@ -1219,6 +1255,7 @@ App::get('/v1/projects/:projectId/webhooks')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'listWebhooks',
description: '/docs/references/projects/list-webhooks.md',
auth: [AuthType::ADMIN],
@@ -1257,6 +1294,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
->label('scope', 'projects.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'getWebhook',
description: '/docs/references/projects/get-webhook.md',
auth: [AuthType::ADMIN],
@@ -1297,6 +1335,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'updateWebhook',
description: '/docs/references/projects/update-webhook.md',
auth: [AuthType::ADMIN],
@@ -1362,6 +1401,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'updateWebhookSignature',
description: '/docs/references/projects/update-webhook-signature.md',
auth: [AuthType::ADMIN],
@@ -1407,6 +1447,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'webhooks',
name: 'deleteWebhook',
description: '/docs/references/projects/delete-webhook.md',
auth: [AuthType::ADMIN],
@@ -1454,6 +1495,7 @@ App::post('/v1/projects/:projectId/keys')
->label('scope', 'keys.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'createKey',
description: '/docs/references/projects/create-key.md',
auth: [AuthType::ADMIN],
@@ -1510,6 +1552,7 @@ App::get('/v1/projects/:projectId/keys')
->label('scope', 'keys.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'listKeys',
description: '/docs/references/projects/list-keys.md',
auth: [AuthType::ADMIN],
@@ -1548,6 +1591,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
->label('scope', 'keys.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'getKey',
description: '/docs/references/projects/get-key.md',
auth: [AuthType::ADMIN],
@@ -1588,6 +1632,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
->label('scope', 'keys.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'updateKey',
description: '/docs/references/projects/update-key.md',
auth: [AuthType::ADMIN],
@@ -1640,6 +1685,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
->label('scope', 'keys.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'keys',
name: 'deleteKey',
description: '/docs/references/projects/delete-key.md',
auth: [AuthType::ADMIN],
@@ -1687,6 +1733,7 @@ App::post('/v1/projects/:projectId/jwts')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'createJWT',
description: '/docs/references/projects/create-jwt.md',
auth: [AuthType::ADMIN],
@@ -1730,6 +1777,7 @@ App::post('/v1/projects/:projectId/platforms')
->label('scope', 'platforms.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'createPlatform',
description: '/docs/references/projects/create-platform.md',
auth: [AuthType::ADMIN],
@@ -1786,6 +1834,7 @@ App::get('/v1/projects/:projectId/platforms')
->label('scope', 'platforms.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'listPlatforms',
description: '/docs/references/projects/list-platforms.md',
auth: [AuthType::ADMIN],
@@ -1824,6 +1873,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
->label('scope', 'platforms.read')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'getPlatform',
description: '/docs/references/projects/get-platform.md',
auth: [AuthType::ADMIN],
@@ -1864,6 +1914,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
->label('scope', 'platforms.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'updatePlatform',
description: '/docs/references/projects/update-platform.md',
auth: [AuthType::ADMIN],
@@ -1919,6 +1970,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
->label('scope', 'platforms.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'platforms',
name: 'deletePlatform',
description: '/docs/references/projects/delete-platform.md',
auth: [AuthType::ADMIN],
@@ -1966,6 +2018,7 @@ App::patch('/v1/projects/:projectId/smtp')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'updateSmtp',
description: '/docs/references/projects/update-smtp.md',
auth: [AuthType::ADMIN],
@@ -2062,6 +2115,7 @@ App::post('/v1/projects/:projectId/smtp/tests')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'createSmtpTest',
description: '/docs/references/projects/create-smtp-test.md',
auth: [AuthType::ADMIN],
@@ -2128,6 +2182,7 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'getSmsTemplate',
description: '/docs/references/projects/get-sms-template.md',
auth: [AuthType::ADMIN],
@@ -2175,6 +2230,7 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'getEmailTemplate',
description: '/docs/references/projects/get-email-template.md',
auth: [AuthType::ADMIN],
@@ -2233,6 +2289,7 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'updateSmsTemplate',
description: '/docs/references/projects/update-sms-template.md',
auth: [AuthType::ADMIN],
@@ -2279,6 +2336,7 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'updateEmailTemplate',
description: '/docs/references/projects/update-email-template.md',
auth: [AuthType::ADMIN],
@@ -2335,6 +2393,7 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'deleteSmsTemplate',
description: '/docs/references/projects/delete-sms-template.md',
auth: [AuthType::ADMIN],
@@ -2380,11 +2439,12 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
});
App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
->desc('Reset custom email template')
->desc('Delete custom email template')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'templates',
name: 'deleteEmailTemplate',
description: '/docs/references/projects/delete-email-template.md',
auth: [AuthType::ADMIN],
+21 -3
View File
@@ -35,6 +35,7 @@ App::post('/v1/proxy/rules')
->label('audits.resource', 'rule/{response.$id}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'createRule',
description: '/docs/references/proxy/create-rule.md',
auth: [AuthType::ADMIN],
@@ -55,14 +56,25 @@ App::post('/v1/proxy/rules')
->inject('dbForPlatform')
->inject('dbForProject')
->action(function (string $domain, string $resourceType, string $resourceId, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject) {
$mainDomain = System::getEnv('_APP_DOMAIN', '');
if ($domain === $mainDomain) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.');
}
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if ($functionsDomain != '' && str_ends_with($domain, $functionsDomain)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or it\'s subdomain to specific resource. Please use different domain.');
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS');
$denyListDomains = System::getEnv('_APP_CUSTOM_DOMAIN_DENY_LIST');
if (!empty($denyListDomains)) {
$functionsDomain .= ',' . $denyListDomains;
}
$deniedDomains = array_map('trim', explode(',', $functionsDomain));
foreach ($deniedDomains as $deniedDomain) {
if (str_ends_with($domain, $deniedDomain)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or its subdomain to a specific resource. Please use a different domain.');
}
}
if ($domain === 'localhost' || $domain === APP_HOSTNAME_INTERNAL) {
@@ -130,6 +142,8 @@ App::post('/v1/proxy/rules')
'resourceId' => $resourceId,
'resourceInternalId' => $resourceInternalId,
'certificateId' => '',
'owner' => '',
'region' => $project->getAttribute('region')
]);
$status = 'created';
@@ -171,6 +185,7 @@ App::get('/v1/proxy/rules')
->label('scope', 'rules.read')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'listRules',
description: '/docs/references/proxy/list-rules.md',
auth: [AuthType::ADMIN],
@@ -245,6 +260,7 @@ App::get('/v1/proxy/rules/:ruleId')
->label('scope', 'rules.read')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'getRule',
description: '/docs/references/proxy/get-rule.md',
auth: [AuthType::ADMIN],
@@ -282,6 +298,7 @@ App::delete('/v1/proxy/rules/:ruleId')
->label('audits.resource', 'rule/{request.ruleId}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'deleteRule',
description: '/docs/references/proxy/delete-rule.md',
auth: [AuthType::ADMIN],
@@ -326,6 +343,7 @@ App::patch('/v1/proxy/rules/:ruleId/verification')
->label('audits.resource', 'rule/{response.$id}')
->label('sdk', new Method(
namespace: 'proxy',
group: null,
name: 'updateRuleVerification',
description: '/docs/references/proxy/update-rule-verification.md',
auth: [AuthType::ADMIN],
+49 -28
View File
@@ -6,7 +6,6 @@ use Appwrite\Auth\Auth;
use Appwrite\ClamAV\Network;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception;
use Appwrite\OpenSSL\OpenSSL;
use Appwrite\SDK\AuthType;
@@ -25,6 +24,7 @@ use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\NotFound as NotFoundException;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -63,6 +63,7 @@ App::post('/v1/storage/buckets')
->label('audits.resource', 'bucket/{response.$id}')
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'createBucket',
description: '/docs/references/storage/create-bucket.md',
auth: [AuthType::KEY],
@@ -165,6 +166,7 @@ App::get('/v1/storage/buckets')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'listBuckets',
description: '/docs/references/storage/list-buckets.md',
auth: [AuthType::KEY],
@@ -217,10 +219,15 @@ App::get('/v1/storage/buckets')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$buckets = $dbForProject->find('buckets', $queries);
$total = $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'buckets' => $dbForProject->find('buckets', $queries),
'total' => $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT),
'buckets' => $buckets,
'total' => $total,
]), Response::MODEL_BUCKET_LIST);
});
@@ -231,6 +238,7 @@ App::get('/v1/storage/buckets/:bucketId')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'getBucket',
description: '/docs/references/storage/get-bucket.md',
auth: [AuthType::KEY],
@@ -265,6 +273,7 @@ App::put('/v1/storage/buckets/:bucketId')
->label('audits.resource', 'bucket/{response.$id}')
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'updateBucket',
description: '/docs/references/storage/update-bucket.md',
auth: [AuthType::KEY],
@@ -335,6 +344,7 @@ App::delete('/v1/storage/buckets/:bucketId')
->label('audits.resource', 'bucket/{request.bucketId}')
->label('sdk', new Method(
namespace: 'storage',
group: 'buckets',
name: 'deleteBucket',
description: '/docs/references/storage/delete-bucket.md',
auth: [AuthType::KEY],
@@ -388,6 +398,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'createFile',
description: '/docs/references/storage/create-file.md',
type: MethodType::UPLOAD,
@@ -757,6 +768,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'listFiles',
description: '/docs/references/storage/list-files.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -838,6 +850,8 @@ App::get('/v1/storage/buckets/:bucketId/files')
}
} catch (NotFoundException) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
@@ -854,6 +868,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'getFile',
description: '/docs/references/storage/get-file.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -910,6 +925,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->label('cache.resource', 'file/{request.fileId}')
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'getFilePreview',
description: '/docs/references/storage/get-file-preview.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -935,15 +951,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->param('rotation', 0, new Range(-360, 360), 'Preview image rotation in degrees. Pass an integer between -360 and 360.', true)
->param('background', '', new HexColor(), 'Preview image background color. Only works with transparent images (png). Use a valid HEX color, no # is needed for prefix.', true)
->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true)
->inject('request')
->inject('response')
->inject('project')
->inject('dbForProject')
->inject('mode')
->inject('deviceForFiles')
->inject('deviceForLocal')
->inject('queueForUsage')
->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Request $request, Response $response, Document $project, Database $dbForProject, string $mode, Device $deviceForFiles, Device $deviceForLocal, Usage $queueForUsage) {
->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Response $response, Database $dbForProject, Device $deviceForFiles, Device $deviceForLocal) {
if (!\extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
@@ -1071,22 +1083,19 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg'];
$queueForUsage
->addMetric(METRIC_FILES_TRANSFORMATIONS, 1)
->addMetric(str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_TRANSFORMATIONS), 1)
;
$transformedAt = $file->getAttribute('transformedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) {
$file->setAttribute('transformedAt', DateTime::now());
Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
//Do not update transformedAt if it's a console user
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
$transformedAt = $file->getAttribute('transformedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) {
$file->setAttribute('transformedAt', DateTime::now());
Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
}
}
$response
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType($contentType)
->file($data)
;
->file($data);
unset($image);
});
@@ -1099,6 +1108,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'getFileDownload',
description: '/docs/references/storage/get-file-download.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1247,6 +1257,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'getFileView',
description: '/docs/references/storage/get-file-view.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1404,9 +1415,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->groups(['api', 'storage'])
->label('scope', 'public')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location')
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
->param('fileId', '', new UID(), 'File ID.')
->param('jwt', '', new Text(2048, 0), 'JSON Web Token to validate', true)
@@ -1567,6 +1575,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'updateFile',
description: '/docs/references/storage/update-file.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1681,6 +1690,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('sdk', new Method(
namespace: 'storage',
group: 'files',
name: 'deleteFile',
description: '/docs/references/storage/delete-file.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1780,6 +1790,7 @@ App::get('/v1/storage/usage')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: null,
name: 'getUsage',
description: '/docs/references/storage/get-usage.md',
auth: [AuthType::ADMIN],
@@ -1866,6 +1877,7 @@ App::get('/v1/storage/:bucketId/usage')
->label('resourceType', RESOURCE_TYPE_BUCKETS)
->label('sdk', new Method(
namespace: 'storage',
group: null,
name: 'getBucketUsage',
description: '/docs/references/storage/get-bucket-usage.md',
auth: [AuthType::ADMIN],
@@ -1879,9 +1891,12 @@ App::get('/v1/storage/:bucketId/usage')
->param('bucketId', '', new UID(), 'Bucket ID.')
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->inject('response')
->inject('project')
->inject('dbForProject')
->action(function (string $bucketId, string $range, Response $response, Database $dbForProject) {
->inject('getLogsDB')
->action(function (string $bucketId, string $range, Response $response, Document $project, Database $dbForProject, callable $getLogsDB) {
$dbForLogs = call_user_func($getLogsDB, $project);
$bucket = $dbForProject->getDocument('buckets', $bucketId);
if ($bucket->isEmpty()) {
@@ -1894,12 +1909,16 @@ App::get('/v1/storage/:bucketId/usage')
$metrics = [
str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES),
str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE),
str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED),
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
Authorization::skip(function () use ($dbForProject, $dbForLogs, $bucket, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
$db = ($metric === str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED))
? $dbForLogs
: $dbForProject;
$result = $db->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
@@ -1907,7 +1926,7 @@ App::get('/v1/storage/:bucketId/usage')
$stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit'];
$period = $days['period'];
$results = $dbForProject->find('stats', [
$results = $db->find('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', [$period]),
Query::limit($limit),
@@ -1948,5 +1967,7 @@ App::get('/v1/storage/:bucketId/usage')
'filesStorageTotal' => $usage[$metrics[1]]['total'],
'files' => $usage[$metrics[0]]['data'],
'storage' => $usage[$metrics[1]]['data'],
'imageTransformations' => $usage[$metrics[2]]['data'],
'imageTransformationsTotal' => $usage[$metrics[2]]['total'],
]), Response::MODEL_USAGE_BUCKETS);
});
+75 -33
View File
@@ -8,7 +8,7 @@ use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
use Appwrite\Event\Usage;
use Appwrite\Event\StatsUsage;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Platform\Workers\Deletes;
@@ -33,6 +33,7 @@ use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Authorization as AuthorizationException;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -62,6 +63,7 @@ App::post('/v1/teams')
->label('audits.resource', 'team/{response.$id}')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'create',
description: '/docs/references/teams/create-team.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -152,6 +154,7 @@ App::get('/v1/teams')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'list',
description: '/docs/references/teams/list-teams.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -204,9 +207,12 @@ App::get('/v1/teams')
}
$filterQueries = Query::groupByType($queries)['filters'];
$results = $dbForProject->find('teams', $queries);
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
try {
$results = $dbForProject->find('teams', $queries);
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'teams' => $results,
@@ -220,6 +226,7 @@ App::get('/v1/teams/:teamId')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'get',
description: '/docs/references/teams/get-team.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -250,6 +257,7 @@ App::get('/v1/teams/:teamId/prefs')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'getPrefs',
description: '/docs/references/teams/get-team-prefs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -285,6 +293,7 @@ App::put('/v1/teams/:teamId')
->label('audits.resource', 'team/{response.$id}')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'updateName',
description: '/docs/references/teams/update-team-name.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -332,6 +341,7 @@ App::put('/v1/teams/:teamId/prefs')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'updatePrefs',
description: '/docs/references/teams/update-team-prefs.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -371,6 +381,7 @@ App::delete('/v1/teams/:teamId')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'teams',
name: 'delete',
description: '/docs/references/teams/delete-team.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -429,6 +440,7 @@ App::post('/v1/teams/:teamId/memberships')
->label('audits.userId', '{request.userId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'createMembership',
description: '/docs/references/teams/create-team-membership.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -466,9 +478,9 @@ App::post('/v1/teams/:teamId/memberships')
->inject('queueForMessaging')
->inject('queueForEvents')
->inject('timelimit')
->inject('queueForUsage')
->inject('queueForStatsUsage')
->inject('plan')
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, Usage $queueForUsage, array $plan) {
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) {
$isAppUser = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
@@ -488,7 +500,7 @@ App::post('/v1/teams/:teamId/memberships')
}
$email = \strtolower($email);
$name = (empty($name)) ? $email : $name;
$name = empty($name) ? $email : $name;
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -507,7 +519,7 @@ App::post('/v1/teams/:teamId/memberships')
}
$email = $invitee->getAttribute('email', '');
$phone = $invitee->getAttribute('phone', '');
$name = empty($name) ? $invitee->getAttribute('name', '') : $name;
$name = $invitee->getAttribute('name', '') ?: $name;
} elseif (!empty($email)) {
$invitee = $dbForProject->findOne('users', [Query::equal('email', [$email])]); // Get user by email address
if (!$invitee->isEmpty() && !empty($phone) && $invitee->getAttribute('phone', '') !== $phone) {
@@ -588,9 +600,8 @@ App::post('/v1/teams/:teamId/memberships')
Query::equal('teamInternalId', [$team->getInternalId()]),
]);
$secret = Auth::tokenGenerator();
if ($membership->isEmpty()) {
$secret = Auth::tokenGenerator();
$membershipId = ID::unique();
$membership = new Document([
'$id' => $membershipId,
@@ -616,9 +627,13 @@ App::post('/v1/teams/:teamId/memberships')
$membership = ($isPrivilegedUser || $isAppUser) ?
Authorization::skip(fn () => $dbForProject->createDocument('memberships', $membership)) :
$dbForProject->createDocument('memberships', $membership);
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
} else {
if ($isPrivilegedUser || $isAppUser) {
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
}
} elseif ($membership->getAttribute('confirm') === false) {
$membership->setAttribute('secret', Auth::hash($secret));
$membership->setAttribute('invited', DateTime::now());
if ($isPrivilegedUser || $isAppUser) {
@@ -629,9 +644,10 @@ App::post('/v1/teams/:teamId/memberships')
$membership = ($isPrivilegedUser || $isAppUser) ?
Authorization::skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), $membership)) :
$dbForProject->updateDocument('memberships', $membership->getId(), $membership);
} else {
throw new Exception(Exception::MEMBERSHIP_ALREADY_CONFIRMED);
}
if ($isPrivilegedUser || $isAppUser) {
$dbForProject->purgeCachedDocument('users', $invitee->getId());
} else {
@@ -714,7 +730,7 @@ App::post('/v1/teams/:teamId/memberships')
->setSubject($subject)
->setBody($body)
->setRecipient($invitee->getAttribute('email'))
->setName($invitee->getAttribute('name'))
->setName($invitee->getAttribute('name', ''))
->setVariables($emailVariables)
->trigger();
@@ -757,11 +773,11 @@ App::post('/v1/teams/:teamId/memberships')
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForUsage
$queueForStatsUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
@@ -792,6 +808,7 @@ App::get('/v1/teams/:teamId/memberships')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'listMemberships',
description: '/docs/references/teams/list-team-members.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -855,17 +872,20 @@ App::get('/v1/teams/:teamId/memberships')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$memberships = $dbForProject->find(
collection: 'memberships',
queries: $queries,
);
$total = $dbForProject->count(
collection: 'memberships',
queries: $filterQueries,
max: APP_LIMIT_COUNT
);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$memberships = $dbForProject->find(
collection: 'memberships',
queries: $queries,
);
$total = $dbForProject->count(
collection: 'memberships',
queries: $filterQueries,
max: APP_LIMIT_COUNT
);
$memberships = array_filter($memberships, fn (Document $membership) => !empty($membership->getAttribute('userId')));
@@ -930,6 +950,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'getMembership',
description: '/docs/references/teams/get-team-member.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1016,6 +1037,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'updateMembership',
description: '/docs/references/teams/update-team-membership.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1030,7 +1052,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->param('membershipId', '', new UID(), 'Membership ID.')
->param('roles', [], function (Document $project) {
if ($project->getId() === 'console') {
;
$roles = array_keys(Config::getParam('roles', []));
array_filter($roles, function ($role) {
return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]);
@@ -1042,9 +1063,10 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->inject('request')
->inject('response')
->inject('user')
->inject('project')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -1065,6 +1087,21 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
$isAppUser = Auth::isAppUser(Authorization::getRoles());
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
if ($project->getId() === 'console') {
// Quick check: fetch up to 2 owners to determine if only one exists
$ownersCount = $dbForProject->count(
collection: 'memberships',
queries: [Query::contains('roles', ['owner'])],
max: 2
);
// Prevent role change if there's only one owner left,
// the requester is that owner, and the new `$roles` no longer include 'owner'!
if ($ownersCount === 1 && $isOwner && !\in_array('owner', $roles)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'There must be at least one owner in the organization.');
}
}
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to modify roles');
}
@@ -1104,6 +1141,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->label('audits.userId', '{request.userId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'updateMembershipStatus',
description: '/docs/references/teams/update-team-membership-status.md',
auth: [AuthType::SESSION, AuthType::JWT],
@@ -1261,6 +1299,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
->label('audits.resource', 'team/{request.teamId}')
->label('sdk', new Method(
namespace: 'teams',
group: 'memberships',
name: 'deleteMembership',
description: '/docs/references/teams/delete-team-membership.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -1331,6 +1370,7 @@ App::get('/v1/teams/:teamId/logs')
->label('scope', 'teams.read')
->label('sdk', new Method(
namespace: 'teams',
group: 'logs',
name: 'listLogs',
description: '/docs/references/teams/get-team-logs.md',
auth: [AuthType::ADMIN],
@@ -1361,13 +1401,15 @@ App::get('/v1/teams/:teamId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
// Temp fix for logs
$queries[] = Query::or([
Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))),
Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))),
]);
$audit = new Audit($dbForProject);
$resource = 'team/' . $team->getId();
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$logs = $audit->getLogsByResource($resource, $queries);
$output = [];
@@ -1414,7 +1456,7 @@ App::get('/v1/teams/:teamId/logs')
}
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource),
'total' => $audit->countLogsByResource($resource, $queries),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
+76 -15
View File
@@ -33,6 +33,7 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -191,6 +192,7 @@ App::post('/v1/users')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'create',
description: '/docs/references/users/create-user.md',
auth: [AuthType::KEY],
@@ -225,6 +227,7 @@ App::post('/v1/users/bcrypt')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createBcryptUser',
description: '/docs/references/users/create-bcrypt-user.md',
auth: [AuthType::KEY],
@@ -259,6 +262,7 @@ App::post('/v1/users/md5')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createMD5User',
description: '/docs/references/users/create-md5-user.md',
auth: [AuthType::KEY],
@@ -293,6 +297,7 @@ App::post('/v1/users/argon2')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createArgon2User',
description: '/docs/references/users/create-argon2-user.md',
auth: [AuthType::KEY],
@@ -327,6 +332,7 @@ App::post('/v1/users/sha')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createSHAUser',
description: '/docs/references/users/create-sha-user.md',
auth: [AuthType::KEY],
@@ -368,6 +374,7 @@ App::post('/v1/users/phpass')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createPHPassUser',
description: '/docs/references/users/create-phpass-user.md',
auth: [AuthType::KEY],
@@ -402,6 +409,7 @@ App::post('/v1/users/scrypt')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createScryptUser',
description: '/docs/references/users/create-scrypt-user.md',
auth: [AuthType::KEY],
@@ -449,6 +457,7 @@ App::post('/v1/users/scrypt-modified')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'createScryptModifiedUser',
description: '/docs/references/users/create-scrypt-modified-user.md',
auth: [AuthType::KEY],
@@ -487,6 +496,7 @@ App::post('/v1/users/:userId/targets')
->label('scope', 'targets.write')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'createTarget',
description: '/docs/references/users/create-target.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -578,6 +588,7 @@ App::get('/v1/users')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'list',
description: '/docs/references/users/list-users.md',
auth: [AuthType::KEY],
@@ -630,10 +641,15 @@ App::get('/v1/users')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$users = $dbForProject->find('users', $queries);
$total = $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'users' => $dbForProject->find('users', $queries),
'total' => $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT),
'users' => $users,
'total' => $total,
]), Response::MODEL_USER_LIST);
});
@@ -643,6 +659,7 @@ App::get('/v1/users/:userId')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'get',
description: '/docs/references/users/get-user.md',
auth: [AuthType::KEY],
@@ -673,6 +690,7 @@ App::get('/v1/users/:userId/prefs')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'getPrefs',
description: '/docs/references/users/get-user-prefs.md',
auth: [AuthType::KEY],
@@ -705,6 +723,7 @@ App::get('/v1/users/:userId/targets/:targetId')
->label('scope', 'targets.read')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'getTarget',
description: '/docs/references/users/get-user-target.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -742,6 +761,7 @@ App::get('/v1/users/:userId/sessions')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'listSessions',
description: '/docs/references/users/list-user-sessions.md',
auth: [AuthType::KEY],
@@ -788,6 +808,7 @@ App::get('/v1/users/:userId/memberships')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'memberships',
name: 'listMemberships',
description: '/docs/references/users/list-user-memberships.md',
auth: [AuthType::KEY],
@@ -832,6 +853,7 @@ App::get('/v1/users/:userId/logs')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'logs',
name: 'listLogs',
description: '/docs/references/users/list-user-logs.md',
auth: [AuthType::KEY],
@@ -862,13 +884,15 @@ App::get('/v1/users/:userId/logs')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
// Temp fix for logs
$queries[] = Query::or([
Query::greaterThan('$createdAt', DateTime::format(new \DateTime('2025-02-26T01:30+00:00'))),
Query::lessThan('$createdAt', DateTime::format(new \DateTime('2025-02-13T00:00+00:00'))),
]);
$audit = new Audit($dbForProject);
$logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset);
$logs = $audit->getLogsByUser($user->getInternalId(), $queries);
$output = [];
@@ -915,7 +939,7 @@ App::get('/v1/users/:userId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByUser($user->getInternalId()),
'total' => $audit->countLogsByUser($user->getInternalId(), $queries),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -926,6 +950,7 @@ App::get('/v1/users/:userId/targets')
->label('scope', 'targets.read')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'listTargets',
description: '/docs/references/users/list-user-targets.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -978,10 +1003,15 @@ App::get('/v1/users/:userId/targets')
$cursor->setValue($cursorDocument);
}
try {
$targets = $dbForProject->find('targets', $queries);
$total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'targets' => $dbForProject->find('targets', $queries),
'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT),
'targets' => $targets,
'total' => $total,
]), Response::MODEL_TARGET_LIST);
});
@@ -991,6 +1021,7 @@ App::get('/v1/users/identities')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: 'identities',
name: 'listIdentities',
description: '/docs/references/users/list-identities.md',
auth: [AuthType::KEY],
@@ -1043,10 +1074,15 @@ App::get('/v1/users/identities')
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
$identities = $dbForProject->find('identities', $queries);
$total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'identities' => $dbForProject->find('identities', $queries),
'total' => $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT),
'identities' => $identities,
'total' => $total,
]), Response::MODEL_IDENTITY_LIST);
});
@@ -1060,6 +1096,7 @@ App::patch('/v1/users/:userId/status')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateStatus',
description: '/docs/references/users/update-user-status.md',
auth: [AuthType::KEY],
@@ -1100,6 +1137,7 @@ App::put('/v1/users/:userId/labels')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateLabels',
description: '/docs/references/users/update-user-labels.md',
auth: [AuthType::KEY],
@@ -1142,6 +1180,7 @@ App::patch('/v1/users/:userId/verification/phone')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updatePhoneVerification',
description: '/docs/references/users/update-user-phone-verification.md',
auth: [AuthType::KEY],
@@ -1183,6 +1222,7 @@ App::patch('/v1/users/:userId/name')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateName',
description: '/docs/references/users/update-user-name.md',
auth: [AuthType::KEY],
@@ -1225,6 +1265,7 @@ App::patch('/v1/users/:userId/password')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updatePassword',
description: '/docs/references/users/update-user-password.md',
auth: [AuthType::KEY],
@@ -1307,6 +1348,7 @@ App::patch('/v1/users/:userId/email')
->label('audits.userId', '{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateEmail',
description: '/docs/references/users/update-user-email.md',
auth: [AuthType::KEY],
@@ -1406,6 +1448,7 @@ App::patch('/v1/users/:userId/phone')
->label('audits.resource', 'user/{response.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updatePhone',
description: '/docs/references/users/update-user-phone.md',
auth: [AuthType::KEY],
@@ -1495,6 +1538,7 @@ App::patch('/v1/users/:userId/verification')
->label('audits.userId', '{request.userId}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateEmailVerification',
description: '/docs/references/users/update-user-email-verification.md',
auth: [AuthType::KEY],
@@ -1532,6 +1576,7 @@ App::patch('/v1/users/:userId/prefs')
->label('scope', 'users.write')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updatePrefs',
description: '/docs/references/users/update-user-prefs.md',
auth: [AuthType::KEY],
@@ -1572,6 +1617,7 @@ App::patch('/v1/users/:userId/targets/:targetId')
->label('scope', 'targets.write')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'updateTarget',
description: '/docs/references/users/update-target.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -1676,6 +1722,7 @@ App::patch('/v1/users/:userId/mfa')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'updateMfa',
description: '/docs/references/users/update-user-mfa.md',
auth: [AuthType::KEY],
@@ -1715,6 +1762,7 @@ App::get('/v1/users/:userId/mfa/factors')
->label('usage.metric', 'users.{scope}.requests.read')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'listMfaFactors',
description: '/docs/references/users/list-mfa-factors.md',
auth: [AuthType::KEY],
@@ -1753,6 +1801,7 @@ App::get('/v1/users/:userId/mfa/recovery-codes')
->label('usage.metric', 'users.{scope}.requests.read')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'getMfaRecoveryCodes',
description: '/docs/references/users/get-mfa-recovery-codes.md',
auth: [AuthType::KEY],
@@ -1797,6 +1846,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'createMfaRecoveryCodes',
description: '/docs/references/users/create-mfa-recovery-codes.md',
auth: [AuthType::KEY],
@@ -1838,7 +1888,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
});
App::put('/v1/users/:userId/mfa/recovery-codes')
->desc('Regenerate MFA recovery codes')
->desc('Update MFA recovery codes (regenerate)')
->groups(['api', 'users'])
->label('event', 'users.[userId].update.mfa.recovery-codes')
->label('scope', 'users.write')
@@ -1848,6 +1898,7 @@ App::put('/v1/users/:userId/mfa/recovery-codes')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'updateMfaRecoveryCodes',
description: '/docs/references/users/update-mfa-recovery-codes.md',
auth: [AuthType::KEY],
@@ -1898,6 +1949,7 @@ App::delete('/v1/users/:userId/mfa/authenticators/:type')
->label('usage.metric', 'users.{scope}.requests.update')
->label('sdk', new Method(
namespace: 'users',
group: 'mfa',
name: 'deleteMfaAuthenticator',
description: '/docs/references/users/delete-mfa-authenticator.md',
auth: [AuthType::KEY],
@@ -1945,6 +1997,7 @@ App::post('/v1/users/:userId/sessions')
->label('usage.metric', 'sessions.{scope}.requests.create')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'createSession',
description: '/docs/references/users/create-session.md',
auth: [AuthType::KEY],
@@ -2029,6 +2082,7 @@ App::post('/v1/users/:userId/tokens')
->label('audits.resource', 'user/{request.userId}')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'createToken',
description: '/docs/references/users/create-token.md',
auth: [AuthType::KEY],
@@ -2091,6 +2145,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
->label('audits.resource', 'user/{request.userId}')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'deleteSession',
description: '/docs/references/users/delete-user-session.md',
auth: [AuthType::KEY],
@@ -2141,6 +2196,7 @@ App::delete('/v1/users/:userId/sessions')
->label('audits.resource', 'user/{user.$id}')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'deleteSessions',
description: '/docs/references/users/delete-user-sessions.md',
auth: [AuthType::KEY],
@@ -2190,6 +2246,7 @@ App::delete('/v1/users/:userId')
->label('audits.resource', 'user/{request.userId}')
->label('sdk', new Method(
namespace: 'users',
group: 'users',
name: 'delete',
description: '/docs/references/users/delete.md',
auth: [AuthType::KEY],
@@ -2239,6 +2296,7 @@ App::delete('/v1/users/:userId/targets/:targetId')
->label('scope', 'targets.write')
->label('sdk', new Method(
namespace: 'users',
group: 'targets',
name: 'deleteTarget',
description: '/docs/references/users/delete-target.md',
auth: [AuthType::KEY, AuthType::ADMIN],
@@ -2296,6 +2354,7 @@ App::delete('/v1/users/identities/:identityId')
->label('audits.resource', 'identity/{request.$identityId}')
->label('sdk', new Method(
namespace: 'users',
group: 'identities',
name: 'deleteIdentity',
description: '/docs/references/users/delete-identity.md',
auth: [AuthType::KEY],
@@ -2335,6 +2394,7 @@ App::post('/v1/users/:userId/jwts')
->label('scope', 'users.write')
->label('sdk', new Method(
namespace: 'users',
group: 'sessions',
name: 'createJWT',
description: '/docs/references/users/create-user-jwt.md',
auth: [AuthType::KEY],
@@ -2390,6 +2450,7 @@ App::get('/v1/users/usage')
->label('scope', 'users.read')
->label('sdk', new Method(
namespace: 'users',
group: null,
name: 'getUsage',
description: '/docs/references/users/get-usage.md',
auth: [AuthType::ADMIN],
+22 -7
View File
@@ -19,6 +19,7 @@ use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Order as OrderException;
use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -269,12 +270,13 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
};
App::get('/v1/vcs/github/authorize')
->desc('Install GitHub app')
->desc('Create GitHub app installation')
->groups(['api', 'vcs'])
->label('scope', 'vcs.read')
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('sdk', new Method(
namespace: 'vcs',
group: 'installations',
name: 'createGitHubInstallation',
description: '/docs/references/vcs/create-github-installation.md',
auth: [AuthType::ADMIN],
@@ -318,7 +320,7 @@ App::get('/v1/vcs/github/authorize')
});
App::get('/v1/vcs/github/callback')
->desc('Capture installation and authorization from GitHub app')
->desc('Get installation and authorization from GitHub app')
->groups(['api', 'vcs'])
->label('scope', 'public')
->label('error', __DIR__ . '/../../views/general/error.phtml')
@@ -456,6 +458,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'getRepositoryContents',
description: '/docs/references/vcs/get-repository-contents.md',
auth: [AuthType::ADMIN],
@@ -517,11 +520,12 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
});
App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection')
->desc('Detect runtime settings from source code')
->desc('Create runtime settings detection')
->groups(['api', 'vcs'])
->label('scope', 'vcs.write')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'createRepositoryDetection',
description: '/docs/references/vcs/create-repository-detection.md',
auth: [AuthType::ADMIN],
@@ -599,6 +603,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'listRepositories',
description: '/docs/references/vcs/list-repositories.md',
auth: [AuthType::ADMIN],
@@ -699,6 +704,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
->label('scope', 'vcs.write')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'createRepository',
description: '/docs/references/vcs/create-repository.md',
auth: [AuthType::ADMIN],
@@ -811,6 +817,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'getRepository',
description: '/docs/references/vcs/get-repository.md',
auth: [AuthType::ADMIN],
@@ -865,6 +872,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'listRepositoryBranches',
description: '/docs/references/vcs/list-repository-branches.md',
auth: [AuthType::ADMIN],
@@ -1058,6 +1066,7 @@ App::get('/v1/vcs/installations')
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'installations',
name: 'listInstallations',
description: '/docs/references/vcs/list-installations.md',
auth: [AuthType::ADMIN],
@@ -1113,9 +1122,12 @@ App::get('/v1/vcs/installations')
}
$filterQueries = Query::groupByType($queries)['filters'];
$results = $dbForPlatform->find('installations', $queries);
$total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT);
try {
$results = $dbForPlatform->find('installations', $queries);
$total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT);
} catch (OrderException $e) {
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$response->dynamic(new Document([
'installations' => $results,
@@ -1129,6 +1141,7 @@ App::get('/v1/vcs/installations/:installationId')
->label('scope', 'vcs.read')
->label('sdk', new Method(
namespace: 'vcs',
group: 'installations',
name: 'getInstallation',
description: '/docs/references/vcs/get-installation.md',
auth: [AuthType::ADMIN],
@@ -1163,6 +1176,7 @@ App::delete('/v1/vcs/installations/:installationId')
->label('scope', 'vcs.write')
->label('sdk', new Method(
namespace: 'vcs',
group: 'installations',
name: 'deleteInstallation',
description: '/docs/references/vcs/delete-installation.md',
auth: [AuthType::ADMIN],
@@ -1198,11 +1212,12 @@ App::delete('/v1/vcs/installations/:installationId')
});
App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositoryId')
->desc('Authorize external deployment')
->desc('Update external deployment (authorize)')
->groups(['api', 'vcs'])
->label('scope', 'vcs.write')
->label('sdk', new Method(
namespace: 'vcs',
group: 'repositories',
name: 'updateExternalDeployments',
description: '/docs/references/vcs/update-external-deployments.md',
auth: [AuthType::ADMIN],
+65 -33
View File
@@ -7,7 +7,7 @@ use Appwrite\Auth\Auth;
use Appwrite\Event\Certificate;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Usage;
use Appwrite\Event\StatsUsage;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Network\Validator\Origin;
use Appwrite\SDK\AuthType;
@@ -50,7 +50,7 @@ Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost');
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname)
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname)
{
$utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml');
@@ -72,11 +72,19 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
if ($route->isEmpty()) {
if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) {
$appDomainFunctionsFallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
$appDomainFunctions = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if (!empty($appDomainFunctionsFallback) && \str_ends_with($host, $appDomainFunctionsFallback)) {
$appDomainFunctions = $appDomainFunctionsFallback;
}
if ($host === $appDomainFunctions) {
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.');
}
if (\str_ends_with($host, System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) {
if (\str_ends_with($host, $appDomainFunctions)) {
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.');
}
@@ -125,6 +133,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
if (empty($method)) {
$utopia->getRoute()?->label('sdk', new Method(
namespace: 'functions',
group: 'executions',
name: 'createExecution',
description: '/docs/references/functions/create-execution.md',
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
@@ -339,7 +348,6 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
]);
/** Execute function */
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
try {
$version = $function->getAttribute('version', 'v2');
$command = $runtime['startCommand'];
@@ -434,7 +442,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
}
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_NETWORK_REQUESTS, 1)
->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize)
->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize())
@@ -499,13 +507,14 @@ App::init()
->inject('localeCodes')
->inject('clients')
->inject('geodb')
->inject('queueForUsage')
->inject('queueForStatsUsage')
->inject('queueForEvents')
->inject('queueForCertificates')
->inject('queueForFunctions')
->inject('executor')
->inject('isResourceBlocked')
->inject('previewHostname')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked, string $previewHostname) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, Executor $executor, callable $isResourceBlocked, string $previewHostname) {
/*
* Appwrite Router
*/
@@ -513,7 +522,7 @@ App::init()
$mainDomain = System::getEnv('_APP_DOMAIN', '');
// Only run Router when external domain
if ($host !== $mainDomain || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname)) {
return;
}
}
@@ -582,6 +591,12 @@ App::init()
]);
}
$owner = '';
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if (!empty($functionsDomain) && \str_ends_with($domain->get(), $functionsDomain)) {
$owner = 'Appwrite';
}
if ($domainDocument->isEmpty()) {
$domainDocument = new Document([
// TODO: @christyjacob remove once we migrate the rules in 1.7.x
@@ -589,8 +604,10 @@ App::init()
'domain' => $domain->get(),
'resourceType' => 'api',
'status' => 'verifying',
'projectId' => 'console',
'projectInternalId' => 'console'
'projectId' => $console->getId(),
'projectInternalId' => $console->getInternalId(),
'owner' => $owner,
'region' => $console->getAttribute('region')
]);
$domainDocument = $dbForPlatform->createDocument('rules', $domainDocument);
@@ -732,12 +749,14 @@ App::options()
->inject('dbForPlatform')
->inject('getProjectDB')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('queueForStatsUsage')
->inject('queueForFunctions')
->inject('executor')
->inject('geodb')
->inject('isResourceBlocked')
->inject('previewHostname')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
->inject('project')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project) {
/*
* Appwrite Router
*/
@@ -745,7 +764,7 @@ App::options()
$mainDomain = System::getEnv('_APP_DOMAIN', '');
// Only run Router when external domain
if ($host !== $mainDomain || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname)) {
return;
}
}
@@ -760,6 +779,16 @@ App::options()
->addHeader('Access-Control-Allow-Origin', $origin)
->addHeader('Access-Control-Allow-Credentials', 'true')
->noContent();
/** OPTIONS requests in utopia do not execute shutdown handlers, as a result we need to track the OPTIONS requests explicitly
* @see https://github.com/utopia-php/http/blob/0.33.16/src/App.php#L825-L855
*/
$queueForStatsUsage
->addMetric(METRIC_NETWORK_REQUESTS, 1)
->addMetric(METRIC_NETWORK_INBOUND, $request->getSize())
->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize())
->setProject($project)
->trigger();
});
App::error()
@@ -770,8 +799,8 @@ App::error()
->inject('project')
->inject('logger')
->inject('log')
->inject('queueForUsage')
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage) {
->inject('queueForStatsUsage')
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage) {
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$route = $utopia->getRoute();
$class = \get_class($error);
@@ -848,23 +877,22 @@ App::error()
$publish = $error->getCode() === 0 || $error->getCode() >= 500;
}
if ($error->getCode() >= 400 && $error->getCode() < 500) {
$providerConfig = System::getEnv('_APP_EXPERIMENT_LOGGING_CONFIG', '');
if (!empty($providerConfig) && $error->getCode() >= 400 && $error->getCode() < 500) {
// Register error logger
$providerName = System::getEnv('_APP_EXPERIMENT_LOGGING_PROVIDER', '');
$providerConfig = System::getEnv('_APP_EXPERIMENT_LOGGING_CONFIG', '');
try {
$loggingProvider = new DSN($providerConfig ?? '');
$loggingProvider = new DSN($providerConfig);
$providerName = $loggingProvider->getScheme();
if (!empty($providerName) && $providerName === 'sentry') {
$key = $loggingProvider->getPassword();
$projectId = $loggingProvider->getUser() ?? '';
$host = 'https://' . $loggingProvider->getHost();
$sampleRate = $loggingProvider->getParam('sample', 0.01);
$adapter = new Sentry($projectId, $key, $host);
$logger = new Logger($adapter);
$logger->setSample(0.01);
$logger->setSample($sampleRate);
$publish = true;
} else {
throw new \Exception('Invalid experimental logging provider');
@@ -874,7 +902,10 @@ App::error()
}
}
if ($publish && $project->getId() !== 'console') {
/**
* If its not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php
*/
if (!$publish && $project->getId() !== 'console') {
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
$fileSize = 0;
$file = $request->getFiles('file');
@@ -882,18 +913,17 @@ App::error()
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
}
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_NETWORK_REQUESTS, 1)
->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize)
->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize());
}
$queueForUsage
$queueForStatsUsage
->setProject($project)
->trigger();
}
if ($logger && $publish) {
try {
/** @var Utopia\Database\Document $user */
@@ -1040,12 +1070,13 @@ App::get('/robots.txt')
->inject('dbForPlatform')
->inject('getProjectDB')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('queueForStatsUsage')
->inject('queueForFunctions')
->inject('executor')
->inject('geodb')
->inject('isResourceBlocked')
->inject('previewHostname')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
@@ -1053,7 +1084,7 @@ App::get('/robots.txt')
$template = new View(__DIR__ . '/../views/general/robots.phtml');
$response->text($template->render(false));
} else {
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname);
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname);
}
});
@@ -1068,12 +1099,13 @@ App::get('/humans.txt')
->inject('dbForPlatform')
->inject('getProjectDB')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('queueForStatsUsage')
->inject('queueForFunctions')
->inject('executor')
->inject('geodb')
->inject('isResourceBlocked')
->inject('previewHostname')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
@@ -1081,7 +1113,7 @@ App::get('/humans.txt')
$template = new View(__DIR__ . '/../views/general/humans.phtml');
$response->text($template->render(false));
} else {
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname);
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname);
}
});
@@ -1148,7 +1180,7 @@ App::get('/v1/ping')
->inject('dbForPlatform')
->inject('queueForEvents')
->action(function (Response $response, Document $project, Database $dbForPlatform, Event $queueForEvents) {
if ($project->isEmpty()) {
if ($project->isEmpty() || $project->getId() === 'console') {
throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND);
}
+117 -155
View File
@@ -1,8 +1,7 @@
<?php
use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Auth\Auth;
use Appwrite\Auth\Key;
use Appwrite\Auth\MFA\Type\TOTP;
use Appwrite\Event\Audit;
use Appwrite\Event\Build;
@@ -12,10 +11,11 @@ use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Messaging;
use Appwrite\Event\Realtime;
use Appwrite\Event\Usage;
use Appwrite\Event\StatsUsage;
use Appwrite\Event\Webhook;
use Appwrite\Extend\Exception;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\SDK\Method;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Utopia\Abuse\Abuse;
@@ -90,7 +90,7 @@ $eventDatabaseListener = function (Document $project, Document $document, Respon
}
};
$usageDatabaseListener = function (string $event, Document $document, Usage $queueForUsage) {
$usageDatabaseListener = function (string $event, Document $document, StatsUsage $queueForStatsUsage) {
$value = 1;
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$value = -1;
@@ -98,80 +98,72 @@ $usageDatabaseListener = function (string $event, Document $document, Usage $que
switch (true) {
case $document->getCollection() === 'teams':
$queueForUsage
->addMetric(METRIC_TEAMS, $value); // per project
$queueForStatsUsage->addMetric(METRIC_TEAMS, $value); // per project
break;
case $document->getCollection() === 'users':
$queueForUsage
->addMetric(METRIC_USERS, $value); // per project
$queueForStatsUsage->addMetric(METRIC_USERS, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForUsage
->addReduce($document);
$queueForStatsUsage->addReduce($document);
}
break;
case $document->getCollection() === 'sessions': // sessions
$queueForUsage
->addMetric(METRIC_SESSIONS, $value); //per project
$queueForStatsUsage->addMetric(METRIC_SESSIONS, $value); //per project
break;
case $document->getCollection() === 'databases': // databases
$queueForUsage
->addMetric(METRIC_DATABASES, $value); // per project
$queueForStatsUsage->addMetric(METRIC_DATABASES, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForUsage
->addReduce($document);
$queueForStatsUsage->addReduce($document);
}
break;
case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections
$parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0;
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_COLLECTIONS, $value) // per project
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value)
;
->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value);
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForUsage
->addReduce($document);
$queueForStatsUsage->addReduce($document);
}
break;
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;
$queueForUsage
$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
break;
case $document->getCollection() === 'buckets': //buckets
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_BUCKETS, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForUsage
$queueForStatsUsage
->addReduce($document);
}
break;
case str_starts_with($document->getCollection(), 'bucket_'): // files
$parts = explode('_', $document->getCollection());
$bucketInternalId = $parts[1];
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_FILES, $value) // per project
->addMetric(METRIC_FILES_STORAGE, $document->getAttribute('sizeOriginal') * $value) // per project
->addMetric(str_replace('{bucketInternalId}', $bucketInternalId, METRIC_BUCKET_ID_FILES), $value) // per bucket
->addMetric(str_replace('{bucketInternalId}', $bucketInternalId, METRIC_BUCKET_ID_FILES_STORAGE), $document->getAttribute('sizeOriginal') * $value); // per bucket
break;
case $document->getCollection() === 'functions':
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_FUNCTIONS, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForUsage
$queueForStatsUsage
->addReduce($document);
}
break;
case $document->getCollection() === 'deployments':
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_DEPLOYMENTS, $value) // per project
->addMetric(METRIC_DEPLOYMENTS_STORAGE, $document->getAttribute('size') * $value) // per project
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS), $value) // per function
@@ -195,126 +187,89 @@ App::init()
->inject('servers')
->inject('mode')
->inject('team')
->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team) {
->inject('apiKey')
->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey) {
$route = $utopia->getRoute();
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
/** Default role */
$roles = Config::getParam('roles', []);
$role = ($user->isEmpty())
$role = $user->isEmpty()
? Role::guests()->toString()
: Role::users()->toString();
/** Allowed Scopes for the role */
$scopes = $roles[$role]['scopes'];
$apiKey = $request->getHeader('x-appwrite-key', '');
// API Key authentication
if (!empty($apiKey)) {
// Do not allow API key and session to be set at the same time
if (!$user->isEmpty()) {
throw new Exception(Exception::USER_API_KEY_AND_SESSION_SET);
}
// Remove after migration
if (!\str_contains($apiKey, '_')) {
$keyType = API_KEY_STANDARD;
$authKey = $apiKey;
} else {
[ $keyType, $authKey ] = \explode('_', $apiKey, 2);
if ($apiKey->isExpired()) {
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
}
if ($keyType === API_KEY_DYNAMIC) {
// Dynamic key
$role = $apiKey->getRole();
$scopes = $apiKey->getScopes();
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 86400, 0);
// Disable authorization checks for API keys
Authorization::setDefaultStatus(false);
try {
$payload = $jwtObj->decode($authKey);
} catch (JWTException $error) {
throw new Exception(Exception::API_KEY_EXPIRED);
if ($apiKey->getRole() === Auth::USER_ROLE_APPS) {
$user = new Document([
'$id' => '',
'status' => true,
'type' => Auth::ACTIVITY_TYPE_APP,
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
'password' => '',
'name' => $apiKey->getName(),
]);
$queueForAudits->setUser($user);
}
if ($apiKey->getType() === API_KEY_STANDARD) {
$dbKey = $project->find(
key: 'secret',
find: $request->getHeader('x-appwrite-key', ''),
subject: 'keys'
);
if (!$dbKey) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$projectId = $payload['projectId'] ?? '';
$tokenScopes = $payload['scopes'] ?? [];
$accessedAt = $dbKey->getAttribute('accessedAt', '');
// JWT includes project ID for better security
if ($projectId === $project->getId()) {
$user = new Document([
'$id' => '',
'status' => true,
'type' => Auth::ACTIVITY_TYPE_APP,
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
'password' => '',
'name' => 'Dynamic Key',
]);
$role = Auth::USER_ROLE_APPS;
$scopes = \array_merge($roles[$role]['scopes'], $tokenScopes);
Authorization::setRole(Auth::USER_ROLE_APPS);
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
$queueForAudits->setUser($user);
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$dbKey->setAttribute('accessedAt', DateTime::now());
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
} elseif ($keyType === API_KEY_STANDARD) {
// No underline means no prefix. Backwards compatibility.
// Regular key
// Check if given key match project API keys
$key = $project->find('secret', $apiKey, 'keys');
if ($key) {
$user = new Document([
'$id' => '',
'status' => true,
'type' => Auth::ACTIVITY_TYPE_APP,
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
'password' => '',
'name' => $key->getAttribute('name', 'UNKNOWN'),
]);
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
$role = Auth::USER_ROLE_APPS;
$scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
if ($sdkValidator->isValid($sdk)) {
$sdks = $dbKey->getAttribute('sdks', []);
$expire = $key->getAttribute('expire');
if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) {
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
}
if (!in_array($sdk, $sdks)) {
$sdks[] = $sdk;
$dbKey->setAttribute('sdks', $sdks);
Authorization::setRole(Auth::USER_ROLE_APPS);
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
$accessedAt = $key->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$key->setAttribute('accessedAt', DateTime::now());
$dbForPlatform->updateDocument('keys', $key->getId(), $key);
/** Update access time as well */
$dbKey->setAttribute('accessedAt', Datetime::now());
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdkValidator->isValid($sdk)) {
$sdks = $key->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
array_push($sdks, $sdk);
$key->setAttribute('sdks', $sdks);
/** Update access time as well */
$key->setAttribute('accessedAt', Datetime::now());
$dbForPlatform->updateDocument('keys', $key->getId(), $key);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
}
$queueForAudits->setUser($user);
}
$queueForAudits->setUser($user);
}
}
// Admin User Authentication
} // Admin User Authentication
elseif (($project->getId() === 'console' && !$team->isEmpty() && !$user->isEmpty()) || ($project->getId() !== 'console' && !$user->isEmpty() && $mode === APP_MODE_ADMIN)) {
$teamId = $team->getId();
$adminRoles = [];
@@ -330,7 +285,7 @@ App::init()
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$scopes = []; // reset scope if admin
$scopes = []; // Reset scope if admin
foreach ($adminRoles as $role) {
$scopes = \array_merge($scopes, $roles[$role]['scopes']);
}
@@ -345,9 +300,7 @@ App::init()
Authorization::setRole($authRole);
}
/**
* Update project last activity
*/
// Update project last activity
if (!$project->isEmpty() && $project->getId() !== 'console') {
$accessedAt = $project->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
@@ -356,9 +309,7 @@ App::init()
}
}
/**
* Update user last activity
*/
// Update user last activity
if (!empty($user->getId())) {
$accessedAt = $user->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCESS)) > $accessedAt) {
@@ -372,18 +323,18 @@ App::init()
}
}
/** Do not allow access to disabled services */
/**
* @var ?\Appwrite\SDK\Method $method
* @var ?Method $method
*/
$method = $route->getLabel('sdk', false);
if (is_array($method)) {
if (\is_array($method)) {
$method = $method[0];
}
if (!empty($method)) {
$namespace = $method->getNamespace();
if (
array_key_exists($namespace, $project->getAttribute('services', []))
&& !$project->getAttribute('services', [])[$namespace]
@@ -393,13 +344,13 @@ App::init()
}
}
/** Do now allow access if scope is not allowed */
// Do now allow access if scope is not allowed
$scope = $route->getLabel('scope', 'none');
if (!\in_array($scope, $scopes)) {
throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')');
}
/** Do not allow access to blocked accounts */
// Do not allow access to blocked accounts
if (false === $user->getAttribute('status')) { // Account is blocked
throw new Exception(Exception::USER_BLOCKED);
}
@@ -436,11 +387,13 @@ App::init()
->inject('queueForDeletes')
->inject('queueForDatabase')
->inject('queueForBuilds')
->inject('queueForUsage')
->inject('queueForStatsUsage')
->inject('dbForProject')
->inject('timelimit')
->inject('mode')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, callable $timelimit, string $mode) use ($usageDatabaseListener, $eventDatabaseListener) {
->inject('apiKey')
->inject('plan')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, string $mode, ?Key $apiKey, array $plan) use ($usageDatabaseListener, $eventDatabaseListener) {
$route = $utopia->getRoute();
@@ -471,7 +424,7 @@ App::init()
->setParam('{ip}', $request->getIP())
->setParam('{url}', $request->getHostname() . $route->getPath())
->setParam('{method}', $request->getMethod())
->setParam('{chunkId}', (int) ($start / ($end + 1 - $start)));
->setParam('{chunkId}', (int)($start / ($end + 1 - $start)));
$timeLimitArray[] = $timeLimit;
}
@@ -537,6 +490,12 @@ App::init()
$queueForAudits->setUser($userClone);
}
if (!empty($apiKey) && !empty($apiKey->getDisabledMetrics())) {
foreach ($apiKey->getDisabledMetrics() as $key) {
$queueForStatsUsage->disableMetric($key);
}
}
$queueForDeletes->setProject($project);
$queueForDatabase->setProject($project);
$queueForBuilds->setProject($project);
@@ -550,8 +509,8 @@ App::init()
$queueForRealtime = new Realtime();
$dbForProject
->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForUsage))
->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForUsage))
->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_DOCUMENT_CREATE, 'create-trigger-events', fn ($event, $document) => $eventDatabaseListener(
$project,
$document,
@@ -564,6 +523,10 @@ App::init()
$useCache = $route->getLabel('cache', false);
if ($useCache) {
$route = $utopia->match($request);
$isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview';
$isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !Auth::isPrivilegedUser(Authorization::getRoles());
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$cache = new Cache(
@@ -573,17 +536,14 @@ App::init()
$data = $cache->load($key, $timestamp);
if (!empty($data) && !$cacheLog->isEmpty()) {
$parts = explode('/', $cacheLog->getAttribute('resourceType'));
$parts = explode('/', $cacheLog->getAttribute('resourceType', ''));
$type = $parts[0] ?? null;
if ($type === 'bucket') {
if ($type === 'bucket' && (!$isImageTransformation || !$isDisabled)) {
$bucketId = $parts[1] ?? null;
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
@@ -607,26 +567,29 @@ App::init()
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
$transformedAt = $file->getAttribute('transformedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) {
$file->setAttribute('transformedAt', DateTime::now());
Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
//Do not update transformedAt if it's a console user
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
$transformedAt = $file->getAttribute('transformedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) {
$file->setAttribute('transformedAt', DateTime::now());
Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
}
}
}
$response
->addHeader('Cache-Control', sprintf('private, max-age=%d', $timestamp))
->addHeader('X-Appwrite-Cache', 'hit')
->setContentType($cacheLog->getAttribute('mimeType'))
->send($data);
->setContentType($cacheLog->getAttribute('mimeType'));
if (!$isImageTransformation || !$isDisabled) {
$response->send($data);
}
} else {
$response
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
->addHeader('Pragma', 'no-cache')
->addHeader('Expires', '0')
->addHeader('X-Appwrite-Cache', 'miss')
;
->addHeader('X-Appwrite-Cache', 'miss');
}
}
});
@@ -694,7 +657,7 @@ App::shutdown()
->inject('user')
->inject('queueForEvents')
->inject('queueForAudits')
->inject('queueForUsage')
->inject('queueForStatsUsage')
->inject('queueForDeletes')
->inject('queueForDatabase')
->inject('queueForBuilds')
@@ -703,7 +666,7 @@ App::shutdown()
->inject('queueForWebhooks')
->inject('queueForRealtime')
->inject('dbForProject')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject) use ($parseLabel) {
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, StatsUsage $queueForStatsUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject) use ($parseLabel) {
$responsePayload = $response->getPayload();
@@ -787,6 +750,7 @@ App::shutdown()
foreach ($queueForEvents->getParams() as $key => $value) {
$queueForAudits->setParam($key, $value);
}
$queueForAudits->trigger();
}
@@ -806,9 +770,7 @@ App::shutdown()
$queueForMessaging->trigger();
}
/**
* Cache label
*/
// Cache label
$useCache = $route->getLabel('cache', false);
if ($useCache) {
$resource = $resourceType = null;
@@ -860,13 +822,13 @@ App::shutdown()
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
}
$queueForUsage
$queueForStatsUsage
->addMetric(METRIC_NETWORK_REQUESTS, 1)
->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize)
->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize());
}
$queueForUsage
$queueForStatsUsage
->setProject($project)
->trigger();
}
+135 -80
View File
@@ -17,7 +17,6 @@ use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
@@ -156,6 +155,79 @@ function dispatch(Server $server, int $fd, int $type, $data = null): int
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;
$attempts = 0;
do {
try {
$attempts++;
$resource = $app->getResource($resourceKey);
/* @var $database Database */
$database = is_callable($resource) ? $resource() : $resource;
break; // exit loop on success
} catch (\Exception $e) {
Console::warning(" └── Database not ready. Retrying connection ({$attempts})...");
$pools->reclaim();
if ($attempts >= $max) {
throw new \Exception(' └── Failed to connect to database: ' . $e->getMessage());
}
sleep($sleep);
}
} while ($attempts < $max);
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");
}
// Process collections
foreach ($collections as $key => $collection) {
if (($collection['$collection'] ?? '') !== Database::METADATA) {
continue;
}
if (!$database->getCollection($key)->isEmpty()) {
continue;
}
Console::info(" └── Creating collection: {$collection['$id']}...");
$attributes = array_map(fn ($attr) => new Document([
'$id' => ID::custom($attr['$id']),
'type' => $attr['type'],
'size' => $attr['size'],
'required' => $attr['required'],
'signed' => $attr['signed'],
'array' => $attr['array'],
'filters' => $attr['filters'],
'default' => $attr['default'] ?? null,
'format' => $attr['format'] ?? ''
]), $collection['attributes']);
$indexes = array_map(fn ($index) => new Document([
'$id' => ID::custom($index['$id']),
'type' => $index['type'],
'attributes' => $index['attributes'],
'lengths' => $index['lengths'],
'orders' => $index['orders'],
]), $collection['indexes']);
$database->createCollection($key, $attributes, $indexes);
}
if ($extraSetup) {
$extraSetup($database);
}
}
$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) {
$app = new App('UTC');
@@ -164,94 +236,73 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
/** @var Group $pools */
App::setResource('pools', fn () => $pools);
// wait for database to be ready
$attempts = 0;
$max = 10;
$sleep = 1;
do {
try {
$attempts++;
$dbForPlatform = $app->getResource('dbForPlatform');
/** @var Utopia\Database\Database $dbForPlatform */
break; // leave the do-while if successful
} catch (\Throwable $e) {
Console::warning("Database not ready. Retrying connection ({$attempts})...");
if ($attempts >= $max) {
throw new \Exception('Failed to connect to database: ' . $e->getMessage());
}
sleep($sleep);
}
} while ($attempts < $max);
Console::success('[Setup] - Server database init started...');
try {
Console::success('[Setup] - Creating console database...');
$dbForPlatform->create();
} catch (Duplicate) {
Console::success('[Setup] - Skip: metadata table already exists');
}
if ($dbForPlatform->getCollection(Audit::COLLECTION)->isEmpty()) {
$audit = new Audit($dbForPlatform);
$audit->setup();
}
/** @var array $collections */
$collections = Config::getParam('collections', []);
$consoleCollections = $collections['console'];
foreach ($consoleCollections as $key => $collection) {
if (($collection['$collection'] ?? '') !== Database::METADATA) {
continue;
}
if (!$dbForPlatform->getCollection($key)->isEmpty()) {
continue;
// create logs database first, `getLogsDB` is a callable.
createDatabase($app, 'getLogsDB', 'logs', $collections['logs'], $pools);
// create appwrite database, `dbForPlatform` is a direct access call.
createDatabase($app, 'dbForPlatform', 'appwrite', $collections['console'], $pools, function (Database $dbForPlatform) use ($collections) {
if ($dbForPlatform->getCollection(Audit::COLLECTION)->isEmpty()) {
$audit = new Audit($dbForPlatform);
$audit->setup();
}
Console::success('[Setup] - Creating console collection: ' . $collection['$id'] . '...');
if ($dbForPlatform->getDocument('buckets', 'default')->isEmpty() &&
!$dbForPlatform->exists($dbForPlatform->getDatabase(), 'bucket_1')) {
Console::info(" └── Creating default bucket...");
$dbForPlatform->createDocument('buckets', new Document([
'$id' => ID::custom('default'),
'$collection' => ID::custom('buckets'),
'name' => 'Default',
'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0),
'allowedFileExtensions' => [],
'enabled' => true,
'compression' => 'gzip',
'encryption' => true,
'antivirus' => true,
'fileSecurity' => true,
'$permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'search' => 'buckets Default',
]));
$attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']);
$indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']);
$bucket = $dbForPlatform->getDocument('buckets', 'default');
$dbForPlatform->createCollection($key, $attributes, $indexes);
}
Console::info(" └── Creating files collection for default bucket...");
$files = $collections['buckets']['files'] ?? [];
if (empty($files)) {
throw new Exception('Files collection is not configured.');
}
if ($dbForPlatform->getDocument('buckets', 'default')->isEmpty() && !$dbForPlatform->exists($dbForPlatform->getDatabase(), 'bucket_1')) {
Console::success('[Setup] - Creating default bucket...');
$dbForPlatform->createDocument('buckets', new Document([
'$id' => ID::custom('default'),
'$collection' => ID::custom('buckets'),
'name' => 'Default',
'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB
'allowedFileExtensions' => [],
'enabled' => true,
'compression' => 'gzip',
'encryption' => true,
'antivirus' => true,
'fileSecurity' => true,
'$permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'search' => 'buckets Default',
]));
$attributes = array_map(fn ($attr) => new Document([
'$id' => ID::custom($attr['$id']),
'type' => $attr['type'],
'size' => $attr['size'],
'required' => $attr['required'],
'signed' => $attr['signed'],
'array' => $attr['array'],
'filters' => $attr['filters'],
'default' => $attr['default'] ?? null,
'format' => $attr['format'] ?? ''
]), $files['attributes']);
$bucket = $dbForPlatform->getDocument('buckets', 'default');
$indexes = array_map(fn ($index) => new Document([
'$id' => ID::custom($index['$id']),
'type' => $index['type'],
'attributes' => $index['attributes'],
'lengths' => $index['lengths'],
'orders' => $index['orders'],
]), $files['indexes']);
Console::success('[Setup] - Creating files collection for default bucket...');
$files = $collections['buckets']['files'] ?? [];
if (empty($files)) {
throw new Exception('Files collection is not configured.');
$dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
}
$attributes = \array_map(fn ($attribute) => new Document($attribute), $files['attributes']);
$indexes = \array_map(fn (array $index) => new Document($index), $files['indexes']);
$dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
}
});
$projectCollections = $collections['projects'];
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
@@ -279,6 +330,11 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
Console::success('[Setup] - Skip: metadata table already exists');
}
if ($dbForProject->getCollection(Audit::COLLECTION)->isEmpty()) {
$audit = new Audit($dbForProject);
$audit->setup();
}
foreach ($projectCollections as $key => $collection) {
if (($collection['$collection'] ?? '') !== Database::METADATA) {
continue;
@@ -297,7 +353,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
}
$pools->reclaim();
Console::success('[Setup] - Server database init completed...');
});
+9 -1864
View File
File diff suppressed because it is too large Load Diff
+37
View File
@@ -0,0 +1,37 @@
<?php
use Utopia\Config\Config;
Config::load('events', __DIR__ . '/../config/events.php');
Config::load('auth', __DIR__ . '/../config/auth.php');
Config::load('apis', __DIR__ . '/../config/apis.php'); // List of APIs
Config::load('errors', __DIR__ . '/../config/errors.php');
Config::load('oAuthProviders', __DIR__ . '/../config/oAuthProviders.php');
Config::load('platforms', __DIR__ . '/../config/platforms.php');
Config::load('console', __DIR__ . '/../config/console.php');
Config::load('collections', __DIR__ . '/../config/collections.php');
Config::load('runtimes', __DIR__ . '/../config/runtimes.php');
Config::load('runtimes-v2', __DIR__ . '/../config/runtimes-v2.php');
Config::load('usage', __DIR__ . '/../config/usage.php');
Config::load('roles', __DIR__ . '/../config/roles.php'); // User roles and scopes
Config::load('scopes', __DIR__ . '/../config/scopes.php'); // User roles and scopes
Config::load('services', __DIR__ . '/../config/services.php'); // List of services
Config::load('variables', __DIR__ . '/../config/variables.php'); // List of env variables
Config::load('regions', __DIR__ . '/../config/regions.php'); // List of available regions
Config::load('avatar-browsers', __DIR__ . '/../config/avatars/browsers.php');
Config::load('avatar-credit-cards', __DIR__ . '/../config/avatars/credit-cards.php');
Config::load('avatar-flags', __DIR__ . '/../config/avatars/flags.php');
Config::load('locale-codes', __DIR__ . '/../config/locale/codes.php');
Config::load('locale-currencies', __DIR__ . '/../config/locale/currencies.php');
Config::load('locale-eu', __DIR__ . '/../config/locale/eu.php');
Config::load('locale-languages', __DIR__ . '/../config/locale/languages.php');
Config::load('locale-phones', __DIR__ . '/../config/locale/phones.php');
Config::load('locale-countries', __DIR__ . '/../config/locale/countries.php');
Config::load('locale-continents', __DIR__ . '/../config/locale/continents.php');
Config::load('locale-templates', __DIR__ . '/../config/locale/templates.php');
Config::load('storage-logos', __DIR__ . '/../config/storage/logos.php');
Config::load('storage-mimes', __DIR__ . '/../config/storage/mimes.php');
Config::load('storage-inputs', __DIR__ . '/../config/storage/inputs.php');
Config::load('storage-outputs', __DIR__ . '/../config/storage/outputs.php');
Config::load('runtime-specifications', __DIR__ . '/../config/runtimes/specifications.php');
Config::load('function-templates', __DIR__ . '/../config/function-templates.php');
+239
View File
@@ -0,0 +1,239 @@
<?php
use Appwrite\Functions\Specification;
const APP_NAME = 'Appwrite';
const APP_DOMAIN = 'appwrite.io';
const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address
const APP_EMAIL_SECURITY = ''; // Default security email address
const APP_USERAGENT = APP_NAME . '-Server v%s. Please report abuse at %s';
const APP_MODE_DEFAULT = 'default';
const APP_MODE_ADMIN = 'admin';
const APP_PAGING_LIMIT = 12;
const APP_LIMIT_COUNT = 5000;
const APP_LIMIT_USERS = 10_000;
const APP_LIMIT_USER_PASSWORD_HISTORY = 20;
const APP_LIMIT_USER_SESSIONS_MAX = 100;
const APP_LIMIT_USER_SESSIONS_DEFAULT = 10;
const APP_LIMIT_ANTIVIRUS = 20_000_000; //20MB
const APP_LIMIT_ENCRYPTION = 20_000_000; //20MB
const APP_LIMIT_COMPRESSION = 20_000_000; //20MB
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
const APP_LIMIT_ARRAY_LABELS_SIZE = 1000; // Default maximum of how many labels elements can there be in API parameter that expects array value
const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length.
const APP_LIMIT_SUBQUERY = 1000;
const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000;
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours
const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours
const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 4318;
const APP_VERSION_STABLE = '1.6.2';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime';
const APP_DATABASE_ATTRIBUTE_URL = 'url';
const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange';
const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange';
const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char
const APP_DATABASE_TIMEOUT_MILLISECONDS_API = 15 * 1000; // 15 seconds
const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes
const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes
const APP_DATABASE_QUERY_MAX_VALUES = 500;
const APP_STORAGE_UPLOADS = '/storage/uploads';
const APP_STORAGE_FUNCTIONS = '/storage/functions';
const APP_STORAGE_BUILDS = '/storage/builds';
const APP_STORAGE_CACHE = '/storage/cache';
const APP_STORAGE_CERTIFICATES = '/storage/certificates';
const APP_STORAGE_CONFIG = '/storage/config';
const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT`
const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite';
const APP_SOCIAL_TWITTER_HANDLE = 'appwrite';
const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io';
const APP_SOCIAL_LINKEDIN = 'https://www.linkedin.com/company/appwrite';
const APP_SOCIAL_INSTAGRAM = 'https://www.instagram.com/appwrite.io';
const APP_SOCIAL_GITHUB = 'https://github.com/appwrite';
const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord';
const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244';
const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
const APP_HOSTNAME_INTERNAL = 'appwrite';
const APP_FUNCTION_SPECIFICATION_DEFAULT = Specification::S_1VCPU_512MB;
const APP_FUNCTION_CPUS_DEFAULT = 0.5;
const APP_FUNCTION_MEMORY_DEFAULT = 512;
const APP_PLATFORM_SERVER = 'server';
const APP_PLATFORM_CLIENT = 'client';
const APP_PLATFORM_CONSOLE = 'console';
// Database Reconnect
const DATABASE_RECONNECT_SLEEP = 2;
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
// Database Worker Types
const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute';
const DATABASE_TYPE_CREATE_INDEX = 'createIndex';
const DATABASE_TYPE_DELETE_ATTRIBUTE = 'deleteAttribute';
const DATABASE_TYPE_DELETE_INDEX = 'deleteIndex';
const DATABASE_TYPE_DELETE_COLLECTION = 'deleteCollection';
const DATABASE_TYPE_DELETE_DATABASE = 'deleteDatabase';
// Build Worker Types
const BUILD_TYPE_DEPLOYMENT = 'deployment';
const BUILD_TYPE_RETRY = 'retry';
// Deletion Types
const DELETE_TYPE_DATABASES = 'databases';
const DELETE_TYPE_DOCUMENT = 'document';
const DELETE_TYPE_COLLECTIONS = 'collections';
const DELETE_TYPE_PROJECTS = 'projects';
const DELETE_TYPE_FUNCTIONS = 'functions';
const DELETE_TYPE_DEPLOYMENTS = 'deployments';
const DELETE_TYPE_USERS = 'users';
const DELETE_TYPE_TEAM_PROJECTS = 'teams_projects';
const DELETE_TYPE_EXECUTIONS = 'executions';
const DELETE_TYPE_AUDIT = 'audit';
const DELETE_TYPE_ABUSE = 'abuse';
const DELETE_TYPE_USAGE = 'usage';
const DELETE_TYPE_REALTIME = 'realtime';
const DELETE_TYPE_BUCKETS = 'buckets';
const DELETE_TYPE_INSTALLATIONS = 'installations';
const DELETE_TYPE_RULES = 'rules';
const DELETE_TYPE_SESSIONS = 'sessions';
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';
const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
const DELETE_TYPE_SCHEDULES = 'schedules';
const DELETE_TYPE_TOPIC = 'topic';
const DELETE_TYPE_TARGET = 'target';
const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets';
const DELETE_TYPE_SESSION_TARGETS = 'session_targets';
const DELETE_TYPE_MAINTENANCE = 'maintenance';
// Message types
const MESSAGE_SEND_TYPE_INTERNAL = 'internal';
const MESSAGE_SEND_TYPE_EXTERNAL = 'external';
// Mail Types
const MAIL_TYPE_VERIFICATION = 'verification';
const MAIL_TYPE_MAGIC_SESSION = 'magicSession';
const MAIL_TYPE_RECOVERY = 'recovery';
const MAIL_TYPE_INVITATION = 'invitation';
const MAIL_TYPE_CERTIFICATE = 'certificate';
// Auth Types
const APP_AUTH_TYPE_SESSION = 'Session';
const APP_AUTH_TYPE_JWT = 'JWT';
const APP_AUTH_TYPE_KEY = 'Key';
const APP_AUTH_TYPE_ADMIN = 'Admin';
// Response related
const MAX_OUTPUT_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB
// Function headers
const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host'];
const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length'];
// Message types
const MESSAGE_TYPE_EMAIL = 'email';
const MESSAGE_TYPE_SMS = 'sms';
const MESSAGE_TYPE_PUSH = 'push';
// API key types
const API_KEY_STANDARD = 'standard';
const API_KEY_DYNAMIC = 'dynamic';
// Usage metrics
const METRIC_TEAMS = 'teams';
const METRIC_USERS = 'users';
const METRIC_WEBHOOKS_SENT = 'webhooks.events.sent';
const METRIC_WEBHOOKS_FAILED = 'webhooks.events.failed';
const METRIC_WEBHOOK_ID_SENT = '{webhookInternalId}.webhooks.events.sent';
const METRIC_WEBHOOK_ID_FAILED = '{webhookInternalId}.webhooks.events.failed';
const METRIC_AUTH_METHOD_PHONE = 'auth.method.phone';
const METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE = METRIC_AUTH_METHOD_PHONE . '.{countryCode}';
const METRIC_MESSAGES = 'messages';
const METRIC_MESSAGES_SENT = METRIC_MESSAGES . '.sent';
const METRIC_MESSAGES_FAILED = METRIC_MESSAGES . '.failed';
const METRIC_MESSAGES_TYPE = METRIC_MESSAGES . '.{type}';
const METRIC_MESSAGES_TYPE_SENT = METRIC_MESSAGES . '.{type}.sent';
const METRIC_MESSAGES_TYPE_FAILED = METRIC_MESSAGES . '.{type}.failed';
const METRIC_MESSAGES_TYPE_PROVIDER = METRIC_MESSAGES . '.{type}.{provider}';
const METRIC_MESSAGES_TYPE_PROVIDER_SENT = METRIC_MESSAGES . '.{type}.{provider}.sent';
const METRIC_MESSAGES_TYPE_PROVIDER_FAILED = METRIC_MESSAGES . '.{type}.{provider}.failed';
const METRIC_SESSIONS = 'sessions';
const METRIC_DATABASES = 'databases';
const METRIC_COLLECTIONS = 'collections';
const METRIC_DATABASES_STORAGE = 'databases.storage';
const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections';
const METRIC_DATABASE_ID_STORAGE = '{databaseInternalId}.databases.storage';
const METRIC_DOCUMENTS = 'documents';
const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE = '{databaseInternalId}.{collectionInternalId}.databases.storage';
const METRIC_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';
const METRIC_BUCKETS = 'buckets';
const METRIC_FILES = 'files';
const METRIC_FILES_STORAGE = 'files.storage';
const METRIC_FILES_TRANSFORMATIONS = 'files.transformations';
const METRIC_BUCKET_ID_FILES_TRANSFORMATIONS = '{bucketInternalId}.files.transformations';
const METRIC_FILES_IMAGES_TRANSFORMED = 'files.imagesTransformed';
const METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED = '{bucketInternalId}.files.imagesTransformed';
const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files';
const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage';
const METRIC_FUNCTIONS = 'functions';
const METRIC_DEPLOYMENTS = 'deployments';
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
const METRIC_BUILDS = 'builds';
const METRIC_BUILDS_SUCCESS = 'builds.success';
const METRIC_BUILDS_FAILED = 'builds.failed';
const METRIC_BUILDS_STORAGE = 'builds.storage';
const METRIC_BUILDS_COMPUTE = 'builds.compute';
const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success';
const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed';
const METRIC_BUILDS_MB_SECONDS = 'builds.mbSeconds';
const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds';
const METRIC_FUNCTION_ID_BUILDS_SUCCESS = '{functionInternalId}.builds.success';
const METRIC_FUNCTION_ID_BUILDS_FAILED = '{functionInternalId}.builds.failed';
const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS = '{functionInternalId}.builds.compute.success';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED = '{functionInternalId}.builds.compute.failed';
const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
const METRIC_FUNCTION_ID_BUILDS_MB_SECONDS = '{functionInternalId}.builds.mbSeconds';
const METRIC_EXECUTIONS = 'executions';
const METRIC_EXECUTIONS_COMPUTE = 'executions.compute';
const METRIC_EXECUTIONS_MB_SECONDS = 'executions.mbSeconds';
const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions';
const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute';
const METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS = '{functionInternalId}.executions.mbSeconds';
const METRIC_NETWORK_REQUESTS = 'network.requests';
const METRIC_NETWORK_INBOUND = 'network.inbound';
const METRIC_NETWORK_OUTBOUND = 'network.outbound';
const METRIC_MAU = 'users.mau';
const METRIC_DAU = 'users.dau';
const METRIC_WAU = 'users.wau';
const METRIC_WEBHOOKS = 'webhooks';
const METRIC_PLATFORMS = 'platforms';
const METRIC_PROVIDERS = 'providers';
const METRIC_TOPICS = 'topics';
const METRIC_TARGETS = 'targets';
const METRIC_PROVIDER_TYPE_TARGETS = '{providerType}.targets';
const METRIC_KEYS = 'keys';
const METRIC_DOMAINS = 'domains';
const METRIC_RESOURCE_TYPE_ID_BUILDS = '{resourceType}.{resourceInternalId}.builds';
const METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE = '{resourceType}.{resourceInternalId}.builds.storage';
const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
// Resource types
const RESOURCE_TYPE_PROJECTS = 'projects';
const RESOURCE_TYPE_FUNCTIONS = 'functions';
const RESOURCE_TYPE_DATABASES = 'databases';
const RESOURCE_TYPE_BUCKETS = 'buckets';
const RESOURCE_TYPE_PROVIDERS = 'providers';
const RESOURCE_TYPE_TOPICS = 'topics';
const RESOURCE_TYPE_SUBSCRIBERS = 'subscribers';
const RESOURCE_TYPE_MESSAGES = 'messages';
+404
View File
@@ -0,0 +1,404 @@
<?php
use Appwrite\OpenSSL\OpenSSL;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\System\System;
Database::addFilter(
'casting',
function (mixed $value) {
return json_encode(['value' => $value], JSON_PRESERVE_ZERO_FRACTION);
},
function (mixed $value) {
if (is_null($value)) {
return;
}
return json_decode($value, true)['value'];
}
);
Database::addFilter(
'enum',
function (mixed $value, Document $attribute) {
if ($attribute->isSet('elements')) {
$attribute->removeAttribute('elements');
}
return $value;
},
function (mixed $value, Document $attribute) {
$formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true);
if (isset($formatOptions['elements'])) {
$attribute->setAttribute('elements', $formatOptions['elements']);
}
return $value;
}
);
Database::addFilter(
'range',
function (mixed $value, Document $attribute) {
if ($attribute->isSet('min')) {
$attribute->removeAttribute('min');
}
if ($attribute->isSet('max')) {
$attribute->removeAttribute('max');
}
return $value;
},
function (mixed $value, Document $attribute) {
$formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true);
if (isset($formatOptions['min']) || isset($formatOptions['max'])) {
$attribute
->setAttribute('min', $formatOptions['min'])
->setAttribute('max', $formatOptions['max']);
}
return $value;
}
);
Database::addFilter(
'subQueryAttributes',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
$attributes = $database->find('attributes', [
Query::equal('collectionInternalId', [$document->getInternalId()]),
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
Query::limit($database->getLimitForAttributes()),
]);
foreach ($attributes as $attribute) {
if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) {
$options = $attribute->getAttribute('options');
foreach ($options as $key => $value) {
$attribute->setAttribute($key, $value);
}
$attribute->removeAttribute('options');
}
}
return $attributes;
}
);
Database::addFilter(
'subQueryIndexes',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('indexes', [
Query::equal('collectionInternalId', [$document->getInternalId()]),
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
Query::limit($database->getLimitForIndexes()),
]);
}
);
Database::addFilter(
'subQueryPlatforms',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('platforms', [
Query::equal('projectInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
Database::addFilter(
'subQueryKeys',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('keys', [
Query::equal('projectInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
Database::addFilter(
'subQueryWebhooks',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('webhooks', [
Query::equal('projectInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
Database::addFilter(
'subQuerySessions',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database->find('sessions', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
Database::addFilter(
'subQueryTokens',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
->find('tokens', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
Database::addFilter(
'subQueryChallenges',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
->find('challenges', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
Database::addFilter(
'subQueryAuthenticators',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
->find('authenticators', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
Database::addFilter(
'subQueryMemberships',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
->find('memberships', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
Database::addFilter(
'subQueryVariables',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('variables', [
Query::equal('resourceInternalId', [$document->getInternalId()]),
Query::equal('resourceType', ['function']),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
Database::addFilter(
'encrypt',
function (mixed $value) {
$key = System::getEnv('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$tag = null;
return json_encode([
'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
'method' => OpenSSL::CIPHER_AES_128_GCM,
'iv' => \bin2hex($iv),
'tag' => \bin2hex($tag ?? ''),
'version' => '1',
]);
},
function (mixed $value) {
if (is_null($value)) {
return;
}
$value = json_decode($value, true);
$key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']);
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
}
);
Database::addFilter(
'subQueryProjectVariables',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('variables', [
Query::equal('resourceType', ['project']),
Query::limit(APP_LIMIT_SUBQUERY)
]);
}
);
Database::addFilter(
'userSearch',
function (mixed $value, Document $user) {
$searchValues = [
$user->getId(),
$user->getAttribute('email', ''),
$user->getAttribute('name', ''),
$user->getAttribute('phone', '')
];
foreach ($user->getAttribute('labels', []) as $label) {
$searchValues[] = 'label:' . $label;
}
$search = implode(' ', \array_filter($searchValues));
return $search;
},
function (mixed $value) {
return $value;
}
);
Database::addFilter(
'subQueryTargets',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
->find('targets', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY)
]));
}
);
Database::addFilter(
'subQueryTopicTargets',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
$targetIds = Authorization::skip(fn () => \array_map(
fn ($document) => $document->getAttribute('targetInternalId'),
$database->find('subscribers', [
Query::equal('topicInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY)
])
));
if (\count($targetIds) > 0) {
return $database->skipValidation(fn () => $database->find('targets', [
Query::equal('$internalId', $targetIds)
]));
}
return [];
}
);
Database::addFilter(
'providerSearch',
function (mixed $value, Document $provider) {
$searchValues = [
$provider->getId(),
$provider->getAttribute('name', ''),
$provider->getAttribute('provider', ''),
$provider->getAttribute('type', '')
];
$search = \implode(' ', \array_filter($searchValues));
return $search;
},
function (mixed $value) {
return $value;
}
);
Database::addFilter(
'topicSearch',
function (mixed $value, Document $topic) {
$searchValues = [
$topic->getId(),
$topic->getAttribute('name', ''),
$topic->getAttribute('description', ''),
];
$search = \implode(' ', \array_filter($searchValues));
return $search;
},
function (mixed $value) {
return $value;
}
);
Database::addFilter(
'messageSearch',
function (mixed $value, Document $message) {
$searchValues = [
$message->getId(),
$message->getAttribute('description', ''),
$message->getAttribute('status', ''),
];
$data = \json_decode($message->getAttribute('data', []), true);
$providerType = $message->getAttribute('providerType', '');
switch ($providerType) {
case MESSAGE_TYPE_EMAIL:
$searchValues[] = $data['subject'];
$searchValues[] = MESSAGE_TYPE_EMAIL;
break;
case MESSAGE_TYPE_SMS:
$searchValues[] = $data['content'];
$searchValues[] = MESSAGE_TYPE_SMS;
break;
case MESSAGE_TYPE_PUSH:
$searchValues[] = $data['title'] ?? '';
$searchValues[] = MESSAGE_TYPE_PUSH;
break;
}
$search = \implode(' ', \array_filter($searchValues));
return $search;
},
function (mixed $value) {
return $value;
}
);
+43
View File
@@ -0,0 +1,43 @@
<?php
use Appwrite\Network\Validator\Email;
use Utopia\Database\Database;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Structure;
use Utopia\Validator\IP;
use Utopia\Validator\Range;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList;
Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () {
return new Email();
}, Database::VAR_STRING);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () {
return new DatetimeValidator();
}, Database::VAR_DATETIME);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) {
$elements = $attribute['formatOptions']['elements'] ?? [];
return new WhiteList($elements, true);
}, Database::VAR_STRING);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () {
return new IP();
}, Database::VAR_STRING);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () {
return new URL();
}, Database::VAR_STRING);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) {
$min = $attribute['formatOptions']['min'] ?? -INF;
$max = $attribute['formatOptions']['max'] ?? INF;
return new Range($min, $max, Range::TYPE_INTEGER);
}, Database::VAR_INTEGER);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) {
$min = $attribute['formatOptions']['min'] ?? -INF;
$max = $attribute['formatOptions']['max'] ?? INF;
return new Range($min, $max, Range::TYPE_FLOAT);
}, Database::VAR_FLOAT);
+23
View File
@@ -0,0 +1,23 @@
<?php
use Utopia\Config\Config;
use Utopia\Locale\Locale;
Locale::$exceptions = false;
$locales = Config::getParam('locale-codes', []);
foreach ($locales as $locale) {
$code = $locale['code'];
$path = __DIR__ . '/../config/locale/translations/' . $code . '.json';
if (!\file_exists($path)) {
$path = __DIR__ . '/../config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar`
if (!\file_exists($path)) {
$path = __DIR__ . '/../config/locale/translations/en.json'; // if none translation exists, use default from `en.json`
}
}
Locale::setLanguageFromJSON($code, $path);
}
+340
View File
@@ -0,0 +1,340 @@
<?php
use Appwrite\Extend\Exception;
use Appwrite\GraphQL\Promises\Adapter\Swoole;
use Appwrite\Hooks\Hooks;
use Appwrite\PubSub\Adapter\Redis as PubSub;
use Appwrite\URL\URL as AppwriteURL;
use MaxMind\Db\Reader;
use PHPMailer\PHPMailer\PHPMailer;
use Swoole\Database\PDOProxy;
use Utopia\App;
use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Adapter\MySQL;
use Utopia\Database\Adapter\SQL;
use Utopia\Domains\Validator\PublicDomain;
use Utopia\DSN\DSN;
use Utopia\Logger\Adapter\AppSignal;
use Utopia\Logger\Adapter\LogOwl;
use Utopia\Logger\Adapter\Raygun;
use Utopia\Logger\Adapter\Sentry;
use Utopia\Logger\Logger;
use Utopia\Pools\Group;
use Utopia\Pools\Pool;
use Utopia\Queue;
use Utopia\Registry\Registry;
use Utopia\System\System;
$register = new Registry();
App::setMode(System::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
if (!App::isProduction()) {
// Allow specific domains to skip public domain validation in dev environment
// Useful for existing tests involving webhooks
PublicDomain::allow(['request-catcher']);
}
$register->set('logger', function () {
// Register error logger
$providerName = System::getEnv('_APP_LOGGING_PROVIDER', '');
$providerConfig = System::getEnv('_APP_LOGGING_CONFIG', '');
if (empty($providerConfig)) {
return;
}
try {
$loggingProvider = new DSN($providerConfig ?? '');
$providerName = $loggingProvider->getScheme();
$providerConfig = match ($providerName) {
'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()],
'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()],
default => ['key' => $loggingProvider->getHost()],
};
} catch (Throwable $th) {
// Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables
Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage());
$configChunks = \explode(";", $providerConfig);
$providerConfig = match ($providerName) {
'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',],
'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''],
default => ['key' => $providerConfig],
};
}
if (empty($providerName) || empty($providerConfig)) {
return;
}
if (!Logger::hasProvider($providerName)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled");
}
try {
$adapter = match ($providerName) {
'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']),
'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']),
'raygun' => new Raygun($providerConfig['key']),
'appsignal' => new AppSignal($providerConfig['key']),
default => null
};
} catch (Throwable $th) {
$adapter = null;
}
if ($adapter === null) {
Console::error("Logging provider not supported. Logging is disabled");
return;
}
return new Logger($adapter);
});
$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'),
'user' => System::getEnv('_APP_DB_USER', ''),
'pass' => System::getEnv('_APP_DB_PASS', ''),
'path' => System::getEnv('_APP_DB_SCHEMA', ''),
]);
$fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([
'scheme' => 'redis',
'host' => System::getEnv('_APP_REDIS_HOST', 'redis'),
'port' => System::getEnv('_APP_REDIS_PORT', '6379'),
'user' => System::getEnv('_APP_REDIS_USER', ''),
'pass' => System::getEnv('_APP_REDIS_PASS', ''),
]);
$connections = [
'console' => [
'type' => 'database',
'dsns' => $fallbackForDB,
'multiple' => false,
'schemes' => ['mariadb', 'mysql'],
],
'database' => [
'type' => 'database',
'dsns' => $fallbackForDB,
'multiple' => true,
'schemes' => ['mariadb', 'mysql'],
],
'logs' => [
'type' => 'database',
'dsns' => System::getEnv('_APP_CONNECTIONS_DB_LOGS', $fallbackForDB),
'multiple' => false,
'schemes' => ['mariadb', 'mysql'],
],
'publisher' => [
'type' => 'publisher',
'dsns' => $fallbackForRedis,
'multiple' => false,
'schemes' => ['redis'],
],
'consumer' => [
'type' => 'consumer',
'dsns' => $fallbackForRedis,
'multiple' => false,
'schemes' => ['redis'],
],
'pubsub' => [
'type' => 'pubsub',
'dsns' => $fallbackForRedis,
'multiple' => false,
'schemes' => ['redis'],
],
'cache' => [
'type' => 'cache',
'dsns' => $fallbackForRedis,
'multiple' => true,
'schemes' => ['redis'],
],
];
$maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151);
$instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14);
$multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled';
if ($multiprocessing) {
$workerCount = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
} else {
$workerCount = 1;
}
if ($workerCount > $instanceConnections) {
throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500);
}
$poolSize = (int)($instanceConnections / $workerCount);
foreach ($connections as $key => $connection) {
$type = $connection['type'] ?? '';
$multiple = $connection['multiple'] ?? false;
$schemes = $connection['schemes'] ?? [];
$config = [];
$dsns = explode(',', $connection['dsns'] ?? '');
foreach ($dsns as &$dsn) {
$dsn = explode('=', $dsn);
$name = ($multiple) ? $key . '_' . $dsn[0] : $key;
$dsn = $dsn[1] ?? '';
$config[] = $name;
if (empty($dsn)) {
//throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}");
continue;
}
$dsn = new DSN($dsn);
$dsnHost = $dsn->getHost();
$dsnPort = $dsn->getPort();
$dsnUser = $dsn->getUser();
$dsnPass = $dsn->getPassword();
$dsnScheme = $dsn->getScheme();
$dsnDatabase = $dsn->getPath();
if (!in_array($dsnScheme, $schemes)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme");
}
/**
* Get Resource
*
* Creation could be reused across connection types like database, cache, queue, etc.
*
* Resource assignment to an adapter will happen below.
*/
$resource = match ($dsnScheme) {
'mysql',
'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array(
PDO::ATTR_TIMEOUT => 3, // Seconds
PDO::ATTR_PERSISTENT => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => true,
PDO::ATTR_STRINGIFY_FETCHES => true
));
});
},
'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) {
$redis = new \Redis();
@$redis->pconnect($dsnHost, (int)$dsnPort);
if ($dsnPass) {
$redis->auth($dsnPass);
}
$redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
return $redis;
},
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'),
};
$pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) {
// Get Adapter
switch ($type) {
case 'database':
$adapter = match ($dsn->getScheme()) {
'mariadb' => new MariaDB($resource()),
'mysql' => new MySQL($resource()),
default => null
};
$adapter->setDatabase($dsn->getPath());
return $adapter;
case 'pubsub':
return match ($dsn->getScheme()) {
'redis' => new PubSub($resource()),
default => null
};
case 'publisher':
case 'consumer':
return match ($dsn->getScheme()) {
'redis' => new Queue\Broker\Redis(new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort())),
default => null
};
case 'cache':
return match ($dsn->getScheme()) {
'redis' => new RedisCache($resource()),
default => null
};
default:
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation.");
}
});
$group->add($pool);
}
Config::setParam('pools-' . $key, $config);
}
return $group;
});
$register->set('db', function () {
// This is usually for our workers or CLI commands scope
$dbHost = System::getEnv('_APP_DB_HOST', '');
$dbPort = System::getEnv('_APP_DB_PORT', '');
$dbUser = System::getEnv('_APP_DB_USER', '');
$dbPass = System::getEnv('_APP_DB_PASS', '');
$dbScheme = System::getEnv('_APP_DB_SCHEMA', '');
return new PDO(
"mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4",
$dbUser,
$dbPass,
SQL::getPDOAttributes()
);
});
$register->set('smtp', function () {
$mail = new PHPMailer(true);
$mail->isSMTP();
$username = System::getEnv('_APP_SMTP_USERNAME');
$password = System::getEnv('_APP_SMTP_PASSWORD');
$mail->XMailer = 'Appwrite Mailer';
$mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp');
$mail->Port = System::getEnv('_APP_SMTP_PORT', 25);
$mail->SMTPAuth = !empty($username) && !empty($password);
$mail->Username = $username;
$mail->Password = $password;
$mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', '');
$mail->SMTPAutoTLS = false;
$mail->CharSet = 'UTF-8';
$from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
$email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$mail->setFrom($email, $from);
$mail->addReplyTo($email, $from);
$mail->isHTML(true);
return $mail;
});
$register->set('geodb', function () {
return new Reader(__DIR__ . '/../assets/dbip/dbip-country-lite-2024-09.mmdb');
});
$register->set('passwordsDictionary', function () {
$content = \file_get_contents(__DIR__ . '/../assets/security/10k-common-passwords');
$content = explode("\n", $content);
$content = array_flip($content);
return $content;
});
$register->set('promiseAdapter', function () {
return new Swoole();
});
$register->set('hooks', function () {
return new Hooks();
});
+845
View File
@@ -0,0 +1,845 @@
<?php
use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Auth\Auth;
use Appwrite\Auth\Key;
use Appwrite\Event\Audit;
use Appwrite\Event\Build;
use Appwrite\Event\Certificate;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
use Appwrite\Event\Migration;
use Appwrite\Event\Realtime;
use Appwrite\Event\StatsUsage;
use Appwrite\Event\Webhook;
use Appwrite\Extend\Exception;
use Appwrite\GraphQL\Schema;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Request;
use Executor\Executor;
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
use Utopia\App;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\DSN\DSN;
use Utopia\Locale\Locale;
use Utopia\Logger\Log;
use Utopia\Pools\Group;
use Utopia\Queue\Publisher;
use Utopia\Storage\Device;
use Utopia\Storage\Device\AWS;
use Utopia\Storage\Device\Backblaze;
use Utopia\Storage\Device\DOSpaces;
use Utopia\Storage\Device\Linode;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Device\S3;
use Utopia\Storage\Device\Wasabi;
use Utopia\Storage\Storage;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
use Utopia\Validator\Hostname;
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
// Runtime Execution
App::setResource('log', fn () => new Log());
App::setResource('logger', function ($register) {
return $register->get('logger');
}, ['register']);
App::setResource('hooks', function ($register) {
return $register->get('hooks');
}, ['register']);
App::setResource('register', fn () => $register);
App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en')));
App::setResource('localeCodes', function () {
return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []));
});
// Queues
App::setResource('publisher', function (Group $pools) {
return $pools->get('publisher')->pop()->getResource();
}, ['pools']);
App::setResource('consumer', function (Group $pools) {
return $pools->get('consumer')->pop()->getResource();
}, ['pools']);
App::setResource('queueForMessaging', function (Publisher $publisher) {
return new Messaging($publisher);
}, ['publisher']);
App::setResource('queueForMails', function (Publisher $publisher) {
return new Mail($publisher);
}, ['publisher']);
App::setResource('queueForBuilds', function (Publisher $publisher) {
return new Build($publisher);
}, ['publisher']);
App::setResource('queueForDatabase', function (Publisher $publisher) {
return new EventDatabase($publisher);
}, ['publisher']);
App::setResource('queueForDeletes', function (Publisher $publisher) {
return new Delete($publisher);
}, ['publisher']);
App::setResource('queueForEvents', function (Publisher $publisher) {
return new Event($publisher);
}, ['publisher']);
App::setResource('queueForWebhooks', function (Publisher $publisher) {
return new Webhook($publisher);
}, ['publisher']);
App::setResource('queueForRealtime', function () {
return new Realtime();
}, []);
App::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
App::setResource('queueForAudits', function (Publisher $publisher) {
return new Audit($publisher);
}, ['publisher']);
App::setResource('queueForFunctions', function (Publisher $publisher) {
return new Func($publisher);
}, ['publisher']);
App::setResource('queueForCertificates', function (Publisher $publisher) {
return new Certificate($publisher);
}, ['publisher']);
App::setResource('queueForMigrations', function (Publisher $publisher) {
return new Migration($publisher);
}, ['publisher']);
App::setResource('clients', function ($request, $console, $project) {
$console->setAttribute('platforms', [ // Always allow current host
'$collection' => ID::custom('platforms'),
'name' => 'Current Host',
'type' => Origin::CLIENT_TYPE_WEB,
'hostname' => $request->getHostname(),
], Document::SET_TYPE_APPEND);
$hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', ''));
$validator = new Hostname();
foreach ($hostnames as $hostname) {
$hostname = trim($hostname);
if (!$validator->isValid($hostname)) {
continue;
}
$console->setAttribute('platforms', [
'$collection' => ID::custom('platforms'),
'type' => Origin::CLIENT_TYPE_WEB,
'name' => $hostname,
'hostname' => $hostname,
], Document::SET_TYPE_APPEND);
}
/**
* Get All verified client URLs for both console and current projects
* + Filter for duplicated entries
*/
$clientsConsole = \array_map(
fn ($node) => $node['hostname'],
\array_filter(
$console->getAttribute('platforms', []),
fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname']))
)
);
$clients = $clientsConsole;
$platforms = $project->getAttribute('platforms', []);
foreach ($platforms as $node) {
if (
isset($node['type']) &&
($node['type'] === Origin::CLIENT_TYPE_WEB ||
$node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) &&
!empty($node['hostname'])
) {
$clients[] = $node['hostname'];
}
}
return \array_unique($clients);
}, ['request', 'console', 'project']);
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) {
/** @var Appwrite\Utopia\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Database $dbForPlatform */
/** @var string $mode */
Authorization::setDefaultStatus(true);
Auth::setCookieName('a_session_' . $project->getId());
if (APP_MODE_ADMIN === $mode) {
Auth::setCookieName('a_session_' . $console->getId());
}
$session = Auth::decodeSession(
$request->getCookie(
Auth::$cookieName, // Get sessions
$request->getCookie(Auth::$cookieName . '_legacy', '')
)
);
// Get session from header for SSR clients
if (empty($session['id']) && empty($session['secret'])) {
$sessionHeader = $request->getHeader('x-appwrite-session', '');
if (!empty($sessionHeader)) {
$session = Auth::decodeSession($sessionHeader);
}
}
// Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies
if ($response) {
$response->addHeader('X-Debug-Fallback', 'false');
}
if (empty($session['id']) && empty($session['secret'])) {
if ($response) {
$response->addHeader('X-Debug-Fallback', 'true');
}
$fallback = $request->getHeader('x-fallback-cookies', '');
$fallback = \json_decode($fallback, true);
$session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : ''));
}
Auth::$unique = $session['id'] ?? '';
Auth::$secret = $session['secret'] ?? '';
if (APP_MODE_ADMIN !== $mode) {
if ($project->isEmpty()) {
$user = new Document([]);
} else {
if ($project->getId() === 'console') {
$user = $dbForPlatform->getDocument('users', Auth::$unique);
} else {
$user = $dbForProject->getDocument('users', Auth::$unique);
}
}
} else {
$user = $dbForPlatform->getDocument('users', Auth::$unique);
}
if (
$user->isEmpty() // Check a document has been found in the DB
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)
) { // Validate user has valid login token
$user = new Document([]);
}
// if (APP_MODE_ADMIN === $mode) {
// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) {
// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
// } else {
// $user = new Document([]);
// }
// }
$authJWT = $request->getHeader('x-appwrite-jwt', '');
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
try {
$payload = $jwt->decode($authJWT);
} catch (JWTException $error) {
throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage());
}
$jwtUserId = $payload['userId'] ?? '';
if (!empty($jwtUserId)) {
$user = $dbForProject->getDocument('users', $jwtUserId);
}
$jwtSessionId = $payload['sessionId'] ?? '';
if (!empty($jwtSessionId)) {
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
$user = new Document([]);
}
}
}
$dbForProject->setMetadata('user', $user->getId());
$dbForPlatform->setMetadata('user', $user->getId());
return $user;
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform']);
App::setResource('project', function ($dbForPlatform, $request, $console) {
/** @var Appwrite\Utopia\Request $request */
/** @var Utopia\Database\Database $dbForPlatform */
/** @var Utopia\Database\Document $console */
$projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', ''));
if (empty($projectId) || $projectId === 'console') {
return $console;
}
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
return $project;
}, ['dbForPlatform', 'request', 'console']);
App::setResource('session', function (Document $user) {
if ($user->isEmpty()) {
return;
}
$sessions = $user->getAttribute('sessions', []);
$sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret);
if (!$sessionId) {
return;
}
foreach ($sessions as $session) {/** @var Document $session */
if ($sessionId === $session->getId()) {
return $session;
}
}
return;
}, ['user']);
App::setResource('console', function () {
return new Document(Config::getParam('console'));
}, []);
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
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'));
}
$dbAdapter = $pools
->get($dsn->getHost())
->pop()
->getResource();
$database = new Database($dbAdapter, $cache);
$database
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getInternalId());
}
return $database;
}, ['pools', 'dbForPlatform', 'cache', 'project']);
App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
$dbAdapter = $pools
->get('console')
->pop()
->getResource();
$database = new Database($dbAdapter, $cache);
$database
->setNamespace('_console')
->setMetadata('host', \gethostname())
->setMetadata('project', 'console')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
return $database;
}, ['pools', 'cache']);
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
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'));
}
$configure = (function (Database $database) use ($project, $dsn) {
$database
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getInternalId());
}
});
if (isset($databases[$dsn->getHost()])) {
$database = $databases[$dsn->getHost()];
$configure($database);
return $database;
}
$dbAdapter = $pools
->get($dsn->getHost())
->pop()
->getResource();
$database = new Database($dbAdapter, $cache);
$databases[$dsn->getHost()] = $database;
$configure($database);
return $database;
};
}, ['pools', 'dbForPlatform', 'cache']);
App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, $database) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant($project->getInternalId());
return $database;
}
$dbAdapter = $pools
->get('logs')
->pop()
->getResource();
$database = new Database(
$dbAdapter,
$cache
);
$database
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
// set tenant
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant($project->getInternalId());
}
return $database;
};
}, ['pools', 'cache']);
App::setResource('telemetry', fn () => new NoTelemetry());
App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
$list = Config::getParam('pools-cache', []);
$adapters = [];
foreach ($list as $value) {
$adapters[] = $pools
->get($value)
->pop()
->getResource();
}
$cache = new Cache(new Sharding($adapters));
$cache->setTelemetry($telemetry);
return $cache;
}, ['pools', 'telemetry']);
App::setResource('redis', function () {
$host = System::getEnv('_APP_REDIS_HOST', 'localhost');
$port = System::getEnv('_APP_REDIS_PORT', 6379);
$pass = System::getEnv('_APP_REDIS_PASS', '');
$redis = new \Redis();
@$redis->pconnect($host, (int)$port);
if ($pass) {
$redis->auth($pass);
}
$redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
return $redis;
});
App::setResource('timelimit', function (\Redis $redis) {
return function (string $key, int $limit, int $time) use ($redis) {
return new TimeLimitRedis($key, $limit, $time, $redis);
};
}, ['redis']);
App::setResource('deviceForLocal', function () {
return new Local();
});
App::setResource('deviceForFiles', function ($project) {
return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
}, ['project']);
App::setResource('deviceForFunctions', function ($project) {
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
}, ['project']);
App::setResource('deviceForBuilds', function ($project) {
return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
}, ['project']);
function getDevice(string $root, string $connection = ''): Device
{
$connection = !empty($connection) ? $connection : System::getEnv('_APP_CONNECTIONS_STORAGE', '');
if (!empty($connection)) {
$acl = 'private';
$device = Storage::DEVICE_LOCAL;
$accessKey = '';
$accessSecret = '';
$bucket = '';
$region = '';
$url = System::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
try {
$dsn = new DSN($connection);
$device = $dsn->getScheme();
$accessKey = $dsn->getUser() ?? '';
$accessSecret = $dsn->getPassword() ?? '';
$bucket = $dsn->getPath() ?? '';
$region = $dsn->getParam('region');
} catch (\Throwable $e) {
Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.');
}
switch ($device) {
case Storage::DEVICE_S3:
if (!empty($url)) {
return new S3($root, $accessKey, $accessSecret, $url, $region, $acl);
} else {
return new AWS($root, $accessKey, $accessSecret, $bucket, $region, $acl);
}
// no break
case STORAGE::DEVICE_DO_SPACES:
$device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl);
$device->setHttpVersion(S3::HTTP_VERSION_1_1);
return $device;
case Storage::DEVICE_BACKBLAZE:
return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl);
case Storage::DEVICE_LINODE:
return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl);
case Storage::DEVICE_WASABI:
return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl);
case Storage::DEVICE_LOCAL:
default:
return new Local($root);
}
} else {
switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) {
case Storage::DEVICE_LOCAL:
default:
return new Local($root);
case Storage::DEVICE_S3:
$s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
$s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', '');
$s3Region = System::getEnv('_APP_STORAGE_S3_REGION', '');
$s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', '');
$s3Acl = 'private';
$s3EndpointUrl = System::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
if (!empty($s3EndpointUrl)) {
return new S3($root, $s3AccessKey, $s3SecretKey, $s3EndpointUrl, $s3Region, $s3Acl);
} else {
return new AWS($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
}
// no break
case Storage::DEVICE_DO_SPACES:
$doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', '');
$doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', '');
$doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', '');
$doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', '');
$doSpacesAcl = 'private';
$device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
$device->setHttpVersion(S3::HTTP_VERSION_1_1);
return $device;
case Storage::DEVICE_BACKBLAZE:
$backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', '');
$backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', '');
$backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', '');
$backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', '');
$backblazeAcl = 'private';
return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl);
case Storage::DEVICE_LINODE:
$linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', '');
$linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', '');
$linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', '');
$linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', '');
$linodeAcl = 'private';
return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl);
case Storage::DEVICE_WASABI:
$wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', '');
$wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', '');
$wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', '');
$wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', '');
$wasabiAcl = 'private';
return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl);
}
}
}
App::setResource('mode', function ($request) {
/** @var Appwrite\Utopia\Request $request */
/**
* Defines the mode for the request:
* - 'default' => Requests for Client and Server Side
* - 'admin' => Request from the Console on non-console projects
*/
return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT));
}, ['request']);
App::setResource('geodb', function ($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('geodb');
}, ['register']);
App::setResource('passwordsDictionary', function ($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('passwordsDictionary');
}, ['register']);
App::setResource('servers', function () {
$platforms = Config::getParam('platforms');
$server = $platforms[APP_PLATFORM_SERVER];
$languages = array_map(function ($language) {
return strtolower($language['name']);
}, $server['sdks']);
return $languages;
});
App::setResource('promiseAdapter', function ($register) {
return $register->get('promiseAdapter');
}, ['register']);
App::setResource('schema', function ($utopia, $dbForProject) {
$complexity = function (int $complexity, array $args) {
$queries = Query::parseQueries($args['queries'] ?? []);
$query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null;
$limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT;
return $complexity * $limit;
};
$attributes = function (int $limit, int $offset) use ($dbForProject) {
$attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [
Query::limit($limit),
Query::offset($offset),
]));
return \array_map(function ($attr) {
return $attr->getArrayCopy();
}, $attrs);
};
$urls = [
'list' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents";
},
'create' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents";
},
'read' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}";
},
'update' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}";
},
'delete' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}";
},
];
$params = [
'list' => function (string $databaseId, string $collectionId, array $args) {
return [ 'queries' => $args['queries']];
},
'create' => function (string $databaseId, string $collectionId, array $args) {
$id = $args['id'] ?? 'unique()';
$permissions = $args['permissions'] ?? null;
unset($args['id']);
unset($args['permissions']);
// Order must be the same as the route params
return [
'databaseId' => $databaseId,
'documentId' => $id,
'collectionId' => $collectionId,
'data' => $args,
'permissions' => $permissions,
];
},
'update' => function (string $databaseId, string $collectionId, array $args) {
$documentId = $args['id'];
$permissions = $args['permissions'] ?? null;
unset($args['id']);
unset($args['permissions']);
// Order must be the same as the route params
return [
'databaseId' => $databaseId,
'collectionId' => $collectionId,
'documentId' => $documentId,
'data' => $args,
'permissions' => $permissions,
];
},
];
return Schema::build(
$utopia,
$complexity,
$attributes,
$urls,
$params,
);
}, ['utopia', 'dbForProject']);
App::setResource('contributors', function () {
$path = 'app/config/contributors.json';
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
return $list;
});
App::setResource('employees', function () {
$path = 'app/config/employees.json';
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
return $list;
});
App::setResource('heroes', function () {
$path = 'app/config/heroes.json';
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
return $list;
});
App::setResource('gitHub', function (Cache $cache) {
return new VcsGitHub($cache);
}, ['cache']);
App::setResource('requestTimestamp', function ($request) {
//TODO: Move this to the Request class itself
$timestampHeader = $request->getHeader('x-appwrite-timestamp');
$requestTimestamp = null;
if (!empty($timestampHeader)) {
try {
$requestTimestamp = new \DateTime($timestampHeader);
} catch (\Throwable $e) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value');
}
}
return $requestTimestamp;
}, ['request']);
App::setResource('plan', function (array $plan = []) {
return [];
});
App::setResource('smsRates', function () {
return [];
});
App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request) {
$teamInternalId = '';
if ($project->getId() !== 'console') {
$teamInternalId = $project->getAttribute('teamInternalId', '');
} else {
$route = $utopia->match($request);
$path = $route->getPath();
if (str_starts_with($path, '/v1/projects/:projectId')) {
$uri = $request->getURI();
$pid = explode('/', $uri)[3];
$p = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $pid));
$teamInternalId = $p->getAttribute('teamInternalId', '');
} elseif ($path === '/v1/projects') {
$teamId = $request->getParam('teamId', '');
$team = Authorization::skip(fn () => $dbForPlatform->getDocument('teams', $teamId));
return $team;
}
}
$team = Authorization::skip(function () use ($dbForPlatform, $teamInternalId) {
return $dbForPlatform->findOne('teams', [
Query::equal('$internalId', [$teamInternalId]),
]);
});
return $team;
}, ['project', 'dbForPlatform', 'utopia', 'request']);
App::setResource(
'isResourceBlocked',
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
);
App::setResource('previewHostname', function (Request $request) {
if (App::isDevelopment()) {
$host = $request->getQuery('appwrite-hostname') ?? '';
if (!empty($host)) {
return $host;
}
}
return '';
}, ['request']);
App::setResource('apiKey', function (Request $request, Document $project): ?Key {
$key = $request->getHeader('x-appwrite-key');
if (empty($key)) {
return null;
}
return Key::decode($project, $key);
}, ['request', 'project']);
App::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
+49 -9
View File
@@ -116,6 +116,7 @@ $image = $this->getParam('image', '');
- _APP_STORAGE_S3_SECRET
- _APP_STORAGE_S3_REGION
- _APP_STORAGE_S3_BUCKET
- _APP_STORAGE_S3_ENDPOINT
- _APP_STORAGE_DO_SPACES_ACCESS_KEY
- _APP_STORAGE_DO_SPACES_SECRET
- _APP_STORAGE_DO_SPACES_REGION
@@ -147,6 +148,7 @@ $image = $this->getParam('image', '');
- _APP_MAINTENANCE_RETENTION_CACHE
- _APP_MAINTENANCE_RETENTION_ABUSE
- _APP_MAINTENANCE_RETENTION_AUDIT
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
- _APP_MAINTENANCE_RETENTION_SCHEDULES
- _APP_SMS_PROVIDER
@@ -163,11 +165,10 @@ $image = $this->getParam('image', '');
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
- _APP_ASSISTANT_OPENAI_API_KEY
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: <?php echo $organization; ?>/console:5.2.27
image: <?php echo $organization; ?>/console:5.2.58
restart: unless-stopped
networks:
- appwrite
@@ -318,6 +319,7 @@ $image = $this->getParam('image', '');
- _APP_STORAGE_S3_SECRET
- _APP_STORAGE_S3_REGION
- _APP_STORAGE_S3_BUCKET
- _APP_STORAGE_S3_ENDPOINT
- _APP_STORAGE_DO_SPACES_ACCESS_KEY
- _APP_STORAGE_DO_SPACES_SECRET
- _APP_STORAGE_DO_SPACES_REGION
@@ -339,7 +341,10 @@ $image = $this->getParam('image', '');
- _APP_EXECUTOR_HOST
- _APP_MAINTENANCE_RETENTION_ABUSE
- _APP_MAINTENANCE_RETENTION_AUDIT
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
- _APP_MAINTENANCE_RETENTION_EXECUTION
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- _APP_EMAIL_CERTIFICATES
appwrite-worker-databases:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
@@ -413,6 +418,7 @@ $image = $this->getParam('image', '');
- _APP_STORAGE_S3_SECRET
- _APP_STORAGE_S3_REGION
- _APP_STORAGE_S3_BUCKET
- _APP_STORAGE_S3_ENDPOINT
- _APP_STORAGE_DO_SPACES_ACCESS_KEY
- _APP_STORAGE_DO_SPACES_SECRET
- _APP_STORAGE_DO_SPACES_REGION
@@ -568,6 +574,7 @@ $image = $this->getParam('image', '');
- _APP_STORAGE_S3_SECRET
- _APP_STORAGE_S3_REGION
- _APP_STORAGE_S3_BUCKET
- _APP_STORAGE_S3_ENDPOINT
- _APP_STORAGE_DO_SPACES_ACCESS_KEY
- _APP_STORAGE_DO_SPACES_SECRET
- _APP_STORAGE_DO_SPACES_REGION
@@ -646,13 +653,45 @@ $image = $this->getParam('image', '');
- _APP_MAINTENANCE_RETENTION_CACHE
- _APP_MAINTENANCE_RETENTION_ABUSE
- _APP_MAINTENANCE_RETENTION_AUDIT
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
- _APP_MAINTENANCE_RETENTION_SCHEDULES
appwrite-worker-usage:
appwrite-task-stats-resources:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-usage
container_name: appwrite-worker-usage
container_name: appwrite-task-stats-resources
entrypoint: stats-resources
<<: *x-logging
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
- _APP_STATS_RESOURCES_INTERVAL
appwrite-worker-stats-resources:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-stats-resources
container_name: appwrite-worker-stats-resources
<<: *x-logging
restart: unless-stopped
networks:
@@ -675,13 +714,13 @@ $image = $this->getParam('image', '');
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_STATS_RESOURCES_INTERVAL
appwrite-worker-usage-dump:
appwrite-worker-stats-usage:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-usage-dump
entrypoint: worker-stats-usage
container_name: appwrite-worker-stats-usage
<<: *x-logging
container_name: appwrite-worker-usage-dump
restart: unless-stopped
networks:
- appwrite
@@ -822,6 +861,7 @@ $image = $this->getParam('image', '');
- OPR_EXECUTOR_STORAGE_S3_SECRET=$_APP_STORAGE_S3_SECRET
- OPR_EXECUTOR_STORAGE_S3_REGION=$_APP_STORAGE_S3_REGION
- OPR_EXECUTOR_STORAGE_S3_BUCKET=$_APP_STORAGE_S3_BUCKET
- OPR_EXECUTOR_STORAGE_S3_ENDPOINT=$_APP_STORAGE_S3_ENDPOINT
- OPR_EXECUTOR_STORAGE_DO_SPACES_ACCESS_KEY=$_APP_STORAGE_DO_SPACES_ACCESS_KEY
- OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=$_APP_STORAGE_DO_SPACES_SECRET
- OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=$_APP_STORAGE_DO_SPACES_REGION
+61 -13
View File
@@ -13,9 +13,11 @@ use Appwrite\Event\Func;
use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
use Appwrite\Event\Migration;
use Appwrite\Event\Usage;
use Appwrite\Event\UsageDump;
use Appwrite\Event\Realtime;
use Appwrite\Event\StatsUsage;
use Appwrite\Event\Webhook;
use Appwrite\Platform\Appwrite;
use Executor\Executor;
use Swoole\Runtime;
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
use Utopia\Cache\Adapter\Sharding;
@@ -108,6 +110,8 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
->setNamespace('_' . $project->getInternalId());
}
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
return $database;
}, ['cache', 'register', 'message', 'project', 'dbForPlatform']);
@@ -169,20 +173,58 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
->setNamespace('_' . $project->getInternalId());
}
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
return $database;
};
}, ['pools', 'dbForPlatform', 'cache']);
Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, $database) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant($project->getInternalId());
return $database;
}
$dbAdapter = $pools
->get('logs')
->pop()
->getResource();
$database = new Database(
$dbAdapter,
$cache
);
$database
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
// set tenant
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant($project->getInternalId());
}
return $database;
};
}, ['pools', 'cache']);
Server::setResource('abuseRetention', function () {
return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400);
return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400); // 1 day
});
Server::setResource('auditRetention', function () {
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600));
});
Server::setResource('auditRetention', function (Document $project) {
if ($project->getId() === 'console') {
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE', 15778800)); // 6 months
}
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600)); // 14 days
}, ['project']);
Server::setResource('executionRetention', function () {
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600));
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); // 14 days
});
Server::setResource('cache', function (Registry $register) {
@@ -232,12 +274,8 @@ Server::setResource('consumer', function (Group $pools) {
return $pools->get('consumer')->pop()->getResource();
}, ['pools']);
Server::setResource('queueForUsage', function (Publisher $publisher) {
return new Usage($publisher);
}, ['publisher']);
Server::setResource('queueForUsageDump', function (Publisher $publisher) {
return new UsageDump($publisher);
Server::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
Server::setResource('queueForDatabase', function (Publisher $publisher) {
@@ -268,10 +306,18 @@ Server::setResource('queueForAudits', function (Publisher $publisher) {
return new Audit($publisher);
}, ['publisher']);
Server::setResource('queueForWebhooks', function (Publisher $publisher) {
return new Webhook($publisher);
}, ['publisher']);
Server::setResource('queueForFunctions', function (Publisher $publisher) {
return new Func($publisher);
}, ['publisher']);
Server::setResource('queueForRealtime', function () {
return new Realtime();
}, []);
Server::setResource('queueForCertificates', function (Publisher $publisher) {
return new Certificate($publisher);
}, ['publisher']);
@@ -363,6 +409,8 @@ Server::setResource('logError', function (Registry $register, Document $project)
};
}, ['register', 'project']);
Server::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
$pools = $register->get('pools');
$platform = new Appwrite();
$args = $platform->getEnv('argv');
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/cli.php stats-resources $@
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/worker.php stats-resources $@
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/worker.php stats-usage $@
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
php /usr/src/code/app/worker.php usage $@
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
php /usr/src/code/app/worker.php usage-dump $@
+18 -18
View File
@@ -45,34 +45,34 @@
"ext-sockets": "*",
"appwrite/php-runtimes": "0.16.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "0.49.*",
"utopia-php/abuse": "0.52.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.49.*",
"utopia-php/cache": "0.11.*",
"utopia-php/audit": "0.55.*",
"utopia-php/cache": "0.12.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.58.4",
"utopia-php/database": "0.66.*",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
"utopia-php/fetch": "0.3.*",
"utopia-php/image": "0.7.*",
"utopia-php/fetch": "0.4.*",
"utopia-php/image": "0.8.*",
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.14.*",
"utopia-php/migration": "0.6.*",
"utopia-php/messaging": "0.16.*",
"utopia-php/migration": "0.8.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.3",
"utopia-php/pools": "0.5.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.8.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/queue": "0.8.*",
"utopia-php/queue": "0.9.*",
"utopia-php/registry": "0.5.*",
"utopia-php/storage": "0.18.*",
"utopia-php/swoole": "0.8.*",
"utopia-php/system": "0.9.*",
"utopia-php/telemetry": "0.1.*",
"utopia-php/vcs": "0.8.*",
"utopia-php/websocket": "0.1.*",
"utopia-php/vcs": "0.9.*",
"utopia-php/websocket": "0.3.*",
"matomo/device-detector": "6.1.*",
"dragonmantank/cron-expression": "3.3.2",
"phpmailer/phpmailer": "6.9.1",
@@ -84,12 +84,12 @@
},
"require-dev": {
"ext-fileinfo": "*",
"appwrite/sdk-generator": "0.39.32",
"phpunit/phpunit": "9.5.20",
"appwrite/sdk-generator": "0.40.*",
"phpunit/phpunit": "9.*",
"swoole/ide-helper": "5.1.2",
"textalk/websocket": "1.5.7",
"laravel/pint": "^1.14",
"phpbench/phpbench": "^1.2"
"textalk/websocket": "1.5.*",
"laravel/pint": "1.*",
"phpbench/phpbench": "1.*"
},
"provide": {
"ext-phpiredis": "*"
Generated
+294 -915
View File
File diff suppressed because it is too large Load Diff
+53 -8
View File
@@ -140,6 +140,7 @@ services:
- _APP_STORAGE_S3_SECRET
- _APP_STORAGE_S3_REGION
- _APP_STORAGE_S3_BUCKET
- _APP_STORAGE_S3_ENDPOINT
- _APP_STORAGE_DO_SPACES_ACCESS_KEY
- _APP_STORAGE_DO_SPACES_SECRET
- _APP_STORAGE_DO_SPACES_REGION
@@ -170,6 +171,7 @@ services:
- _APP_MAINTENANCE_RETENTION_CACHE
- _APP_MAINTENANCE_RETENTION_ABUSE
- _APP_MAINTENANCE_RETENTION_AUDIT
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
- _APP_MAINTENANCE_RETENTION_SCHEDULES
- _APP_SMS_PROVIDER
@@ -196,11 +198,14 @@ services:
- _APP_DATABASE_SHARED_TABLES_V1
- _APP_DATABASE_SHARED_NAMESPACE
- _APP_FUNCTIONS_CREATION_ABUSE_LIMIT
- _APP_CUSTOM_DOMAIN_DENY_LIST
extra_hosts:
- "host.docker.internal:host-gateway"
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: appwrite/console:5.2.27
image: appwrite/console:5.2.58
restart: unless-stopped
networks:
- appwrite
@@ -365,6 +370,7 @@ services:
- _APP_STORAGE_S3_SECRET
- _APP_STORAGE_S3_REGION
- _APP_STORAGE_S3_BUCKET
- _APP_STORAGE_S3_ENDPOINT
- _APP_STORAGE_DO_SPACES_ACCESS_KEY
- _APP_STORAGE_DO_SPACES_SECRET
- _APP_STORAGE_DO_SPACES_REGION
@@ -387,6 +393,8 @@ services:
- _APP_DATABASE_SHARED_TABLES
- _APP_DATABASE_SHARED_TABLES_V1
- _APP_EMAIL_CERTIFICATES
- _APP_MAINTENANCE_RETENTION_AUDIT
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
appwrite-worker-databases:
entrypoint: worker-databases
@@ -466,6 +474,7 @@ services:
- _APP_STORAGE_S3_SECRET
- _APP_STORAGE_S3_REGION
- _APP_STORAGE_S3_BUCKET
- _APP_STORAGE_S3_ENDPOINT
- _APP_STORAGE_DO_SPACES_ACCESS_KEY
- _APP_STORAGE_DO_SPACES_SECRET
- _APP_STORAGE_DO_SPACES_REGION
@@ -483,6 +492,8 @@ services:
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
- _APP_DATABASE_SHARED_TABLES
extra_hosts:
- "host.docker.internal:host-gateway"
appwrite-worker-certificates:
entrypoint: worker-certificates
@@ -631,6 +642,7 @@ services:
- _APP_STORAGE_S3_SECRET
- _APP_STORAGE_S3_REGION
- _APP_STORAGE_S3_BUCKET
- _APP_STORAGE_S3_ENDPOINT
- _APP_STORAGE_DO_SPACES_ACCESS_KEY
- _APP_STORAGE_DO_SPACES_SECRET
- _APP_STORAGE_DO_SPACES_REGION
@@ -717,15 +729,47 @@ services:
- _APP_MAINTENANCE_RETENTION_CACHE
- _APP_MAINTENANCE_RETENTION_ABUSE
- _APP_MAINTENANCE_RETENTION_AUDIT
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
- _APP_MAINTENANCE_RETENTION_SCHEDULES
- _APP_MAINTENANCE_DELAY
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-usage:
entrypoint: worker-usage
appwrite-task-stats-resources:
container_name: appwrite-task-stats-resources
entrypoint: stats-resources
<<: *x-logging
container_name: appwrite-worker-usage
image: appwrite-dev
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
- _APP_STATS_RESOURCES_INTERVAL
appwrite-worker-stats-resources:
entrypoint: worker-stats-resources
<<: *x-logging
container_name: appwrite-worker-stats-resources
image: appwrite-dev
networks:
- appwrite
@@ -753,10 +797,10 @@ services:
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-usage-dump:
entrypoint: worker-usage-dump
appwrite-worker-stats-usage:
entrypoint: worker-stats-usage
<<: *x-logging
container_name: appwrite-worker-usage-dump
container_name: appwrite-worker-stats-usage
image: appwrite-dev
networks:
- appwrite
@@ -908,6 +952,7 @@ services:
- OPR_EXECUTOR_STORAGE_S3_SECRET=$_APP_STORAGE_S3_SECRET
- OPR_EXECUTOR_STORAGE_S3_REGION=$_APP_STORAGE_S3_REGION
- OPR_EXECUTOR_STORAGE_S3_BUCKET=$_APP_STORAGE_S3_BUCKET
- OPR_EXECUTOR_STORAGE_S3_ENDPOINT=$_APP_STORAGE_S3_ENDPOINT
- OPR_EXECUTOR_STORAGE_DO_SPACES_ACCESS_KEY=$_APP_STORAGE_DO_SPACES_ACCESS_KEY
- OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=$_APP_STORAGE_DO_SPACES_SECRET
- OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=$_APP_STORAGE_DO_SPACES_REGION
@@ -1059,4 +1104,4 @@ volumes:
appwrite-certificates:
appwrite-functions:
appwrite-builds:
appwrite-config:
appwrite-config:
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
Client client = new Client(getApplicationContext())
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("5df5acd0d48c2"); // Your project ID
Account account = new Account(client);

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