Compare commits

..
143 Commits
Author SHA1 Message Date
Jake Barnby fffa8bdc7f Update lock 2025-01-30 22:58:48 +13:00
Jake Barnby 7f699910b6 Merge remote-tracking branch 'origin/1.6.x' into poc-timeout
# Conflicts:
#	composer.lock
2025-01-30 22:52:16 +13:00
Jake Barnby e8722c10e8 Update tests 2025-01-29 23:41:43 +13:00
Jake Barnby d32299fe1d Format 2025-01-28 22:12:13 +13:00
Jake Barnby 3d0884aaec Update hooks 2025-01-28 22:10:00 +13:00
Jake Barnby 3b71215b4f Fix merge 2025-01-28 21:55:11 +13:00
Jake Barnby 34074f74a7 Merge remote-tracking branch 'origin/1.6.x' into poc-timeout
# Conflicts:
#	.env
#	app/config/collections.php
#	app/config/errors.php
#	app/controllers/api/databases.php
#	app/views/install/compose.phtml
#	composer.lock
#	docker-compose.yml
#	src/Appwrite/Extend/Exception.php
#	src/Appwrite/Utopia/Response.php
2025-01-28 21:46:12 +13:00
fogelito cd5f6d6ecd uncomment appwrite-assistant 2023-08-15 11:50:27 +03:00
fogelito a0057a6e5d Merge branch '1.4.x' of github.com:appwrite/appwrite into poc-timeout 2023-08-15 11:49:35 +03:00
fogelito 943a7af885 Slow queries response model 2023-08-15 11:47:04 +03:00
Jake BarnbyandGitHub c93aea7669 Update docker-compose.yml 2023-08-15 01:35:59 -04:00
Jake Barnby 603c2083ef Uncomment service 2023-08-15 01:24:37 -04:00
Jake Barnby 0ceef6b24f Add slow query model 2023-08-15 01:06:52 -04:00
Jake Barnby 7cf899b302 Fix tests 2023-08-15 00:12:38 -04:00
Jake Barnby 5128c10348 Fix tests 2023-08-14 23:08:56 -04:00
Jake Barnby 479b41d914 Fix tests 2023-08-14 22:24:15 -04:00
fogelito 86abba7618 fix collections.php ID::custom 2023-08-15 00:50:40 +03:00
fogelito 0522affba0 fix collections.php 2023-08-15 00:41:34 +03:00
fogelito ab9ee84ed0 composer.lock 2023-08-14 23:36:15 +03:00
fogelito e67292a614 Merge branch '1.4.x' of github.com:appwrite/appwrite into poc-timeout
 Conflicts:
	composer.lock
	tests/e2e/Services/Databases/DatabasesBase.php
2023-08-14 22:01:31 +03:00
fogelito 7519e281a3 composer 2023-08-14 21:58:18 +03:00
fogelito 94c7fd74e4 composer versions 2023-08-09 18:27:31 +03:00
fogelito 41ca0c4165 composer.lock 2023-08-09 17:51:37 +03:00
fogelito 5578cea29f comment out appwrite-assistant 2023-08-09 17:20:05 +03:00
fogelito aa48432acd composer.json update 2023-08-09 13:10:29 +03:00
fogelito 7c7678b0cb composer.lock 2023-08-09 13:03:58 +03:00
fogelito a46a405b5c docker-compose.yml fixed 2023-08-09 13:02:08 +03:00
fogelito fa13eb1064 .env 2023-08-09 13:00:10 +03:00
fogelito c823e36183 remove unchanged setResource 2023-08-09 12:47:26 +03:00
ecb142fa50 Update app/controllers/api/databases.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-08-09 12:40:18 +03:00
fogelito aceb49505c pull changes 2023-08-09 12:38:34 +03:00
fogelito eed89b9fd3 Merge branch '1.4.x' of github.com:appwrite/appwrite into poc-timeout
 Conflicts:
	.env
	app/controllers/api/databases.php
	composer.json
	composer.lock
	docker-compose.yml
2023-08-09 12:36:23 +03:00
fogelito e6cf0a6ac3 addressing comments 2023-08-09 12:27:32 +03:00
fogelito 0f57d87c55 composer.lock 2023-08-01 11:32:38 +03:00
fogelito d61921573b composer 2023-08-01 11:32:23 +03:00
fogelito 82c9c074bc merge in master 2023-08-01 11:29:39 +03:00
fogelito 632d41ec6c Merge branch 'master' of github.com:appwrite/appwrite into poc-timeout
 Conflicts:
	app/controllers/api/databases.php
	composer.json
	composer.lock
	tests/e2e/Services/Databases/DatabasesBase.php
2023-08-01 11:25:21 +03:00
fogelito 59dd6bfbcc composer update 2023-08-01 11:13:14 +03:00
fogelito 54cae4ab00 composer update 2023-08-01 11:13:01 +03:00
fogelito 1e9daf98cb .env _APP_SLOW_QUERIES_MAX_HITS set to 5 2023-03-06 11:42:38 +02:00
fogelito 408e611b76 .env _APP_SLOW_QUERIES_MAX_HITS set to 5 2023-03-06 11:40:35 +02:00
fogelito bd582bd00d addressing comments 2023-03-06 10:57:32 +02:00
fogelito 25aa6049cd space removes 2023-03-06 10:45:56 +02:00
fogelito 2a7ca5fe67 set setResource return 2023-03-06 10:41:50 +02:00
fogelito 13b52a67a9 datetime validator name change 2023-03-06 10:26:20 +02:00
fogelito 466a469035 Merge remote-tracking branch 'origin/poc-timeout' into poc-timeout 2023-03-06 10:09:39 +02:00
fogelito 96a78a0763 collection require = true 2023-03-06 10:08:09 +02:00
33fb110bac Update docs/references/databases/delete-slow-query-document.md
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-03-06 09:57:53 +02:00
0936072e17 Update app/config/errors.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-03-02 09:05:59 +02:00
fogelito d34af2764d stopOnFailure="false" 2023-03-02 09:04:36 +02:00
fogelito 05485afb99 remove cursor hint + merge in 0.13.x 2023-03-02 08:58:58 +02:00
fogelito ef6a95252e Merge branch '1.3.x' of github.com:appwrite/appwrite into poc-timeout 2023-03-02 08:56:36 +02:00
fogelito 9322d1636e Last fixed before pull 2023-03-01 19:05:15 +02:00
fogelito 2f46d5c4a6 tests fixes 2023-03-01 18:39:38 +02:00
fogelito 3d3ffba93a console route change 2023-03-01 18:32:13 +02:00
72b30d4f60 Update app/config/errors.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-03-01 16:12:57 +02:00
962a44b3a8 Update app/controllers/api/databases.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-03-01 16:12:15 +02:00
6b884150cd Update app/controllers/api/databases.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-03-01 16:12:02 +02:00
fogelito c315240bce addressing comments 2023-03-01 15:56:16 +02:00
fogelito c0febfff5b Merge remote-tracking branch 'origin/poc-timeout' into poc-timeout 2023-02-28 19:34:17 +02:00
fogelito 3f9b8afbb4 addressing comments 2023-02-28 19:34:04 +02:00
707c8033b8 Update app/controllers/api/databases.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-02-28 19:21:12 +02:00
ae981d90c9 Update app/controllers/api/databases.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-02-28 18:39:11 +02:00
3964e86340 Update app/controllers/api/databases.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-02-28 18:34:38 +02:00
5a1ac151d9 Update app/controllers/api/databases.php
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-02-28 18:32:47 +02:00
fogelito 031d85e5d6 remove comment 2023-02-23 12:26:24 +02:00
fogelito ad320e97c6 fix empty queries 2023-02-23 12:15:11 +02:00
fogelito 497e505775 longtext.txt 2023-02-23 10:57:57 +02:00
fogelito 947193ef24 _APP_SLOW_QUERIES enabled 2023-02-23 10:09:43 +02:00
fogelito 79e60e84da collections.php changes 2023-02-23 10:06:16 +02:00
03e3522568 _APP_SLOW_QUERIES_BLOCK
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
2023-02-23 10:05:25 +02:00
fogelito 8a30738f52 remove var_dump graphql 2023-02-23 09:46:40 +02:00
fogelito 188abb04a7 remove sleep 2023-02-21 13:33:09 +02:00
fogelito 04f2a0610d remove sleep 2023-02-21 13:22:28 +02:00
fogelito 89c9c293e8 Less docs for timeout? 2023-02-21 10:20:12 +02:00
fogelito 601e1648a7 Timeouts tests 2023-02-21 10:11:08 +02:00
fogelito 470ff2289d Timeouts tests 2023-02-21 10:05:23 +02:00
Jake Barnby 3a2ce3bf2b Fix graphql tests 2023-02-21 13:59:23 +13:00
fogelito 211ef70279 native timeouts try 2023-02-20 12:06:09 +02:00
fogelito 2f37057e7a native timeouts try 2023-02-20 11:36:57 +02:00
fogelito c3a6cefd0a native timeouts try 2023-02-20 11:06:00 +02:00
fogelito 82bcbf3306 native timeouts try 2023-02-16 19:48:15 +02:00
fogelito f6a27cbd84 remove line 2023-02-16 11:50:31 +02:00
fogelito 318076297b graphQL tests fails 2023-02-16 10:42:08 +02:00
fogelito 93c25e87d5 tests updates 2023-02-15 13:36:30 +02:00
fogelito ea0c7e9a4c composer.lock 2023-02-15 12:45:19 +02:00
fogelito 95e204b8d9 filter todo 2023-02-15 12:21:03 +02:00
fogelito 72a30cf65f Merge branch 'master' of github.com:appwrite/appwrite into poc-timeout
 Conflicts:
	app/controllers/api/databases.php
	app/init.php
	composer.json
	composer.lock
	tests/e2e/General/UsageTest.php
	tests/e2e/Services/Account/AccountCustomClientTest.php
	tests/e2e/Services/Databases/DatabasesBase.php
	tests/e2e/Services/Databases/DatabasesCustomServerTest.php
	tests/e2e/Services/Functions/FunctionsCustomServerTest.php
	tests/e2e/Services/Storage/StorageBase.php
	tests/e2e/Services/Storage/StorageCustomClientTest.php
	tests/e2e/Services/Storage/StorageCustomServerTest.php
	tests/e2e/Services/Teams/TeamsBase.php
	tests/e2e/Services/Teams/TeamsBaseClient.php
	tests/e2e/Services/Webhooks/WebhooksBase.php
	tests/e2e/Services/Webhooks/WebhooksCustomClientTest.php
	tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php
2023-02-15 11:22:35 +02:00
fogelito 558869b118 update tests 2023-02-14 18:33:00 +02:00
fogelito ff2a300caf _APP_SLOW_QUERIES bool 2023-02-14 15:14:16 +02:00
fogelito 49fe9a9fea timeouts tests pass 2023-02-14 15:09:58 +02:00
fogelito 57c8cde568 timeouts tests pass 2023-02-14 14:42:05 +02:00
fogelito e2bb3270b3 composer .lock 2023-02-14 13:06:23 +02:00
fogelito 895c546884 403 error 2023-02-14 13:05:59 +02:00
fogelito 60c7d19be6 env variables 2023-02-14 09:39:19 +02:00
fogelito a71e23a943 env variables 2023-02-14 09:35:03 +02:00
fogelito dd1b9a5338 env variables 2023-02-13 19:24:29 +02:00
fogelito a90b3eb307 build after changes 2023-02-13 19:11:57 +02:00
fogelito 8779ce855d slow_queries 2023-02-13 18:38:57 +02:00
fogelito 84609e208f slow queries collection name to snake case 2023-02-13 18:34:22 +02:00
fogelito 9650d9867c _APP_SLOW_QUERIES_ACTIVE 2023-02-13 17:50:12 +02:00
fogelito d2142e7313 add helpers namespace 2023-02-13 17:43:58 +02:00
fogelito 238c405fc0 .env variables for slow queries 2023-02-13 17:23:05 +02:00
fogelito 7cd1ffc085 DatabasesCustomClientTest.php uncomment tests 2023-02-12 23:53:52 +02:00
fogelito bf84ac3adc Merge remote-tracking branch 'origin/poc-timeout' into poc-timeout 2023-02-12 23:51:35 +02:00
fogelito 3e66b764ec resolve comments $timeoutMilliseconds 2023-02-12 23:51:24 +02:00
167d127481 Update app/init.php
Co-authored-by: Eldad A. Fux <eldad.fux@gmail.com>
2023-02-12 23:47:31 +02:00
09128267e0 Update docker-compose.yml
Co-authored-by: Eldad A. Fux <eldad.fux@gmail.com>
2023-02-12 23:47:23 +02:00
a32019e8dc Update app/controllers/api/databases.php
Co-authored-by: Eldad A. Fux <eldad.fux@gmail.com>
2023-02-12 23:46:48 +02:00
87bc7ad8b0 Update app/controllers/api/databases.php
Co-authored-by: Eldad A. Fux <eldad.fux@gmail.com>
2023-02-12 23:35:09 +02:00
fogelito cf96f8baca change timeout colllection to slow queries 2023-02-12 23:33:42 +02:00
fogelito 3d76da1f25 change timeout colllection to slow queries 2023-02-12 23:05:10 +02:00
fogelito e5cd1dd842 Merge remote-tracking branch 'origin/poc-timeout' into poc-timeout 2023-02-12 22:49:37 +02:00
fogelito 3841287fc2 composer changes 2023-02-12 22:49:12 +02:00
fogelito 9cfeab7c64 default 2023-02-05 17:36:14 +02:00
fogelito 3a88457052 default 2023-02-05 17:21:08 +02:00
fogelito 00abaef65f add method key 2023-02-05 16:51:04 +02:00
fogelito d01be37a9c md5 2023-02-05 16:49:22 +02:00
fogelito 898f02e1c8 Update list with databaseId 2023-02-02 10:55:23 +02:00
fogelito 593f7154a4 Update list with databaseId 2023-01-31 18:17:07 +02:00
fogelito 3ac2adf13f Update list with databaseId 2023-01-31 18:16:24 +02:00
fogelito 76592b8ea4 Tests for new routes 2023-01-30 17:04:26 +02:00
fogelito b19365a35f Tests for new routes 2023-01-30 16:59:39 +02:00
fogelito 6aab350669 change route names 2023-01-30 12:36:15 +02:00
fogelito f58581663e framework upgrade 0.27.0 2023-01-30 10:38:29 +02:00
fogelito b95f52edf7 Some tests 2023-01-29 14:40:27 +02:00
fogelito 73a2d103fb Query filters + cursor for list slow queries 2023-01-29 14:33:52 +02:00
fogelito 14a6f407b8 timeout get 2023-01-25 11:07:19 +02:00
fogelito 1bcb2b561a timeout get 2023-01-25 11:05:49 +02:00
fogelito bad3b40ae9 timeout patch 2023-01-25 10:48:44 +02:00
fogelito a69d6fd3f8 New timeout rout for list blocked docs 2023-01-24 19:30:59 +02:00
fogelito 55ca917edf Code clean up 2023-01-22 14:01:22 +02:00
fogelito d610a14388 Code clean up 2023-01-19 17:51:23 +02:00
fogelito 017ef1f13c remove comments 2023-01-19 11:20:42 +02:00
fogelito 1a7e65d339 error resource changes 2023-01-19 11:19:16 +02:00
fogelito 317469c8fc debugging var dumps 2023-01-18 19:25:18 +02:00
fogelito d615674da6 Some changes 2023-01-17 19:10:41 +02:00
fogelito c39235410e remove the hardcoded throw Timeout Exception; 2023-01-16 08:41:20 +02:00
fogelito 40bd8d54e5 remove the hardcoded throw Timeout Exception; 2023-01-16 08:32:47 +02:00
fogelito 3c5b14abe7 add tests for sleep operator 2023-01-15 12:57:41 +02:00
fogelito e746634491 validate the sleep operator 2023-01-12 18:30:34 +02:00
fogelito 9241d37c26 starting using hooks 2023-01-11 17:38:28 +02:00
fogelito 347d4afa24 Some POC for exceed timeout case 2023-01-08 12:05:50 +02:00
244 changed files with 62113 additions and 7564 deletions
+3 -4
View File
@@ -39,7 +39,6 @@ _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
@@ -86,9 +85,7 @@ _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
@@ -113,4 +110,6 @@ _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
_APP_SLOW_QUERIES_MAX_HITS=5
_APP_SLOW_QUERIES_TIMEOUT=15000
_APP_SLOW_QUERIES_BLOCK=enabled
-5
View File
@@ -13,13 +13,8 @@ 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: ${{ vars.DOCKERHUB_USERNAME }}
username: ${{ secrets.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: ${{ vars.DOCKERHUB_USERNAME }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
+8 -83
View File
@@ -8,33 +8,9 @@ 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
@@ -127,62 +103,6 @@ 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:
@@ -208,6 +128,7 @@ jobs:
Migrations
]
tables-mode: [
'Project',
'Shared V1',
'Shared V2',
]
@@ -239,8 +160,12 @@ 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 \
@@ -326,4 +251,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
+13 -13
View File
@@ -1,6 +1,6 @@
# Contributing
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.
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.
## 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 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!
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!
## 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 :rocket:
## Submit a Pull Request 🚀
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 [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.
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.
```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 :sparkling_heart: you to contribute to Appwrite, but we also want to ensure Appwrite is loyal to its vision and mission statement :pray:.
We would 💖 you to contribute to Appwrite, but we also want to ensure Appwrite is loyal to its vision and mission statement 🙏.
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':
$queueForStatsUsage
$queueForUsage
->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
$queueForStatsUsage
$queueForUsage
->addMetric(METRIC_BUCKETS, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForStatsUsage
$queueForUsage
->addReduce($document);
}
break;
@@ -428,16 +428,16 @@ public function __construct()
->inject('dbForProject')
->inject('queueForFunctions')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('queueForUsage')
->inject('log')
->callback(fn (Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForStatsUsage, $log));
->callback(fn (Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage, $log));
}
```
and then trigger the queue with the new metric like so:
```php
$queueForStatsUsage
$queueForUsage
->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 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.
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.
### Presenting at Meetups
+3 -5
View File
@@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \
--no-plugins --no-scripts --prefer-dist \
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
FROM appwrite/base:0.10.1 AS final
FROM appwrite/base:0.9.5 AS final
LABEL maintainer="team@appwrite.io"
@@ -85,10 +85,8 @@ 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-stats-usage && \
chmod +x /usr/local/bin/worker-stats-usage-dump && \
chmod +x /usr/local/bin/stats-resources && \
chmod +x /usr/local/bin/worker-stats-resources
chmod +x /usr/local/bin/worker-usage && \
chmod +x /usr/local/bin/worker-usage-dump
# Letsencrypt Permissions
RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
+25 -61
View File
@@ -1,8 +1,8 @@
> 好消息!Appwrite 云现已进入公开测试版!立即访问 cloud.appwrite.io 注册,体验无忧的托管服务。今天就加入我们的云端吧!:cloud: :tada:
> 好消息!Appwrite 云现已进入公开测试版!立即访问 cloud.appwrite.io 注册,体验无忧的托管服务。今天就加入我们的云端吧!☁️🎉
<br />
<p align="center">
<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>
<a href="https://appwrite.io" target="_blank"><img src="./public/images/banner.png" alt="Appwrite Logo"></a>
<br />
<br />
<b>适用于[Flutter/Vue/Angular/React/iOS/Android/* 等等平台 *]的完整后端服务</b>
@@ -36,8 +36,6 @@ Appwrite 可以提供给开发者用户验证,外部授权,用户数据读
内容:
- [开始](#开始)
- [安装](#安装)
- [Unix](#unix)
- [Windows](#windows)
@@ -56,9 +54,6 @@ 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。
@@ -72,7 +67,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.2
appwrite/appwrite:1.6.1
```
### Windows
@@ -84,7 +79,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.2
appwrite/appwrite:1.6.1
```
#### PowerShell
@@ -94,7 +89,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.2
appwrite/appwrite:1.6.1
```
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
@@ -103,42 +98,7 @@ docker run -it --rm `
### 从旧版本升级
如果您从旧版本升级 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>
如果您从旧版本升级 Appwrite 服务器,则应在设置完成后使用 Appwrite 迁移工具。有关这方面的更多信息,请查看 [安装文档](https://appwrite.io/docs/installation)。
## 入门
@@ -186,25 +146,29 @@ docker run -it --rm `
以下是当前支持的平台和语言列表。如果您想帮助我们为您选择的平台添加支持,您可以访问我们的 [SDK 生成器](https://github.com/appwrite/sdk-generator) 项目并查看我们的 [贡献指南](https://github.com/appwrite/sdk-generator/blob/master/CONTRIBUTING.md)。
#### 客户端
* :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; [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; [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; [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; [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))
- ✅ &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))
找不到需要的的 SDK? - 欢迎通过发起 PR 来帮助我们完善 Appwrite 的软件生态环境 [SDK 生成器](https://github.com/appwrite/sdk-generator)!
+38 -40
View File
@@ -1,8 +1,8 @@
> [Get started with Appwrite](https://apwr.dev/appcloud)
> Appwrite Init has concluded! You can check out all the latest announcements [on our Init website](https://appwrite.io/init) 🚀
<br />
<p align="center">
<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>
<a href="https://appwrite.io" target="_blank"><img src="./public/images/banner.png" alt="Appwrite Logo"></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 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)
[![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)
<!-- [![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,9 +24,11 @@
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, messaging, 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, and [more services](https://appwrite.io/docs).
<p align="center">
<br />
@@ -35,14 +37,13 @@ Using Appwrite, you can easily integrate your app with user authentication and m
<br />
</p>
![Appwrite project dashboard showing various Appwrite features](public/images/github.png)
![Appwrite](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:
- [Getting Started](#getting-started)
- [Self-Hosting](#self-hosting)
- [Installation](#installation)
- [Unix](#unix)
- [Windows](#windows)
- [CMD](#cmd)
@@ -50,7 +51,7 @@ Table of Contents:
- [Upgrade from an Older Version](#upgrade-from-an-older-version)
- [One-Click Setups](#one-click-setups)
- [Getting Started](#getting-started)
- [Products](#products)
- [Services](#services)
- [SDKs](#sdks)
- [Client](#client)
- [Server](#server)
@@ -61,14 +62,11 @@ Table of Contents:
- [Follow Us](#follow-us)
- [License](#license)
## 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
## Installation
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/).
Before running the installation command, make sure you have [Docker](https://www.docker.com/products/docker-desktop) installed on your machine:
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:
### Unix
@@ -77,7 +75,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.2
appwrite/appwrite:1.6.1
```
### Windows
@@ -89,7 +87,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.2
appwrite/appwrite:1.6.1
```
#### PowerShell
@@ -99,7 +97,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.2
appwrite/appwrite:1.6.1
```
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.
@@ -108,7 +106,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/self-hosting).
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).
## One-Click Setups
@@ -194,34 +192,34 @@ Below is a list of currently supported platforms and languages. If you would lik
#### Client
- :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)
- &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)
#### Server
- :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)
- &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)
#### Community
- :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))
- &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))
Looking for more SDKs? - Help us by contributing a pull request to our [SDK Generator](https://github.com/appwrite/sdk-generator)!
## Architecture
![Appwrite Architecture showing how Appwrite is built and the services and tools it uses](docs/specs/overview.drawio.svg)
![Appwrite Architecture](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.
@@ -231,7 +229,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 :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).
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).
## Security
+15 -59
View File
@@ -5,8 +5,6 @@ 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 Utopia\Cache\Adapter\Sharding;
@@ -21,11 +19,11 @@ use Utopia\DSN\DSN;
use Utopia\Logger\Log;
use Utopia\Platform\Service;
use Utopia\Pools\Group;
use Utopia\Queue\Publisher;
use Utopia\Queue\Connection;
use Utopia\Registry\Registry;
use Utopia\System\System;
// Overwriting runtimes to be architecture agnostic for CLI
// overwriting runtimes to be architectur agnostic for CLI
Config::setParam('runtimes', (new Runtimes('v4'))->getAll(supported: false));
// require controllers after overwriting runtimes
@@ -43,7 +41,8 @@ CLI::setResource('cache', function ($pools) {
$adapters[] = $pools
->get($value)
->pop()
->getResource();
->getResource()
;
}
return new Cache(new Sharding($adapters));
@@ -98,10 +97,6 @@ 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
@@ -165,57 +160,18 @@ 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();
CLI::setResource('queue', function (Group $pools) {
return $pools->get('queue')->pop()->getResource();
}, ['pools']);
CLI::setResource('queueForFunctions', function (Publisher $publisher) {
return new Func($publisher);
}, ['publisher']);
CLI::setResource('queueForDeletes', function (Publisher $publisher) {
return new Delete($publisher);
}, ['publisher']);
CLI::setResource('queueForCertificates', function (Publisher $publisher) {
return new Certificate($publisher);
}, ['publisher']);
CLI::setResource('queueForFunctions', function (Connection $queue) {
return new Func($queue);
}, ['queue']);
CLI::setResource('queueForDeletes', function (Connection $queue) {
return new Delete($queue);
}, ['queue']);
CLI::setResource('queueForCertificates', function (Connection $queue) {
return new Certificate($queue);
}, ['queue']);
CLI::setResource('logError', function (Registry $register) {
return function (Throwable $error, string $namespace, string $action) use ($register) {
$logger = $register->get('logger');
+1 -2
View File
@@ -16,6 +16,5 @@ 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'],
'rupay' => ['name' => 'Rupay', 'path' => __DIR__ . '/credit-cards/rupay.png']
'maestro' => ['name' => 'Maestro', 'path' => __DIR__ . '/credit-cards/maestro.png']
];
Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

-2
View File
@@ -5,7 +5,6 @@ $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'];
@@ -28,7 +27,6 @@ $collections = [
'databases' => $databases,
'projects' => array_merge($projects, $common),
'console' => array_merge($platform, $common),
'logs' => $logs,
];
return $collections;
+92 -3
View File
@@ -1038,7 +1038,7 @@ return [
'$id' => ID::custom('providerUid'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 2048, // Decrease to 128 as in index length?
'size' => 2048,
'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], // providerUid is length 2000!
'lengths' => [11, 128, 128],
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_provider_providerUid'),
'type' => Database::INDEX_UNIQUE,
'attributes' => ['provider', 'providerUid'],
'lengths' => [128, 128], // providerUid is length 2000!
'lengths' => [128, 128],
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
],
[
@@ -2637,4 +2637,93 @@ return [
]
]
],
'slowQueries' => [
'$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('slowQueries'),
'name' => 'slowQueries',
'attributes' => [
[
'$id' => ID::custom('blocked'),
'type' => Database::VAR_BOOLEAN,
'format' => '',
'size' => 0,
'signed' => true,
'required' => true,
'default' => false,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('count'),
'type' => Database::VAR_INTEGER,
'format' => '',
'size' => 0,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('queries'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 410002,
'signed' => false,
'required' => true,
'default' => [],
'array' => false,
'filters' => ['json'],
],
[
'$id' => ID::custom('path'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 2500,
'signed' => false,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('databaseId'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => Database::LENGTH_KEY,
'format' => '',
'filters' => [],
'required' => true,
'default' => null,
'array' => false,
],
[
'$id' => ID::custom('collectionId'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => Database::LENGTH_KEY,
'format' => '',
'filters' => [],
'required' => true,
'default' => null,
'array' => false,
],
],
'indexes' => [
[
'$id' => '_key_database_id',
'type' => Database::INDEX_KEY,
'attributes' => ['databaseId', 'collectionId'],
'lengths' => [],
'orders' => []
],
[
'$id' => '_key_blocked',
'type' => Database::INDEX_KEY,
'attributes' => ['blocked', 'databaseId', 'collectionId'],
'lengths' => [],
'orders' => []
]
],
],
];
-94
View File
@@ -1,94 +0,0 @@
<?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;
+1 -70
View File
@@ -1066,29 +1066,7 @@ 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' => [
[
@@ -1133,20 +1111,6 @@ 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],
],
],
],
@@ -1221,39 +1185,6 @@ 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
@@ -1,53 +0,0 @@
<?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;
+13 -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 => [
@@ -1023,4 +1023,16 @@ return [
'description' => 'Target has an invalid provider type.',
'code' => 400,
],
/* Slow queries */
Exception::QUERY_BLOCKED => [
'name' => Exception::QUERY_BLOCKED,
'description' => 'Request blocked after exceeding the maximum number of timeouts',
'code' => 403,
],
Exception::QUERY_TIMEOUT => [
'name' => Exception::QUERY_TIMEOUT,
'description' => 'Request timed out, this query will be blocked if it keeps timing out, try adjusting the "queries" parameter or adding indexes for the queried attributes.',
'code' => 408,
],
];
+7 -42
View File
@@ -1,46 +1,11 @@
<?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' => [
'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
],
'AF',
'AN',
'AS',
'EU',
'NA',
'OC',
'SA',
];
+198 -199
View File
@@ -1,210 +1,209 @@
<?php
/**
* ISO 3166 standard country codes with country names and coordinates
* ISO 3166 standard country codes
* 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' => ['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],
'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',
];
+9 -9
View File
@@ -11,7 +11,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '17.0.1',
'version' => '16.1.0',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -59,7 +59,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '15.0.0',
'version' => '13.1.1',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -77,7 +77,7 @@ return [
[
'key' => 'apple',
'name' => 'Apple',
'version' => '9.0.0',
'version' => '7.1.0',
'url' => 'https://github.com/appwrite/sdk-for-apple',
'package' => 'https://github.com/appwrite/sdk-for-apple',
'enabled' => true,
@@ -134,7 +134,7 @@ return [
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.7.1',
'version' => '0.6.0',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
@@ -217,7 +217,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '6.2.2',
'version' => '6.2.0',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -245,7 +245,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '15.0.1',
'version' => '14.2.0',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -299,7 +299,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '9.0.2',
'version' => '6.2.0',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -371,7 +371,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '14.0.0',
'version' => '12.2.0',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@@ -411,7 +411,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '8.0.0',
'version' => '6.2.0',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,
+65 -1
View File
@@ -3,8 +3,72 @@
return [
'default' => [
'$id' => 'default',
'name' => 'default',
'name' => 'Frankfurt',
'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,
],
];
+4 -6
View File
@@ -3383,7 +3383,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, rupay.",
"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.",
"required": true,
"schema": {
"type": "string",
@@ -3404,8 +3404,7 @@
"union-china-pay",
"visa",
"mir",
"maestro",
"rupay"
"maestro"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3424,8 +3423,7 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro",
"Rupay"
"Maestro"
]
},
"in": "path"
@@ -4367,7 +4365,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.\n",
"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.",
"responses": {
"201": {
"description": "Document",
+84 -128
View File
@@ -3387,7 +3387,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, rupay.",
"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.",
"required": true,
"schema": {
"type": "string",
@@ -3408,8 +3408,7 @@
"union-china-pay",
"visa",
"mir",
"maestro",
"rupay"
"maestro"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3428,8 +3427,7 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro",
"Rupay"
"Maestro"
]
},
"in": "path"
@@ -6409,6 +6407,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -6644,6 +6644,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -7821,7 +7823,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.\n",
"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.",
"responses": {
"201": {
"description": "Document",
@@ -11583,7 +11585,7 @@
},
"x-appwrite": {
"method": "getCertificate",
"weight": 133,
"weight": 134,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11690,7 +11692,7 @@
},
"x-appwrite": {
"method": "getPubSub",
"weight": 129,
"weight": 130,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11716,6 +11718,54 @@
]
}
},
"\/health\/queue": {
"get": {
"summary": "Get queue",
"operationId": "healthGetQueue",
"tags": [
"health"
],
"description": "Check the Appwrite queue messaging servers are up and connection is successful.",
"responses": {
"200": {
"description": "Health Status",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/healthStatus"
}
}
}
}
},
"x-appwrite": {
"method": "getQueue",
"weight": 129,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Key": []
}
]
}
},
"\/health\/queue\/builds": {
"get": {
"summary": "Get builds queue",
@@ -11738,7 +11788,7 @@
},
"x-appwrite": {
"method": "getQueueBuilds",
"weight": 135,
"weight": 136,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11799,7 +11849,7 @@
},
"x-appwrite": {
"method": "getQueueCertificates",
"weight": 134,
"weight": 135,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11860,7 +11910,7 @@
},
"x-appwrite": {
"method": "getQueueDatabases",
"weight": 136,
"weight": 137,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11932,7 +11982,7 @@
},
"x-appwrite": {
"method": "getQueueDeletes",
"weight": 137,
"weight": 138,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12031,9 +12081,8 @@
"v1-audits",
"v1-mails",
"v1-functions",
"v1-stats-resources",
"v1-stats-usage",
"v1-stats-usage-dump",
"v1-usage",
"v1-usage-dump",
"v1-webhooks",
"v1-certificates",
"v1-builds",
@@ -12081,7 +12130,7 @@
},
"x-appwrite": {
"method": "getQueueFunctions",
"weight": 141,
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12142,7 +12191,7 @@
},
"x-appwrite": {
"method": "getQueueLogs",
"weight": 132,
"weight": 133,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12203,7 +12252,7 @@
},
"x-appwrite": {
"method": "getQueueMails",
"weight": 138,
"weight": 139,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12264,7 +12313,7 @@
},
"x-appwrite": {
"method": "getQueueMessaging",
"weight": 139,
"weight": 140,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12325,7 +12374,7 @@
},
"x-appwrite": {
"method": "getQueueMigrations",
"weight": 140,
"weight": 141,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12364,70 +12413,9 @@
]
}
},
"\/health\/queue\/stats-resources": {
"\/health\/queue\/usage": {
"get": {
"summary": "Get stats resources queue",
"operationId": "healthGetQueueStatsResources",
"tags": [
"health"
],
"description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.",
"responses": {
"200": {
"description": "Health Queue",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/healthQueue"
}
}
}
}
},
"x-appwrite": {
"method": "getQueueStatsResources",
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-resources.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Key": []
}
],
"parameters": [
{
"name": "threshold",
"description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.",
"required": false,
"schema": {
"type": "integer",
"format": "int32",
"default": 5000
},
"in": "query"
}
]
}
},
"\/health\/queue\/stats-usage": {
"get": {
"summary": "Get stats usage queue",
"summary": "Get usage queue",
"operationId": "healthGetQueueUsage",
"tags": [
"health"
@@ -12452,7 +12440,7 @@
"type": "",
"deprecated": false,
"demo": "health\/get-queue-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -12486,10 +12474,10 @@
]
}
},
"\/health\/queue\/stats-usage-dump": {
"\/health\/queue\/usage-dump": {
"get": {
"summary": "Get usage dump queue",
"operationId": "healthGetQueueStatsUsageDump",
"operationId": "healthGetQueueUsageDump",
"tags": [
"health"
],
@@ -12507,13 +12495,13 @@
}
},
"x-appwrite": {
"method": "getQueueStatsUsageDump",
"method": "getQueueUsageDump",
"weight": 144,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md",
"demo": "health\/get-queue-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -12569,7 +12557,7 @@
},
"x-appwrite": {
"method": "getQueueWebhooks",
"weight": 131,
"weight": 132,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12726,7 +12714,7 @@
},
"x-appwrite": {
"method": "getTime",
"weight": 130,
"weight": 131,
"cookies": false,
"type": "",
"deprecated": false,
@@ -17621,17 +17609,17 @@
},
"endpoint": {
"type": "string",
"description": "Source Appwrite endpoint",
"description": "Source's Appwrite Endpoint",
"x-example": "https:\/\/example.com"
},
"projectId": {
"type": "string",
"description": "Source Project ID",
"description": "Source's Project ID",
"x-example": "<PROJECT_ID>"
},
"apiKey": {
"type": "string",
"description": "Source API Key",
"description": "Source's API Key",
"x-example": "<API_KEY>"
}
},
@@ -36064,20 +36052,6 @@
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
},
"imageTransformations": {
"type": "array",
"description": "Aggregated number of files transformations per period.",
"items": {
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
},
"imageTransformationsTotal": {
"type": "integer",
"description": "Total aggregated number of files transformations.",
"x-example": 0,
"format": "int32"
}
},
"required": [
@@ -36085,9 +36059,7 @@
"filesTotal",
"filesStorageTotal",
"files",
"storage",
"imageTransformations",
"imageTransformationsTotal"
"storage"
]
},
"usageFunctions": {
@@ -36625,20 +36597,6 @@
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
},
"imageTransformations": {
"type": "array",
"description": "An array of aggregated number of image transformations.",
"items": {
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
},
"imageTransformationsTotal": {
"type": "integer",
"description": "Total aggregated number of image transformations.",
"x-example": 0,
"format": "int32"
}
},
"required": [
@@ -36670,9 +36628,7 @@
"authPhoneEstimate",
"authPhoneCountryBreakdown",
"databasesReads",
"databasesWrites",
"imageTransformations",
"imageTransformationsTotal"
"databasesWrites"
]
},
"headers": {
+80 -92
View File
@@ -3077,7 +3077,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, rupay.",
"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.",
"required": true,
"schema": {
"type": "string",
@@ -3098,8 +3098,7 @@
"union-china-pay",
"visa",
"mir",
"maestro",
"rupay"
"maestro"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3118,8 +3117,7 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro",
"Rupay"
"Maestro"
]
},
"in": "path"
@@ -5953,6 +5951,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -6190,6 +6190,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -7379,7 +7381,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.\n",
"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.",
"responses": {
"201": {
"description": "Document",
@@ -10459,7 +10461,7 @@
},
"x-appwrite": {
"method": "getCertificate",
"weight": 133,
"weight": 134,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10568,7 +10570,7 @@
},
"x-appwrite": {
"method": "getPubSub",
"weight": 129,
"weight": 130,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10595,6 +10597,55 @@
]
}
},
"\/health\/queue": {
"get": {
"summary": "Get queue",
"operationId": "healthGetQueue",
"tags": [
"health"
],
"description": "Check the Appwrite queue messaging servers are up and connection is successful.",
"responses": {
"200": {
"description": "Health Status",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/healthStatus"
}
}
}
}
},
"x-appwrite": {
"method": "getQueue",
"weight": 129,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": [],
"Key": []
}
},
"security": [
{
"Project": [],
"Key": []
}
]
}
},
"\/health\/queue\/builds": {
"get": {
"summary": "Get builds queue",
@@ -10617,7 +10668,7 @@
},
"x-appwrite": {
"method": "getQueueBuilds",
"weight": 135,
"weight": 136,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10679,7 +10730,7 @@
},
"x-appwrite": {
"method": "getQueueCertificates",
"weight": 134,
"weight": 135,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10741,7 +10792,7 @@
},
"x-appwrite": {
"method": "getQueueDatabases",
"weight": 136,
"weight": 137,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10814,7 +10865,7 @@
},
"x-appwrite": {
"method": "getQueueDeletes",
"weight": 137,
"weight": 138,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10915,9 +10966,8 @@
"v1-audits",
"v1-mails",
"v1-functions",
"v1-stats-resources",
"v1-stats-usage",
"v1-stats-usage-dump",
"v1-usage",
"v1-usage-dump",
"v1-webhooks",
"v1-certificates",
"v1-builds",
@@ -10965,7 +11015,7 @@
},
"x-appwrite": {
"method": "getQueueFunctions",
"weight": 141,
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11027,7 +11077,7 @@
},
"x-appwrite": {
"method": "getQueueLogs",
"weight": 132,
"weight": 133,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11089,7 +11139,7 @@
},
"x-appwrite": {
"method": "getQueueMails",
"weight": 138,
"weight": 139,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11151,7 +11201,7 @@
},
"x-appwrite": {
"method": "getQueueMessaging",
"weight": 139,
"weight": 140,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11213,7 +11263,7 @@
},
"x-appwrite": {
"method": "getQueueMigrations",
"weight": 140,
"weight": 141,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11253,71 +11303,9 @@
]
}
},
"\/health\/queue\/stats-resources": {
"\/health\/queue\/usage": {
"get": {
"summary": "Get stats resources queue",
"operationId": "healthGetQueueStatsResources",
"tags": [
"health"
],
"description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.",
"responses": {
"200": {
"description": "Health Queue",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/healthQueue"
}
}
}
}
},
"x-appwrite": {
"method": "getQueueStatsResources",
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-resources.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": [],
"Key": []
}
},
"security": [
{
"Project": [],
"Key": []
}
],
"parameters": [
{
"name": "threshold",
"description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.",
"required": false,
"schema": {
"type": "integer",
"format": "int32",
"default": 5000
},
"in": "query"
}
]
}
},
"\/health\/queue\/stats-usage": {
"get": {
"summary": "Get stats usage queue",
"summary": "Get usage queue",
"operationId": "healthGetQueueUsage",
"tags": [
"health"
@@ -11342,7 +11330,7 @@
"type": "",
"deprecated": false,
"demo": "health\/get-queue-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -11377,10 +11365,10 @@
]
}
},
"\/health\/queue\/stats-usage-dump": {
"\/health\/queue\/usage-dump": {
"get": {
"summary": "Get usage dump queue",
"operationId": "healthGetQueueStatsUsageDump",
"operationId": "healthGetQueueUsageDump",
"tags": [
"health"
],
@@ -11398,13 +11386,13 @@
}
},
"x-appwrite": {
"method": "getQueueStatsUsageDump",
"method": "getQueueUsageDump",
"weight": 144,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md",
"demo": "health\/get-queue-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -11461,7 +11449,7 @@
},
"x-appwrite": {
"method": "getQueueWebhooks",
"weight": 131,
"weight": 132,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11621,7 +11609,7 @@
},
"x-appwrite": {
"method": "getTime",
"weight": 130,
"weight": 131,
"cookies": false,
"type": "",
"deprecated": false,
@@ -3383,7 +3383,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, rupay.",
"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.",
"required": true,
"schema": {
"type": "string",
@@ -3404,8 +3404,7 @@
"union-china-pay",
"visa",
"mir",
"maestro",
"rupay"
"maestro"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3424,8 +3423,7 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro",
"Rupay"
"Maestro"
]
},
"in": "path"
@@ -4367,7 +4365,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.\n",
"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.",
"responses": {
"201": {
"description": "Document",
+84 -128
View File
@@ -3387,7 +3387,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, rupay.",
"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.",
"required": true,
"schema": {
"type": "string",
@@ -3408,8 +3408,7 @@
"union-china-pay",
"visa",
"mir",
"maestro",
"rupay"
"maestro"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3428,8 +3427,7 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro",
"Rupay"
"Maestro"
]
},
"in": "path"
@@ -6409,6 +6407,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -6644,6 +6644,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -7821,7 +7823,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.\n",
"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.",
"responses": {
"201": {
"description": "Document",
@@ -11583,7 +11585,7 @@
},
"x-appwrite": {
"method": "getCertificate",
"weight": 133,
"weight": 134,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11690,7 +11692,7 @@
},
"x-appwrite": {
"method": "getPubSub",
"weight": 129,
"weight": 130,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11716,6 +11718,54 @@
]
}
},
"\/health\/queue": {
"get": {
"summary": "Get queue",
"operationId": "healthGetQueue",
"tags": [
"health"
],
"description": "Check the Appwrite queue messaging servers are up and connection is successful.",
"responses": {
"200": {
"description": "Health Status",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/healthStatus"
}
}
}
}
},
"x-appwrite": {
"method": "getQueue",
"weight": 129,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Key": []
}
]
}
},
"\/health\/queue\/builds": {
"get": {
"summary": "Get builds queue",
@@ -11738,7 +11788,7 @@
},
"x-appwrite": {
"method": "getQueueBuilds",
"weight": 135,
"weight": 136,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11799,7 +11849,7 @@
},
"x-appwrite": {
"method": "getQueueCertificates",
"weight": 134,
"weight": 135,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11860,7 +11910,7 @@
},
"x-appwrite": {
"method": "getQueueDatabases",
"weight": 136,
"weight": 137,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11932,7 +11982,7 @@
},
"x-appwrite": {
"method": "getQueueDeletes",
"weight": 137,
"weight": 138,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12031,9 +12081,8 @@
"v1-audits",
"v1-mails",
"v1-functions",
"v1-stats-resources",
"v1-stats-usage",
"v1-stats-usage-dump",
"v1-usage",
"v1-usage-dump",
"v1-webhooks",
"v1-certificates",
"v1-builds",
@@ -12081,7 +12130,7 @@
},
"x-appwrite": {
"method": "getQueueFunctions",
"weight": 141,
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12142,7 +12191,7 @@
},
"x-appwrite": {
"method": "getQueueLogs",
"weight": 132,
"weight": 133,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12203,7 +12252,7 @@
},
"x-appwrite": {
"method": "getQueueMails",
"weight": 138,
"weight": 139,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12264,7 +12313,7 @@
},
"x-appwrite": {
"method": "getQueueMessaging",
"weight": 139,
"weight": 140,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12325,7 +12374,7 @@
},
"x-appwrite": {
"method": "getQueueMigrations",
"weight": 140,
"weight": 141,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12364,70 +12413,9 @@
]
}
},
"\/health\/queue\/stats-resources": {
"\/health\/queue\/usage": {
"get": {
"summary": "Get stats resources queue",
"operationId": "healthGetQueueStatsResources",
"tags": [
"health"
],
"description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.",
"responses": {
"200": {
"description": "Health Queue",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/healthQueue"
}
}
}
}
},
"x-appwrite": {
"method": "getQueueStatsResources",
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-resources.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Key": []
}
],
"parameters": [
{
"name": "threshold",
"description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.",
"required": false,
"schema": {
"type": "integer",
"format": "int32",
"default": 5000
},
"in": "query"
}
]
}
},
"\/health\/queue\/stats-usage": {
"get": {
"summary": "Get stats usage queue",
"summary": "Get usage queue",
"operationId": "healthGetQueueUsage",
"tags": [
"health"
@@ -12452,7 +12440,7 @@
"type": "",
"deprecated": false,
"demo": "health\/get-queue-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -12486,10 +12474,10 @@
]
}
},
"\/health\/queue\/stats-usage-dump": {
"\/health\/queue\/usage-dump": {
"get": {
"summary": "Get usage dump queue",
"operationId": "healthGetQueueStatsUsageDump",
"operationId": "healthGetQueueUsageDump",
"tags": [
"health"
],
@@ -12507,13 +12495,13 @@
}
},
"x-appwrite": {
"method": "getQueueStatsUsageDump",
"method": "getQueueUsageDump",
"weight": 144,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md",
"demo": "health\/get-queue-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -12569,7 +12557,7 @@
},
"x-appwrite": {
"method": "getQueueWebhooks",
"weight": 131,
"weight": 132,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12726,7 +12714,7 @@
},
"x-appwrite": {
"method": "getTime",
"weight": 130,
"weight": 131,
"cookies": false,
"type": "",
"deprecated": false,
@@ -17621,17 +17609,17 @@
},
"endpoint": {
"type": "string",
"description": "Source Appwrite endpoint",
"description": "Source's Appwrite Endpoint",
"x-example": "https:\/\/example.com"
},
"projectId": {
"type": "string",
"description": "Source Project ID",
"description": "Source's Project ID",
"x-example": "<PROJECT_ID>"
},
"apiKey": {
"type": "string",
"description": "Source API Key",
"description": "Source's API Key",
"x-example": "<API_KEY>"
}
},
@@ -36064,20 +36052,6 @@
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
},
"imageTransformations": {
"type": "array",
"description": "Aggregated number of files transformations per period.",
"items": {
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
},
"imageTransformationsTotal": {
"type": "integer",
"description": "Total aggregated number of files transformations.",
"x-example": 0,
"format": "int32"
}
},
"required": [
@@ -36085,9 +36059,7 @@
"filesTotal",
"filesStorageTotal",
"files",
"storage",
"imageTransformations",
"imageTransformationsTotal"
"storage"
]
},
"usageFunctions": {
@@ -36625,20 +36597,6 @@
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
},
"imageTransformations": {
"type": "array",
"description": "An array of aggregated number of image transformations.",
"items": {
"$ref": "#\/components\/schemas\/metric"
},
"x-example": []
},
"imageTransformationsTotal": {
"type": "integer",
"description": "Total aggregated number of image transformations.",
"x-example": 0,
"format": "int32"
}
},
"required": [
@@ -36670,9 +36628,7 @@
"authPhoneEstimate",
"authPhoneCountryBreakdown",
"databasesReads",
"databasesWrites",
"imageTransformations",
"imageTransformationsTotal"
"databasesWrites"
]
},
"headers": {
+80 -92
View File
@@ -3077,7 +3077,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, rupay.",
"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.",
"required": true,
"schema": {
"type": "string",
@@ -3098,8 +3098,7 @@
"union-china-pay",
"visa",
"mir",
"maestro",
"rupay"
"maestro"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3118,8 +3117,7 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro",
"Rupay"
"Maestro"
]
},
"in": "path"
@@ -5953,6 +5951,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -6190,6 +6190,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -7379,7 +7381,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.\n",
"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.",
"responses": {
"201": {
"description": "Document",
@@ -10459,7 +10461,7 @@
},
"x-appwrite": {
"method": "getCertificate",
"weight": 133,
"weight": 134,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10568,7 +10570,7 @@
},
"x-appwrite": {
"method": "getPubSub",
"weight": 129,
"weight": 130,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10595,6 +10597,55 @@
]
}
},
"\/health\/queue": {
"get": {
"summary": "Get queue",
"operationId": "healthGetQueue",
"tags": [
"health"
],
"description": "Check the Appwrite queue messaging servers are up and connection is successful.",
"responses": {
"200": {
"description": "Health Status",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/healthStatus"
}
}
}
}
},
"x-appwrite": {
"method": "getQueue",
"weight": 129,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": [],
"Key": []
}
},
"security": [
{
"Project": [],
"Key": []
}
]
}
},
"\/health\/queue\/builds": {
"get": {
"summary": "Get builds queue",
@@ -10617,7 +10668,7 @@
},
"x-appwrite": {
"method": "getQueueBuilds",
"weight": 135,
"weight": 136,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10679,7 +10730,7 @@
},
"x-appwrite": {
"method": "getQueueCertificates",
"weight": 134,
"weight": 135,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10741,7 +10792,7 @@
},
"x-appwrite": {
"method": "getQueueDatabases",
"weight": 136,
"weight": 137,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10814,7 +10865,7 @@
},
"x-appwrite": {
"method": "getQueueDeletes",
"weight": 137,
"weight": 138,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10915,9 +10966,8 @@
"v1-audits",
"v1-mails",
"v1-functions",
"v1-stats-resources",
"v1-stats-usage",
"v1-stats-usage-dump",
"v1-usage",
"v1-usage-dump",
"v1-webhooks",
"v1-certificates",
"v1-builds",
@@ -10965,7 +11015,7 @@
},
"x-appwrite": {
"method": "getQueueFunctions",
"weight": 141,
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11027,7 +11077,7 @@
},
"x-appwrite": {
"method": "getQueueLogs",
"weight": 132,
"weight": 133,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11089,7 +11139,7 @@
},
"x-appwrite": {
"method": "getQueueMails",
"weight": 138,
"weight": 139,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11151,7 +11201,7 @@
},
"x-appwrite": {
"method": "getQueueMessaging",
"weight": 139,
"weight": 140,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11213,7 +11263,7 @@
},
"x-appwrite": {
"method": "getQueueMigrations",
"weight": 140,
"weight": 141,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11253,71 +11303,9 @@
]
}
},
"\/health\/queue\/stats-resources": {
"\/health\/queue\/usage": {
"get": {
"summary": "Get stats resources queue",
"operationId": "healthGetQueueStatsResources",
"tags": [
"health"
],
"description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.",
"responses": {
"200": {
"description": "Health Queue",
"content": {
"application\/json": {
"schema": {
"$ref": "#\/components\/schemas\/healthQueue"
}
}
}
}
},
"x-appwrite": {
"method": "getQueueStatsResources",
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-resources.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": [],
"Key": []
}
},
"security": [
{
"Project": [],
"Key": []
}
],
"parameters": [
{
"name": "threshold",
"description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.",
"required": false,
"schema": {
"type": "integer",
"format": "int32",
"default": 5000
},
"in": "query"
}
]
}
},
"\/health\/queue\/stats-usage": {
"get": {
"summary": "Get stats usage queue",
"summary": "Get usage queue",
"operationId": "healthGetQueueUsage",
"tags": [
"health"
@@ -11342,7 +11330,7 @@
"type": "",
"deprecated": false,
"demo": "health\/get-queue-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -11377,10 +11365,10 @@
]
}
},
"\/health\/queue\/stats-usage-dump": {
"\/health\/queue\/usage-dump": {
"get": {
"summary": "Get usage dump queue",
"operationId": "healthGetQueueStatsUsageDump",
"operationId": "healthGetQueueUsageDump",
"tags": [
"health"
],
@@ -11398,13 +11386,13 @@
}
},
"x-appwrite": {
"method": "getQueueStatsUsageDump",
"method": "getQueueUsageDump",
"weight": 144,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md",
"demo": "health\/get-queue-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -11461,7 +11449,7 @@
},
"x-appwrite": {
"method": "getQueueWebhooks",
"weight": 131,
"weight": 132,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11621,7 +11609,7 @@
},
"x-appwrite": {
"method": "getTime",
"weight": 130,
"weight": 131,
"cookies": false,
"type": "",
"deprecated": false,
+4 -6
View File
@@ -3557,7 +3557,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, rupay.",
"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.",
"required": true,
"type": "string",
"x-example": "amex",
@@ -3577,8 +3577,7 @@
"union-china-pay",
"visa",
"mir",
"maestro",
"rupay"
"maestro"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3597,8 +3596,7 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro",
"Rupay"
"Maestro"
],
"in": "path"
},
@@ -4549,7 +4547,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.\n",
"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.",
"responses": {
"201": {
"description": "Document",
+86 -130
View File
@@ -3577,7 +3577,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, rupay.",
"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.",
"required": true,
"type": "string",
"x-example": "amex",
@@ -3597,8 +3597,7 @@
"union-china-pay",
"visa",
"mir",
"maestro",
"rupay"
"maestro"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3617,8 +3616,7 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro",
"Rupay"
"Maestro"
],
"in": "path"
},
@@ -6609,6 +6607,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -6845,6 +6845,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -8010,7 +8012,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.\n",
"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.",
"responses": {
"201": {
"description": "Document",
@@ -11808,7 +11810,7 @@
},
"x-appwrite": {
"method": "getCertificate",
"weight": 133,
"weight": 134,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11917,7 +11919,7 @@
},
"x-appwrite": {
"method": "getPubSub",
"weight": 129,
"weight": 130,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11943,6 +11945,56 @@
]
}
},
"\/health\/queue": {
"get": {
"summary": "Get queue",
"operationId": "healthGetQueue",
"consumes": [
"application\/json"
],
"produces": [
"application\/json"
],
"tags": [
"health"
],
"description": "Check the Appwrite queue messaging servers are up and connection is successful.",
"responses": {
"200": {
"description": "Health Status",
"schema": {
"$ref": "#\/definitions\/healthStatus"
}
}
},
"x-appwrite": {
"method": "getQueue",
"weight": 129,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Key": []
}
]
}
},
"\/health\/queue\/builds": {
"get": {
"summary": "Get builds queue",
@@ -11967,7 +12019,7 @@
},
"x-appwrite": {
"method": "getQueueBuilds",
"weight": 135,
"weight": 136,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12028,7 +12080,7 @@
},
"x-appwrite": {
"method": "getQueueCertificates",
"weight": 134,
"weight": 135,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12089,7 +12141,7 @@
},
"x-appwrite": {
"method": "getQueueDatabases",
"weight": 136,
"weight": 137,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12159,7 +12211,7 @@
},
"x-appwrite": {
"method": "getQueueDeletes",
"weight": 137,
"weight": 138,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12257,9 +12309,8 @@
"v1-audits",
"v1-mails",
"v1-functions",
"v1-stats-resources",
"v1-stats-usage",
"v1-stats-usage-dump",
"v1-usage",
"v1-usage-dump",
"v1-webhooks",
"v1-certificates",
"v1-builds",
@@ -12306,7 +12357,7 @@
},
"x-appwrite": {
"method": "getQueueFunctions",
"weight": 141,
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12367,7 +12418,7 @@
},
"x-appwrite": {
"method": "getQueueLogs",
"weight": 132,
"weight": 133,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12428,7 +12479,7 @@
},
"x-appwrite": {
"method": "getQueueMails",
"weight": 138,
"weight": 139,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12489,7 +12540,7 @@
},
"x-appwrite": {
"method": "getQueueMessaging",
"weight": 139,
"weight": 140,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12550,7 +12601,7 @@
},
"x-appwrite": {
"method": "getQueueMigrations",
"weight": 140,
"weight": 141,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12587,70 +12638,9 @@
]
}
},
"\/health\/queue\/stats-resources": {
"\/health\/queue\/usage": {
"get": {
"summary": "Get stats resources queue",
"operationId": "healthGetQueueStatsResources",
"consumes": [
"application\/json"
],
"produces": [
"application\/json"
],
"tags": [
"health"
],
"description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.",
"responses": {
"200": {
"description": "Health Queue",
"schema": {
"$ref": "#\/definitions\/healthQueue"
}
}
},
"x-appwrite": {
"method": "getQueueStatsResources",
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-resources.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Key": []
}
],
"parameters": [
{
"name": "threshold",
"description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.",
"required": false,
"type": "integer",
"format": "int32",
"default": 5000,
"in": "query"
}
]
}
},
"\/health\/queue\/stats-usage": {
"get": {
"summary": "Get stats usage queue",
"summary": "Get usage queue",
"operationId": "healthGetQueueUsage",
"consumes": [
"application\/json"
@@ -12677,7 +12667,7 @@
"type": "",
"deprecated": false,
"demo": "health\/get-queue-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -12709,10 +12699,10 @@
]
}
},
"\/health\/queue\/stats-usage-dump": {
"\/health\/queue\/usage-dump": {
"get": {
"summary": "Get usage dump queue",
"operationId": "healthGetQueueStatsUsageDump",
"operationId": "healthGetQueueUsageDump",
"consumes": [
"application\/json"
],
@@ -12732,13 +12722,13 @@
}
},
"x-appwrite": {
"method": "getQueueStatsUsageDump",
"method": "getQueueUsageDump",
"weight": 144,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md",
"demo": "health\/get-queue-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -12794,7 +12784,7 @@
},
"x-appwrite": {
"method": "getQueueWebhooks",
"weight": 131,
"weight": 132,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12955,7 +12945,7 @@
},
"x-appwrite": {
"method": "getTime",
"weight": 130,
"weight": 131,
"cookies": false,
"type": "",
"deprecated": false,
@@ -18080,19 +18070,19 @@
},
"endpoint": {
"type": "string",
"description": "Source Appwrite endpoint",
"description": "Source's Appwrite Endpoint",
"default": null,
"x-example": "https:\/\/example.com"
},
"projectId": {
"type": "string",
"description": "Source Project ID",
"description": "Source's Project ID",
"default": null,
"x-example": "<PROJECT_ID>"
},
"apiKey": {
"type": "string",
"description": "Source API Key",
"description": "Source's API Key",
"default": null,
"x-example": "<API_KEY>"
}
@@ -36618,21 +36608,6 @@
"$ref": "#\/definitions\/metric"
},
"x-example": []
},
"imageTransformations": {
"type": "array",
"description": "Aggregated number of files transformations per period.",
"items": {
"type": "object",
"$ref": "#\/definitions\/metric"
},
"x-example": []
},
"imageTransformationsTotal": {
"type": "integer",
"description": "Total aggregated number of files transformations.",
"x-example": 0,
"format": "int32"
}
},
"required": [
@@ -36640,9 +36615,7 @@
"filesTotal",
"filesStorageTotal",
"files",
"storage",
"imageTransformations",
"imageTransformationsTotal"
"storage"
]
},
"usageFunctions": {
@@ -37212,21 +37185,6 @@
"$ref": "#\/definitions\/metric"
},
"x-example": []
},
"imageTransformations": {
"type": "array",
"description": "An array of aggregated number of image transformations.",
"items": {
"type": "object",
"$ref": "#\/definitions\/metric"
},
"x-example": []
},
"imageTransformationsTotal": {
"type": "integer",
"description": "Total aggregated number of image transformations.",
"x-example": 0,
"format": "int32"
}
},
"required": [
@@ -37258,9 +37216,7 @@
"authPhoneEstimate",
"authPhoneCountryBreakdown",
"databasesReads",
"databasesWrites",
"imageTransformations",
"imageTransformationsTotal"
"databasesWrites"
]
},
"headers": {
+82 -92
View File
@@ -3261,7 +3261,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, rupay.",
"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.",
"required": true,
"type": "string",
"x-example": "amex",
@@ -3281,8 +3281,7 @@
"union-china-pay",
"visa",
"mir",
"maestro",
"rupay"
"maestro"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3301,8 +3300,7 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro",
"Rupay"
"Maestro"
],
"in": "path"
},
@@ -6135,6 +6133,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -6373,6 +6373,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -7550,7 +7552,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.\n",
"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.",
"responses": {
"201": {
"description": "Document",
@@ -10687,7 +10689,7 @@
},
"x-appwrite": {
"method": "getCertificate",
"weight": 133,
"weight": 134,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10798,7 +10800,7 @@
},
"x-appwrite": {
"method": "getPubSub",
"weight": 129,
"weight": 130,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10825,6 +10827,57 @@
]
}
},
"\/health\/queue": {
"get": {
"summary": "Get queue",
"operationId": "healthGetQueue",
"consumes": [
"application\/json"
],
"produces": [
"application\/json"
],
"tags": [
"health"
],
"description": "Check the Appwrite queue messaging servers are up and connection is successful.",
"responses": {
"200": {
"description": "Health Status",
"schema": {
"$ref": "#\/definitions\/healthStatus"
}
}
},
"x-appwrite": {
"method": "getQueue",
"weight": 129,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": [],
"Key": []
}
},
"security": [
{
"Project": [],
"Key": []
}
]
}
},
"\/health\/queue\/builds": {
"get": {
"summary": "Get builds queue",
@@ -10849,7 +10902,7 @@
},
"x-appwrite": {
"method": "getQueueBuilds",
"weight": 135,
"weight": 136,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10911,7 +10964,7 @@
},
"x-appwrite": {
"method": "getQueueCertificates",
"weight": 134,
"weight": 135,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10973,7 +11026,7 @@
},
"x-appwrite": {
"method": "getQueueDatabases",
"weight": 136,
"weight": 137,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11044,7 +11097,7 @@
},
"x-appwrite": {
"method": "getQueueDeletes",
"weight": 137,
"weight": 138,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11144,9 +11197,8 @@
"v1-audits",
"v1-mails",
"v1-functions",
"v1-stats-resources",
"v1-stats-usage",
"v1-stats-usage-dump",
"v1-usage",
"v1-usage-dump",
"v1-webhooks",
"v1-certificates",
"v1-builds",
@@ -11193,7 +11245,7 @@
},
"x-appwrite": {
"method": "getQueueFunctions",
"weight": 141,
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11255,7 +11307,7 @@
},
"x-appwrite": {
"method": "getQueueLogs",
"weight": 132,
"weight": 133,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11317,7 +11369,7 @@
},
"x-appwrite": {
"method": "getQueueMails",
"weight": 138,
"weight": 139,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11379,7 +11431,7 @@
},
"x-appwrite": {
"method": "getQueueMessaging",
"weight": 139,
"weight": 140,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11441,7 +11493,7 @@
},
"x-appwrite": {
"method": "getQueueMigrations",
"weight": 140,
"weight": 141,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11479,71 +11531,9 @@
]
}
},
"\/health\/queue\/stats-resources": {
"\/health\/queue\/usage": {
"get": {
"summary": "Get stats resources queue",
"operationId": "healthGetQueueStatsResources",
"consumes": [
"application\/json"
],
"produces": [
"application\/json"
],
"tags": [
"health"
],
"description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.",
"responses": {
"200": {
"description": "Health Queue",
"schema": {
"$ref": "#\/definitions\/healthQueue"
}
}
},
"x-appwrite": {
"method": "getQueueStatsResources",
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-resources.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": [],
"Key": []
}
},
"security": [
{
"Project": [],
"Key": []
}
],
"parameters": [
{
"name": "threshold",
"description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.",
"required": false,
"type": "integer",
"format": "int32",
"default": 5000,
"in": "query"
}
]
}
},
"\/health\/queue\/stats-usage": {
"get": {
"summary": "Get stats usage queue",
"summary": "Get usage queue",
"operationId": "healthGetQueueUsage",
"consumes": [
"application\/json"
@@ -11570,7 +11560,7 @@
"type": "",
"deprecated": false,
"demo": "health\/get-queue-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -11603,10 +11593,10 @@
]
}
},
"\/health\/queue\/stats-usage-dump": {
"\/health\/queue\/usage-dump": {
"get": {
"summary": "Get usage dump queue",
"operationId": "healthGetQueueStatsUsageDump",
"operationId": "healthGetQueueUsageDump",
"consumes": [
"application\/json"
],
@@ -11626,13 +11616,13 @@
}
},
"x-appwrite": {
"method": "getQueueStatsUsageDump",
"method": "getQueueUsageDump",
"weight": 144,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md",
"demo": "health\/get-queue-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -11689,7 +11679,7 @@
},
"x-appwrite": {
"method": "getQueueWebhooks",
"weight": 131,
"weight": 132,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11853,7 +11843,7 @@
},
"x-appwrite": {
"method": "getTime",
"weight": 130,
"weight": 131,
"cookies": false,
"type": "",
"deprecated": false,
+4 -6
View File
@@ -3557,7 +3557,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, rupay.",
"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.",
"required": true,
"type": "string",
"x-example": "amex",
@@ -3577,8 +3577,7 @@
"union-china-pay",
"visa",
"mir",
"maestro",
"rupay"
"maestro"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3597,8 +3596,7 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro",
"Rupay"
"Maestro"
],
"in": "path"
},
@@ -4549,7 +4547,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.\n",
"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.",
"responses": {
"201": {
"description": "Document",
+86 -130
View File
@@ -3577,7 +3577,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, rupay.",
"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.",
"required": true,
"type": "string",
"x-example": "amex",
@@ -3597,8 +3597,7 @@
"union-china-pay",
"visa",
"mir",
"maestro",
"rupay"
"maestro"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3617,8 +3616,7 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro",
"Rupay"
"Maestro"
],
"in": "path"
},
@@ -6609,6 +6607,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -6845,6 +6845,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -8010,7 +8012,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.\n",
"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.",
"responses": {
"201": {
"description": "Document",
@@ -11808,7 +11810,7 @@
},
"x-appwrite": {
"method": "getCertificate",
"weight": 133,
"weight": 134,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11917,7 +11919,7 @@
},
"x-appwrite": {
"method": "getPubSub",
"weight": 129,
"weight": 130,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11943,6 +11945,56 @@
]
}
},
"\/health\/queue": {
"get": {
"summary": "Get queue",
"operationId": "healthGetQueue",
"consumes": [
"application\/json"
],
"produces": [
"application\/json"
],
"tags": [
"health"
],
"description": "Check the Appwrite queue messaging servers are up and connection is successful.",
"responses": {
"200": {
"description": "Health Status",
"schema": {
"$ref": "#\/definitions\/healthStatus"
}
}
},
"x-appwrite": {
"method": "getQueue",
"weight": 129,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Key": []
}
]
}
},
"\/health\/queue\/builds": {
"get": {
"summary": "Get builds queue",
@@ -11967,7 +12019,7 @@
},
"x-appwrite": {
"method": "getQueueBuilds",
"weight": 135,
"weight": 136,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12028,7 +12080,7 @@
},
"x-appwrite": {
"method": "getQueueCertificates",
"weight": 134,
"weight": 135,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12089,7 +12141,7 @@
},
"x-appwrite": {
"method": "getQueueDatabases",
"weight": 136,
"weight": 137,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12159,7 +12211,7 @@
},
"x-appwrite": {
"method": "getQueueDeletes",
"weight": 137,
"weight": 138,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12257,9 +12309,8 @@
"v1-audits",
"v1-mails",
"v1-functions",
"v1-stats-resources",
"v1-stats-usage",
"v1-stats-usage-dump",
"v1-usage",
"v1-usage-dump",
"v1-webhooks",
"v1-certificates",
"v1-builds",
@@ -12306,7 +12357,7 @@
},
"x-appwrite": {
"method": "getQueueFunctions",
"weight": 141,
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12367,7 +12418,7 @@
},
"x-appwrite": {
"method": "getQueueLogs",
"weight": 132,
"weight": 133,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12428,7 +12479,7 @@
},
"x-appwrite": {
"method": "getQueueMails",
"weight": 138,
"weight": 139,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12489,7 +12540,7 @@
},
"x-appwrite": {
"method": "getQueueMessaging",
"weight": 139,
"weight": 140,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12550,7 +12601,7 @@
},
"x-appwrite": {
"method": "getQueueMigrations",
"weight": 140,
"weight": 141,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12587,70 +12638,9 @@
]
}
},
"\/health\/queue\/stats-resources": {
"\/health\/queue\/usage": {
"get": {
"summary": "Get stats resources queue",
"operationId": "healthGetQueueStatsResources",
"consumes": [
"application\/json"
],
"produces": [
"application\/json"
],
"tags": [
"health"
],
"description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.",
"responses": {
"200": {
"description": "Health Queue",
"schema": {
"$ref": "#\/definitions\/healthQueue"
}
}
},
"x-appwrite": {
"method": "getQueueStatsResources",
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-resources.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": []
}
},
"security": [
{
"Project": [],
"Key": []
}
],
"parameters": [
{
"name": "threshold",
"description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.",
"required": false,
"type": "integer",
"format": "int32",
"default": 5000,
"in": "query"
}
]
}
},
"\/health\/queue\/stats-usage": {
"get": {
"summary": "Get stats usage queue",
"summary": "Get usage queue",
"operationId": "healthGetQueueUsage",
"consumes": [
"application\/json"
@@ -12677,7 +12667,7 @@
"type": "",
"deprecated": false,
"demo": "health\/get-queue-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -12709,10 +12699,10 @@
]
}
},
"\/health\/queue\/stats-usage-dump": {
"\/health\/queue\/usage-dump": {
"get": {
"summary": "Get usage dump queue",
"operationId": "healthGetQueueStatsUsageDump",
"operationId": "healthGetQueueUsageDump",
"consumes": [
"application\/json"
],
@@ -12732,13 +12722,13 @@
}
},
"x-appwrite": {
"method": "getQueueStatsUsageDump",
"method": "getQueueUsageDump",
"weight": 144,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md",
"demo": "health\/get-queue-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -12794,7 +12784,7 @@
},
"x-appwrite": {
"method": "getQueueWebhooks",
"weight": 131,
"weight": 132,
"cookies": false,
"type": "",
"deprecated": false,
@@ -12955,7 +12945,7 @@
},
"x-appwrite": {
"method": "getTime",
"weight": 130,
"weight": 131,
"cookies": false,
"type": "",
"deprecated": false,
@@ -18080,19 +18070,19 @@
},
"endpoint": {
"type": "string",
"description": "Source Appwrite endpoint",
"description": "Source's Appwrite Endpoint",
"default": null,
"x-example": "https:\/\/example.com"
},
"projectId": {
"type": "string",
"description": "Source Project ID",
"description": "Source's Project ID",
"default": null,
"x-example": "<PROJECT_ID>"
},
"apiKey": {
"type": "string",
"description": "Source API Key",
"description": "Source's API Key",
"default": null,
"x-example": "<API_KEY>"
}
@@ -36618,21 +36608,6 @@
"$ref": "#\/definitions\/metric"
},
"x-example": []
},
"imageTransformations": {
"type": "array",
"description": "Aggregated number of files transformations per period.",
"items": {
"type": "object",
"$ref": "#\/definitions\/metric"
},
"x-example": []
},
"imageTransformationsTotal": {
"type": "integer",
"description": "Total aggregated number of files transformations.",
"x-example": 0,
"format": "int32"
}
},
"required": [
@@ -36640,9 +36615,7 @@
"filesTotal",
"filesStorageTotal",
"files",
"storage",
"imageTransformations",
"imageTransformationsTotal"
"storage"
]
},
"usageFunctions": {
@@ -37212,21 +37185,6 @@
"$ref": "#\/definitions\/metric"
},
"x-example": []
},
"imageTransformations": {
"type": "array",
"description": "An array of aggregated number of image transformations.",
"items": {
"type": "object",
"$ref": "#\/definitions\/metric"
},
"x-example": []
},
"imageTransformationsTotal": {
"type": "integer",
"description": "Total aggregated number of image transformations.",
"x-example": 0,
"format": "int32"
}
},
"required": [
@@ -37258,9 +37216,7 @@
"authPhoneEstimate",
"authPhoneCountryBreakdown",
"databasesReads",
"databasesWrites",
"imageTransformations",
"imageTransformationsTotal"
"databasesWrites"
]
},
"headers": {
+82 -92
View File
@@ -3261,7 +3261,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, rupay.",
"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.",
"required": true,
"type": "string",
"x-example": "amex",
@@ -3281,8 +3281,7 @@
"union-china-pay",
"visa",
"mir",
"maestro",
"rupay"
"maestro"
],
"x-enum-name": "CreditCard",
"x-enum-keys": [
@@ -3301,8 +3300,7 @@
"Union China Pay",
"Visa",
"MIR",
"Maestro",
"Rupay"
"Maestro"
],
"in": "path"
},
@@ -6135,6 +6133,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -6373,6 +6373,8 @@
},
"required": [
"required",
"min",
"max",
"default"
]
}
@@ -7550,7 +7552,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.\n",
"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.",
"responses": {
"201": {
"description": "Document",
@@ -10687,7 +10689,7 @@
},
"x-appwrite": {
"method": "getCertificate",
"weight": 133,
"weight": 134,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10798,7 +10800,7 @@
},
"x-appwrite": {
"method": "getPubSub",
"weight": 129,
"weight": 130,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10825,6 +10827,57 @@
]
}
},
"\/health\/queue": {
"get": {
"summary": "Get queue",
"operationId": "healthGetQueue",
"consumes": [
"application\/json"
],
"produces": [
"application\/json"
],
"tags": [
"health"
],
"description": "Check the Appwrite queue messaging servers are up and connection is successful.",
"responses": {
"200": {
"description": "Health Status",
"schema": {
"$ref": "#\/definitions\/healthStatus"
}
}
},
"x-appwrite": {
"method": "getQueue",
"weight": 129,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": [],
"Key": []
}
},
"security": [
{
"Project": [],
"Key": []
}
]
}
},
"\/health\/queue\/builds": {
"get": {
"summary": "Get builds queue",
@@ -10849,7 +10902,7 @@
},
"x-appwrite": {
"method": "getQueueBuilds",
"weight": 135,
"weight": 136,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10911,7 +10964,7 @@
},
"x-appwrite": {
"method": "getQueueCertificates",
"weight": 134,
"weight": 135,
"cookies": false,
"type": "",
"deprecated": false,
@@ -10973,7 +11026,7 @@
},
"x-appwrite": {
"method": "getQueueDatabases",
"weight": 136,
"weight": 137,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11044,7 +11097,7 @@
},
"x-appwrite": {
"method": "getQueueDeletes",
"weight": 137,
"weight": 138,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11144,9 +11197,8 @@
"v1-audits",
"v1-mails",
"v1-functions",
"v1-stats-resources",
"v1-stats-usage",
"v1-stats-usage-dump",
"v1-usage",
"v1-usage-dump",
"v1-webhooks",
"v1-certificates",
"v1-builds",
@@ -11193,7 +11245,7 @@
},
"x-appwrite": {
"method": "getQueueFunctions",
"weight": 141,
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11255,7 +11307,7 @@
},
"x-appwrite": {
"method": "getQueueLogs",
"weight": 132,
"weight": 133,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11317,7 +11369,7 @@
},
"x-appwrite": {
"method": "getQueueMails",
"weight": 138,
"weight": 139,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11379,7 +11431,7 @@
},
"x-appwrite": {
"method": "getQueueMessaging",
"weight": 139,
"weight": 140,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11441,7 +11493,7 @@
},
"x-appwrite": {
"method": "getQueueMigrations",
"weight": 140,
"weight": 141,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11479,71 +11531,9 @@
]
}
},
"\/health\/queue\/stats-resources": {
"\/health\/queue\/usage": {
"get": {
"summary": "Get stats resources queue",
"operationId": "healthGetQueueStatsResources",
"consumes": [
"application\/json"
],
"produces": [
"application\/json"
],
"tags": [
"health"
],
"description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.",
"responses": {
"200": {
"description": "Health Queue",
"schema": {
"$ref": "#\/definitions\/healthQueue"
}
}
},
"x-appwrite": {
"method": "getQueueStatsResources",
"weight": 142,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-resources.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
"scope": "health.read",
"platforms": [
"server"
],
"packaging": false,
"auth": {
"Project": [],
"Key": []
}
},
"security": [
{
"Project": [],
"Key": []
}
],
"parameters": [
{
"name": "threshold",
"description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.",
"required": false,
"type": "integer",
"format": "int32",
"default": 5000,
"in": "query"
}
]
}
},
"\/health\/queue\/stats-usage": {
"get": {
"summary": "Get stats usage queue",
"summary": "Get usage queue",
"operationId": "healthGetQueueUsage",
"consumes": [
"application\/json"
@@ -11570,7 +11560,7 @@
"type": "",
"deprecated": false,
"demo": "health\/get-queue-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -11603,10 +11593,10 @@
]
}
},
"\/health\/queue\/stats-usage-dump": {
"\/health\/queue\/usage-dump": {
"get": {
"summary": "Get usage dump queue",
"operationId": "healthGetQueueStatsUsageDump",
"operationId": "healthGetQueueUsageDump",
"consumes": [
"application\/json"
],
@@ -11626,13 +11616,13 @@
}
},
"x-appwrite": {
"method": "getQueueStatsUsageDump",
"method": "getQueueUsageDump",
"weight": 144,
"cookies": false,
"type": "",
"deprecated": false,
"demo": "health\/get-queue-stats-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md",
"demo": "health\/get-queue-usage-dump.md",
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md",
"rate-limit": 0,
"rate-time": 3600,
"rate-key": "url:{url},ip:{ip}",
@@ -11689,7 +11679,7 @@
},
"x-appwrite": {
"method": "getQueueWebhooks",
"weight": 131,
"weight": 132,
"cookies": false,
"type": "",
"deprecated": false,
@@ -11853,7 +11843,7 @@
},
"x-appwrite": {
"method": "getTime",
"weight": 130,
"weight": 131,
"cookies": false,
"type": "",
"deprecated": false,
-1
View File
@@ -7,5 +7,4 @@ return [
"gif" => "image/gif",
"png" => "image/png",
"heic" => "image/heic",
"webp" => "image/webp",
];
+5 -22
View File
@@ -572,7 +572,7 @@ return [
],
[
'name' => '_APP_STORAGE_S3_ACCESS_KEY',
'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',
'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',
'introduction' => '0.13.0',
'default' => '',
'required' => false,
@@ -580,7 +580,7 @@ return [
],
[
'name' => '_APP_STORAGE_S3_SECRET',
'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.',
'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.',
'introduction' => '0.13.0',
'default' => '',
'required' => false,
@@ -588,7 +588,7 @@ return [
],
[
'name' => '_APP_STORAGE_S3_REGION',
'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.',
'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.',
'introduction' => '0.13.0',
'default' => 'us-east-1',
'required' => false,
@@ -596,20 +596,12 @@ return [
],
[
'name' => '_APP_STORAGE_S3_BUCKET',
'description' => 'S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your S3 storage provider.',
'description' => 'AWS S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your AWS console.',
'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.',
@@ -1048,22 +1040,13 @@ return [
],
[
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT',
'description' => 'The maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days).',
'description' => 'IThe 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).',
+19 -21
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\StatsUsage;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception;
use Appwrite\Hooks\Hooks;
use Appwrite\Network\Validator\Email;
@@ -2400,7 +2400,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', 'auth'])
->groups(['api', 'account'])
->label('scope', 'sessions.write')
->label('auth.type', 'phone')
->label('audits.event', 'session.create')
@@ -2432,9 +2432,9 @@ App::post('/v1/account/tokens/phone')
->inject('queueForMessaging')
->inject('locale')
->inject('timelimit')
->inject('queueForStatsUsage')
->inject('queueForUsage')
->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, StatsUsage $queueForStatsUsage, 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, Usage $queueForUsage, array $plan) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
@@ -2583,11 +2583,11 @@ App::post('/v1/account/tokens/phone')
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForStatsUsage
$queueForUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForStatsUsage
$queueForUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
@@ -2715,15 +2715,13 @@ App::get('/v1/account/logs')
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'))),
]);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new EventAudit($dbForProject);
$logs = $audit->getLogsByUser($user->getInternalId(), $queries);
$logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset);
$output = [];
@@ -2752,7 +2750,7 @@ App::get('/v1/account/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByUser($user->getInternalId(), $queries),
'total' => $audit->countLogsByUser($user->getInternalId()),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -3680,9 +3678,9 @@ App::post('/v1/account/verification/phone')
->inject('project')
->inject('locale')
->inject('timelimit')
->inject('queueForStatsUsage')
->inject('queueForUsage')
->inject('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) {
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, Usage $queueForUsage, array $plan) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
@@ -3777,11 +3775,11 @@ App::post('/v1/account/verification/phone')
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForStatsUsage
$queueForUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForStatsUsage
$queueForUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
@@ -4312,9 +4310,9 @@ App::post('/v1/account/mfa/challenge')
->inject('queueForMessaging')
->inject('queueForMails')
->inject('timelimit')
->inject('queueForStatsUsage')
->inject('queueForUsage')
->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, StatsUsage $queueForStatsUsage, 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, Usage $queueForUsage, array $plan) {
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
$code = Auth::codeGenerator();
@@ -4385,11 +4383,11 @@ App::post('/v1/account/mfa/challenge')
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForStatsUsage
$queueForUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForStatsUsage
$queueForUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
+273 -83
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\StatsUsage;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\SDK\AuthType;
@@ -23,17 +23,16 @@ 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\Query as QueryException;
use Utopia\Database\Exception\Restricted as RestrictedException;
use Utopia\Database\Exception\Structure as StructureException;
use Utopia\Database\Exception\Timeout as TimeoutException;
use Utopia\Database\Exception\Truncate as TruncateException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
@@ -243,8 +242,8 @@ function updateAttribute(
string $filter = null,
string|bool|int|float $default = null,
bool $required = null,
int|float|null $min = null,
int|float|null $max = null,
int|float $min = null,
int|float $max = null,
array $elements = null,
array $options = [],
string $newKey = null,
@@ -300,9 +299,6 @@ 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');
}
@@ -398,8 +394,6 @@ 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());
}
}
@@ -456,6 +450,88 @@ App::init()
}
});
App::init()
->groups(['timeout'])
->inject('request')
->inject('dbForProject')
->action(function (Request $request, Database $dbForProject) {
$key = md5(json_encode([
$request->getMethod(),
$request->getURI(), // Contains databaseId & collectionId
$request->getParam('queries')
]));
/** @var Document $document */
$document = Authorization::skip(fn () => $dbForProject->getDocument(
'slowQueries',
$key
));
if ($document->getAttribute('blocked')) {
throw new Exception(Exception::QUERY_BLOCKED);
}
});
App::error()
->groups(['timeout'])
->inject('error')
->inject('request')
->inject('dbForProject')
->action(function (Throwable $error, Request $request, Database $dbForProject) {
if (!$error instanceof TimeoutException) {
return;
}
$route = Request::getRoute();
$collectionId = $route->getParamValue('collectionId');
$databaseId = $route->getParamValue('databaseId');
$queries = $request->getParam('queries', []);
$key = md5(json_encode([
$request->getMethod(),
$request->getURI(),
$queries
]));
/** @var Document $document */
$document = Authorization::skip(fn () => $dbForProject->getDocument(
'slowQueries',
$key
));
if ($document->isEmpty()) {
$document = Authorization::skip(fn () => $dbForProject->createDocument('slowQueries', new Document([
'$id' => $key,
'blocked' => false,
'count' => 1,
'queries' => $queries,
'databaseId' => $databaseId,
'collectionId' => $collectionId,
'path' => $request->getURI()
])));
} else {
$document->setAttribute('count', $document->getAttribute('count') + 1);
$max = intval(App::getEnv('_APP_SLOW_QUERIES_MAX_HITS', 5));
if ($document->getAttribute('count') >= $max) {
$document->setAttribute('blocked', true);
}
$document = Authorization::skip(fn () => $dbForProject->updateDocument(
'slowQueries',
$document->getId(),
$document
));
}
// Set the correct exception to pass to the next shutdown hook
if ($document->getAttribute('blocked')) {
App::setResource('error', fn () => new Exception(Exception::QUERY_BLOCKED));
} else {
App::setResource('error', fn () => new Exception(Exception::QUERY_TIMEOUT));
}
});
App::post('/v1/databases')
->desc('Create database')
->groups(['api', 'database'])
@@ -483,8 +559,8 @@ App::post('/v1/databases')
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
->inject('queueForUsage')
->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents, Usage $queueForUsage) {
$databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId;
@@ -534,7 +610,7 @@ App::post('/v1/databases')
}
$queueForEvents->setParam('databaseId', $database->getId());
$queueForStatsUsage->addMetric(str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_STORAGE), 1); // per database
$queueForUsage->addMetric(str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_STORAGE), 1); // per database
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -673,15 +749,13 @@ App::get('/v1/databases/:databaseId/logs')
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'))),
]);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'database/' . $databaseId;
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$output = [];
@@ -729,7 +803,7 @@ App::get('/v1/databases/:databaseId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource, $queries),
'total' => $audit->countLogsByResource($resource),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -806,8 +880,8 @@ App::delete('/v1/databases/:databaseId')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
->inject('queueForUsage')
->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) {
$database = $dbForProject->getDocument('databases', $databaseId);
@@ -830,7 +904,7 @@ App::delete('/v1/databases/:databaseId')
->setParam('databaseId', $database->getId())
->setPayload($response->output($database, Response::MODEL_DATABASE));
$queueForStatsUsage
$queueForUsage
->addMetric(METRIC_DATABASES_STORAGE, 1); // Global, deletion forces full recalculation
$response->noContent();
@@ -933,8 +1007,7 @@ App::get('/v1/databases/:databaseId/collections')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->inject('response')
->inject('dbForProject')
->inject('mode')
->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject, string $mode) {
->action(function (string $databaseId, array $queries, string $search, Response $response, Database $dbForProject) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@@ -1063,21 +1136,14 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
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'))),
]);
$queries = Query::parseQueries($queries);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'database/' . $databaseId . '/collection/' . $collectionId;
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$output = [];
@@ -1125,7 +1191,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource, $queries),
'total' => $audit->countLogsByResource($resource),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -1233,8 +1299,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('mode')
->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, string $mode) {
->action(function (string $databaseId, string $collectionId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@@ -1563,8 +1628,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 ??= PHP_INT_MIN;
$max ??= PHP_INT_MAX;
$min = \is_null($min) ? PHP_INT_MIN : $min;
$max = \is_null($max) ? PHP_INT_MAX : $max;
if ($min > $max) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value');
@@ -1640,8 +1705,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 ??= -PHP_FLOAT_MAX;
$max ??= PHP_FLOAT_MAX;
$min = \is_null($min) ? -PHP_FLOAT_MAX : $min;
$max = \is_null($max) ? PHP_FLOAT_MAX : $max;
if ($min > $max) {
throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value');
@@ -2352,8 +2417,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', true)
->param('max', null, new Integer(), 'Maximum value to enforce on new documents', true)
->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('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')
@@ -2411,8 +2476,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', true)
->param('max', null, new FloatValidator(), 'Maximum value to enforce on new documents', true)
->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('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')
@@ -2634,8 +2699,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
->inject('queueForUsage')
->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) {
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
@@ -2732,7 +2797,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key
->setContext('database', $db)
->setPayload($response->output($attribute, $model));
$queueForStatsUsage
$queueForUsage
->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$db->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection
$response->noContent();
@@ -2838,7 +2903,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 . '. Verify the attribute name or create the attribute.');
throw new Exception(Exception::ATTRIBUTE_UNKNOWN, 'Unknown attribute: ' . $attribute);
}
$attributeStatus = $oldAttributes[$attributeIndex]['status'];
@@ -3150,8 +3215,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
->inject('dbForProject')
->inject('user')
->inject('queueForEvents')
->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) {
->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) {
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
@@ -3354,8 +3420,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
$processDocument($collection, $document);
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1))
$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
@@ -3385,7 +3451,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
->alias('/v1/database/collections/:collectionId/documents', ['databaseId' => 'default'])
->desc('List documents')
->groups(['api', 'database'])
->groups(['api', 'database', 'timeout'])
->label('scope', 'documents.read')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('sdk', new Method(
@@ -3404,10 +3470,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
->param('databaseId', '', new UID(), 'Database ID.')
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).')
->param('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('request')
->inject('response')
->inject('dbForProject')
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) {
->inject('mode')
->inject('queueForUsage')
->action(function (string $databaseId, string $collectionId, array $queries, Request $request, Response $response, Database $dbForProject, string $mode, Usage $queueForUsage) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
@@ -3454,9 +3522,17 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
$cursor->setValue($cursorDocument);
}
$timeout = App::getEnv('_APP_SLOW_QUERIES') === 'enabled'
? App::getEnv('_APP_SLOW_QUERIES_TIMEOUT')
: null;
if (App::isDevelopment() && intval($request->getHeader('x-appwrite-timeout')) > 0) {
$timeout = intval($request->getHeader('x-appwrite-timeout'));
$dbForProject->setTimeout($timeout);
}
$documents = $dbForProject->find('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries);
$total = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $queries, APP_LIMIT_COUNT);
$operations = 0;
// Add $collectionId and $databaseId for all documents
@@ -3519,9 +3595,10 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents')
$processDocument($collection, $document);
}
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations);
$queueForUsage
->addMetric(METRIC_DATABASES_OPERATIONS_READS, $operations)
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations)
;
$response->addHeader('X-Debug-Operations', $operations);
@@ -3583,8 +3660,9 @@ 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('queueForStatsUsage')
->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) {
->inject('mode')
->inject('queueForUsage')
->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, string $mode, Usage $queueForUsage) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
@@ -3660,9 +3738,10 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
$processDocument($collection, $document);
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_READS, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations);
$queueForUsage
->addMetric(METRIC_DATABASES_OPERATIONS_READS, $operations)
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations)
;
$response->addHeader('X-Debug-Operations', $operations);
@@ -3722,15 +3801,13 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
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'))),
]);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'database/' . $databaseId . '/collection/' . $collectionId . '/document/' . $document->getId();
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$output = [];
@@ -3778,7 +3855,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource, $queries),
'total' => $audit->countLogsByResource($resource),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -3817,8 +3894,9 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->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) {
->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) {
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
@@ -3958,9 +4036,10 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
$setCollection($collection, $newDocument);
$queueForStatsUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1))
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations);
$queueForUsage
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, $operations)
->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations)
;
$response->addHeader('X-Debug-Operations', $operations);
@@ -4069,8 +4148,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
->inject('queueForUsage')
->inject('mode')
->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, Usage $queueForUsage, string $mode) {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
@@ -4139,7 +4219,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu
$processDocument($collection, $document);
$queueForStatsUsage
$queueForUsage
->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
@@ -4451,3 +4531,113 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage')
'documents' => $usage[$metrics[0]]['data'],
]), Response::MODEL_USAGE_COLLECTION);
});
App::get('/v1/databases/:databaseId/slow-queries')
->desc('List slow queries')
->groups(['api', 'database'])
->label('docs', false)
->label('scope', 'documents.read')
->label('sdk', new Method(
namespace: 'databases',
name: 'listSlowQueries',
description: '/docs/references/databases/list-slow-queries.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_SLOW_QUERY_LIST,
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->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/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
->inject('response')
->inject('dbForProject')
->action(function ($databaseId, array $queries, Response $response, Database $dbForProject) {
$queries = Query::parseQueries($queries);
$queries[] = Query::equal('databaseId', [$databaseId]);
$cursor = Query::getByType($queries, [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
$cursor = reset($cursor);
if ($cursor) {
$documentId = $cursor->getValue();
$cursorDocument = $dbForProject->getDocument('slowQueries', $documentId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Slow query '{$documentId}' for the 'cursor' value not found.");
}
$cursor->setValue($cursorDocument);
}
$response->dynamic(new Document([
'documents' => $dbForProject->find('slowQueries', $queries),
'total' => $dbForProject->count('slowQueries', Query::groupByType($queries)['filters'], APP_LIMIT_COUNT),
]), Response::MODEL_DOCUMENT_LIST);
});
App::get('/v1/databases/:databaseId/slow-queries/:slowQueryId')
->desc('Get slow query')
->groups(['api', 'database'])
->label('docs', false)
->label('scope', 'documents.read')
->label('usage.metric', 'documents.{scope}.requests.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk', new Method(
namespace: 'databases',
name: 'getSlowQuery',
description: '/docs/references/databases/get-slow-query.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_SLOW_QUERY,
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('slowQueryId', '', new UID(), 'Document ID.')
->inject('response')
->inject('dbForProject')
->action(function (string $databaseId, string $slowQueryId, Response $response, Database $dbForProject) {
$document = $dbForProject->getDocument('slowQueries', $slowQueryId);
if ($document->isEmpty() || $document->getAttribute('databaseId') !== $databaseId) {
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
}
$response->dynamic($document, Response::MODEL_SLOW_QUERY);
});
App::delete('/v1/databases/:databaseId/slow-queries/:slowQueryId')
->desc('Delete slow query')
->desc('List Documents')
->groups(['api', 'database'])
->label('scope', 'documents.write')
->label('usage.metric', 'documents.{scope}.requests.delete')
->label('docs', false)
->label('sdk', new Method(
namespace: 'databases',
name: 'deleteSlowQuery',
description: '/docs/references/databases/delete-slow-query.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_NOCONTENT,
model: Response::MODEL_NONE,
)
]
))
->param('databaseId', '', new UID(), 'Database ID.')
->param('slowQueryId', '', new UID(), 'Document ID.')
->inject('response')
->inject('dbForProject')
->action(function (string $databaseId, string $slowQueryId, Response $response, Database $dbForProject) {
$document = $dbForProject->getDocument('slowQueries', $slowQueryId);
if ($document->isEmpty() || $document->getAttribute('databaseId') !== $databaseId) {
throw new Exception(Exception::DOCUMENT_NOT_FOUND);
}
$dbForProject->deleteDocument('slowQueries', $slowQueryId);
$response->noContent();
});
+45 -29
View File
@@ -6,14 +6,13 @@ use Appwrite\Event\Build;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Realtime;
use Appwrite\Event\StatsUsage;
use Appwrite\Event\Usage;
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;
@@ -195,12 +194,9 @@ 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, Webhook $queueForWebhooks, Func $queueForFunctions, Realtime $queueForRealtime, 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, Database $dbForPlatform, GitHub $github) use ($redeployVcs) {
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
// Temporary abuse check
@@ -295,7 +291,7 @@ App::post('/v1/functions')
$schedule = Authorization::skip(
fn () => $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'region' => System::getEnv('_APP_REGION', 'default'), // Todo replace with projects region
'resourceType' => 'function',
'resourceId' => $function->getId(),
'resourceInternalId' => $function->getInternalId(),
@@ -387,34 +383,54 @@ App::post('/v1/functions')
'resourceInternalId' => $function->getInternalId(),
'status' => 'verified',
'certificateId' => '',
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
/** Trigger Webhook */
$ruleModel = new Rule();
$ruleCreate =
$queueForEvents
->setProject($project)
->setEvent('rules.[ruleId].create')
->setParam('ruleId', $rule->getId())
->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules())));
->setClass(Event::WEBHOOK_CLASS_NAME)
->setQueue(Event::WEBHOOK_QUEUE_NAME);
/** Trigger Webhook */
$queueForWebhooks
->from($ruleCreate)
$ruleCreate
->setProject($project)
->setEvent('rules.[ruleId].create')
->setParam('ruleId', $rule->getId())
->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules())))
->trigger();
/** Trigger Functions */
$queueForFunctions
->from($ruleCreate)
$ruleCreate
->setClass(Event::FUNCTIONS_CLASS_NAME)
->setQueue(Event::FUNCTIONS_QUEUE_NAME)
->trigger();
/** Trigger Realtime Events */
$queueForRealtime
->from($ruleCreate)
->setSubscribers(['console', $project->getId()])
->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']
);
}
$queueForEvents->setParam('functionId', $function->getId());
@@ -1261,9 +1277,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)
@@ -1884,10 +1900,10 @@ App::post('/v1/functions/:functionId/executions')
->inject('dbForPlatform')
->inject('user')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('queueForUsage')
->inject('queueForFunctions')
->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, StatsUsage $queueForStatsUsage, 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, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) {
$async = \strval($async) === 'true' || \strval($async) === '1';
if (!$async && !is_null($scheduledAt)) {
@@ -2079,7 +2095,7 @@ App::post('/v1/functions/:functionId/executions')
];
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => ScheduleExecutions::getSupportedResource(),
'resourceId' => $execution->getId(),
'resourceInternalId' => $execution->getInternalId(),
@@ -2214,7 +2230,7 @@ App::post('/v1/functions/:functionId/executions')
throw $th;
}
} finally {
$queueForStatsUsage
$queueForUsage
->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
+125 -82
View File
@@ -13,8 +13,8 @@ use Utopia\Config\Config;
use Utopia\Database\Document;
use Utopia\Domains\Validator\PublicDomain;
use Utopia\Pools\Group;
use Utopia\Queue\Publisher;
use Utopia\Queue\Queue;
use Utopia\Queue\Client;
use Utopia\Queue\Connection;
use Utopia\Registry\Registry;
use Utopia\Storage\Device;
use Utopia\Storage\Device\Local;
@@ -188,6 +188,69 @@ App::get('/v1/health/cache')
]), Response::MODEL_HEALTH_STATUS_LIST);
});
App::get('/v1/health/queue')
->desc('Get queue')
->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
name: 'getQueue',
description: '/docs/references/health/get-queue.md',
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_HEALTH_STATUS,
)
],
contentType: ContentType::JSON
))
->inject('response')
->inject('pools')
->action(function (Response $response, Group $pools) {
$output = [];
$configs = [
'Queue' => Config::getParam('pools-queue'),
];
foreach ($configs as $key => $config) {
foreach ($config as $database) {
$checkStart = \microtime(true);
try {
/** @var Connection $adapter */
$adapter = $pools->get($database)->pop()->getResource();
if ($adapter->ping()) {
$output[] = new Document([
'name' => $key . " ($database)",
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
]);
} else {
$output[] = new Document([
'name' => $key . " ($database)",
'status' => 'fail',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
]);
}
} catch (\Throwable $th) {
$output[] = new Document([
'name' => $key . " ($database)",
'status' => 'fail',
'ping' => \round((\microtime(true) - $checkStart) / 1000)
]);
}
}
}
$response->dynamic(new Document([
'statuses' => $output,
'total' => count($output),
]), Response::MODEL_HEALTH_STATUS_LIST);
});
App::get('/v1/health/pubsub')
->desc('Get pubsub')
->groups(['api', 'health'])
@@ -333,12 +396,13 @@ App::get('/v1/health/queue/webhooks')
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('queue')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::WEBHOOK_QUEUE_NAME));
$client = new Client(Event::WEBHOOK_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -365,12 +429,13 @@ App::get('/v1/health/queue/logs')
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('queue')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::AUDITS_QUEUE_NAME));
$client = new Client(Event::AUDITS_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -453,12 +518,13 @@ App::get('/v1/health/queue/certificates')
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('queue')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::CERTIFICATES_QUEUE_NAME));
$client = new Client(Event::CERTIFICATES_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -485,12 +551,13 @@ App::get('/v1/health/queue/builds')
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('queue')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::BUILDS_QUEUE_NAME));
$client = new Client(Event::BUILDS_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -518,12 +585,13 @@ App::get('/v1/health/queue/databases')
))
->param('name', 'database_db_main', new Text(256), 'Queue name for which to check the queue size', true)
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('queue')
->inject('response')
->action(function (string $name, int|string $threshold, Publisher $publisher, Response $response) {
->action(function (string $name, int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue($name));
$client = new Client($name, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -550,12 +618,13 @@ App::get('/v1/health/queue/deletes')
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('queue')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::DELETE_QUEUE_NAME));
$client = new Client(Event::DELETE_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -582,12 +651,13 @@ App::get('/v1/health/queue/mails')
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('queue')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::MAILS_QUEUE_NAME));
$client = new Client(Event::MAILS_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -614,12 +684,13 @@ App::get('/v1/health/queue/messaging')
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('queue')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::MESSAGING_QUEUE_NAME));
$client = new Client(Event::MESSAGING_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -646,12 +717,13 @@ App::get('/v1/health/queue/migrations')
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('queue')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::MIGRATIONS_QUEUE_NAME));
$client = new Client(Event::MIGRATIONS_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -678,12 +750,13 @@ App::get('/v1/health/queue/functions')
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('queue')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::FUNCTIONS_QUEUE_NAME));
$client = new Client(Event::FUNCTIONS_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -692,47 +765,15 @@ App::get('/v1/health/queue/functions')
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
}, ['response']);
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: 'getQueueStatsResources',
description: '/docs/references/health/get-queue-stats-resources.md',
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_HEALTH_QUEUE,
)
],
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::STATS_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}.");
}
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
});
App::get('/v1/health/queue/stats-usage')
->desc('Get stats usage queue')
App::get('/v1/health/queue/usage')
->desc('Get usage 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-stats-usage.md',
description: '/docs/references/health/get-queue-usage.md',
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
@@ -742,12 +783,13 @@ App::get('/v1/health/queue/stats-usage')
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('queue')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::STATS_USAGE_QUEUE_NAME));
$client = new Client(Event::USAGE_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -756,15 +798,15 @@ App::get('/v1/health/queue/stats-usage')
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
});
App::get('/v1/health/queue/stats-usage-dump')
App::get('/v1/health/queue/usage-dump')
->desc('Get usage dump queue')
->groups(['api', 'health'])
->label('scope', 'health.read')
->label('sdk', new Method(
auth: [AuthType::KEY],
namespace: 'health',
name: 'getQueueStatsUsageDump',
description: '/docs/references/health/get-queue-stats-usage-dump.md',
name: 'getQueueUsageDump',
description: '/docs/references/health/get-queue-usage-dump.md',
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
@@ -774,12 +816,13 @@ App::get('/v1/health/queue/stats-usage-dump')
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisher')
->inject('queue')
->inject('response')
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
->action(function (int|string $threshold, Connection $queue, Response $response) {
$threshold = \intval($threshold);
$size = $publisher->getQueueSize(new Queue(Event::STATS_USAGE_DUMP_QUEUE_NAME));
$client = new Client(Event::USAGE_DUMP_QUEUE_NAME, $queue);
$size = $client->getQueueSize();
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -952,9 +995,8 @@ App::get('/v1/health/queue/failed/:name')
Event::AUDITS_QUEUE_NAME,
Event::MAILS_QUEUE_NAME,
Event::FUNCTIONS_QUEUE_NAME,
Event::STATS_RESOURCES_QUEUE_NAME,
Event::STATS_USAGE_QUEUE_NAME,
Event::STATS_USAGE_DUMP_QUEUE_NAME,
Event::USAGE_QUEUE_NAME,
Event::USAGE_DUMP_QUEUE_NAME,
Event::WEBHOOK_QUEUE_NAME,
Event::CERTIFICATES_QUEUE_NAME,
Event::BUILDS_QUEUE_NAME,
@@ -963,11 +1005,12 @@ App::get('/v1/health/queue/failed/:name')
]), 'The name of the queue')
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('response')
->inject('publisher')
->action(function (string $name, int|string $threshold, Response $response, Publisher $publisher) {
->inject('queue')
->action(function (string $name, int|string $threshold, Response $response, Connection $queue) {
$threshold = \intval($threshold);
$failed = $publisher->getQueueSize(new Queue($name), failedJobs: true);
$client = new Client($name, $queue);
$failed = $client->countFailedJobs();
if ($failed >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue failed jobs threshold hit. Current size is {$failed} and threshold is {$threshold}.");
+2 -2
View File
@@ -118,7 +118,7 @@ App::get('/v1/locale/countries')
->inject('response')
->inject('locale')
->action(function (Response $response, Locale $locale) {
$list = array_keys(Config::getParam('locale-countries')); /* @var $list array */
$list = Config::getParam('locale-countries'); /* @var $list array */
$output = [];
foreach ($list as $value) {
@@ -229,7 +229,7 @@ App::get('/v1/locale/continents')
->inject('response')
->inject('locale')
->action(function (Response $response, Locale $locale) {
$list = array_keys(Config::getParam('locale-continents'));
$list = Config::getParam('locale-continents');
foreach ($list as $value) {
$output[] = new Document([
+26 -34
View File
@@ -994,15 +994,13 @@ App::get('/v1/messaging/providers/:providerId/logs')
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'))),
]);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'provider/' . $providerId;
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$output = [];
foreach ($logs as $i => &$log) {
@@ -1049,7 +1047,7 @@ App::get('/v1/messaging/providers/:providerId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource, $queries),
'total' => $audit->countLogsByResource($resource),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -2224,15 +2222,13 @@ App::get('/v1/messaging/topics/:topicId/logs')
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'))),
]);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'topic/' . $topicId;
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$output = [];
@@ -2280,7 +2276,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource, $queries),
'total' => $audit->countLogsByResource($resource),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -2636,15 +2632,13 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
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'))),
]);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'subscriber/' . $subscriberId;
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$output = [];
@@ -2692,7 +2686,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource, $queries),
'total' => $audit->countLogsByResource($resource),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -2936,7 +2930,7 @@ App::post('/v1/messaging/messages/email')
break;
case MessageStatus::SCHEDULED:
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@@ -3058,7 +3052,7 @@ App::post('/v1/messaging/messages/sms')
break;
case MessageStatus::SCHEDULED:
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@@ -3275,7 +3269,7 @@ App::post('/v1/messaging/messages/push')
break;
case MessageStatus::SCHEDULED:
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@@ -3403,15 +3397,13 @@ App::get('/v1/messaging/messages/:messageId/logs')
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'))),
]);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'message/' . $messageId;
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$output = [];
@@ -3459,7 +3451,7 @@ App::get('/v1/messaging/messages/:messageId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource, $queries),
'total' => $audit->countLogsByResource($resource),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
@@ -3661,7 +3653,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@@ -3862,7 +3854,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
@@ -4035,7 +4027,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
if (\is_null($currentScheduledAt) && !\is_null($scheduledAt)) {
$schedule = $dbForPlatform->createDocument('schedules', new Document([
'region' => $project->getAttribute('region'),
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => 'message',
'resourceId' => $message->getId(),
'resourceInternalId' => $message->getInternalId(),
+3 -3
View File
@@ -48,9 +48,9 @@ App::post('/v1/migrations/appwrite')
]
))
->param('resources', [], new ArrayList(new WhiteList(Appwrite::getSupportedResources())), 'List of resources to migrate')
->param('endpoint', '', new URL(), 'Source Appwrite endpoint')
->param('projectId', '', new UID(), 'Source Project ID')
->param('apiKey', '', new Text(512), 'Source API Key')
->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")
->inject('response')
->inject('dbForProject')
->inject('project')
+4 -16
View File
@@ -40,18 +40,14 @@ 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, Document $project, Database $dbForProject, callable $getLogsDB, array $smsRates) {
->action(function (string $startDate, string $endDate, string $period, Response $response, Database $dbForProject, 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,
@@ -67,7 +63,6 @@ App::get('/v1/project/usage')
METRIC_BUILDS_STORAGE,
METRIC_DATABASES_OPERATIONS_READS,
METRIC_DATABASES_OPERATIONS_WRITES,
METRIC_FILES_IMAGES_TRANSFORMED,
],
'period' => [
METRIC_NETWORK_REQUESTS,
@@ -80,7 +75,6 @@ App::get('/v1/project/usage')
METRIC_BUILDS_MB_SECONDS,
METRIC_DATABASES_OPERATIONS_READS,
METRIC_DATABASES_OPERATIONS_WRITES,
METRIC_FILES_IMAGES_TRANSFORMED,
]
];
@@ -99,11 +93,9 @@ App::get('/v1/project/usage')
'1d' => 'Y-m-d\T00:00:00.000P',
};
Authorization::skip(function () use ($dbForProject, $dbForLogs, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) {
Authorization::skip(function () use ($dbForProject, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) {
foreach ($metrics['total'] as $metric) {
$db = ($metric === METRIC_FILES_IMAGES_TRANSFORMED) ? $dbForLogs : $dbForProject;
$result = $db->findOne('stats', [
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
@@ -111,9 +103,7 @@ App::get('/v1/project/usage')
}
foreach ($metrics['period'] as $metric) {
$db = ($metric === METRIC_FILES_IMAGES_TRANSFORMED) ? $dbForLogs : $dbForProject;
$results = $db->find('stats', [
$results = $dbForProject->find('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', [$period]),
Query::greaterThanEqual('time', $firstDay),
@@ -373,8 +363,6 @@ 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);
});
-2
View File
@@ -130,8 +130,6 @@ App::post('/v1/proxy/rules')
'resourceId' => $resourceId,
'resourceInternalId' => $resourceInternalId,
'certificateId' => '',
'owner' => '',
'region' => $project->getAttribute('region')
]);
$status = 'created';
+22 -23
View File
@@ -6,6 +6,7 @@ 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;
@@ -934,11 +935,15 @@ 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')
->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) {
->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) {
if (!\extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
@@ -1066,19 +1071,22 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg'];
//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));
}
$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));
}
$response
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType($contentType)
->file($data);
->file($data)
;
unset($image);
});
@@ -1871,12 +1879,9 @@ 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')
->inject('getLogsDB')
->action(function (string $bucketId, string $range, Response $response, Document $project, Database $dbForProject, callable $getLogsDB) {
->action(function (string $bucketId, string $range, Response $response, Database $dbForProject) {
$dbForLogs = call_user_func($getLogsDB, $project);
$bucket = $dbForProject->getDocument('buckets', $bucketId);
if ($bucket->isEmpty()) {
@@ -1889,16 +1894,12 @@ 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, $dbForLogs, $bucket, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$db = ($metric === str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED))
? $dbForLogs
: $dbForProject;
$result = $db->findOne('stats', [
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
@@ -1906,7 +1907,7 @@ App::get('/v1/storage/:bucketId/usage')
$stats[$metric]['total'] = $result['value'] ?? 0;
$limit = $days['limit'];
$period = $days['period'];
$results = $db->find('stats', [
$results = $dbForProject->find('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', [$period]),
Query::limit($limit),
@@ -1947,7 +1948,5 @@ 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);
});
+23 -27
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\StatsUsage;
use Appwrite\Event\Usage;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Platform\Workers\Deletes;
@@ -466,15 +466,15 @@ App::post('/v1/teams/:teamId/memberships')
->inject('queueForMessaging')
->inject('queueForEvents')
->inject('timelimit')
->inject('queueForStatsUsage')
->inject('queueForUsage')
->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, StatsUsage $queueForStatsUsage, array $plan) {
$isAppUser = Auth::isAppUser(Authorization::getRoles());
->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) {
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$url = htmlentities($url);
if (empty($url)) {
if (!$isAppUser && !$isPrivilegedUser) {
if (!$isAPIKey && !$isPrivilegedUser) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'URL is required');
}
}
@@ -482,13 +482,15 @@ App::post('/v1/teams/:teamId/memberships')
if (empty($userId) && empty($email) && empty($phone)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'At least one of userId, email, or phone is required');
}
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAppUser = Auth::isAppUser(Authorization::getRoles());
if (!$isPrivilegedUser && !$isAppUser && empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED);
}
$email = \strtolower($email);
$name = empty($name) ? $email : $name;
$name = (empty($name)) ? $email : $name;
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -507,7 +509,7 @@ App::post('/v1/teams/:teamId/memberships')
}
$email = $invitee->getAttribute('email', '');
$phone = $invitee->getAttribute('phone', '');
$name = $invitee->getAttribute('name', '') ?: $name;
$name = empty($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,8 +590,9 @@ 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,
@@ -615,13 +618,9 @@ 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));
if ($isPrivilegedUser || $isAppUser) {
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
}
} elseif ($membership->getAttribute('confirm') === false) {
$membership->setAttribute('secret', Auth::hash($secret));
} else {
$membership->setAttribute('invited', DateTime::now());
if ($isPrivilegedUser || $isAppUser) {
@@ -632,15 +631,14 @@ 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 {
$url = Template::parseURL($url);
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['membershipId' => $membership->getId(), 'userId' => $invitee->getId(), 'secret' => $secret, 'teamId' => $teamId, 'teamName' => $team->getAttribute('name')]);
$url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['membershipId' => $membership->getId(), 'userId' => $invitee->getId(), 'secret' => $secret, 'teamId' => $teamId]);
$url = Template::unParseURL($url);
if (!empty($email)) {
$projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]');
@@ -718,7 +716,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();
@@ -761,11 +759,11 @@ App::post('/v1/teams/:teamId/memberships')
$countryCode = $helper->parse($phone)->getCountryCode();
if (!empty($countryCode)) {
$queueForStatsUsage
$queueForUsage
->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1);
}
}
$queueForStatsUsage
$queueForUsage
->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
->setProject($project)
->trigger();
@@ -1365,15 +1363,13 @@ App::get('/v1/teams/:teamId/logs')
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'))),
]);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$resource = 'team/' . $team->getId();
$logs = $audit->getLogsByResource($resource, $queries);
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$output = [];
@@ -1420,7 +1416,7 @@ App::get('/v1/teams/:teamId/logs')
}
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource, $queries),
'total' => $audit->countLogsByResource($resource),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
+5 -7
View File
@@ -862,15 +862,13 @@ App::get('/v1/users/:userId/logs')
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'))),
]);
$grouped = Query::groupByType($queries);
$limit = $grouped['limit'] ?? APP_LIMIT_COUNT;
$offset = $grouped['offset'] ?? 0;
$audit = new Audit($dbForProject);
$logs = $audit->getLogsByUser($user->getInternalId(), $queries);
$logs = $audit->getLogsByUser($user->getInternalId(), $limit, $offset);
$output = [];
@@ -917,7 +915,7 @@ App::get('/v1/users/:userId/logs')
}
$response->dynamic(new Document([
'total' => $audit->countLogsByUser($user->getInternalId(), $queries),
'total' => $audit->countLogsByUser($user->getInternalId()),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});
+28 -47
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\StatsUsage;
use Appwrite\Event\Usage;
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, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname)
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)
{
$utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml');
@@ -434,7 +434,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
}
$queueForStatsUsage
$queueForUsage
->addMetric(METRIC_NETWORK_REQUESTS, 1)
->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize)
->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize())
@@ -499,13 +499,13 @@ App::init()
->inject('localeCodes')
->inject('clients')
->inject('geodb')
->inject('queueForStatsUsage')
->inject('queueForUsage')
->inject('queueForEvents')
->inject('queueForCertificates')
->inject('queueForFunctions')
->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, StatsUsage $queueForStatsUsage, 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, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked, string $previewHostname) {
/*
* Appwrite Router
*/
@@ -513,7 +513,7 @@ App::init()
$mainDomain = System::getEnv('_APP_DOMAIN', '');
// Only run Router when external domain
if ($host !== $mainDomain || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) {
return;
}
}
@@ -582,12 +582,6 @@ 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
@@ -595,10 +589,8 @@ App::init()
'domain' => $domain->get(),
'resourceType' => 'api',
'status' => 'verifying',
'projectId' => $console->getId(),
'projectInternalId' => $console->getInternalId(),
'owner' => $owner,
'region' => $console->getAttribute('region')
'projectId' => 'console',
'projectInternalId' => 'console'
]);
$domainDocument = $dbForPlatform->createDocument('rules', $domainDocument);
@@ -740,13 +732,12 @@ App::options()
->inject('dbForPlatform')
->inject('getProjectDB')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('queueForUsage')
->inject('queueForFunctions')
->inject('geodb')
->inject('isResourceBlocked')
->inject('previewHostname')
->inject('project')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project) {
->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) {
/*
* Appwrite Router
*/
@@ -754,7 +745,7 @@ App::options()
$mainDomain = System::getEnv('_APP_DOMAIN', '');
// Only run Router when external domain
if ($host !== $mainDomain || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) {
return;
}
}
@@ -769,16 +760,6 @@ 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()
@@ -789,8 +770,8 @@ App::error()
->inject('project')
->inject('logger')
->inject('log')
->inject('queueForStatsUsage')
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage) {
->inject('queueForUsage')
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage) {
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$route = $utopia->getRoute();
$class = \get_class($error);
@@ -867,9 +848,11 @@ App::error()
$publish = $error->getCode() === 0 || $error->getCode() >= 500;
}
$providerConfig = System::getEnv('_APP_EXPERIMENT_LOGGING_CONFIG', '');
if (!empty($providerConfig) && $error->getCode() >= 400 && $error->getCode() < 500) {
if ($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 ?? '');
$providerName = $loggingProvider->getScheme();
@@ -891,10 +874,7 @@ App::error()
}
}
/**
* 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 ($publish && $project->getId() !== 'console') {
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
$fileSize = 0;
$file = $request->getFiles('file');
@@ -902,17 +882,18 @@ App::error()
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
}
$queueForStatsUsage
$queueForUsage
->addMetric(METRIC_NETWORK_REQUESTS, 1)
->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize)
->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize());
}
$queueForStatsUsage
$queueForUsage
->setProject($project)
->trigger();
}
if ($logger && $publish) {
try {
/** @var Utopia\Database\Document $user */
@@ -1059,12 +1040,12 @@ App::get('/robots.txt')
->inject('dbForPlatform')
->inject('getProjectDB')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('queueForUsage')
->inject('queueForFunctions')
->inject('geodb')
->inject('isResourceBlocked')
->inject('previewHostname')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $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) {
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
@@ -1072,7 +1053,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, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname);
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname);
}
});
@@ -1087,12 +1068,12 @@ App::get('/humans.txt')
->inject('dbForPlatform')
->inject('getProjectDB')
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('queueForUsage')
->inject('queueForFunctions')
->inject('geodb')
->inject('isResourceBlocked')
->inject('previewHostname')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $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) {
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
@@ -1100,7 +1081,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, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname);
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname);
}
});
@@ -1167,7 +1148,7 @@ App::get('/v1/ping')
->inject('dbForPlatform')
->inject('queueForEvents')
->action(function (Response $response, Document $project, Database $dbForPlatform, Event $queueForEvents) {
if ($project->isEmpty() || $project->getId() === 'console') {
if ($project->isEmpty()) {
throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND);
}
+147 -100
View File
@@ -1,7 +1,8 @@
<?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;
@@ -11,11 +12,10 @@ use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Messaging;
use Appwrite\Event\Realtime;
use Appwrite\Event\StatsUsage;
use Appwrite\Event\Usage;
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;
@@ -28,7 +28,7 @@ use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Queue\Publisher;
use Utopia\Queue\Connection;
use Utopia\System\System;
use Utopia\Validator\WhiteList;
@@ -90,7 +90,7 @@ $eventDatabaseListener = function (Document $project, Document $document, Respon
}
};
$usageDatabaseListener = function (string $event, Document $document, StatsUsage $queueForStatsUsage) {
$usageDatabaseListener = function (string $event, Document $document, Usage $queueForUsage) {
$value = 1;
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$value = -1;
@@ -98,72 +98,80 @@ $usageDatabaseListener = function (string $event, Document $document, StatsUsage
switch (true) {
case $document->getCollection() === 'teams':
$queueForStatsUsage->addMetric(METRIC_TEAMS, $value); // per project
$queueForUsage
->addMetric(METRIC_TEAMS, $value); // per project
break;
case $document->getCollection() === 'users':
$queueForStatsUsage->addMetric(METRIC_USERS, $value); // per project
$queueForUsage
->addMetric(METRIC_USERS, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForStatsUsage->addReduce($document);
$queueForUsage
->addReduce($document);
}
break;
case $document->getCollection() === 'sessions': // sessions
$queueForStatsUsage->addMetric(METRIC_SESSIONS, $value); //per project
$queueForUsage
->addMetric(METRIC_SESSIONS, $value); //per project
break;
case $document->getCollection() === 'databases': // databases
$queueForStatsUsage->addMetric(METRIC_DATABASES, $value); // per project
$queueForUsage
->addMetric(METRIC_DATABASES, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForStatsUsage->addReduce($document);
$queueForUsage
->addReduce($document);
}
break;
case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections
$parts = explode('_', $document->getCollection());
$databaseInternalId = $parts[1] ?? 0;
$queueForStatsUsage
$queueForUsage
->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) {
$queueForStatsUsage->addReduce($document);
$queueForUsage
->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;
$queueForStatsUsage
$queueForUsage
->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
$queueForStatsUsage
$queueForUsage
->addMetric(METRIC_BUCKETS, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForStatsUsage
$queueForUsage
->addReduce($document);
}
break;
case str_starts_with($document->getCollection(), 'bucket_'): // files
$parts = explode('_', $document->getCollection());
$bucketInternalId = $parts[1];
$queueForStatsUsage
$queueForUsage
->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':
$queueForStatsUsage
$queueForUsage
->addMetric(METRIC_FUNCTIONS, $value); // per project
if ($event === Database::EVENT_DOCUMENT_DELETE) {
$queueForStatsUsage
$queueForUsage
->addReduce($document);
}
break;
case $document->getCollection() === 'deployments':
$queueForStatsUsage
$queueForUsage
->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
@@ -187,79 +195,117 @@ App::init()
->inject('servers')
->inject('mode')
->inject('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) {
->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team) {
$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);
}
if ($apiKey->isExpired()) {
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
// Remove after migration
if (!\str_contains($apiKey, '_')) {
$keyType = API_KEY_STANDARD;
$authKey = $apiKey;
} else {
[ $keyType, $authKey ] = \explode('_', $apiKey, 2);
}
$role = $apiKey->getRole();
$scopes = $apiKey->getScopes();
if ($keyType === API_KEY_DYNAMIC) {
// Dynamic key
// Disable authorization checks for API keys
Authorization::setDefaultStatus(false);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 86400, 0);
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(),
]);
try {
$payload = $jwtObj->decode($authKey);
} catch (JWTException $error) {
throw new Exception(Exception::API_KEY_EXPIRED);
}
$queueForAudits->setUser($user);
}
$projectId = $payload['projectId'] ?? '';
$tokenScopes = $payload['scopes'] ?? [];
if ($apiKey->getType() === API_KEY_STANDARD) {
$dbKey = $project->find(
key: 'secret',
find: $request->getHeader('x-appwrite-key', ''),
subject: 'keys'
);
// 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',
]);
if ($dbKey) {
$accessedAt = $dbKey->getAttribute('accessedAt', '');
$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);
}
} 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'),
]);
$role = Auth::USER_ROLE_APPS;
$scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
$expire = $key->getAttribute('expire');
if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) {
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
}
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) {
$dbKey->setAttribute('accessedAt', DateTime::now());
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
$key->setAttribute('accessedAt', DateTime::now());
$dbForPlatform->updateDocument('keys', $key->getId(), $key);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdkValidator->isValid($sdk)) {
$sdks = $dbKey->getAttribute('sdks', []);
$sdks = $key->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
$sdks[] = $sdk;
$dbKey->setAttribute('sdks', $sdks);
array_push($sdks, $sdk);
$key->setAttribute('sdks', $sdks);
/** Update access time as well */
$dbKey->setAttribute('accessedAt', Datetime::now());
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
$key->setAttribute('accessedAt', Datetime::now());
$dbForPlatform->updateDocument('keys', $key->getId(), $key);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
}
@@ -267,7 +313,8 @@ App::init()
$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 = [];
@@ -283,7 +330,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']);
}
@@ -298,7 +345,9 @@ 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) {
@@ -307,7 +356,9 @@ 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) {
@@ -321,18 +372,18 @@ App::init()
}
}
/** Do not allow access to disabled services */
/**
* @var ?Method $method
* @var ?\Appwrite\SDK\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]
@@ -342,13 +393,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);
}
@@ -378,19 +429,18 @@ App::init()
->inject('response')
->inject('project')
->inject('user')
->inject('publisher')
->inject('queue')
->inject('queueForEvents')
->inject('queueForMessaging')
->inject('queueForAudits')
->inject('queueForDeletes')
->inject('queueForDatabase')
->inject('queueForBuilds')
->inject('queueForStatsUsage')
->inject('queueForUsage')
->inject('dbForProject')
->inject('timelimit')
->inject('mode')
->inject('apiKey')
->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) use ($usageDatabaseListener, $eventDatabaseListener) {
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Connection $queue, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, callable $timelimit, string $mode) use ($usageDatabaseListener, $eventDatabaseListener) {
$route = $utopia->getRoute();
@@ -421,7 +471,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;
}
@@ -487,12 +537,6 @@ 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);
@@ -500,14 +544,14 @@ App::init()
// Clone the queues, to prevent events triggered by the database listener
// from overwriting the events that are supposed to be triggered in the shutdown hook.
$queueForEventsClone = new Event($publisher);
$queueForFunctions = new Func($publisher);
$queueForWebhooks = new Webhook($publisher);
$queueForEventsClone = new Event($queue);
$queueForFunctions = new Func($queue);
$queueForWebhooks = new Webhook($queue);
$queueForRealtime = new Realtime();
$dbForProject
->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
->on(Database::EVENT_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, 'create-trigger-events', fn ($event, $document) => $eventDatabaseListener(
$project,
$document,
@@ -534,9 +578,12 @@ App::init()
if ($type === 'bucket') {
$bucketId = $parts[1] ?? null;
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) {
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
@@ -560,13 +607,11 @@ App::init()
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
//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));
}
$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));
}
}
@@ -580,7 +625,8 @@ App::init()
->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')
;
}
}
});
@@ -648,7 +694,7 @@ App::shutdown()
->inject('user')
->inject('queueForEvents')
->inject('queueForAudits')
->inject('queueForStatsUsage')
->inject('queueForUsage')
->inject('queueForDeletes')
->inject('queueForDatabase')
->inject('queueForBuilds')
@@ -657,7 +703,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, StatsUsage $queueForStatsUsage, 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, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject) use ($parseLabel) {
$responsePayload = $response->getPayload();
@@ -741,7 +787,6 @@ App::shutdown()
foreach ($queueForEvents->getParams() as $key => $value) {
$queueForAudits->setParam($key, $value);
}
$queueForAudits->trigger();
}
@@ -761,7 +806,9 @@ App::shutdown()
$queueForMessaging->trigger();
}
// Cache label
/**
* Cache label
*/
$useCache = $route->getLabel('cache', false);
if ($useCache) {
$resource = $resourceType = null;
@@ -813,13 +860,13 @@ App::shutdown()
$fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size'];
}
$queueForStatsUsage
$queueForUsage
->addMetric(METRIC_NETWORK_REQUESTS, 1)
->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize)
->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize());
}
$queueForStatsUsage
$queueForUsage
->setProject($project)
->trigger();
}
+80 -135
View File
@@ -17,6 +17,7 @@ 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;
@@ -155,79 +156,6 @@ 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');
@@ -236,73 +164,94 @@ $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', []);
// 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();
$consoleCollections = $collections['console'];
foreach ($consoleCollections as $key => $collection) {
if (($collection['$collection'] ?? '') !== Database::METADATA) {
continue;
}
if (!$dbForPlatform->getCollection($key)->isEmpty()) {
continue;
}
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',
]));
Console::success('[Setup] - Creating console collection: ' . $collection['$id'] . '...');
$bucket = $dbForPlatform->getDocument('buckets', 'default');
$attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']);
$indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']);
Console::info(" └── Creating files collection for default bucket...");
$files = $collections['buckets']['files'] ?? [];
if (empty($files)) {
throw new Exception('Files collection is not configured.');
}
$dbForPlatform->createCollection($key, $attributes, $indexes);
}
$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']);
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',
]));
$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']);
$bucket = $dbForPlatform->getDocument('buckets', 'default');
$dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $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.');
}
});
$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', ''));
@@ -330,11 +279,6 @@ $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;
@@ -353,6 +297,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
}
$pools->reclaim();
Console::success('[Setup] - Server database init completed...');
});
+1861 -9
View File
File diff suppressed because it is too large Load Diff
-37
View File
@@ -1,37 +0,0 @@
<?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
@@ -1,239 +0,0 @@
<?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
@@ -1,404 +0,0 @@
<?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
@@ -1,43 +0,0 @@
<?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
@@ -1,23 +0,0 @@
<?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
@@ -1,340 +0,0 @@
<?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();
});
-824
View File
@@ -1,824 +0,0 @@
<?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 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\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\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('cache', function (Group $pools) {
$list = Config::getParam('pools-cache', []);
$adapters = [];
foreach ($list as $value) {
$adapters[] = $pools
->get($value)
->pop()
->getResource()
;
}
return new Cache(new Sharding($adapters));
}, ['pools']);
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:
return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl, $url);
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', '');
return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl, $s3EndpointUrl);
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']);
+11 -76
View File
@@ -116,7 +116,6 @@ $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
@@ -148,7 +147,6 @@ $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
@@ -165,10 +163,14 @@ $image = $this->getParam('image', '');
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
- _APP_ASSISTANT_OPENAI_API_KEY
- _APP_SLOW_QUERIES_MAX_HITS
- _APP_SLOW_QUERIES_TIMEOUT
- _APP_SLOW_QUERIES
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: <?php echo $organization; ?>/console:5.2.53
image: <?php echo $organization; ?>/console:5.2.27
restart: unless-stopped
networks:
- appwrite
@@ -319,7 +321,6 @@ $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
@@ -341,10 +342,7 @@ $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"; ?>
@@ -418,7 +416,6 @@ $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
@@ -574,7 +571,6 @@ $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
@@ -653,73 +649,13 @@ $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-task-stats-resources:
appwrite-worker-usage:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
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:
- appwrite
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_CONFIG
- _APP_STATS_RESOURCES_INTERVAL
appwrite-worker-stats-usage:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-stats-usage
container_name: appwrite-worker-stats-usage
entrypoint: worker-usage
container_name: appwrite-worker-usage
<<: *x-logging
restart: unless-stopped
networks:
@@ -744,11 +680,11 @@ $image = $this->getParam('image', '');
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
appwrite-worker-stats-usage-dump:
appwrite-worker-usage-dump:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: worker-stats-usage-dump
entrypoint: worker-usage-dump
<<: *x-logging
container_name: appwrite-worker-stats-usage-dump
container_name: appwrite-worker-usage-dump
restart: unless-stopped
networks:
- appwrite
@@ -889,7 +825,6 @@ $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
+49 -103
View File
@@ -13,10 +13,8 @@ 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\StatsUsageDump;
use Appwrite\Event\Webhook;
use Appwrite\Event\Usage;
use Appwrite\Event\UsageDump;
use Appwrite\Platform\Appwrite;
use Swoole\Runtime;
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
@@ -33,8 +31,8 @@ use Utopia\Logger\Log;
use Utopia\Logger\Logger;
use Utopia\Platform\Service;
use Utopia\Pools\Group;
use Utopia\Queue\Connection;
use Utopia\Queue\Message;
use Utopia\Queue\Publisher;
use Utopia\Queue\Server;
use Utopia\Registry\Registry;
use Utopia\System\System;
@@ -110,8 +108,6 @@ 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']);
@@ -173,58 +169,20 @@ 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); // 1 day
return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400);
});
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('auditRetention', function () {
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600));
});
Server::setResource('executionRetention', function () {
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); // 14 days
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600));
});
Server::setResource('cache', function (Registry $register) {
@@ -266,69 +224,57 @@ Server::setResource('timelimit', function (\Redis $redis) {
Server::setResource('log', fn () => new Log());
Server::setResource('publisher', function (Group $pools) {
return $pools->get('publisher')->pop()->getResource();
Server::setResource('queueForUsage', function (Connection $queue) {
return new Usage($queue);
}, ['queue']);
Server::setResource('queueForUsageDump', function (Connection $queue) {
return new UsageDump($queue);
}, ['queue']);
Server::setResource('queue', function (Group $pools) {
return $pools->get('queue')->pop()->getResource();
}, ['pools']);
Server::setResource('consumer', function (Group $pools) {
return $pools->get('consumer')->pop()->getResource();
}, ['pools']);
Server::setResource('queueForDatabase', function (Connection $queue) {
return new EventDatabase($queue);
}, ['queue']);
Server::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
Server::setResource('queueForMessaging', function (Connection $queue) {
return new Messaging($queue);
}, ['queue']);
Server::setResource('queueForStatsUsageDump', function (Publisher $publisher) {
return new StatsUsageDump($publisher);
}, ['publisher']);
Server::setResource('queueForMails', function (Connection $queue) {
return new Mail($queue);
}, ['queue']);
Server::setResource('queueForDatabase', function (Publisher $publisher) {
return new EventDatabase($publisher);
}, ['publisher']);
Server::setResource('queueForBuilds', function (Connection $queue) {
return new Build($queue);
}, ['queue']);
Server::setResource('queueForMessaging', function (Publisher $publisher) {
return new Messaging($publisher);
}, ['publisher']);
Server::setResource('queueForDeletes', function (Connection $queue) {
return new Delete($queue);
}, ['queue']);
Server::setResource('queueForMails', function (Publisher $publisher) {
return new Mail($publisher);
}, ['publisher']);
Server::setResource('queueForEvents', function (Connection $queue) {
return new Event($queue);
}, ['queue']);
Server::setResource('queueForBuilds', function (Publisher $publisher) {
return new Build($publisher);
}, ['publisher']);
Server::setResource('queueForAudits', function (Connection $queue) {
return new Audit($queue);
}, ['queue']);
Server::setResource('queueForDeletes', function (Publisher $publisher) {
return new Delete($publisher);
}, ['publisher']);
Server::setResource('queueForFunctions', function (Connection $queue) {
return new Func($queue);
}, ['queue']);
Server::setResource('queueForEvents', function (Publisher $publisher) {
return new Event($publisher);
}, ['publisher']);
Server::setResource('queueForCertificates', function (Connection $queue) {
return new Certificate($queue);
}, ['queue']);
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']);
Server::setResource('queueForMigrations', function (Publisher $publisher) {
return new Migration($publisher);
}, ['publisher']);
Server::setResource('queueForMigrations', function (Connection $queue) {
return new Migration($queue);
}, ['queue']);
Server::setResource('logger', function (Registry $register) {
return $register->get('logger');
@@ -440,7 +386,7 @@ try {
*/
$platform->init(Service::TYPE_WORKER, [
'workersNum' => System::getEnv('_APP_WORKERS_NUM', 1),
'connection' => $pools->get('consumer')->pop()->getResource(),
'connection' => $pools->get('queue')->pop()->getResource(),
'workerName' => strtolower($workerName) ?? null,
'queueName' => $queueName
]);
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
php /usr/src/code/app/cli.php stats-resources $@
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
php /usr/src/code/app/worker.php stats-resources $@
-3
View File
@@ -1,3 +0,0 @@
#!/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 stats-usage-dump $@
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/worker.php usage $@
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/worker.php usage-dump $@
+18 -18
View File
@@ -30,7 +30,7 @@
}
},
"require": {
"php": ">=8.0.0",
"php": ">=8.3",
"ext-curl": "*",
"ext-imagick": "*",
"ext-mbstring": "*",
@@ -45,33 +45,33 @@
"ext-sockets": "*",
"appwrite/php-runtimes": "0.16.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "0.52.*",
"utopia-php/abuse": "0.47.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.55.*",
"utopia-php/cache": "0.12.*",
"utopia-php/audit": "0.47.*",
"utopia-php/cache": "0.11.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "dev-bind-params as 0.64.999",
"utopia-php/database": "0.56.4",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
"utopia-php/fetch": "0.4.*",
"utopia-php/image": "0.8.*",
"utopia-php/fetch": "0.3.*",
"utopia-php/image": "0.7.*",
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.16.*",
"utopia-php/migration": "0.8.*",
"utopia-php/messaging": "0.14.*",
"utopia-php/migration": "0.6.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.8.*",
"utopia-php/platform": "0.7.1",
"utopia-php/pools": "0.5.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/queue": "0.9.*",
"utopia-php/queue": "0.7.*",
"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.9.*",
"utopia-php/vcs": "0.8.*",
"utopia-php/websocket": "0.1.*",
"matomo/device-detector": "6.1.*",
"dragonmantank/cron-expression": "3.3.2",
@@ -84,12 +84,12 @@
},
"require-dev": {
"ext-fileinfo": "*",
"appwrite/sdk-generator": "0.40.*",
"phpunit/phpunit": "9.*",
"appwrite/sdk-generator": "0.39.32",
"phpunit/phpunit": "9.5.20",
"swoole/ide-helper": "5.1.2",
"textalk/websocket": "1.5.*",
"laravel/pint": "1.*",
"phpbench/phpbench": "1.*"
"textalk/websocket": "1.5.7",
"laravel/pint": "^1.14",
"phpbench/phpbench": "^1.2"
},
"provide": {
"ext-phpiredis": "*"
Generated
+903 -544
View File
File diff suppressed because it is too large Load Diff
+10 -79
View File
@@ -140,7 +140,6 @@ 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
@@ -171,7 +170,6 @@ 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
@@ -198,11 +196,14 @@ services:
- _APP_DATABASE_SHARED_TABLES_V1
- _APP_DATABASE_SHARED_NAMESPACE
- _APP_FUNCTIONS_CREATION_ABUSE_LIMIT
- _APP_SLOW_QUERIES_MAX_HITS
- _APP_SLOW_QUERIES_TIMEOUT
- _APP_SLOW_QUERIES
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: appwrite/console:5.2.53
image: appwrite/console:5.2.27
restart: unless-stopped
networks:
- appwrite
@@ -367,7 +368,6 @@ 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
@@ -390,8 +390,6 @@ 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
@@ -471,7 +469,6 @@ 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
@@ -637,7 +634,6 @@ 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
@@ -724,47 +720,15 @@ 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-task-stats-resources:
container_name: appwrite-task-stats-resources
entrypoint: stats-resources
appwrite-worker-usage:
entrypoint: worker-usage
<<: *x-logging
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
container_name: appwrite-worker-usage
image: appwrite-dev
networks:
- appwrite
@@ -792,10 +756,10 @@ services:
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-stats-usage:
entrypoint: worker-stats-usage
appwrite-worker-usage-dump:
entrypoint: worker-usage-dump
<<: *x-logging
container_name: appwrite-worker-stats-usage
container_name: appwrite-worker-usage-dump
image: appwrite-dev
networks:
- appwrite
@@ -823,38 +787,6 @@ services:
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
appwrite-worker-stats-usage-dump:
entrypoint: worker-stats-usage-dump
<<: *x-logging
container_name: appwrite-worker-stats-usage-dump
image: appwrite-dev
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- redis
- mariadb
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
- _APP_STATS_USAGE_DUAL_WRITING_DBS
appwrite-task-scheduler-functions:
entrypoint: schedule-functions
<<: *x-logging
@@ -979,7 +911,6 @@ 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
@@ -0,0 +1,23 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Functions;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Functions functions = new Functions(client);
functions.getDeploymentDownload(
"<FUNCTION_ID>", // functionId
"<DEPLOYMENT_ID>", // deploymentId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -0,0 +1,22 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Functions;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Functions functions = new Functions(client);
functions.getTemplate(
"<TEMPLATE_ID>", // templateId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -0,0 +1,25 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Functions;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Functions functions = new Functions(client);
functions.listTemplates(
listOf(), // runtimes (optional)
listOf(), // useCases (optional)
1, // limit (optional)
0, // offset (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -0,0 +1,14 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Functions
val client = Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val functions = Functions(client)
val result = functions.getDeploymentDownload(
functionId = "<FUNCTION_ID>",
deploymentId = "<DEPLOYMENT_ID>",
)
@@ -0,0 +1,13 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Functions
val client = Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val functions = Functions(client)
val result = functions.getTemplate(
templateId = "<TEMPLATE_ID>",
)
@@ -0,0 +1,16 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Functions
val client = Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val functions = Functions(client)
val result = functions.listTemplates(
runtimes = listOf(), // (optional)
useCases = listOf(), // (optional)
limit = 1, // (optional)
offset = 0, // (optional)
)
@@ -6,7 +6,7 @@ let client = Client()
let account = Account(client)
let session = try await account.updateMfaChallenge(
let result = try await account.updateMfaChallenge(
challengeId: "<CHALLENGE_ID>",
otp: "<OTP>"
)
@@ -0,0 +1,13 @@
import Appwrite
let client = Client()
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
let functions = Functions(client)
let bytes = try await functions.getDeploymentDownload(
functionId: "<FUNCTION_ID>",
deploymentId: "<DEPLOYMENT_ID>"
)
@@ -0,0 +1,12 @@
import Appwrite
let client = Client()
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
let functions = Functions(client)
let templateFunction = try await functions.getTemplate(
templateId: "<TEMPLATE_ID>"
)
@@ -0,0 +1,15 @@
import Appwrite
let client = Client()
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
let functions = Functions(client)
let templateFunctionList = try await functions.listTemplates(
runtimes: [], // optional
useCases: [], // optional
limit: 1, // optional
offset: 0 // optional
)
@@ -6,7 +6,7 @@ Client client = Client()
Account account = Account(client);
Session result = await account.updateMfaChallenge(
result = await account.updateMfaChallenge(
challengeId: '<CHALLENGE_ID>',
otp: '<OTP>',
);
@@ -0,0 +1,29 @@
import 'package:appwrite/appwrite.dart';
Client client = Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
Functions functions = Functions(client);
// Downloading file
UInt8List bytes = await functions.getDeploymentDownload(
functionId: '<FUNCTION_ID>',
deploymentId: '<DEPLOYMENT_ID>',
)
final file = File('path_to_file/filename.ext');
file.writeAsBytesSync(bytes);
// Displaying image preview
FutureBuilder(
future: functions.getDeploymentDownload(
functionId:'<FUNCTION_ID>' ,
deploymentId:'<DEPLOYMENT_ID>' ,
), // Works for both public file and private file, for private files you need to be logged in
builder: (context, snapshot) {
return snapshot.hasData && snapshot.data != null
? Image.memory(snapshot.data)
: CircularProgressIndicator();
}
);
@@ -0,0 +1,11 @@
import 'package:appwrite/appwrite.dart';
Client client = Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
Functions functions = Functions(client);
TemplateFunction result = await functions.getTemplate(
templateId: '<TEMPLATE_ID>',
);
@@ -0,0 +1,14 @@
import 'package:appwrite/appwrite.dart';
Client client = Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
Functions functions = Functions(client);
TemplateFunctionList result = await functions.listTemplates(
runtimes: [], // optional
useCases: [], // optional
limit: 1, // optional
offset: 0, // optional
);
@@ -3,34 +3,6 @@ mutation {
challengeId: "<CHALLENGE_ID>",
otp: "<OTP>"
) {
_id
_createdAt
_updatedAt
userId
expire
provider
providerUid
providerAccessToken
providerAccessTokenExpiry
providerRefreshToken
ip
osCode
osName
osVersion
clientType
clientCode
clientName
clientVersion
clientEngine
clientEngineVersion
deviceName
deviceBrand
deviceModel
countryCode
countryName
current
factors
secret
mfaUpdatedAt
status
}
}
@@ -0,0 +1,8 @@
query {
functionsGetDeploymentDownload(
functionId: "<FUNCTION_ID>",
deploymentId: "<DEPLOYMENT_ID>"
) {
status
}
}
@@ -0,0 +1,35 @@
query {
functionsGetTemplate(
templateId: "<TEMPLATE_ID>"
) {
icon
id
name
tagline
permissions
events
cron
timeout
useCases
runtimes {
name
commands
entrypoint
providerRootDirectory
}
instructions
vcsProvider
providerRepositoryId
providerOwner
providerVersion
variables {
name
description
value
placeholder
required
type
}
scopes
}
}
@@ -0,0 +1,41 @@
query {
functionsListTemplates(
runtimes: [],
useCases: [],
limit: 1,
offset: 0
) {
total
templates {
icon
id
name
tagline
permissions
events
cron
timeout
useCases
runtimes {
name
commands
entrypoint
providerRootDirectory
}
instructions
vcsProvider
providerRepositoryId
providerOwner
providerVersion
variables {
name
description
value
placeholder
required
type
}
scopes
}
}
}
@@ -0,0 +1,14 @@
import { Client, Functions } from "react-native-appwrite";
const client = new Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const functions = new Functions(client);
const result = functions.getDeploymentDownload(
'<FUNCTION_ID>', // functionId
'<DEPLOYMENT_ID>' // deploymentId
);
console.log(result);
@@ -0,0 +1,13 @@
import { Client, Functions } from "react-native-appwrite";
const client = new Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const functions = new Functions(client);
const result = await functions.getTemplate(
'<TEMPLATE_ID>' // templateId
);
console.log(result);
@@ -0,0 +1,16 @@
import { Client, Functions } from "react-native-appwrite";
const client = new Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const functions = new Functions(client);
const result = await functions.listTemplates(
[], // runtimes (optional)
[], // useCases (optional)
1, // limit (optional)
0 // offset (optional)
);
console.log(result);
@@ -0,0 +1,8 @@
GET /v1/functions/{functionId}/deployments/{deploymentId}/download HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
X-Appwrite-Project: <YOUR_PROJECT_ID>
X-Appwrite-Session:
X-Appwrite-JWT: <YOUR_JWT>
@@ -0,0 +1,6 @@
GET /v1/functions/templates/{templateId} HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
X-Appwrite-Project: <YOUR_PROJECT_ID>
@@ -0,0 +1,6 @@
GET /v1/functions/templates HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.6.0
X-Appwrite-Project: <YOUR_PROJECT_ID>
@@ -0,0 +1,14 @@
import { Client, Functions } from "appwrite";
const client = new Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const functions = new Functions(client);
const result = functions.getDeploymentDownload(
'<FUNCTION_ID>', // functionId
'<DEPLOYMENT_ID>' // deploymentId
);
console.log(result);
@@ -0,0 +1,13 @@
import { Client, Functions } from "appwrite";
const client = new Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const functions = new Functions(client);
const result = await functions.getTemplate(
'<TEMPLATE_ID>' // templateId
);
console.log(result);
@@ -0,0 +1,16 @@
import { Client, Functions } from "appwrite";
const client = new Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const functions = new Functions(client);
const result = await functions.listTemplates(
[], // runtimes (optional)
[], // useCases (optional)
1, // limit (optional)
0 // offset (optional)
);
console.log(result);
@@ -0,0 +1,3 @@
appwrite functions downloadDeployment \
--functionId <FUNCTION_ID> \
--deploymentId <DEPLOYMENT_ID>
@@ -0,0 +1 @@
appwrite functions getSpecifications
@@ -0,0 +1,3 @@
appwrite migrations createFirebaseOAuthMigration \
--resources one two three \
--projectId <PROJECT_ID>

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