mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
499
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa837c7b43 | ||
|
|
3541fcb237 | ||
|
|
10a7f8acfe | ||
|
|
61ec98bc5e | ||
|
|
2d4e99cb1a | ||
|
|
8fe999d6d7 | ||
|
|
dade82706a | ||
|
|
0109ca218e | ||
|
|
43f0849979 | ||
|
|
b882a5d5b0 | ||
|
|
9c320f9c71 | ||
|
|
f8a75494b8 | ||
|
|
7f690a1048 | ||
|
|
8c3530ee60 | ||
|
|
5e294d0849 | ||
|
|
2a63de0c46 | ||
|
|
48a977526e | ||
|
|
75bf37a074 | ||
|
|
689fbee10c | ||
|
|
4cafd2e07d | ||
|
|
24a9183d90 | ||
|
|
84e3dc0275 | ||
|
|
d28e0152fd | ||
|
|
7a507917c9 | ||
|
|
048dbe81c7 | ||
|
|
19fe5881a4 | ||
|
|
55938fa3c3 | ||
|
|
16685dad4e | ||
|
|
783894a82b | ||
|
|
69dbb3222f | ||
|
|
d48aa60609 | ||
|
|
9f8fabe58a | ||
|
|
484ebb5059 | ||
|
|
10b82716e4 | ||
|
|
4c971910bb | ||
|
|
a00b93416d | ||
|
|
7c5dd9d0f4 | ||
|
|
4adebc8ba2 | ||
|
|
f5682c0516 | ||
|
|
c235870112 | ||
|
|
9929facd73 | ||
|
|
469ac59199 | ||
|
|
58d556aab5 | ||
|
|
596657ac26 | ||
|
|
b3ba91cd52 | ||
|
|
3d5b755115 | ||
|
|
26be1b5caf | ||
|
|
5d9be26a45 | ||
|
|
471d261114 | ||
|
|
59bebe6be9 | ||
|
|
5689fd6e7e | ||
|
|
2d7ae4329b | ||
|
|
81e656e42c | ||
|
|
588087eda6 | ||
|
|
da1fa09e9c | ||
|
|
476ea76814 | ||
|
|
d1c3a84ba1 | ||
|
|
5c0579fbc7 | ||
|
|
1ea9aca90f | ||
|
|
efc21d3dcf | ||
|
|
152882d05d | ||
|
|
a21e37b054 | ||
|
|
1b1c28d319 | ||
|
|
d2dcddaf13 | ||
|
|
5977e62e9a | ||
|
|
7ffa11c75d | ||
|
|
5b12a6c8b1 | ||
|
|
01c8dab34d | ||
|
|
811a1fa701 | ||
|
|
d76db6ec62 | ||
|
|
933f07c175 | ||
|
|
ccf0a23bdf | ||
|
|
66786f20f0 | ||
|
|
1731212ebb | ||
|
|
3db9cd06d5 | ||
|
|
3eb5cd80f3 | ||
|
|
7ad01fcada | ||
|
|
06e4920ee2 | ||
|
|
f89e46817f | ||
|
|
9b0a6bf33a | ||
|
|
b69ad22485 | ||
|
|
360e4ce992 | ||
|
|
2529d64791 | ||
|
|
5924434e1d | ||
|
|
1d736dec54 | ||
|
|
1f13a0d437 | ||
|
|
f2a3369131 | ||
|
|
1f26f98568 | ||
|
|
3c6291024b | ||
|
|
b23f382dcc | ||
|
|
9be01f2ac9 | ||
|
|
f39fe22e6f | ||
|
|
cf3abd0f6c | ||
|
|
aa91189848 | ||
|
|
30d18f6f09 | ||
|
|
eeed9a08ec | ||
|
|
4940dcbf3a | ||
|
|
6bd3e6fe96 | ||
|
|
91fda66d78 | ||
|
|
e67e6303b2 | ||
|
|
2f39e16b97 | ||
|
|
d0b04cd61b | ||
|
|
1d86cc6010 | ||
|
|
8d1bc10cc6 | ||
|
|
607cc9a926 | ||
|
|
e4dc36edf0 | ||
|
|
ce1ba58e73 | ||
|
|
d7f85f9f58 | ||
|
|
a8d5fc9998 | ||
|
|
fc31787e9a | ||
|
|
5ce141b75b | ||
|
|
ee5a644771 | ||
|
|
d607f227a2 | ||
|
|
3439951538 | ||
|
|
9055d41aa0 | ||
|
|
6c5cdfbe85 | ||
|
|
b762e7fb73 | ||
|
|
f70a5e67be | ||
|
|
e68acd3f02 | ||
|
|
2dfd2f4426 | ||
|
|
89b9afd066 | ||
|
|
32bb8b7c7e | ||
|
|
ed4e85f28a | ||
|
|
7ae237e433 | ||
|
|
cc8e04f4fa | ||
|
|
3cbded226b | ||
|
|
7ca03a5889 | ||
|
|
f75b53f3bc | ||
|
|
c5f8bdfd36 | ||
|
|
309f6ca1f7 | ||
|
|
01cd296132 | ||
|
|
b3124f7288 | ||
|
|
e8f53f27e3 | ||
|
|
1340d14e7c | ||
|
|
132b8f8234 | ||
|
|
715ae4857b | ||
|
|
69142048af | ||
|
|
8fe62a2585 | ||
|
|
a53dcd8a23 | ||
|
|
f8d6706fde | ||
|
|
8240c9ccdc | ||
|
|
b194558226 | ||
|
|
c29ef4087b | ||
|
|
f85a7410df | ||
|
|
c83dd7aa13 | ||
|
|
50eca9ebbc | ||
|
|
2a7ce13224 | ||
|
|
9c0e6f4203 | ||
|
|
bf3efea98d | ||
|
|
cd39bd6512 | ||
|
|
7bb760b4a1 | ||
|
|
9da57a457b | ||
|
|
8b88a09374 | ||
|
|
c35a7cc09b | ||
|
|
891db255bc | ||
|
|
f082cfacfa | ||
|
|
d32da05f92 | ||
|
|
8b90acd8c6 | ||
|
|
7fecfc73ca | ||
|
|
c55f039f13 | ||
|
|
d613237fb4 | ||
|
|
5742dad1e4 | ||
|
|
ea0654cf5e | ||
|
|
99ea9c5d80 | ||
|
|
712425c383 | ||
|
|
0cc443b47c | ||
|
|
c729a84d51 | ||
|
|
33435890dd | ||
|
|
0a5fbb7bc7 | ||
|
|
1e61091834 | ||
|
|
94decb26e6 | ||
|
|
a3f9707938 | ||
|
|
0a82b6d30d | ||
|
|
91f3c6e95e | ||
|
|
7b87decba4 | ||
|
|
0f38bb689f | ||
|
|
00889426de | ||
|
|
477682e062 | ||
|
|
054b6a995a | ||
|
|
a2ca08023d | ||
|
|
8a2e0d4970 | ||
|
|
51d07832f7 | ||
|
|
8c57d28f8f | ||
|
|
0bdb64fc77 | ||
|
|
32cb5e0a79 | ||
|
|
e24671f6f4 | ||
|
|
9bab764378 | ||
|
|
fd35043dd7 | ||
|
|
a7e164e019 | ||
|
|
100aea337f | ||
|
|
435a319fb6 | ||
|
|
18492ab917 | ||
|
|
c6f6b6db90 | ||
|
|
89d54f847f | ||
|
|
6dc6ff6ba0 | ||
|
|
581235f972 | ||
|
|
f02ba4c976 | ||
|
|
eaf9b8b228 | ||
|
|
11332ed1f9 | ||
|
|
990e1b91bb | ||
|
|
f202685450 | ||
|
|
9b90d8030e | ||
|
|
53086fe975 | ||
|
|
1d9aac5a49 | ||
|
|
ca61b528b9 | ||
|
|
50109b833a | ||
|
|
5ace34790d | ||
|
|
1a3f8bbd48 | ||
|
|
41fc925103 | ||
|
|
a756e533e8 | ||
|
|
449b9295fd | ||
|
|
3a298b7a21 | ||
|
|
13bd2341c0 | ||
|
|
c36720e3b0 | ||
|
|
9cc61d89f0 | ||
|
|
cc1129a7b9 | ||
|
|
e42275e0b7 | ||
|
|
5de2547cf6 | ||
|
|
26496e68c4 | ||
|
|
f7cbe5b961 | ||
|
|
bd63f0cf3d | ||
|
|
bc9caeaae0 | ||
|
|
a5a1860ba6 | ||
|
|
9d7e218e99 | ||
|
|
6d321f6988 | ||
|
|
61a27bfe55 | ||
|
|
7ca951ed81 | ||
|
|
020814e672 | ||
|
|
7ea896301f | ||
|
|
97d5f0f030 | ||
|
|
14e52058c7 | ||
|
|
067b53482f | ||
|
|
1a286ad3f4 | ||
|
|
80c4982482 | ||
|
|
a23c99ad7a | ||
|
|
ea238bc5ec | ||
|
|
b5035c7034 | ||
|
|
edd7a4ac42 | ||
|
|
f678add130 | ||
|
|
543b872f60 | ||
|
|
5fffae39d4 | ||
|
|
9dcb80649f | ||
|
|
18d30c48d5 | ||
|
|
0fb55f6312 | ||
|
|
1cf9963bb6 | ||
|
|
2545977d47 | ||
|
|
dbadfa7bf8 | ||
|
|
37c7411405 | ||
|
|
bc8b10a69e | ||
|
|
7bbf58d0dd | ||
|
|
df9ef1ace9 | ||
|
|
32903d9a81 | ||
|
|
131cdefac5 | ||
|
|
231225ee10 | ||
|
|
d205ac4579 | ||
|
|
5bb998b02d | ||
|
|
7d37f5b066 | ||
|
|
2a73dddbdb | ||
|
|
f224c09c10 | ||
|
|
6725b31b65 | ||
|
|
e721a9f11c | ||
|
|
72f08e281b | ||
|
|
431a26e151 | ||
|
|
172ba84ec8 | ||
|
|
7c0216c592 | ||
|
|
b6f11144b8 | ||
|
|
199b9f7e6d | ||
|
|
d9486f38c4 | ||
|
|
4747700eae | ||
|
|
58c2f55d1c | ||
|
|
41a1bb928e | ||
|
|
2cf7246c7a | ||
|
|
dd9096fd2b | ||
|
|
60158aec0c | ||
|
|
bb9a271fa8 | ||
|
|
ef2dfb6cca | ||
|
|
f62c3135df | ||
|
|
a8982bfb80 | ||
|
|
642d046d65 | ||
|
|
d38b25e0b1 | ||
|
|
9064fcc24e | ||
|
|
0f37a0e0ef | ||
|
|
253195698e | ||
|
|
55dc965dd6 | ||
|
|
2ce3118ba9 | ||
|
|
ce8ae2294a | ||
|
|
b1724e9c9d | ||
|
|
ed2d12f896 | ||
|
|
6f5d3d53c0 | ||
|
|
6d792fcb6c | ||
|
|
fc1a9a4e16 | ||
|
|
0e50fb7ec5 | ||
|
|
d34635becb | ||
|
|
eb1d4c68ea | ||
|
|
beec2e780d | ||
|
|
aa9a82d8ce | ||
|
|
421510f847 | ||
|
|
49a5e01a53 | ||
|
|
1cd69e26f8 | ||
|
|
99ccd64b4c | ||
|
|
b119ecf92c | ||
|
|
925ac33536 | ||
|
|
aa03045382 | ||
|
|
eea09c56f8 | ||
|
|
4d0bd1d74f | ||
|
|
991c86c937 | ||
|
|
385e4e14b8 | ||
|
|
67bdf8ffdc | ||
|
|
3fa04727c7 | ||
|
|
2da811956c | ||
|
|
8ef24cb2a9 | ||
|
|
e561a38524 | ||
|
|
a1af742c4b | ||
|
|
98dccf6d15 | ||
|
|
609e1adcf0 | ||
|
|
72cd501f21 | ||
|
|
58c1e2d6e2 | ||
|
|
ddb5c52e7c | ||
|
|
5959457211 | ||
|
|
beb8e17013 | ||
|
|
448a8df1a2 | ||
|
|
fd7628efe8 | ||
|
|
d6326758a8 | ||
|
|
2d00052db6 | ||
|
|
db56361ad9 | ||
|
|
843d4d9ea0 | ||
|
|
8a2ccd32b4 | ||
|
|
39db3a3066 | ||
|
|
9242ef85eb | ||
|
|
332c82e61e | ||
|
|
bfee98ca87 | ||
|
|
36aa22793f | ||
|
|
ae72688c8e | ||
|
|
63a02e11ac | ||
|
|
ba7903af55 | ||
|
|
cb19798284 | ||
|
|
156daca044 | ||
|
|
f056a8ba2f | ||
|
|
151711aba9 | ||
|
|
14a4bb8828 | ||
|
|
a36525373d | ||
|
|
5a48fd6300 | ||
|
|
8027251224 | ||
|
|
fffdab23f9 | ||
|
|
718cefdd94 | ||
|
|
ee225834d9 | ||
|
|
23f350c7d1 | ||
|
|
b12ea0b3b1 | ||
|
|
3a8dafe763 | ||
|
|
42a0d64137 | ||
|
|
df902d2624 | ||
|
|
a356e86ced | ||
|
|
bda9adc52e | ||
|
|
8dcc3c4981 | ||
|
|
368f928fa9 | ||
|
|
ee2796f870 | ||
|
|
d4b53175dc | ||
|
|
572ff06f53 | ||
|
|
87c100db28 | ||
|
|
918ad8979d | ||
|
|
2fa3ca27af | ||
|
|
1555abcbd6 | ||
|
|
f8e74d073a | ||
|
|
be40f1cbe3 | ||
|
|
043cead66b | ||
|
|
7e378a3a1e | ||
|
|
e13089b1ce | ||
|
|
9861f60666 | ||
|
|
06ce606ac7 | ||
|
|
9ab8097387 | ||
|
|
1dfba85bcc | ||
|
|
484d5ca4b4 | ||
|
|
204b00850d | ||
|
|
5761320bfb | ||
|
|
dbc7e5b42d | ||
|
|
22900e3de8 | ||
|
|
e5621f3218 | ||
|
|
276a294391 | ||
|
|
8bf3a8bf5d | ||
|
|
72956d1995 | ||
|
|
038b5d7418 | ||
|
|
8f5b53d16c | ||
|
|
d2753bd491 | ||
|
|
921b2bcc5d | ||
|
|
e8982e6b11 | ||
|
|
7508a22ddf | ||
|
|
620266d9d6 | ||
|
|
f0ea206f29 | ||
|
|
d52d8f7697 | ||
|
|
255be3babf | ||
|
|
e37cea5146 | ||
|
|
d7d7fa3096 | ||
|
|
92a04535ac | ||
|
|
c479a11850 | ||
|
|
3cd31e2c06 | ||
|
|
5173e03636 | ||
|
|
3fae6ae91c | ||
|
|
19152f50dc | ||
|
|
bfbcd648f1 | ||
|
|
f07abce6f8 | ||
|
|
975f653e3e | ||
|
|
c85cfe132e | ||
|
|
4fff9b94f8 | ||
|
|
4b5fa0652a | ||
|
|
f1590652cb | ||
|
|
df82340dae | ||
|
|
c268b632fb | ||
|
|
17428ef433 | ||
|
|
e712ba5763 | ||
|
|
1a9664a679 | ||
|
|
65b5af1b00 | ||
|
|
6dda6e1fda | ||
|
|
c331a3caae | ||
|
|
dbc58655b2 | ||
|
|
b646eb7758 | ||
|
|
fe080153e6 | ||
|
|
4759407696 | ||
|
|
213b1ae20e | ||
|
|
4e0faf67f7 | ||
|
|
3059eecbeb | ||
|
|
dd8894ad1e | ||
|
|
28f420f10c | ||
|
|
54748b7d32 | ||
|
|
b69ff36ddb | ||
|
|
c1bd493fbe | ||
|
|
1319bf7ec1 | ||
|
|
e56c03a007 | ||
|
|
d8c88fb420 | ||
|
|
68fc0f898f | ||
|
|
0f8c5fe292 | ||
|
|
e65ed58ca4 | ||
|
|
095159d89b | ||
|
|
179acec25c | ||
|
|
8379ea2469 | ||
|
|
2acb9c5b99 | ||
|
|
8b160d7dc0 | ||
|
|
509392c48b | ||
|
|
520006864d | ||
|
|
5a3e44a36d | ||
|
|
22cda269b1 | ||
|
|
e8e7ee01b4 | ||
|
|
b7ffd2ca94 | ||
|
|
9356fb4cb4 | ||
|
|
41bc8272ff | ||
|
|
ad60ff9972 | ||
|
|
8c8abfdece | ||
|
|
5e345c0388 | ||
|
|
362618c5af | ||
|
|
c0c30fe767 | ||
|
|
d7e2552999 | ||
|
|
7d54a6191d | ||
|
|
4d41c90367 | ||
|
|
96c035401b | ||
|
|
3ad5a2652e | ||
|
|
28db9a289b | ||
|
|
88d9bdd395 | ||
|
|
995482ee20 | ||
|
|
d2abdea6cf | ||
|
|
fc656167d5 | ||
|
|
37388fcf1b | ||
|
|
55be41f633 | ||
|
|
cee6420fa2 | ||
|
|
8bac836483 | ||
|
|
01aa9fbb48 | ||
|
|
d7c3100fe9 | ||
|
|
43f9254302 | ||
|
|
d799891f53 | ||
|
|
fe86a446da | ||
|
|
710b8bda56 | ||
|
|
dde7570072 | ||
|
|
ef12f49277 | ||
|
|
fa8e5b5b8e | ||
|
|
54552bf1b4 | ||
|
|
6530d31e7a | ||
|
|
45e5509113 | ||
|
|
e44d1b6600 | ||
|
|
2088792d09 | ||
|
|
4d66aeed5a | ||
|
|
848d38b75d | ||
|
|
7a465dc8f8 | ||
|
|
623369e426 | ||
|
|
fed881a611 | ||
|
|
ba4592609c | ||
|
|
1030053d5a | ||
|
|
6b8aa1544b | ||
|
|
c82d0b5dc3 | ||
|
|
f306e5e868 | ||
|
|
e27f1e1f6a | ||
|
|
e9c6ab491a | ||
|
|
8c2235816e | ||
|
|
d82812cc04 | ||
|
|
2427f950b5 | ||
|
|
e551e4ad5b | ||
|
|
67a313cef3 | ||
|
|
3c2b88dc86 | ||
|
|
944624f13a | ||
|
|
4ac2571135 | ||
|
|
0a4c430ae7 | ||
|
|
0da3602426 |
@@ -21,12 +21,15 @@ _APP_OPTIONS_ROUTER_PROTECTION=disabled
|
||||
_APP_OPTIONS_FORCE_HTTPS=disabled
|
||||
_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled
|
||||
_APP_OPENSSL_KEY_V1=your-secret-key
|
||||
_APP_DNS=8.8.8.8
|
||||
_APP_DOMAIN=traefik
|
||||
_APP_CONSOLE_DOMAIN=localhost
|
||||
_APP_DOMAIN_FUNCTIONS=functions.localhost
|
||||
_APP_DOMAIN_SITES=sites.localhost
|
||||
_APP_DOMAIN_TARGET_CNAME=test.localhost
|
||||
_APP_DOMAIN_TARGET_A=127.0.0.1
|
||||
_APP_DOMAIN_TARGET_AAAA=::1
|
||||
_APP_DOMAIN_TARGET_CAA=digicert.com
|
||||
_APP_RULES_FORMAT=md5
|
||||
_APP_REDIS_HOST=redis
|
||||
_APP_REDIS_PORT=6379
|
||||
@@ -84,8 +87,9 @@ _APP_COMPUTE_MAINTENANCE_INTERVAL=600
|
||||
_APP_COMPUTE_RUNTIMES_NETWORK=runtimes
|
||||
_APP_EXECUTOR_SECRET=your-secret-key
|
||||
_APP_EXECUTOR_HOST=http://exc1/v1
|
||||
_APP_FUNCTIONS_RUNTIMES=php-8.0,node-18.0,python-3.9,ruby-3.1
|
||||
_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.32
|
||||
_APP_BROWSER_HOST=http://appwrite-browser:3000/v1
|
||||
_APP_FUNCTIONS_RUNTIMES=node-22
|
||||
_APP_SITES_RUNTIMES=static-1,node-22
|
||||
_APP_MAINTENANCE_INTERVAL=86400
|
||||
_APP_MAINTENANCE_START_TIME=12:00
|
||||
_APP_MAINTENANCE_RETENTION_CACHE=2592000
|
||||
|
||||
@@ -64,8 +64,9 @@ jobs:
|
||||
sudo wget -O /usr/share/keyrings/azlux-archive-keyring.gpg https://azlux.fr/repo.gpg
|
||||
sudo apt update
|
||||
sudo apt install oha
|
||||
oha --version
|
||||
- name: Benchmark PR
|
||||
run: 'oha -z 180s http://localhost/v1/health/version --output_format json > benchmark.json'
|
||||
run: 'oha -z 180s http://localhost/v1/health/version --output-format json > benchmark.json'
|
||||
- name: Cleaning
|
||||
run: docker compose down -v
|
||||
- name: Installing latest version
|
||||
@@ -78,7 +79,7 @@ jobs:
|
||||
docker compose up -d
|
||||
sleep 10
|
||||
- name: Benchmark Latest
|
||||
run: oha -z 180s http://localhost/v1/health/version --output_format json > benchmark-latest.json
|
||||
run: oha -z 180s http://localhost/v1/health/version --output-format json > benchmark-latest.json
|
||||
- name: Prepare comment
|
||||
run: |
|
||||
echo '## :sparkles: Benchmark results' > benchmark.txt
|
||||
|
||||
@@ -178,6 +178,7 @@ jobs:
|
||||
- name: Load and Start Appwrite
|
||||
run: |
|
||||
docker load --input /tmp/${{ env.IMAGE }}.tar
|
||||
sed -i 's|^_APP_BROWSER_HOST=.*|_APP_BROWSER_HOST=http://invalid-browser/v1|' .env
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
|
||||
@@ -198,7 +199,7 @@ jobs:
|
||||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots
|
||||
|
||||
e2e_shared_mode_test:
|
||||
name: E2E Shared Mode Service Test
|
||||
@@ -277,14 +278,12 @@ jobs:
|
||||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots
|
||||
|
||||
e2e_dev_keys:
|
||||
name: E2E Service Test (Dev Keys)
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -360,3 +359,85 @@ jobs:
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys
|
||||
|
||||
e2e_screenshots_keys:
|
||||
name: E2E Service Test (Site Screenshots)
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Load Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: ${{ env.CACHE_KEY }}
|
||||
path: /tmp/${{ env.IMAGE }}.tar
|
||||
fail-on-cache-miss: true
|
||||
|
||||
- name: Load and Start Appwrite
|
||||
run: |
|
||||
docker load --input /tmp/${{ env.IMAGE }}.tar
|
||||
sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
|
||||
- name: Run Site tests with browser connected in dedicated table mode
|
||||
run: |
|
||||
echo "Keeping original value of _APP_BROWSER_HOST"
|
||||
echo "Using project tables"
|
||||
export _APP_DATABASE_SHARED_TABLES=
|
||||
export _APP_DATABASE_SHARED_TABLES_V1=
|
||||
|
||||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Sites --debug --group=screenshots
|
||||
|
||||
e2e_screenshots_shared_mode:
|
||||
name: E2E Shared Mode Service Test (Site Screenshots)
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ setup, check_database_changes ]
|
||||
if: needs.check_database_changes.outputs.database_changed == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
tables-mode: [
|
||||
'Shared V1',
|
||||
'Shared V2',
|
||||
]
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Load Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: ${{ env.CACHE_KEY }}
|
||||
path: /tmp/${{ env.IMAGE }}.tar
|
||||
fail-on-cache-miss: true
|
||||
|
||||
- name: Load and Start Appwrite
|
||||
run: |
|
||||
docker load --input /tmp/${{ env.IMAGE }}.tar
|
||||
sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env
|
||||
docker compose up -d
|
||||
sleep 30
|
||||
|
||||
- name: Run Site tests with browser connected in ${{ matrix.tables-mode }} table mode
|
||||
run: |
|
||||
echo "Keeping original value of _APP_BROWSER_HOST"
|
||||
if [ "${{ matrix.tables-mode }}" == "Shared V1" ]; then
|
||||
echo "Using shared tables V1"
|
||||
export _APP_DATABASE_SHARED_TABLES=database_db_main
|
||||
export _APP_DATABASE_SHARED_TABLES_V1=database_db_main
|
||||
elif [ "${{ matrix.tables-mode }}" == "Shared V2" ]; then
|
||||
echo "Using shared tables V2"
|
||||
export _APP_DATABASE_SHARED_TABLES=database_db_main
|
||||
export _APP_DATABASE_SHARED_TABLES_V1=
|
||||
fi
|
||||
|
||||
docker compose exec -T \
|
||||
-e _APP_DATABASE_SHARED_TABLES \
|
||||
-e _APP_DATABASE_SHARED_TABLES_V1 \
|
||||
appwrite test /usr/src/code/tests/e2e/Services/Sites --debug --group=screenshots
|
||||
|
||||
+1
-1
@@ -16,5 +16,5 @@ app/sdks
|
||||
dev/yasd_init.php
|
||||
.phpunit.result.cache
|
||||
Makefile
|
||||
appwrite.json
|
||||
appwrite.config.json
|
||||
/.zed/
|
||||
+25
-1
@@ -9,7 +9,31 @@ You can [find issues using this query](https://github.com/search?q=org%3Aappwrit
|
||||
|
||||
## How to Start?
|
||||
|
||||
If you are worried or don’t 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!
|
||||
Welcome! We're excited that you're interested in contributing to Appwrite. To make sure your time is valued and your contributions are successful, please follow these steps before writing any code:
|
||||
|
||||
### 🔍 Step 1: Find an Issue
|
||||
|
||||
Browse open issues and look for ones labeled [good first issue](https://github.com/search?q=org%3Aappwrite+is%3Aopen+type%3Aissue+label%3A%22good+first+issue%22&type=issues) or [help wanted](https://github.com/search?q=org%3Aappwrite+is%3Aopen+type%3Aissue+label%3A%22help+wanted%22&type=issues).
|
||||
|
||||
If you're not sure which issue to pick, ask in our [maintainers channel](https://discord.com/channels/564160730845151244/636852860709240842) on Discord.
|
||||
|
||||
### 📝 Step 2: Ask to Be Assigned
|
||||
|
||||
Before working on an issue, comment on the GitHub issue asking to be assigned. This prevents multiple people working on the same task.
|
||||
|
||||
Then, create a thread in the [maintainers channel](https://discord.com/channels/564160730845151244/636852860709240842) on Discord with a link to the issue.
|
||||
|
||||
Our team is small and may not see your GitHub comment right away - posting in the [maintainers channel](https://discord.com/channels/564160730845151244/636852860709240842) ensures it gets seen.
|
||||
|
||||
### 💬 Step 3: Don’t Submit Random PRs
|
||||
|
||||
If you're not working on an assigned issue, create a GitHub issue first.
|
||||
|
||||
PRs submitted without context or discussion may not align with our roadmap and may be closed without review.
|
||||
|
||||
### ⚠️ Please Note
|
||||
|
||||
We’re a very small team managing a large project. Many PRs are submitted, and while we appreciate every effort, we can only review contributions that follow the process above. This helps us keep things fair and organized.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
|
||||
+3
-1
@@ -26,7 +26,7 @@
|
||||
|
||||
[**Appwrite 云公开测试版!立即注册!**](https://cloud.appwrite.io)
|
||||
|
||||
Appwrite 是一个基于 Docker 的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,以及后端。Appwrite 通过视觉化界面简化了从零开始编写 API 的繁琐过程,在保证软件安全的前提下为开发者创造了一个高效的开发环境。
|
||||
Appwrite 是一个基于 Docker 的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,原生应用,以及后端。它既包含后端服务器,也提供了用于部署静态和服务器端渲染前端的完全集成托管解决方案。Appwrite 通过视觉化界面简化了从零开始构建现代应用的复杂性和重复性,让您能够更快地构建安全的全栈应用。
|
||||
|
||||
Appwrite 可以提供给开发者用户验证,外部授权,用户数据读写检索,文件储存,图像处理,云函数计算,[等多种服务](https://appwrite.io/docs).
|
||||
|
||||
@@ -179,6 +179,8 @@ docker run -it --rm `
|
||||
- [**消息传递**](https://appwrite.io/docs/references/cloud/client-web/messaging) - 使用 Appwrite 消息传递功能通过推送通知、电子邮件和短信与用户进行通信。
|
||||
- [**语言适配**](https://appwrite.io/docs/references/cloud/client-web/locale) - 根据用户所在的的国家和地区做出合适的语言适配。
|
||||
- [**头像**](https://appwrite.io/docs/references/cloud/client-web/avatars) -管理用户头像、国家旗帜、浏览器图标、信用卡符号,和生成二维码。
|
||||
- [**MCP**](https://appwrite.io/docs/tooling/mcp) - 使用 Appwrite 的模型上下文协议(Model Context Protocol)服务器,允许大语言模型(LLM)和 AI 工具(如 Claude Desktop、Cursor 和 Windsurf Editor)通过自然语言直接与您的 Appwrite 项目交互。
|
||||
- [**站点**](https://appwrite.io/docs/products/sites) - 直接从 Appwrite 开发、部署和扩展您的 Web 应用程序,与您的后端一起。
|
||||
如需完整的 API 界面文档,请访问 [https://appwrite.io/docs](https://appwrite.io/docs)。如需更多教程、新闻和公告,请订阅我们的 [博客](https://medium.com/appwrite-io) 和 加入我们的[Discord 社区](https://discord.gg/GSeTUeA)。
|
||||
|
||||
### 开发套件
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
|
||||
> We just announced Auto-increment support for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-auto-increment-support)
|
||||
|
||||
> [Get started with Appwrite](https://apwr.dev/appcloud)
|
||||
> [Join the Init kick off event 19th of May: The future of Appwrite with Founder & CEO Eldad Fux](https://www.youtube.com/watch?v=1g8tuogsp7A)
|
||||
|
||||
<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>
|
||||
<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>
|
||||
<b>Appwrite is an all-in-one development platform for Web, Mobile, and Flutter applications. Use built-in backend infrastructure and web hosting, all from a single place. Built with the open source community and optimized for developer experience in the coding languages you love.</b>
|
||||
<br />
|
||||
<br />
|
||||
</p>
|
||||
@@ -25,7 +27,7 @@
|
||||
|
||||
English | [简体中文](README-CN.md)
|
||||
|
||||
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.
|
||||
Appwrite is an end-to-end platform for building Web, Mobile, Native, or Backend apps, packaged as a set of Docker microservices. It includes both a backend server and a fully integrated hosting solution for deploying static and server-side rendered frontends. Appwrite abstracts the complexity and repetitiveness required to build modern apps from scratch and allows you to build secure, full-stack applications 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).
|
||||
|
||||
@@ -42,7 +44,7 @@ Find out more at: [https://appwrite.io](https://appwrite.io).
|
||||
|
||||
Table of Contents:
|
||||
|
||||
- [Getting Started](#getting-started)
|
||||
- [Installation \& Setup](#installation--setup)
|
||||
- [Self-Hosting](#self-hosting)
|
||||
- [Unix](#unix)
|
||||
- [Windows](#windows)
|
||||
@@ -62,8 +64,9 @@ 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.
|
||||
## Installation & Setup
|
||||
|
||||
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 your credit card information.
|
||||
|
||||
## Self-Hosting
|
||||
|
||||
@@ -128,19 +131,19 @@ Choose from one of the providers below:
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<br /><sub><b>AWS Marketplace</b></sub></a>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -186,6 +189,8 @@ Getting started with Appwrite is as easy as creating a new project, choosing you
|
||||
- [**Realtime**](https://appwrite.io/docs/realtime) - Listen to real-time events for any of your Appwrite services including users, storage, functions, databases, and more.
|
||||
- [**Locale**](https://appwrite.io/docs/references/cloud/client-web/locale) - Track your user's location and manage your app locale-based data.
|
||||
- [**Avatars**](https://appwrite.io/docs/references/cloud/client-web/avatars) - Manage your users' avatars, countries' flags, browser icons, and credit card symbols. Generate QR codes from links or plaintext strings.
|
||||
- [**MCP**](https://appwrite.io/docs/tooling/mcp) - Use Appwrite's Model Context Protocol (MCP) server to allow LLMs and AI tools like Claude Desktop, Cursor, and Windsurf Editor to directly interact with your Appwrite project through natural language.
|
||||
- [**Sites**](https://appwrite.io/docs/products/sites) - Develop, deploy, and scale your web applications directly from Appwrite, alongside your backend.
|
||||
|
||||
For the complete API documentation, visit [https://appwrite.io/docs](https://appwrite.io/docs). For more tutorials, news and announcements check out our [blog](https://medium.com/appwrite-io) and [Discord Server](https://discord.gg/GSeTUeA).
|
||||
|
||||
|
||||
+12
-4
@@ -188,16 +188,24 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
return $database;
|
||||
};
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
CLI::setResource('publisher', function (Group $pools) {
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
CLI::setResource('publisherDatabases', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
CLI::setResource('publisherMigrations', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
CLI::setResource('publisherStatsUsage', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
CLI::setResource('queueForStatsUsage', function (Publisher $publisher) {
|
||||
return new StatsUsage($publisher);
|
||||
}, ['publisher']);
|
||||
CLI::setResource('queueForStatsResources', function (Publisher $publisher) {
|
||||
return new StatsResources($publisher);
|
||||
}, ['publisher']);
|
||||
CLI::setResource('publisher', function (Group $pools) {
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
CLI::setResource('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\Network\Platform;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\System\System;
|
||||
|
||||
@@ -23,7 +23,7 @@ $console = [
|
||||
[
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Localhost',
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'type' => Platform::TYPE_WEB,
|
||||
'hostname' => 'localhost',
|
||||
], // Current host is added on app init
|
||||
],
|
||||
|
||||
@@ -120,6 +120,11 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="display: none; overflow: hidden; max-height: 0; max-width: 0; opacity: 0; line-height: 1px;">
|
||||
{{preview}}
|
||||
<div>{{previewWhitespace}}</div>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<table>
|
||||
<tr>
|
||||
|
||||
@@ -121,6 +121,11 @@
|
||||
|
||||
<body style="direction: {{direction}}">
|
||||
|
||||
<div style="display: none; overflow: hidden; max-height: 0; max-width: 0; opacity: 0; line-height: 1px;">
|
||||
{{preview}}
|
||||
<div>{{previewWhitespace}}</div>
|
||||
</div>
|
||||
|
||||
<div style="max-width:650px; word-wrap: break-word; overflow-wrap: break-word;
|
||||
word-break: normal; margin:0 auto;">
|
||||
<table style="margin-top: 32px">
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
<p>If you have trouble with the sender's image, ensure it is set in the <a href="https://gravatar.com/">Gravatar database</a>.</p>
|
||||
|
||||
<p style="margin-block-end: 0;">Best regards,</p>
|
||||
<p style="margin-block-start: 0;">Appwrtite team</p>
|
||||
<p style="margin-block-start: 0;">Appwrite team</p>
|
||||
@@ -14,7 +14,7 @@
|
||||
<table border="0" cellspacing="0" cellpadding="0" style="padding-top: 10px; padding-bottom: 10px; margin-top: 32px">
|
||||
<tr>
|
||||
<td style="border-radius: 8px; display: block; width: 100%;">
|
||||
<a class="mobile-full-width" rel="noopener" target="_blank" href="{{host}}{{path}}" style="font-size: 14px; font-family: Inter; color: #ffffff; text-decoration: none; background-color: #FD366E; border-radius: 8px; padding: 9px 14px; border: 1px solid #FD366E; display: inline-block; text-align:center; box-sizing: border-box;">Webhook settings</a>
|
||||
<a class="mobile-full-width" rel="noopener" target="_blank" href="{{host}}{{path}}" style="font-size: 14px; font-family: Inter; color: #ffffff; text-decoration: none; background-color: #2D2D31; border-radius: 8px; padding: 9px 14px; border: 1px solid #414146; display: inline-block; text-align:center; box-sizing: border-box;">Webhook settings</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -4,6 +4,7 @@
|
||||
"settings.direction": "ltr",
|
||||
"emails.sender": "%s Team",
|
||||
"emails.verification.subject": "Account Verification",
|
||||
"emails.verification.preview": "Verify your email to activate your {{project}} account.",
|
||||
"emails.verification.hello": "Hello {{user}},",
|
||||
"emails.verification.body": "Follow this link to verify your email address to your {{b}}{{project}}{{/b}} account.",
|
||||
"emails.verification.footer": "If you didn’t ask to verify this address, you can ignore this message.",
|
||||
@@ -11,6 +12,7 @@
|
||||
"emails.verification.buttonText": "Confirm email address",
|
||||
"emails.verification.signature": "{{project}} team",
|
||||
"emails.magicSession.subject": "{{project}} Login",
|
||||
"emails.magicSession.preview": "Sign in to {{project}} with your secure link. Expires in 1 hour.",
|
||||
"emails.magicSession.hello": "Hello {{user}},",
|
||||
"emails.magicSession.optionButton": "Click the button below to securely sign in to your {{b}}{{project}}{{/b}} account. This link will expire in 1 hour.",
|
||||
"emails.magicSession.buttonText": "Sign in to {{project}}",
|
||||
@@ -20,6 +22,7 @@
|
||||
"emails.magicSession.thanks": "Thanks,",
|
||||
"emails.magicSession.signature": "{{project}} team",
|
||||
"emails.sessionAlert.subject": "Security alert: new session on your {{project}} account",
|
||||
"emails.sessionAlert.preview": "New login detected on {{project}} at {{time}} UTC.",
|
||||
"emails.sessionAlert.hello": "Hello {{user}},",
|
||||
"emails.sessionAlert.body": "A new session has been created on your {{b}}{{project}}{{/b}} account, {{b}}on {{date}}, {{year}} at {{time}} UTC{{/b}}.\nHere are the details of the new session: ",
|
||||
"emails.sessionAlert.listDevice": "Device: {{b}}{{device}}{{/b}}",
|
||||
@@ -29,6 +32,7 @@
|
||||
"emails.sessionAlert.thanks": "Thanks,",
|
||||
"emails.sessionAlert.signature": "{{project}} team",
|
||||
"emails.otpSession.subject": "OTP for {{project}} Login",
|
||||
"emails.otpSession.preview": "Use OTP {{otp}} to sign in to {{project}}. Expires in 15 minutes.",
|
||||
"emails.otpSession.hello": "Hello {{user}},",
|
||||
"emails.otpSession.description": "Enter the following verification code when prompted to securely sign in to your {{b}}{{project}}{{/b}} account. This code will expire in 15 minutes.",
|
||||
"emails.otpSession.clientInfo": "This sign in was requested using {{b}}{{agentClient}}{{/b}} on {{b}}{{agentDevice}}{{/b}} {{b}}{{agentOs}}{{/b}}. If you didn't request the sign in, you can safely ignore this email.",
|
||||
@@ -36,12 +40,14 @@
|
||||
"emails.otpSession.thanks": "Thanks,",
|
||||
"emails.otpSession.signature": "{{project}} team",
|
||||
"emails.mfaChallenge.subject": "Verification Code for {{project}}",
|
||||
"emails.mfaChallenge.preview": "Use code {{otp}} for two-step verification in {{project}}. Expires in 15 minutes.",
|
||||
"emails.mfaChallenge.hello": "Hello {{user}},",
|
||||
"emails.mfaChallenge.description": "Enter the following verification code to verify your email and activate two-step verification in {{b}}{{project}}{{/b}}. This code will expire in 15 minutes.",
|
||||
"emails.mfaChallenge.description": "Enter the following code to confirm your two-step verification in {{b}}{{project}}{{/b}}. This code will expire in 15 minutes.",
|
||||
"emails.mfaChallenge.clientInfo": "This verification code was requested using {{b}}{{agentClient}}{{/b}} on {{b}}{{agentDevice}}{{/b}} {{b}}{{agentOs}}{{/b}}. If you didn't request the verification code, you can safely ignore this email.",
|
||||
"emails.mfaChallenge.thanks": "Thanks,",
|
||||
"emails.mfaChallenge.signature": "{{project}} team",
|
||||
"emails.recovery.subject": "Password Reset",
|
||||
"emails.recovery.preview": "Reset your {{project}} password using the link.",
|
||||
"emails.recovery.hello": "Hello {{user}},",
|
||||
"emails.recovery.body": "Follow this link to reset your {{b}}{{project}}{{/b}} password.",
|
||||
"emails.recovery.footer": "If you didn't ask to reset your password, you can ignore this message.",
|
||||
@@ -49,6 +55,7 @@
|
||||
"emails.recovery.buttonText": "Reset password",
|
||||
"emails.recovery.signature": "{{project}} team",
|
||||
"emails.invitation.subject": "Invitation to %s Team at %s",
|
||||
"emails.invitation.preview": "{{owner}} invited you to join {{team}} at {{project}}",
|
||||
"emails.invitation.hello": "Hello {{user}},",
|
||||
"emails.invitation.body": "This mail was sent to you because {{b}}{{owner}}{{/b}} wanted to invite you to become a member of the {{b}}{{team}}{{/b}} team at {{b}}{{project}}{{/b}}.",
|
||||
"emails.invitation.footer": "If you are not interested, you can ignore this message.",
|
||||
@@ -56,6 +63,7 @@
|
||||
"emails.invitation.buttonText": "Accept invite to {{team}}",
|
||||
"emails.invitation.signature": "{{project}} team",
|
||||
"emails.certificate.subject": "Certificate failure for %s",
|
||||
"emails.certificate.preview": "Your domain %s certificate generation has failed.",
|
||||
"emails.certificate.hello": "Hello,",
|
||||
"emails.certificate.body": "Certificate for your domain '{{domain}}' could not be generated. This is attempt no. {{attempt}}, and the failure was caused by: {{error}}",
|
||||
"emails.certificate.footer": "Your previous certificate will be valid for 30 days since the first failure. We highly recommend investigating this case, otherwise your domain will end up without a valid SSL communication.",
|
||||
|
||||
+44
-21
@@ -11,7 +11,7 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Web',
|
||||
'version' => '18.1.0',
|
||||
'version' => '18.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-web',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -25,6 +25,7 @@ return [
|
||||
'gitRepoName' => 'sdk-for-web',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/web/CHANGELOG.md'),
|
||||
'demos' => [
|
||||
[
|
||||
'icon' => 'react.svg',
|
||||
@@ -59,7 +60,7 @@ return [
|
||||
[
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
'version' => '17.0.1',
|
||||
'version' => '17.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-flutter',
|
||||
'package' => 'https://pub.dev/packages/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -73,11 +74,12 @@ return [
|
||||
'gitRepoName' => 'sdk-for-flutter',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/flutter/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'apple',
|
||||
'name' => 'Apple',
|
||||
'version' => '10.1.1',
|
||||
'version' => '10.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'enabled' => true,
|
||||
@@ -91,6 +93,7 @@ return [
|
||||
'gitRepoName' => 'sdk-for-apple',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/apple/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'objective-c',
|
||||
@@ -108,11 +111,12 @@ return [
|
||||
'gitRepoName' => 'sdk-for-objective-c',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/objective-c/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'android',
|
||||
'name' => 'Android',
|
||||
'version' => '8.1.0',
|
||||
'version' => '8.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-android',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
|
||||
'enabled' => true,
|
||||
@@ -130,11 +134,12 @@ return [
|
||||
'Kotlin' => 'kotlin',
|
||||
'Java' => 'java',
|
||||
],
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/android/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'react-native',
|
||||
'name' => 'React Native',
|
||||
'version' => '0.10.0',
|
||||
'version' => '0.11.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-react-native',
|
||||
'package' => 'https://npmjs.com/package/react-native-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -148,6 +153,7 @@ return [
|
||||
'gitRepoName' => 'sdk-for-react-native',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/react-native/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'graphql',
|
||||
@@ -167,6 +173,7 @@ return [
|
||||
'gitUserName' => '',
|
||||
'gitBranch' => '',
|
||||
'isSDK' => false,
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/graphql/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'rest',
|
||||
@@ -186,6 +193,7 @@ return [
|
||||
'gitUserName' => '',
|
||||
'gitBranch' => '',
|
||||
'isSDK' => false,
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/rest/CHANGELOG.md'),
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -199,8 +207,8 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Console',
|
||||
'version' => '1.7.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-console',
|
||||
'version' => '0.1.0',
|
||||
'url' => '',
|
||||
'package' => '',
|
||||
'enabled' => true,
|
||||
'beta' => false,
|
||||
@@ -209,15 +217,16 @@ return [
|
||||
'family' => APP_PLATFORM_CONSOLE,
|
||||
'prism' => 'javascript',
|
||||
'source' => \realpath(__DIR__ . '/../sdks/console-web'),
|
||||
'gitUrl' => 'git@github.com:appwrite/sdk-for-console.git',
|
||||
'gitUrl' => '',
|
||||
'gitBranch' => 'dev',
|
||||
'gitRepoName' => 'sdk-for-console',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitRepoName' => '',
|
||||
'gitUserName' => '',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/console/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'cli',
|
||||
'name' => 'Command Line',
|
||||
'version' => '8.0.2',
|
||||
'version' => '8.3.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
@@ -231,6 +240,8 @@ return [
|
||||
'gitRepoName' => 'sdk-for-cli',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'repoBranch' => 'master',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/cli/CHANGELOG.md'),
|
||||
'exclude' => [
|
||||
'services' => [
|
||||
['name' => 'assistant'],
|
||||
@@ -250,7 +261,7 @@ return [
|
||||
[
|
||||
'key' => 'nodejs',
|
||||
'name' => 'Node.js',
|
||||
'version' => '17.0.0',
|
||||
'version' => '17.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-node',
|
||||
'package' => 'https://www.npmjs.com/package/node-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -264,11 +275,12 @@ return [
|
||||
'gitRepoName' => 'sdk-for-node',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/nodejs/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'deno',
|
||||
'name' => 'Deno',
|
||||
'version' => '15.0.0',
|
||||
'version' => '15.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-deno',
|
||||
'package' => 'https://deno.land/x/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -282,11 +294,12 @@ return [
|
||||
'gitRepoName' => 'sdk-for-deno',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/deno/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'php',
|
||||
'name' => 'PHP',
|
||||
'version' => '15.0.0',
|
||||
'version' => '15.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-php',
|
||||
'package' => 'https://packagist.org/packages/appwrite/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -300,11 +313,12 @@ return [
|
||||
'gitRepoName' => 'sdk-for-php',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/php/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'python',
|
||||
'name' => 'Python',
|
||||
'version' => '11.0.0',
|
||||
'version' => '11.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-python',
|
||||
'package' => 'https://pypi.org/project/appwrite/',
|
||||
'enabled' => true,
|
||||
@@ -318,11 +332,12 @@ return [
|
||||
'gitRepoName' => 'sdk-for-python',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/python/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'ruby',
|
||||
'name' => 'Ruby',
|
||||
'version' => '16.0.0',
|
||||
'version' => '16.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-ruby',
|
||||
'package' => 'https://rubygems.org/gems/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -336,11 +351,12 @@ return [
|
||||
'gitRepoName' => 'sdk-for-ruby',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/ruby/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'go',
|
||||
'name' => 'Go',
|
||||
'version' => '0.8.0',
|
||||
'version' => '0.9.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'enabled' => true,
|
||||
@@ -354,11 +370,12 @@ return [
|
||||
'gitRepoName' => 'sdk-for-go',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/go/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'dotnet',
|
||||
'name' => '.NET',
|
||||
'version' => '0.13.0',
|
||||
'version' => '0.15.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
||||
'package' => 'https://www.nuget.org/packages/Appwrite',
|
||||
'enabled' => true,
|
||||
@@ -372,11 +389,12 @@ return [
|
||||
'gitRepoName' => 'sdk-for-dotnet',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/dotnet/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'dart',
|
||||
'name' => 'Dart',
|
||||
'version' => '16.0.0',
|
||||
'version' => '16.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
||||
'package' => 'https://pub.dev/packages/dart_appwrite',
|
||||
'enabled' => true,
|
||||
@@ -390,11 +408,12 @@ return [
|
||||
'gitRepoName' => 'sdk-for-dart',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/dart/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'kotlin',
|
||||
'name' => 'Kotlin',
|
||||
'version' => '9.0.0',
|
||||
'version' => '9.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
|
||||
'enabled' => true,
|
||||
@@ -412,11 +431,12 @@ return [
|
||||
'Kotlin' => 'kotlin',
|
||||
'Java' => 'java',
|
||||
],
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/kotlin/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'swift',
|
||||
'name' => 'Swift',
|
||||
'version' => '10.1.0',
|
||||
'version' => '10.2.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'enabled' => true,
|
||||
@@ -430,6 +450,7 @@ return [
|
||||
'gitRepoName' => 'sdk-for-swift',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/swift/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'graphql',
|
||||
@@ -449,6 +470,7 @@ return [
|
||||
'gitUserName' => '',
|
||||
'gitBranch' => '',
|
||||
'isSDK' => false,
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/graphql/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'rest',
|
||||
@@ -468,6 +490,7 @@ return [
|
||||
'gitUserName' => '',
|
||||
'gitBranch' => '',
|
||||
'isSDK' => false,
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/rest/CHANGELOG.md'),
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4467,10 +4467,7 @@
|
||||
{
|
||||
"name": "createDocument",
|
||||
"auth": {
|
||||
"Admin": [],
|
||||
"Session": [],
|
||||
"Key": [],
|
||||
"JWT": []
|
||||
"Project": []
|
||||
},
|
||||
"parameters": [
|
||||
"databaseId",
|
||||
@@ -5206,7 +5203,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listExecutions",
|
||||
"group": "executions",
|
||||
"weight": 394,
|
||||
"weight": 393,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5281,7 +5278,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createExecution",
|
||||
"group": "executions",
|
||||
"weight": 392,
|
||||
"weight": 391,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5364,7 +5361,7 @@
|
||||
"scheduledAt": {
|
||||
"type": "string",
|
||||
"description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.",
|
||||
"x-example": null
|
||||
"x-example": "<SCHEDULED_AT>"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5396,7 +5393,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getExecution",
|
||||
"group": "executions",
|
||||
"weight": 393,
|
||||
"weight": 392,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5470,7 +5467,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 308,
|
||||
"weight": 307,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
@@ -5522,7 +5519,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 307,
|
||||
"weight": 306,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
@@ -5990,7 +5987,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 354,
|
||||
"weight": 353,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6073,7 +6070,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 358,
|
||||
"weight": 357,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -8036,7 +8033,8 @@
|
||||
"any": {
|
||||
"description": "Any",
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
"additionalProperties": true,
|
||||
"example": []
|
||||
},
|
||||
"error": {
|
||||
"description": "Error",
|
||||
@@ -8068,7 +8066,13 @@
|
||||
"code",
|
||||
"type",
|
||||
"version"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"message": "Not found",
|
||||
"code": "404",
|
||||
"type": "not_found",
|
||||
"version": "1.0"
|
||||
}
|
||||
},
|
||||
"documentList": {
|
||||
"description": "Documents List",
|
||||
@@ -8092,7 +8096,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"documents"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"documents": ""
|
||||
}
|
||||
},
|
||||
"sessionList": {
|
||||
"description": "Sessions List",
|
||||
@@ -8116,7 +8124,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"sessions"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"sessions": ""
|
||||
}
|
||||
},
|
||||
"identityList": {
|
||||
"description": "Identities List",
|
||||
@@ -8140,7 +8152,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"identities"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"identities": ""
|
||||
}
|
||||
},
|
||||
"logList": {
|
||||
"description": "Logs List",
|
||||
@@ -8164,7 +8180,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"logs"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"logs": ""
|
||||
}
|
||||
},
|
||||
"fileList": {
|
||||
"description": "Files List",
|
||||
@@ -8188,7 +8208,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"files"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"files": ""
|
||||
}
|
||||
},
|
||||
"teamList": {
|
||||
"description": "Teams List",
|
||||
@@ -8212,7 +8236,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"teams"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"teams": ""
|
||||
}
|
||||
},
|
||||
"membershipList": {
|
||||
"description": "Memberships List",
|
||||
@@ -8236,7 +8264,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"memberships"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"memberships": ""
|
||||
}
|
||||
},
|
||||
"executionList": {
|
||||
"description": "Executions List",
|
||||
@@ -8260,7 +8292,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"executions"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"executions": ""
|
||||
}
|
||||
},
|
||||
"countryList": {
|
||||
"description": "Countries List",
|
||||
@@ -8284,7 +8320,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"countries"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"countries": ""
|
||||
}
|
||||
},
|
||||
"continentList": {
|
||||
"description": "Continents List",
|
||||
@@ -8308,7 +8348,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"continents"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"continents": ""
|
||||
}
|
||||
},
|
||||
"languageList": {
|
||||
"description": "Languages List",
|
||||
@@ -8332,7 +8376,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"languages"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"languages": ""
|
||||
}
|
||||
},
|
||||
"currencyList": {
|
||||
"description": "Currencies List",
|
||||
@@ -8356,7 +8404,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"currencies"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"currencies": ""
|
||||
}
|
||||
},
|
||||
"phoneList": {
|
||||
"description": "Phones List",
|
||||
@@ -8380,7 +8432,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"phones"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"phones": ""
|
||||
}
|
||||
},
|
||||
"localeCodeList": {
|
||||
"description": "Locale codes list",
|
||||
@@ -8404,7 +8460,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"localeCodes"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"localeCodes": ""
|
||||
}
|
||||
},
|
||||
"document": {
|
||||
"description": "Document",
|
||||
@@ -8461,7 +8521,23 @@
|
||||
"$createdAt",
|
||||
"$updatedAt",
|
||||
"$permissions"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$sequence": 1,
|
||||
"$collectionId": "5e5ea5c15117e",
|
||||
"$databaseId": "5e5ea5c15117e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$permissions": [
|
||||
"read(\"any\")"
|
||||
],
|
||||
"username": "john.doe",
|
||||
"email": "john.doe@example.com",
|
||||
"fullName": "John Doe",
|
||||
"age": 30,
|
||||
"isAdmin": false
|
||||
}
|
||||
},
|
||||
"log": {
|
||||
"description": "Log",
|
||||
@@ -8595,7 +8671,30 @@
|
||||
"deviceModel",
|
||||
"countryCode",
|
||||
"countryName"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"event": "account.sessions.create",
|
||||
"userId": "610fc2f985ee0",
|
||||
"userEmail": "john@appwrite.io",
|
||||
"userName": "John Doe",
|
||||
"mode": "admin",
|
||||
"ip": "127.0.0.1",
|
||||
"time": "2020-10-15T06:38:00.000+00:00",
|
||||
"osCode": "Mac",
|
||||
"osName": "Mac",
|
||||
"osVersion": "Mac",
|
||||
"clientType": "browser",
|
||||
"clientCode": "CM",
|
||||
"clientName": "Chrome Mobile iOS",
|
||||
"clientVersion": "84.0",
|
||||
"clientEngine": "WebKit",
|
||||
"clientEngineVersion": "605.1.15",
|
||||
"deviceName": "smartphone",
|
||||
"deviceBrand": "Google",
|
||||
"deviceModel": "Nexus 5",
|
||||
"countryCode": "US",
|
||||
"countryName": "United States"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"description": "User",
|
||||
@@ -8756,7 +8855,33 @@
|
||||
"prefs",
|
||||
"targets",
|
||||
"accessedAt"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"name": "John Doe",
|
||||
"password": "$argon2id$v=19$m=2048,t=4,p=3$aUZjLnliVWRINmFNTWMudg$5S+x+7uA31xFnrHFT47yFwcJeaP0w92L\/4LdgrVRXxE",
|
||||
"hash": "argon2",
|
||||
"hashOptions": {},
|
||||
"registration": "2020-10-15T06:38:00.000+00:00",
|
||||
"status": true,
|
||||
"labels": [
|
||||
"vip"
|
||||
],
|
||||
"passwordUpdate": "2020-10-15T06:38:00.000+00:00",
|
||||
"email": "john@appwrite.io",
|
||||
"phone": "+4930901820",
|
||||
"emailVerification": true,
|
||||
"phoneVerification": true,
|
||||
"mfa": true,
|
||||
"prefs": {
|
||||
"theme": "pink",
|
||||
"timezone": "UTC"
|
||||
},
|
||||
"targets": [],
|
||||
"accessedAt": "2020-10-15T06:38:00.000+00:00"
|
||||
}
|
||||
},
|
||||
"algoMd5": {
|
||||
"description": "AlgoMD5",
|
||||
@@ -8770,7 +8895,10 @@
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "md5"
|
||||
}
|
||||
},
|
||||
"algoSha": {
|
||||
"description": "AlgoSHA",
|
||||
@@ -8784,7 +8912,10 @@
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "sha"
|
||||
}
|
||||
},
|
||||
"algoPhpass": {
|
||||
"description": "AlgoPHPass",
|
||||
@@ -8798,7 +8929,10 @@
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "phpass"
|
||||
}
|
||||
},
|
||||
"algoBcrypt": {
|
||||
"description": "AlgoBcrypt",
|
||||
@@ -8812,7 +8946,10 @@
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "bcrypt"
|
||||
}
|
||||
},
|
||||
"algoScrypt": {
|
||||
"description": "AlgoScrypt",
|
||||
@@ -8854,7 +8991,14 @@
|
||||
"costMemory",
|
||||
"costParallel",
|
||||
"length"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "scrypt",
|
||||
"costCpu": 8,
|
||||
"costMemory": 14,
|
||||
"costParallel": 1,
|
||||
"length": 64
|
||||
}
|
||||
},
|
||||
"algoScryptModified": {
|
||||
"description": "AlgoScryptModified",
|
||||
@@ -8886,7 +9030,13 @@
|
||||
"salt",
|
||||
"saltSeparator",
|
||||
"signerKey"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "scryptMod",
|
||||
"salt": "UxLMreBr6tYyjQ==",
|
||||
"saltSeparator": "Bw==",
|
||||
"signerKey": "XyEKE9RcTDeLEsL\/RjwPDBv\/RqDl8fb3gpYEOQaPihbxf1ZAtSOHCjuAAa7Q3oHpCYhXSN9tizHgVOwn6krflQ=="
|
||||
}
|
||||
},
|
||||
"algoArgon2": {
|
||||
"description": "AlgoArgon2",
|
||||
@@ -8921,12 +9071,23 @@
|
||||
"memoryCost",
|
||||
"timeCost",
|
||||
"threads"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "argon2",
|
||||
"memoryCost": 65536,
|
||||
"timeCost": 4,
|
||||
"threads": 3
|
||||
}
|
||||
},
|
||||
"preferences": {
|
||||
"description": "Preferences",
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
"additionalProperties": true,
|
||||
"example": {
|
||||
"language": "en",
|
||||
"timezone": "UTC",
|
||||
"darkTheme": true
|
||||
}
|
||||
},
|
||||
"session": {
|
||||
"description": "Session",
|
||||
@@ -9113,7 +9274,40 @@
|
||||
"factors",
|
||||
"secret",
|
||||
"mfaUpdatedAt"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"userId": "5e5bb8c16897e",
|
||||
"expire": "2020-10-15T06:38:00.000+00:00",
|
||||
"provider": "email",
|
||||
"providerUid": "user@example.com",
|
||||
"providerAccessToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
|
||||
"providerAccessTokenExpiry": "2020-10-15T06:38:00.000+00:00",
|
||||
"providerRefreshToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
|
||||
"ip": "127.0.0.1",
|
||||
"osCode": "Mac",
|
||||
"osName": "Mac",
|
||||
"osVersion": "Mac",
|
||||
"clientType": "browser",
|
||||
"clientCode": "CM",
|
||||
"clientName": "Chrome Mobile iOS",
|
||||
"clientVersion": "84.0",
|
||||
"clientEngine": "WebKit",
|
||||
"clientEngineVersion": "605.1.15",
|
||||
"deviceName": "smartphone",
|
||||
"deviceBrand": "Google",
|
||||
"deviceModel": "Nexus 5",
|
||||
"countryCode": "US",
|
||||
"countryName": "United States",
|
||||
"current": true,
|
||||
"factors": [
|
||||
"email"
|
||||
],
|
||||
"secret": "5e5bb8c16897e",
|
||||
"mfaUpdatedAt": "2020-10-15T06:38:00.000+00:00"
|
||||
}
|
||||
},
|
||||
"identity": {
|
||||
"description": "Identity",
|
||||
@@ -9181,7 +9375,19 @@
|
||||
"providerAccessToken",
|
||||
"providerAccessTokenExpiry",
|
||||
"providerRefreshToken"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"userId": "5e5bb8c16897e",
|
||||
"provider": "email",
|
||||
"providerUid": "5e5bb8c16897e",
|
||||
"providerEmail": "user@example.com",
|
||||
"providerAccessToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
|
||||
"providerAccessTokenExpiry": "2020-10-15T06:38:00.000+00:00",
|
||||
"providerRefreshToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3"
|
||||
}
|
||||
},
|
||||
"token": {
|
||||
"description": "Token",
|
||||
@@ -9225,7 +9431,15 @@
|
||||
"secret",
|
||||
"expire",
|
||||
"phrase"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "bb8ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"userId": "5e5ea5c168bb8",
|
||||
"secret": "",
|
||||
"expire": "2020-10-15T06:38:00.000+00:00",
|
||||
"phrase": "Golden Fox"
|
||||
}
|
||||
},
|
||||
"jwt": {
|
||||
"description": "JWT",
|
||||
@@ -9239,7 +9453,10 @@
|
||||
},
|
||||
"required": [
|
||||
"jwt"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
||||
}
|
||||
},
|
||||
"locale": {
|
||||
"description": "Locale",
|
||||
@@ -9289,7 +9506,16 @@
|
||||
"continent",
|
||||
"eu",
|
||||
"currency"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"ip": "127.0.0.1",
|
||||
"countryCode": "US",
|
||||
"country": "United States",
|
||||
"continentCode": "NA",
|
||||
"continent": "North America",
|
||||
"eu": false,
|
||||
"currency": "USD"
|
||||
}
|
||||
},
|
||||
"localeCode": {
|
||||
"description": "LocaleCode",
|
||||
@@ -9309,7 +9535,11 @@
|
||||
"required": [
|
||||
"code",
|
||||
"name"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"code": "en-us",
|
||||
"name": "US"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"description": "File",
|
||||
@@ -9391,7 +9621,22 @@
|
||||
"sizeOriginal",
|
||||
"chunksTotal",
|
||||
"chunksUploaded"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"bucketId": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$permissions": [
|
||||
"read(\"any\")"
|
||||
],
|
||||
"name": "Pink.png",
|
||||
"signature": "5d529fd02b544198ae075bd57c1762bb",
|
||||
"mimeType": "image\/png",
|
||||
"sizeOriginal": 17890,
|
||||
"chunksTotal": 17890,
|
||||
"chunksUploaded": 17890
|
||||
}
|
||||
},
|
||||
"team": {
|
||||
"description": "Team",
|
||||
@@ -9442,7 +9687,18 @@
|
||||
"name",
|
||||
"total",
|
||||
"prefs"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"name": "VIP",
|
||||
"total": 7,
|
||||
"prefs": {
|
||||
"theme": "pink",
|
||||
"timezone": "UTC"
|
||||
}
|
||||
}
|
||||
},
|
||||
"membership": {
|
||||
"description": "Membership",
|
||||
@@ -9533,7 +9789,24 @@
|
||||
"confirm",
|
||||
"mfa",
|
||||
"roles"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"userId": "5e5ea5c16897e",
|
||||
"userName": "John Doe",
|
||||
"userEmail": "john@appwrite.io",
|
||||
"teamId": "5e5ea5c16897e",
|
||||
"teamName": "VIP",
|
||||
"invited": "2020-10-15T06:38:00.000+00:00",
|
||||
"joined": "2020-10-15T06:38:00.000+00:00",
|
||||
"confirm": false,
|
||||
"mfa": false,
|
||||
"roles": [
|
||||
"owner"
|
||||
]
|
||||
}
|
||||
},
|
||||
"execution": {
|
||||
"description": "Execution",
|
||||
@@ -9664,7 +9937,36 @@
|
||||
"logs",
|
||||
"errors",
|
||||
"duration"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$permissions": [
|
||||
"any"
|
||||
],
|
||||
"functionId": "5e5ea6g16897e",
|
||||
"trigger": "http",
|
||||
"status": "processing",
|
||||
"requestMethod": "GET",
|
||||
"requestPath": "\/articles?id=5",
|
||||
"requestHeaders": [
|
||||
{
|
||||
"Content-Type": "application\/json"
|
||||
}
|
||||
],
|
||||
"responseStatusCode": 200,
|
||||
"responseBody": "",
|
||||
"responseHeaders": [
|
||||
{
|
||||
"Content-Type": "application\/json"
|
||||
}
|
||||
],
|
||||
"logs": "",
|
||||
"errors": "",
|
||||
"duration": 0.4,
|
||||
"scheduledAt": "2020-10-15T06:38:00.000+00:00"
|
||||
}
|
||||
},
|
||||
"country": {
|
||||
"description": "Country",
|
||||
@@ -9684,7 +9986,11 @@
|
||||
"required": [
|
||||
"name",
|
||||
"code"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"name": "United States",
|
||||
"code": "US"
|
||||
}
|
||||
},
|
||||
"continent": {
|
||||
"description": "Continent",
|
||||
@@ -9704,7 +10010,11 @@
|
||||
"required": [
|
||||
"name",
|
||||
"code"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"name": "Europe",
|
||||
"code": "EU"
|
||||
}
|
||||
},
|
||||
"language": {
|
||||
"description": "Language",
|
||||
@@ -9730,7 +10040,12 @@
|
||||
"name",
|
||||
"code",
|
||||
"nativeName"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"name": "Italian",
|
||||
"code": "it",
|
||||
"nativeName": "Italiano"
|
||||
}
|
||||
},
|
||||
"currency": {
|
||||
"description": "Currency",
|
||||
@@ -9782,7 +10097,16 @@
|
||||
"rounding",
|
||||
"code",
|
||||
"namePlural"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"symbol": "$",
|
||||
"name": "US dollar",
|
||||
"symbolNative": "$",
|
||||
"decimalDigits": 2,
|
||||
"rounding": 0,
|
||||
"code": "USD",
|
||||
"namePlural": "US dollars"
|
||||
}
|
||||
},
|
||||
"phone": {
|
||||
"description": "Phone",
|
||||
@@ -9808,7 +10132,12 @@
|
||||
"code",
|
||||
"countryCode",
|
||||
"countryName"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"code": "+1",
|
||||
"countryCode": "US",
|
||||
"countryName": "United States"
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"description": "Headers",
|
||||
@@ -9828,7 +10157,11 @@
|
||||
"required": [
|
||||
"name",
|
||||
"value"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"name": "Content-Type",
|
||||
"value": "application\/json"
|
||||
}
|
||||
},
|
||||
"mfaChallenge": {
|
||||
"description": "MFA Challenge",
|
||||
@@ -9860,7 +10193,13 @@
|
||||
"$createdAt",
|
||||
"userId",
|
||||
"expire"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "bb8ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"userId": "5e5ea5c168bb8",
|
||||
"expire": "2020-10-15T06:38:00.000+00:00"
|
||||
}
|
||||
},
|
||||
"mfaRecoveryCodes": {
|
||||
"description": "MFA Recovery Codes",
|
||||
@@ -9880,7 +10219,13 @@
|
||||
},
|
||||
"required": [
|
||||
"recoveryCodes"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"recoveryCodes": [
|
||||
"a3kf0-s0cl2",
|
||||
"s0co1-as98s"
|
||||
]
|
||||
}
|
||||
},
|
||||
"mfaType": {
|
||||
"description": "MFAType",
|
||||
@@ -9900,7 +10245,11 @@
|
||||
"required": [
|
||||
"secret",
|
||||
"uri"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"secret": true,
|
||||
"uri": true
|
||||
}
|
||||
},
|
||||
"mfaFactors": {
|
||||
"description": "MFAFactors",
|
||||
@@ -9932,7 +10281,13 @@
|
||||
"phone",
|
||||
"email",
|
||||
"recoveryCode"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"totp": true,
|
||||
"phone": true,
|
||||
"email": true,
|
||||
"recoveryCode": true
|
||||
}
|
||||
},
|
||||
"subscriber": {
|
||||
"description": "Subscriber",
|
||||
@@ -10006,7 +10361,27 @@
|
||||
"userName",
|
||||
"topicId",
|
||||
"providerType"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "259125845563242502",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"targetId": "259125845563242502",
|
||||
"target": {
|
||||
"$id": "259125845563242502",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"providerType": "email",
|
||||
"providerId": "259125845563242502",
|
||||
"name": "ageon-app-email",
|
||||
"identifier": "random-mail@email.org",
|
||||
"userId": "5e5ea5c16897e"
|
||||
},
|
||||
"userId": "5e5ea5c16897e",
|
||||
"userName": "Aegon Targaryen",
|
||||
"topicId": "259125845563242502",
|
||||
"providerType": "email"
|
||||
}
|
||||
},
|
||||
"target": {
|
||||
"description": "Target",
|
||||
@@ -10068,7 +10443,18 @@
|
||||
"providerType",
|
||||
"identifier",
|
||||
"expired"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "259125845563242502",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"name": "Apple iPhone 12",
|
||||
"userId": "259125845563242502",
|
||||
"providerId": "259125845563242502",
|
||||
"providerType": "email",
|
||||
"identifier": "token",
|
||||
"expired": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4604,10 +4604,7 @@
|
||||
{
|
||||
"name": "createDocument",
|
||||
"auth": {
|
||||
"Admin": [],
|
||||
"Session": [],
|
||||
"Key": [],
|
||||
"JWT": []
|
||||
"Project": []
|
||||
},
|
||||
"parameters": [
|
||||
"databaseId",
|
||||
@@ -5105,8 +5102,7 @@
|
||||
"platforms": [
|
||||
"console",
|
||||
"server",
|
||||
"client",
|
||||
"server"
|
||||
"client"
|
||||
],
|
||||
"packaging": false,
|
||||
"auth": {
|
||||
@@ -5214,8 +5210,7 @@
|
||||
"platforms": [
|
||||
"console",
|
||||
"server",
|
||||
"client",
|
||||
"server"
|
||||
"client"
|
||||
],
|
||||
"packaging": false,
|
||||
"auth": {
|
||||
@@ -5308,7 +5303,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listExecutions",
|
||||
"group": "executions",
|
||||
"weight": 394,
|
||||
"weight": 393,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5381,7 +5376,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createExecution",
|
||||
"group": "executions",
|
||||
"weight": 392,
|
||||
"weight": 391,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5466,7 +5461,7 @@
|
||||
"type": "string",
|
||||
"description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.",
|
||||
"default": null,
|
||||
"x-example": null
|
||||
"x-example": "<SCHEDULED_AT>"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5497,7 +5492,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getExecution",
|
||||
"group": "executions",
|
||||
"weight": 393,
|
||||
"weight": 392,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5568,7 +5563,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 308,
|
||||
"weight": 307,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
@@ -5641,7 +5636,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 307,
|
||||
"weight": 306,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
@@ -6122,7 +6117,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 354,
|
||||
"weight": 353,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6206,7 +6201,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 358,
|
||||
"weight": 357,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -8099,7 +8094,8 @@
|
||||
"any": {
|
||||
"description": "Any",
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
"additionalProperties": true,
|
||||
"example": []
|
||||
},
|
||||
"documentList": {
|
||||
"description": "Documents List",
|
||||
@@ -8124,7 +8120,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"documents"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"documents": ""
|
||||
}
|
||||
},
|
||||
"sessionList": {
|
||||
"description": "Sessions List",
|
||||
@@ -8149,7 +8149,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"sessions"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"sessions": ""
|
||||
}
|
||||
},
|
||||
"identityList": {
|
||||
"description": "Identities List",
|
||||
@@ -8174,7 +8178,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"identities"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"identities": ""
|
||||
}
|
||||
},
|
||||
"logList": {
|
||||
"description": "Logs List",
|
||||
@@ -8199,7 +8207,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"logs"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"logs": ""
|
||||
}
|
||||
},
|
||||
"fileList": {
|
||||
"description": "Files List",
|
||||
@@ -8224,7 +8236,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"files"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"files": ""
|
||||
}
|
||||
},
|
||||
"teamList": {
|
||||
"description": "Teams List",
|
||||
@@ -8249,7 +8265,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"teams"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"teams": ""
|
||||
}
|
||||
},
|
||||
"membershipList": {
|
||||
"description": "Memberships List",
|
||||
@@ -8274,7 +8294,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"memberships"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"memberships": ""
|
||||
}
|
||||
},
|
||||
"executionList": {
|
||||
"description": "Executions List",
|
||||
@@ -8299,7 +8323,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"executions"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"executions": ""
|
||||
}
|
||||
},
|
||||
"countryList": {
|
||||
"description": "Countries List",
|
||||
@@ -8324,7 +8352,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"countries"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"countries": ""
|
||||
}
|
||||
},
|
||||
"continentList": {
|
||||
"description": "Continents List",
|
||||
@@ -8349,7 +8381,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"continents"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"continents": ""
|
||||
}
|
||||
},
|
||||
"languageList": {
|
||||
"description": "Languages List",
|
||||
@@ -8374,7 +8410,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"languages"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"languages": ""
|
||||
}
|
||||
},
|
||||
"currencyList": {
|
||||
"description": "Currencies List",
|
||||
@@ -8399,7 +8439,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"currencies"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"currencies": ""
|
||||
}
|
||||
},
|
||||
"phoneList": {
|
||||
"description": "Phones List",
|
||||
@@ -8424,7 +8468,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"phones"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"phones": ""
|
||||
}
|
||||
},
|
||||
"localeCodeList": {
|
||||
"description": "Locale codes list",
|
||||
@@ -8449,7 +8497,11 @@
|
||||
"required": [
|
||||
"total",
|
||||
"localeCodes"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"total": 5,
|
||||
"localeCodes": ""
|
||||
}
|
||||
},
|
||||
"document": {
|
||||
"description": "Document",
|
||||
@@ -8506,7 +8558,23 @@
|
||||
"$createdAt",
|
||||
"$updatedAt",
|
||||
"$permissions"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$sequence": 1,
|
||||
"$collectionId": "5e5ea5c15117e",
|
||||
"$databaseId": "5e5ea5c15117e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$permissions": [
|
||||
"read(\"any\")"
|
||||
],
|
||||
"username": "john.doe",
|
||||
"email": "john.doe@example.com",
|
||||
"fullName": "John Doe",
|
||||
"age": 30,
|
||||
"isAdmin": false
|
||||
}
|
||||
},
|
||||
"log": {
|
||||
"description": "Log",
|
||||
@@ -8640,7 +8708,30 @@
|
||||
"deviceModel",
|
||||
"countryCode",
|
||||
"countryName"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"event": "account.sessions.create",
|
||||
"userId": "610fc2f985ee0",
|
||||
"userEmail": "john@appwrite.io",
|
||||
"userName": "John Doe",
|
||||
"mode": "admin",
|
||||
"ip": "127.0.0.1",
|
||||
"time": "2020-10-15T06:38:00.000+00:00",
|
||||
"osCode": "Mac",
|
||||
"osName": "Mac",
|
||||
"osVersion": "Mac",
|
||||
"clientType": "browser",
|
||||
"clientCode": "CM",
|
||||
"clientName": "Chrome Mobile iOS",
|
||||
"clientVersion": "84.0",
|
||||
"clientEngine": "WebKit",
|
||||
"clientEngineVersion": "605.1.15",
|
||||
"deviceName": "smartphone",
|
||||
"deviceBrand": "Google",
|
||||
"deviceModel": "Nexus 5",
|
||||
"countryCode": "US",
|
||||
"countryName": "United States"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"description": "User",
|
||||
@@ -8803,7 +8894,33 @@
|
||||
"prefs",
|
||||
"targets",
|
||||
"accessedAt"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"name": "John Doe",
|
||||
"password": "$argon2id$v=19$m=2048,t=4,p=3$aUZjLnliVWRINmFNTWMudg$5S+x+7uA31xFnrHFT47yFwcJeaP0w92L\/4LdgrVRXxE",
|
||||
"hash": "argon2",
|
||||
"hashOptions": {},
|
||||
"registration": "2020-10-15T06:38:00.000+00:00",
|
||||
"status": true,
|
||||
"labels": [
|
||||
"vip"
|
||||
],
|
||||
"passwordUpdate": "2020-10-15T06:38:00.000+00:00",
|
||||
"email": "john@appwrite.io",
|
||||
"phone": "+4930901820",
|
||||
"emailVerification": true,
|
||||
"phoneVerification": true,
|
||||
"mfa": true,
|
||||
"prefs": {
|
||||
"theme": "pink",
|
||||
"timezone": "UTC"
|
||||
},
|
||||
"targets": [],
|
||||
"accessedAt": "2020-10-15T06:38:00.000+00:00"
|
||||
}
|
||||
},
|
||||
"algoMd5": {
|
||||
"description": "AlgoMD5",
|
||||
@@ -8817,7 +8934,10 @@
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "md5"
|
||||
}
|
||||
},
|
||||
"algoSha": {
|
||||
"description": "AlgoSHA",
|
||||
@@ -8831,7 +8951,10 @@
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "sha"
|
||||
}
|
||||
},
|
||||
"algoPhpass": {
|
||||
"description": "AlgoPHPass",
|
||||
@@ -8845,7 +8968,10 @@
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "phpass"
|
||||
}
|
||||
},
|
||||
"algoBcrypt": {
|
||||
"description": "AlgoBcrypt",
|
||||
@@ -8859,7 +8985,10 @@
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "bcrypt"
|
||||
}
|
||||
},
|
||||
"algoScrypt": {
|
||||
"description": "AlgoScrypt",
|
||||
@@ -8901,7 +9030,14 @@
|
||||
"costMemory",
|
||||
"costParallel",
|
||||
"length"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "scrypt",
|
||||
"costCpu": 8,
|
||||
"costMemory": 14,
|
||||
"costParallel": 1,
|
||||
"length": 64
|
||||
}
|
||||
},
|
||||
"algoScryptModified": {
|
||||
"description": "AlgoScryptModified",
|
||||
@@ -8933,7 +9069,13 @@
|
||||
"salt",
|
||||
"saltSeparator",
|
||||
"signerKey"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "scryptMod",
|
||||
"salt": "UxLMreBr6tYyjQ==",
|
||||
"saltSeparator": "Bw==",
|
||||
"signerKey": "XyEKE9RcTDeLEsL\/RjwPDBv\/RqDl8fb3gpYEOQaPihbxf1ZAtSOHCjuAAa7Q3oHpCYhXSN9tizHgVOwn6krflQ=="
|
||||
}
|
||||
},
|
||||
"algoArgon2": {
|
||||
"description": "AlgoArgon2",
|
||||
@@ -8968,12 +9110,23 @@
|
||||
"memoryCost",
|
||||
"timeCost",
|
||||
"threads"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"type": "argon2",
|
||||
"memoryCost": 65536,
|
||||
"timeCost": 4,
|
||||
"threads": 3
|
||||
}
|
||||
},
|
||||
"preferences": {
|
||||
"description": "Preferences",
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
"additionalProperties": true,
|
||||
"example": {
|
||||
"language": "en",
|
||||
"timezone": "UTC",
|
||||
"darkTheme": true
|
||||
}
|
||||
},
|
||||
"session": {
|
||||
"description": "Session",
|
||||
@@ -9160,7 +9313,40 @@
|
||||
"factors",
|
||||
"secret",
|
||||
"mfaUpdatedAt"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"userId": "5e5bb8c16897e",
|
||||
"expire": "2020-10-15T06:38:00.000+00:00",
|
||||
"provider": "email",
|
||||
"providerUid": "user@example.com",
|
||||
"providerAccessToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
|
||||
"providerAccessTokenExpiry": "2020-10-15T06:38:00.000+00:00",
|
||||
"providerRefreshToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
|
||||
"ip": "127.0.0.1",
|
||||
"osCode": "Mac",
|
||||
"osName": "Mac",
|
||||
"osVersion": "Mac",
|
||||
"clientType": "browser",
|
||||
"clientCode": "CM",
|
||||
"clientName": "Chrome Mobile iOS",
|
||||
"clientVersion": "84.0",
|
||||
"clientEngine": "WebKit",
|
||||
"clientEngineVersion": "605.1.15",
|
||||
"deviceName": "smartphone",
|
||||
"deviceBrand": "Google",
|
||||
"deviceModel": "Nexus 5",
|
||||
"countryCode": "US",
|
||||
"countryName": "United States",
|
||||
"current": true,
|
||||
"factors": [
|
||||
"email"
|
||||
],
|
||||
"secret": "5e5bb8c16897e",
|
||||
"mfaUpdatedAt": "2020-10-15T06:38:00.000+00:00"
|
||||
}
|
||||
},
|
||||
"identity": {
|
||||
"description": "Identity",
|
||||
@@ -9228,7 +9414,19 @@
|
||||
"providerAccessToken",
|
||||
"providerAccessTokenExpiry",
|
||||
"providerRefreshToken"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"userId": "5e5bb8c16897e",
|
||||
"provider": "email",
|
||||
"providerUid": "5e5bb8c16897e",
|
||||
"providerEmail": "user@example.com",
|
||||
"providerAccessToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
|
||||
"providerAccessTokenExpiry": "2020-10-15T06:38:00.000+00:00",
|
||||
"providerRefreshToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3"
|
||||
}
|
||||
},
|
||||
"token": {
|
||||
"description": "Token",
|
||||
@@ -9272,7 +9470,15 @@
|
||||
"secret",
|
||||
"expire",
|
||||
"phrase"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "bb8ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"userId": "5e5ea5c168bb8",
|
||||
"secret": "",
|
||||
"expire": "2020-10-15T06:38:00.000+00:00",
|
||||
"phrase": "Golden Fox"
|
||||
}
|
||||
},
|
||||
"jwt": {
|
||||
"description": "JWT",
|
||||
@@ -9286,7 +9492,10 @@
|
||||
},
|
||||
"required": [
|
||||
"jwt"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
||||
}
|
||||
},
|
||||
"locale": {
|
||||
"description": "Locale",
|
||||
@@ -9336,7 +9545,16 @@
|
||||
"continent",
|
||||
"eu",
|
||||
"currency"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"ip": "127.0.0.1",
|
||||
"countryCode": "US",
|
||||
"country": "United States",
|
||||
"continentCode": "NA",
|
||||
"continent": "North America",
|
||||
"eu": false,
|
||||
"currency": "USD"
|
||||
}
|
||||
},
|
||||
"localeCode": {
|
||||
"description": "LocaleCode",
|
||||
@@ -9356,7 +9574,11 @@
|
||||
"required": [
|
||||
"code",
|
||||
"name"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"code": "en-us",
|
||||
"name": "US"
|
||||
}
|
||||
},
|
||||
"file": {
|
||||
"description": "File",
|
||||
@@ -9438,7 +9660,22 @@
|
||||
"sizeOriginal",
|
||||
"chunksTotal",
|
||||
"chunksUploaded"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"bucketId": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$permissions": [
|
||||
"read(\"any\")"
|
||||
],
|
||||
"name": "Pink.png",
|
||||
"signature": "5d529fd02b544198ae075bd57c1762bb",
|
||||
"mimeType": "image\/png",
|
||||
"sizeOriginal": 17890,
|
||||
"chunksTotal": 17890,
|
||||
"chunksUploaded": 17890
|
||||
}
|
||||
},
|
||||
"team": {
|
||||
"description": "Team",
|
||||
@@ -9490,7 +9727,18 @@
|
||||
"name",
|
||||
"total",
|
||||
"prefs"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"name": "VIP",
|
||||
"total": 7,
|
||||
"prefs": {
|
||||
"theme": "pink",
|
||||
"timezone": "UTC"
|
||||
}
|
||||
}
|
||||
},
|
||||
"membership": {
|
||||
"description": "Membership",
|
||||
@@ -9581,7 +9829,24 @@
|
||||
"confirm",
|
||||
"mfa",
|
||||
"roles"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"userId": "5e5ea5c16897e",
|
||||
"userName": "John Doe",
|
||||
"userEmail": "john@appwrite.io",
|
||||
"teamId": "5e5ea5c16897e",
|
||||
"teamName": "VIP",
|
||||
"invited": "2020-10-15T06:38:00.000+00:00",
|
||||
"joined": "2020-10-15T06:38:00.000+00:00",
|
||||
"confirm": false,
|
||||
"mfa": false,
|
||||
"roles": [
|
||||
"owner"
|
||||
]
|
||||
}
|
||||
},
|
||||
"execution": {
|
||||
"description": "Execution",
|
||||
@@ -9714,7 +9979,36 @@
|
||||
"logs",
|
||||
"errors",
|
||||
"duration"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "5e5ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$permissions": [
|
||||
"any"
|
||||
],
|
||||
"functionId": "5e5ea6g16897e",
|
||||
"trigger": "http",
|
||||
"status": "processing",
|
||||
"requestMethod": "GET",
|
||||
"requestPath": "\/articles?id=5",
|
||||
"requestHeaders": [
|
||||
{
|
||||
"Content-Type": "application\/json"
|
||||
}
|
||||
],
|
||||
"responseStatusCode": 200,
|
||||
"responseBody": "",
|
||||
"responseHeaders": [
|
||||
{
|
||||
"Content-Type": "application\/json"
|
||||
}
|
||||
],
|
||||
"logs": "",
|
||||
"errors": "",
|
||||
"duration": 0.4,
|
||||
"scheduledAt": "2020-10-15T06:38:00.000+00:00"
|
||||
}
|
||||
},
|
||||
"country": {
|
||||
"description": "Country",
|
||||
@@ -9734,7 +10028,11 @@
|
||||
"required": [
|
||||
"name",
|
||||
"code"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"name": "United States",
|
||||
"code": "US"
|
||||
}
|
||||
},
|
||||
"continent": {
|
||||
"description": "Continent",
|
||||
@@ -9754,7 +10052,11 @@
|
||||
"required": [
|
||||
"name",
|
||||
"code"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"name": "Europe",
|
||||
"code": "EU"
|
||||
}
|
||||
},
|
||||
"language": {
|
||||
"description": "Language",
|
||||
@@ -9780,7 +10082,12 @@
|
||||
"name",
|
||||
"code",
|
||||
"nativeName"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"name": "Italian",
|
||||
"code": "it",
|
||||
"nativeName": "Italiano"
|
||||
}
|
||||
},
|
||||
"currency": {
|
||||
"description": "Currency",
|
||||
@@ -9832,7 +10139,16 @@
|
||||
"rounding",
|
||||
"code",
|
||||
"namePlural"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"symbol": "$",
|
||||
"name": "US dollar",
|
||||
"symbolNative": "$",
|
||||
"decimalDigits": 2,
|
||||
"rounding": 0,
|
||||
"code": "USD",
|
||||
"namePlural": "US dollars"
|
||||
}
|
||||
},
|
||||
"phone": {
|
||||
"description": "Phone",
|
||||
@@ -9858,7 +10174,12 @@
|
||||
"code",
|
||||
"countryCode",
|
||||
"countryName"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"code": "+1",
|
||||
"countryCode": "US",
|
||||
"countryName": "United States"
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"description": "Headers",
|
||||
@@ -9878,7 +10199,11 @@
|
||||
"required": [
|
||||
"name",
|
||||
"value"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"name": "Content-Type",
|
||||
"value": "application\/json"
|
||||
}
|
||||
},
|
||||
"mfaChallenge": {
|
||||
"description": "MFA Challenge",
|
||||
@@ -9910,7 +10235,13 @@
|
||||
"$createdAt",
|
||||
"userId",
|
||||
"expire"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "bb8ea5c16897e",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"userId": "5e5ea5c168bb8",
|
||||
"expire": "2020-10-15T06:38:00.000+00:00"
|
||||
}
|
||||
},
|
||||
"mfaRecoveryCodes": {
|
||||
"description": "MFA Recovery Codes",
|
||||
@@ -9930,7 +10261,13 @@
|
||||
},
|
||||
"required": [
|
||||
"recoveryCodes"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"recoveryCodes": [
|
||||
"a3kf0-s0cl2",
|
||||
"s0co1-as98s"
|
||||
]
|
||||
}
|
||||
},
|
||||
"mfaType": {
|
||||
"description": "MFAType",
|
||||
@@ -9950,7 +10287,11 @@
|
||||
"required": [
|
||||
"secret",
|
||||
"uri"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"secret": true,
|
||||
"uri": true
|
||||
}
|
||||
},
|
||||
"mfaFactors": {
|
||||
"description": "MFAFactors",
|
||||
@@ -9982,7 +10323,13 @@
|
||||
"phone",
|
||||
"email",
|
||||
"recoveryCode"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"totp": true,
|
||||
"phone": true,
|
||||
"email": true,
|
||||
"recoveryCode": true
|
||||
}
|
||||
},
|
||||
"subscriber": {
|
||||
"description": "Subscriber",
|
||||
@@ -10057,7 +10404,27 @@
|
||||
"userName",
|
||||
"topicId",
|
||||
"providerType"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "259125845563242502",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"targetId": "259125845563242502",
|
||||
"target": {
|
||||
"$id": "259125845563242502",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"providerType": "email",
|
||||
"providerId": "259125845563242502",
|
||||
"name": "ageon-app-email",
|
||||
"identifier": "random-mail@email.org",
|
||||
"userId": "5e5ea5c16897e"
|
||||
},
|
||||
"userId": "5e5ea5c16897e",
|
||||
"userName": "Aegon Targaryen",
|
||||
"topicId": "259125845563242502",
|
||||
"providerType": "email"
|
||||
}
|
||||
},
|
||||
"target": {
|
||||
"description": "Target",
|
||||
@@ -10119,7 +10486,18 @@
|
||||
"providerType",
|
||||
"identifier",
|
||||
"expired"
|
||||
]
|
||||
],
|
||||
"example": {
|
||||
"$id": "259125845563242502",
|
||||
"$createdAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
|
||||
"name": "Apple iPhone 12",
|
||||
"userId": "259125845563242502",
|
||||
"providerId": "259125845563242502",
|
||||
"providerType": "email",
|
||||
"identifier": "token",
|
||||
"expired": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"externalDocs": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -6,13 +6,8 @@ use Utopia\System\System;
|
||||
* List of Appwrite Sites templates
|
||||
*/
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
|
||||
// TODO: Development override
|
||||
if (System::getEnv('_APP_ENV') === 'development') {
|
||||
$hostname = 'localhost';
|
||||
}
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN', '');
|
||||
|
||||
$url = $protocol . '://' . $hostname;
|
||||
|
||||
|
||||
@@ -151,6 +151,24 @@ return [
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_DOMAIN_TARGET_CAA',
|
||||
'description' => 'A CAA record domain that can be used to validate custom domains. Value should be domain\'s hostname.',
|
||||
'introduction' => '',
|
||||
'default' => '',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_DNS',
|
||||
'description' => 'DNS server to use for domain validation. Default: 8.8.8.8',
|
||||
'introduction' => '',
|
||||
'default' => '8.8.8.8',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_CONSOLE_WHITELIST_ROOT',
|
||||
'description' => 'This option allows you to disable the creation of new users on the Appwrite console. When enabled only 1 user will be able to use the registration form. New users can be added by inviting them to your project. By default this option is enabled.',
|
||||
@@ -827,7 +845,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_FUNCTIONS_TIMEOUT',
|
||||
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new function. The default value is 900 seconds. This is the global limit, timeout for individual functions are configured in the function\'s settings or in appwrite.json.',
|
||||
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new function. The default value is 900 seconds. This is the global limit, timeout for individual functions are configured in the function\'s settings or in appwrite.config.json.',
|
||||
'introduction' => '0.7.0',
|
||||
'default' => '900',
|
||||
'required' => false,
|
||||
@@ -1070,7 +1088,7 @@ return [
|
||||
'variables' => [
|
||||
[
|
||||
'name' => '_APP_SITES_TIMEOUT',
|
||||
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new site. The default value is 900 seconds. This is the global limit, timeout for individual functions are configured in the sites\'s settings or in appwrite.json.',
|
||||
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new site. The default value is 900 seconds. This is the global limit, timeout for individual functions are configured in the sites\'s settings or in appwrite.config.json.',
|
||||
'introduction' => '1.7.0',
|
||||
'default' => '900',
|
||||
'required' => false,
|
||||
|
||||
+144
-43
@@ -21,6 +21,7 @@ use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Hooks\Hooks;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Redirect;
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
@@ -60,7 +61,6 @@ use Utopia\System\System;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Host;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\URL;
|
||||
use Utopia\Validator\WhiteList;
|
||||
@@ -71,6 +71,7 @@ $oauthDefaultFailure = '/console/auth/oauth2/failure';
|
||||
function sendSessionAlert(Locale $locale, Document $user, Document $project, Document $session, Mail $queueForMails)
|
||||
{
|
||||
$subject = $locale->getText("emails.sessionAlert.subject");
|
||||
$preview = $locale->getText("emails.sessionAlert.preview");
|
||||
$customTemplate = $project->getAttribute('templates', [])['email.sessionAlert-' . $locale->default] ?? [];
|
||||
|
||||
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-session-alert.tpl');
|
||||
@@ -132,6 +133,16 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
|
||||
->setSmtpSenderName($senderName);
|
||||
}
|
||||
|
||||
// session alerts should always have a client name!
|
||||
$clientName = $session->getAttribute('clientName');
|
||||
if (empty($clientName)) {
|
||||
// fallback to the user agent and then unknown!
|
||||
$userAgent = $session->getAttribute('userAgent');
|
||||
$clientName = !empty($userAgent) ? $userAgent : 'UNKNOWN';
|
||||
|
||||
$session->setAttribute('clientName', $clientName);
|
||||
}
|
||||
|
||||
$emailVariables = [
|
||||
'direction' => $locale->getText('settings.direction'),
|
||||
'date' => (new \DateTime())->format('F j'),
|
||||
@@ -148,6 +159,7 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
|
||||
|
||||
$queueForMails
|
||||
->setSubject($subject)
|
||||
->setPreview($preview)
|
||||
->setBody($body)
|
||||
->setVariables($emailVariables)
|
||||
->setRecipient($email)
|
||||
@@ -1182,16 +1194,23 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
||||
->label('abuse-limit', 50)
|
||||
->label('abuse-key', 'ip:{ip}')
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('oAuthProviders'), fn ($node) => (!$node['mock'])))) . '.')
|
||||
->param('success', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey'])
|
||||
->param('failure', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey'])
|
||||
->param('success', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey'])
|
||||
->param('failure', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey'])
|
||||
->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->action(function (string $provider, string $success, string $failure, array $scopes, Request $request, Response $response, Document $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
|
||||
$protocol = $request->getProtocol();
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
|
||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
|
||||
|
||||
if (!$providerEnabled) {
|
||||
@@ -1216,12 +1235,20 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
||||
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
}
|
||||
|
||||
$host = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
$redirectBase = $protocol . '://' . $host;
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$redirectBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$redirectBase .= ':' . $port;
|
||||
}
|
||||
|
||||
if (empty($success)) {
|
||||
$success = $protocol . '://' . $request->getHostname() . $oauthDefaultSuccess;
|
||||
$success = $redirectBase . $oauthDefaultSuccess;
|
||||
}
|
||||
|
||||
if (empty($failure)) {
|
||||
$failure = $protocol . '://' . $request->getHostname() . $oauthDefaultFailure;
|
||||
$failure = $redirectBase . $oauthDefaultFailure;
|
||||
}
|
||||
|
||||
$oauth2 = new $className($appId, $appSecret, $callback, [
|
||||
@@ -1251,9 +1278,14 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function (string $projectId, string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response) {
|
||||
|
||||
$domain = $request->getHostname();
|
||||
$protocol = $request->getProtocol();
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
|
||||
$params = $request->getParams();
|
||||
$params['project'] = $projectId;
|
||||
@@ -1262,7 +1294,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($protocol . '://' . $domain . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
|
||||
->redirect($callbackBase . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
|
||||
. \http_build_query($params));
|
||||
});
|
||||
|
||||
@@ -1282,9 +1314,14 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function (string $projectId, string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response) {
|
||||
|
||||
$domain = $request->getHostname();
|
||||
$protocol = $request->getProtocol();
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
|
||||
$params = $request->getParams();
|
||||
$params['project'] = $projectId;
|
||||
@@ -1293,7 +1330,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($protocol . '://' . $domain . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
|
||||
->redirect($callbackBase . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
|
||||
. \http_build_query($params));
|
||||
});
|
||||
|
||||
@@ -1317,15 +1354,24 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('platforms')
|
||||
->inject('devKey')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) {
|
||||
$protocol = $request->getProtocol();
|
||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, array $platforms, Document $devKey, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) {
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
|
||||
$validateURL = new URL();
|
||||
$redirect = new Redirect($platforms);
|
||||
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
|
||||
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
|
||||
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
|
||||
@@ -1352,11 +1398,11 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
$state = $defaultState;
|
||||
}
|
||||
|
||||
if (!$validateURL->isValid($state['success'])) {
|
||||
if ($devKey->isEmpty() && !$redirect->isValid($state['success'])) {
|
||||
throw new Exception(Exception::PROJECT_INVALID_SUCCESS_URL);
|
||||
}
|
||||
|
||||
if (!empty($state['failure']) && !$validateURL->isValid($state['failure'])) {
|
||||
if ($devKey->isEmpty() && !empty($state['failure']) && !$redirect->isValid($state['failure'])) {
|
||||
throw new Exception(Exception::PROJECT_INVALID_FAILURE_URL);
|
||||
}
|
||||
$failure = [];
|
||||
@@ -1423,13 +1469,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
|
||||
$name = '';
|
||||
$nameOAuth = $oauth2->getUserName($accessToken);
|
||||
$userParam = \json_decode($request->getParam('user'), true);
|
||||
$userParam = $request->getParam('user');
|
||||
if (!empty($nameOAuth)) {
|
||||
$name = $nameOAuth;
|
||||
} elseif (is_array($userParam)) {
|
||||
$nameParam = $userParam['name'];
|
||||
if (is_array($nameParam) && isset($nameParam['firstName']) && isset($nameParam['lastName'])) {
|
||||
$name = $nameParam['firstName'] . ' ' . $nameParam['lastName'];
|
||||
} elseif ($userParam !== null) {
|
||||
$userDecoded = \json_decode($userParam, true);
|
||||
if (isset($userDecoded['name']['firstName']) && isset($userDecoded['name']['lastName'])) {
|
||||
$name = $userDecoded['name']['firstName'] . ' ' . $userDecoded['name']['lastName'];
|
||||
}
|
||||
}
|
||||
$email = $oauth2->getUserEmail($accessToken);
|
||||
@@ -1747,8 +1793,6 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
$state['success']['query'] = URLParser::unparseQuery($query);
|
||||
$state['success'] = URLParser::unparse($state['success']);
|
||||
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration));
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
@@ -1779,16 +1823,23 @@ App::get('/v1/account/tokens/oauth2/:provider')
|
||||
->label('abuse-limit', 50)
|
||||
->label('abuse-key', 'ip:{ip}')
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('oAuthProviders'), fn ($node) => (!$node['mock'])))) . '.')
|
||||
->param('success', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey'])
|
||||
->param('failure', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey'])
|
||||
->param('success', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey'])
|
||||
->param('failure', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey'])
|
||||
->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->action(function (string $provider, string $success, string $failure, array $scopes, Request $request, Response $response, Document $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
|
||||
$protocol = $request->getProtocol();
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
|
||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
|
||||
|
||||
if (!$providerEnabled) {
|
||||
@@ -1813,12 +1864,20 @@ App::get('/v1/account/tokens/oauth2/:provider')
|
||||
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
}
|
||||
|
||||
$host = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
$redirectBase = $protocol . '://' . $host;
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$redirectBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$redirectBase .= ':' . $port;
|
||||
}
|
||||
|
||||
if (empty($success)) {
|
||||
$success = $protocol . '://' . $request->getHostname() . $oauthDefaultSuccess;
|
||||
$success = $redirectBase . $oauthDefaultSuccess;
|
||||
}
|
||||
|
||||
if (empty($failure)) {
|
||||
$failure = $protocol . '://' . $request->getHostname() . $oauthDefaultFailure;
|
||||
$failure = $redirectBase . $oauthDefaultFailure;
|
||||
}
|
||||
|
||||
$oauth2 = new $className($appId, $appSecret, $callback, [
|
||||
@@ -1860,7 +1919,7 @@ App::post('/v1/account/tokens/magic-url')
|
||||
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
|
||||
->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey'])
|
||||
->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey'])
|
||||
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
@@ -1961,7 +2020,16 @@ App::post('/v1/account/tokens/magic-url')
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
|
||||
if (empty($url)) {
|
||||
$url = $request->getProtocol() . '://' . $request->getHostname() . '/console/auth/magic-url';
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$host = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $host;
|
||||
if ($protocol === 'https' && $port !== '443') {
|
||||
$callbackBase .= ':' . $port;
|
||||
} elseif ($protocol === 'http' && $port !== '80') {
|
||||
$callbackBase .= ':' . $port;
|
||||
}
|
||||
$url = $callbackBase . '/console/auth/magic-url';
|
||||
}
|
||||
|
||||
$url = Template::parseURL($url);
|
||||
@@ -1969,6 +2037,7 @@ App::post('/v1/account/tokens/magic-url')
|
||||
$url = Template::unParseURL($url);
|
||||
|
||||
$subject = $locale->getText("emails.magicSession.subject");
|
||||
$preview = $locale->getText("emails.magicSession.preview");
|
||||
$customTemplate = $project->getAttribute('templates', [])['email.magicSession-' . $locale->default] ?? [];
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
@@ -2057,6 +2126,7 @@ App::post('/v1/account/tokens/magic-url')
|
||||
|
||||
$queueForMails
|
||||
->setSubject($subject)
|
||||
->setPreview($preview)
|
||||
->setBody($body)
|
||||
->setVariables($emailVariables)
|
||||
->setRecipient($email)
|
||||
@@ -2169,7 +2239,30 @@ App::post('/v1/account/tokens/email')
|
||||
]);
|
||||
|
||||
$user->removeAttribute('$sequence');
|
||||
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
|
||||
$user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
|
||||
try {
|
||||
$target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([
|
||||
'$permissions' => [
|
||||
Permission::read(Role::user($user->getId())),
|
||||
Permission::update(Role::user($user->getId())),
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getSequence(),
|
||||
'providerType' => MESSAGE_TYPE_EMAIL,
|
||||
'identifier' => $email,
|
||||
])));
|
||||
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $target]);
|
||||
} catch (Duplicate) {
|
||||
$existingTarget = $dbForProject->findOne('targets', [
|
||||
Query::equal('identifier', [$email]),
|
||||
]);
|
||||
if (!$existingTarget->isEmpty()) {
|
||||
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
}
|
||||
|
||||
$tokenSecret = Auth::codeGenerator(6);
|
||||
@@ -2198,6 +2291,7 @@ App::post('/v1/account/tokens/email')
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
|
||||
$subject = $locale->getText("emails.otpSession.subject");
|
||||
$preview = $locale->getText("emails.otpSession.preview");
|
||||
$customTemplate = $project->getAttribute('templates', [])['email.otpSession-' . $locale->default] ?? [];
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
@@ -2283,6 +2377,7 @@ App::post('/v1/account/tokens/email')
|
||||
|
||||
$queueForMails
|
||||
->setSubject($subject)
|
||||
->setPreview($preview)
|
||||
->setBody($body)
|
||||
->setVariables($emailVariables)
|
||||
->setRecipient($email)
|
||||
@@ -3146,7 +3241,7 @@ App::post('/v1/account/recovery')
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients', 'devKey'])
|
||||
->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['platforms', 'devKey'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
@@ -3177,7 +3272,7 @@ App::post('/v1/account/recovery')
|
||||
throw new Exception(Exception::USER_BLOCKED);
|
||||
}
|
||||
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_RECOVERY);
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_RECOVERY));
|
||||
|
||||
$secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_RECOVERY);
|
||||
$recovery = new Document([
|
||||
@@ -3209,6 +3304,7 @@ App::post('/v1/account/recovery')
|
||||
$projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]');
|
||||
$body = $locale->getText("emails.recovery.body");
|
||||
$subject = $locale->getText("emails.recovery.subject");
|
||||
$preview = $locale->getText("emails.recovery.preview");
|
||||
$customTemplate = $project->getAttribute('templates', [])['email.recovery-' . $locale->default] ?? [];
|
||||
|
||||
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl');
|
||||
@@ -3283,6 +3379,7 @@ App::post('/v1/account/recovery')
|
||||
->setBody($body)
|
||||
->setVariables($emailVariables)
|
||||
->setSubject($subject)
|
||||
->setPreview($preview)
|
||||
->trigger();
|
||||
|
||||
$recovery->setAttribute('secret', $secret);
|
||||
@@ -3413,7 +3510,7 @@ App::post('/v1/account/verification')
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},userId:{userId}')
|
||||
->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients', 'devKey']) // TODO add built-in confirm page
|
||||
->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['platforms', 'devKey']) // TODO add built-in confirm page
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
@@ -3434,7 +3531,7 @@ App::post('/v1/account/verification')
|
||||
}
|
||||
|
||||
$verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION);
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM));
|
||||
|
||||
$verification = new Document([
|
||||
'$id' => ID::unique(),
|
||||
@@ -3464,6 +3561,7 @@ App::post('/v1/account/verification')
|
||||
|
||||
$projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]');
|
||||
$body = $locale->getText("emails.verification.body");
|
||||
$preview = $locale->getText("emails.verification.preview");
|
||||
$subject = $locale->getText("emails.verification.subject");
|
||||
$customTemplate = $project->getAttribute('templates', [])['email.verification-' . $locale->default] ?? [];
|
||||
|
||||
@@ -3536,6 +3634,7 @@ App::post('/v1/account/verification')
|
||||
|
||||
$queueForMails
|
||||
->setSubject($subject)
|
||||
->setPreview($preview)
|
||||
->setBody($body)
|
||||
->setVariables($emailVariables)
|
||||
->setRecipient($user->getAttribute('email'))
|
||||
@@ -3682,7 +3781,7 @@ App::post('/v1/account/verification/phone')
|
||||
}
|
||||
|
||||
$secret ??= Auth::codeGenerator();
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM));
|
||||
|
||||
$verification = new Document([
|
||||
'$id' => ID::unique(),
|
||||
@@ -4290,7 +4389,7 @@ App::post('/v1/account/mfa/challenge')
|
||||
->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) {
|
||||
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM));
|
||||
$code = Auth::codeGenerator();
|
||||
$challenge = new Document([
|
||||
'userId' => $user->getId(),
|
||||
@@ -4381,6 +4480,7 @@ App::post('/v1/account/mfa/challenge')
|
||||
}
|
||||
|
||||
$subject = $locale->getText("emails.mfaChallenge.subject");
|
||||
$preview = $locale->getText("emails.mfaChallenge.preview");
|
||||
$customTemplate = $project->getAttribute('templates', [])['email.mfaChallenge-' . $locale->default] ?? [];
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
@@ -4457,6 +4557,7 @@ App::post('/v1/account/mfa/challenge')
|
||||
|
||||
$queueForMails
|
||||
->setSubject($subject)
|
||||
->setPreview($preview)
|
||||
->setBody($body)
|
||||
->setVariables($emailVariables)
|
||||
->setRecipient($user->getAttribute('email'))
|
||||
|
||||
@@ -10,6 +10,7 @@ use Appwrite\URL\URL as URLParse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use chillerlan\QRCode\QRCode;
|
||||
use chillerlan\QRCode\QROptions;
|
||||
use enshrined\svgSanitize\Sanitizer as SvgSanitizer;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
@@ -362,7 +363,8 @@ App::get('/v1/avatars/favicon')
|
||||
$client = new Client();
|
||||
try {
|
||||
$res = $client
|
||||
->setAllowRedirects(false)
|
||||
->setAllowRedirects(true)
|
||||
->setMaxRedirects(5)
|
||||
->setUserAgent(\sprintf(
|
||||
APP_USERAGENT,
|
||||
System::getEnv('_APP_VERSION', 'UNKNOWN'),
|
||||
@@ -373,15 +375,11 @@ App::get('/v1/avatars/favicon')
|
||||
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
|
||||
}
|
||||
|
||||
if ($res->getStatusCode() !== 200) {
|
||||
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
|
||||
}
|
||||
|
||||
$doc = new DOMDocument();
|
||||
$doc->strictErrorChecking = false;
|
||||
@$doc->loadHTML($res->getBody());
|
||||
|
||||
$links = $doc->getElementsByTagName('link');
|
||||
$links = $doc->getElementsByTagName('link') ?? [];
|
||||
$outputHref = '';
|
||||
$outputExt = '';
|
||||
$space = 0;
|
||||
@@ -399,6 +397,12 @@ App::get('/v1/avatars/favicon')
|
||||
$ext = \pathinfo(\parse_url($absolute, PHP_URL_PATH), PATHINFO_EXTENSION);
|
||||
|
||||
switch ($ext) {
|
||||
case 'svg':
|
||||
// SVG icons are prioritized by assigning the maximum possible value.
|
||||
$space = PHP_INT_MAX;
|
||||
$outputHref = $absolute;
|
||||
$outputExt = $ext;
|
||||
break;
|
||||
case 'ico':
|
||||
case 'png':
|
||||
case 'jpg':
|
||||
@@ -437,7 +441,8 @@ App::get('/v1/avatars/favicon')
|
||||
$client = new Client();
|
||||
try {
|
||||
$res = $client
|
||||
->setAllowRedirects(false)
|
||||
->setAllowRedirects(true)
|
||||
->setMaxRedirects(5)
|
||||
->fetch($outputHref);
|
||||
} catch (\Throwable) {
|
||||
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
|
||||
@@ -449,14 +454,33 @@ App::get('/v1/avatars/favicon')
|
||||
|
||||
$data = $res->getBody();
|
||||
|
||||
if ('ico' == $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
|
||||
if (empty($data) || (\mb_substr($data, 0, 5) === '<html') || \mb_substr($data, 0, 5) === '<!doc') {
|
||||
if ('ico' === $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
|
||||
if (
|
||||
empty($data) ||
|
||||
stripos($data, '<html') === 0 ||
|
||||
stripos($data, '<!doc') === 0
|
||||
) {
|
||||
throw new Exception(Exception::AVATAR_ICON_NOT_FOUND, 'Favicon not found');
|
||||
}
|
||||
$response
|
||||
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
|
||||
->setContentType('image/x-icon')
|
||||
->file($data);
|
||||
return;
|
||||
}
|
||||
|
||||
if ('svg' === $outputExt) { // Skip crop, Imagick isn\'t supporting svg files
|
||||
$sanitizer = new SvgSanitizer();
|
||||
$sanitizer->minify(true);
|
||||
$cleanSvg = $sanitizer->sanitize($data);
|
||||
if ($cleanSvg === false) {
|
||||
throw new \Exception('SVG sanitization failed');
|
||||
}
|
||||
$response
|
||||
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
|
||||
->setContentType('image/svg+xml')
|
||||
->file($cleanSvg);
|
||||
return;
|
||||
}
|
||||
|
||||
$image = new Image($data);
|
||||
|
||||
@@ -71,6 +71,8 @@ App::get('/v1/console/variables')
|
||||
'_APP_DOMAIN_TARGET_CNAME' => System::getEnv('_APP_DOMAIN_TARGET_CNAME'),
|
||||
'_APP_DOMAIN_TARGET_AAAA' => System::getEnv('_APP_DOMAIN_TARGET_AAAA'),
|
||||
'_APP_DOMAIN_TARGET_A' => System::getEnv('_APP_DOMAIN_TARGET_A'),
|
||||
// Combine CAA domain with most common flags and tag (no parameters)
|
||||
'_APP_DOMAIN_TARGET_CAA' => '0 issue "' . System::getEnv('_APP_DOMAIN_TARGET_CAA') . '"',
|
||||
'_APP_STORAGE_LIMIT' => +System::getEnv('_APP_STORAGE_LIMIT'),
|
||||
'_APP_COMPUTE_SIZE_LIMIT' => +System::getEnv('_APP_COMPUTE_SIZE_LIMIT'),
|
||||
'_APP_USAGE_STATS' => System::getEnv('_APP_USAGE_STATS'),
|
||||
|
||||
@@ -3453,6 +3453,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
} else {
|
||||
$relation->removeAttribute('$collectionId');
|
||||
$relation->removeAttribute('$databaseId');
|
||||
$relation->removeAttribute('$sequence');
|
||||
$relation->setAttribute('$collection', $relatedCollection->getId());
|
||||
$type = Database::PERMISSION_UPDATE;
|
||||
}
|
||||
@@ -3485,6 +3486,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
}
|
||||
}
|
||||
|
||||
// Remove sequence if set
|
||||
unset($document['$sequence']);
|
||||
|
||||
// Assign a unique ID if needed, otherwise use the provided ID.
|
||||
$document['$id'] = $sourceId === 'unique()' ? ID::unique() : $sourceId;
|
||||
$document = new Document($document);
|
||||
@@ -4074,6 +4078,9 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
|
||||
$permissions = $document->getPermissions() ?? [];
|
||||
}
|
||||
|
||||
// Remove sequence if set
|
||||
unset($data['$sequence']);
|
||||
|
||||
$data['$id'] = $documentId;
|
||||
$data['$permissions'] = $permissions;
|
||||
$newDocument = new Document($data);
|
||||
@@ -4126,6 +4133,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum
|
||||
));
|
||||
$relation->removeAttribute('$collectionId');
|
||||
$relation->removeAttribute('$databaseId');
|
||||
$relation->removeAttribute('$sequence');
|
||||
// Attribute $collection is required for Utopia.
|
||||
$relation->setAttribute(
|
||||
'$collection',
|
||||
@@ -4257,10 +4265,11 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
|
||||
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true)
|
||||
->inject('requestTimestamp')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->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) {
|
||||
->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) {
|
||||
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
||||
|
||||
if (empty($data) && \is_null($permissions)) {
|
||||
@@ -4280,12 +4289,28 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
|
||||
throw new Exception(Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Map aggregate permissions into the multiple permissions they represent.
|
||||
$permissions = Permission::aggregate($permissions, [
|
||||
$allowedPermissions = [
|
||||
Database::PERMISSION_READ,
|
||||
Database::PERMISSION_UPDATE,
|
||||
Database::PERMISSION_DELETE,
|
||||
]);
|
||||
];
|
||||
|
||||
$permissions = Permission::aggregate($permissions, $allowedPermissions);
|
||||
// if no permission, upsert permission from the old document if present (update scenario) else add default permission (create scenario)
|
||||
if (\is_null($permissions)) {
|
||||
$oldDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId));
|
||||
if ($oldDocument->isEmpty()) {
|
||||
if (!empty($user->getId())) {
|
||||
$defaultPermissions = [];
|
||||
foreach ($allowedPermissions as $permission) {
|
||||
$defaultPermissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
|
||||
}
|
||||
$permissions = $defaultPermissions;
|
||||
}
|
||||
} else {
|
||||
$permissions = $oldDocument->getPermissions();
|
||||
}
|
||||
}
|
||||
|
||||
// Users can only manage their own roles, API keys and Admin users can manage any
|
||||
$roles = Authorization::getRoles();
|
||||
@@ -4309,7 +4334,7 @@ App::put('/v1/databases/:databaseId/collections/:collectionId/documents/:documen
|
||||
}
|
||||
|
||||
$data['$id'] = $documentId;
|
||||
$data['$permissions'] = $permissions;
|
||||
$data['$permissions'] = $permissions ?? [];
|
||||
$newDocument = new Document($data);
|
||||
|
||||
$operations = 0;
|
||||
@@ -4691,6 +4716,9 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
}
|
||||
}
|
||||
|
||||
// Remove sequence if set
|
||||
unset($data['$sequence']);
|
||||
|
||||
$documents = [];
|
||||
|
||||
try {
|
||||
@@ -4728,7 +4756,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
});
|
||||
|
||||
App::put('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
->desc('Create or update documents')
|
||||
->desc('Upsert documents')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'documents.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
|
||||
@@ -522,12 +522,11 @@ 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('publisherDatabases')
|
||||
->inject('response')
|
||||
->action(function (string $name, int|string $threshold, Publisher $publisher, Response $response) {
|
||||
->action(function (string $name, int|string $threshold, Publisher $publisherDatabases, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$size = $publisher->getQueueSize(new Queue($name));
|
||||
$size = $publisherDatabases->getQueueSize(new Queue($name));
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
@@ -654,12 +653,12 @@ 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('publisherMigrations')
|
||||
->inject('response')
|
||||
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
|
||||
->action(function (int|string $threshold, Publisher $publisherMigrations, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$size = $publisher->getQueueSize(new Queue(Event::MIGRATIONS_QUEUE_NAME));
|
||||
$size = $publisherMigrations->getQueueSize(new Queue(Event::MIGRATIONS_QUEUE_NAME));
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
|
||||
@@ -361,7 +361,7 @@ App::post('/v1/migrations/csv')
|
||||
$hasCompression = $compression !== Compression::NONE;
|
||||
|
||||
$migrationId = ID::unique();
|
||||
$newPath = $deviceForImports->getPath('/' . $migrationId . '_' . $fileId . '.csv');
|
||||
$newPath = $deviceForImports->getPath($migrationId . '_' . $fileId . '.csv');
|
||||
|
||||
if ($hasEncryption || $hasCompression) {
|
||||
$source = $deviceForFiles->read($path);
|
||||
@@ -410,8 +410,8 @@ App::post('/v1/migrations/csv')
|
||||
'resources' => $resources,
|
||||
'resourceId' => $resourceId,
|
||||
'resourceType' => Resource::TYPE_DATABASE,
|
||||
'statusCounters' => [],
|
||||
'resourceData' => [],
|
||||
'statusCounters' => '{}',
|
||||
'resourceData' => '{}',
|
||||
'errors' => [],
|
||||
'options' => [
|
||||
'path' => $newPath,
|
||||
|
||||
@@ -8,8 +8,8 @@ use Appwrite\Event\Mail;
|
||||
use Appwrite\Event\Validator\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Hooks\Hooks;
|
||||
use Appwrite\Network\Platform;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
@@ -1790,7 +1790,7 @@ App::post('/v1/projects/:projectId/platforms')
|
||||
]
|
||||
))
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('type', null, new WhiteList([Origin::CLIENT_TYPE_WEB, Origin::CLIENT_TYPE_FLUTTER_WEB, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_LINUX, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_APPLE_IOS, Origin::CLIENT_TYPE_APPLE_MACOS, Origin::CLIENT_TYPE_APPLE_WATCHOS, Origin::CLIENT_TYPE_APPLE_TVOS, Origin::CLIENT_TYPE_ANDROID, Origin::CLIENT_TYPE_UNITY, Origin::CLIENT_TYPE_REACT_NATIVE_IOS, Origin::CLIENT_TYPE_REACT_NATIVE_ANDROID], true), 'Platform type.')
|
||||
->param('type', null, new WhiteList([Platform::TYPE_WEB, Platform::TYPE_FLUTTER_WEB, Platform::TYPE_FLUTTER_IOS, Platform::TYPE_FLUTTER_ANDROID, Platform::TYPE_FLUTTER_LINUX, Platform::TYPE_FLUTTER_MACOS, Platform::TYPE_FLUTTER_WINDOWS, Platform::TYPE_APPLE_IOS, Platform::TYPE_APPLE_MACOS, Platform::TYPE_APPLE_WATCHOS, Platform::TYPE_APPLE_TVOS, Platform::TYPE_ANDROID, Platform::TYPE_UNITY, Platform::TYPE_REACT_NATIVE_IOS, Platform::TYPE_REACT_NATIVE_ANDROID], true), 'Platform type.')
|
||||
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
|
||||
->param('key', '', new Text(256), 'Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.', true)
|
||||
->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true)
|
||||
@@ -2175,7 +2175,7 @@ App::post('/v1/projects/:projectId/smtp/tests')
|
||||
->setSmtpSenderName($senderName)
|
||||
->setRecipient($email)
|
||||
->setName('')
|
||||
->setbodyTemplate(__DIR__ . '/../../config/locale/templates/email-base-styled.tpl')
|
||||
->setBodyTemplate(__DIR__ . '/../../config/locale/templates/email-base-styled.tpl')
|
||||
->setBody($template->render())
|
||||
->setVariables([])
|
||||
->setSubject($subject)
|
||||
@@ -2268,16 +2268,53 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
|
||||
$localeObj = new Locale($locale);
|
||||
if (is_null($template)) {
|
||||
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl');
|
||||
/**
|
||||
* different templates, different placeholders.
|
||||
*/
|
||||
$templateConfigs = [
|
||||
'magicSession' => [
|
||||
'file' => 'email-magic-url.tpl',
|
||||
'placeholders' => ['optionButton', 'buttonText', 'optionUrl', 'clientInfo', 'securityPhrase']
|
||||
],
|
||||
'mfaChallenge' => [
|
||||
'file' => 'email-mfa-challenge.tpl',
|
||||
'placeholders' => ['description', 'clientInfo']
|
||||
],
|
||||
'otpSession' => [
|
||||
'file' => 'email-otp.tpl',
|
||||
'placeholders' => ['description', 'clientInfo', 'securityPhrase']
|
||||
],
|
||||
'sessionAlert' => [
|
||||
'file' => 'email-session-alert.tpl',
|
||||
'placeholders' => ['body', 'listDevice', 'listIpAddress', 'listCountry', 'footer']
|
||||
],
|
||||
];
|
||||
|
||||
// fallback to the base template.
|
||||
$config = $templateConfigs[$type] ?? [
|
||||
'file' => 'email-inner-base.tpl',
|
||||
'placeholders' => ['buttonText', 'body', 'footer']
|
||||
];
|
||||
|
||||
$templateString = file_get_contents(__DIR__ . '/../../config/locale/templates/' . $config['file']);
|
||||
|
||||
// We use `fromString` due to the replace above
|
||||
$message = Template::fromString($templateString);
|
||||
|
||||
// Set type-specific parameters
|
||||
foreach ($config['placeholders'] as $param) {
|
||||
$escapeHtml = !in_array($param, ['clientInfo', 'body', 'footer', 'description']);
|
||||
$message->setParam("{{{$param}}}", $localeObj->getText("emails.{$type}.{$param}"), escapeHtml: $escapeHtml);
|
||||
}
|
||||
|
||||
$message
|
||||
// common placeholders on all the templates
|
||||
->setParam('{{hello}}', $localeObj->getText("emails.{$type}.hello"))
|
||||
->setParam('{{footer}}', $localeObj->getText("emails.{$type}.footer"))
|
||||
->setParam('{{body}}', $localeObj->getText('emails.' . $type . '.body'), escapeHtml: false)
|
||||
->setParam('{{thanks}}', $localeObj->getText("emails.{$type}.thanks"))
|
||||
->setParam('{{buttonText}}', $localeObj->getText("emails.{$type}.buttonText"))
|
||||
->setParam('{{signature}}', $localeObj->getText("emails.{$type}.signature"))
|
||||
->setParam('{{direction}}', $localeObj->getText('settings.direction'));
|
||||
$message = $message->render();
|
||||
->setParam('{{signature}}', $localeObj->getText("emails.{$type}.signature"));
|
||||
|
||||
// `useContent: false` will strip new lines!
|
||||
$message = $message->render(useContent: true);
|
||||
|
||||
$template = [
|
||||
'message' => $message,
|
||||
|
||||
@@ -286,6 +286,19 @@ App::patch('/v1/proxy/rules/:ruleId/verification')
|
||||
throw new Exception(Exception::RULE_VERIFICATION_FAILED);
|
||||
}
|
||||
|
||||
// Ensure CAA won't block certificate issuance
|
||||
if (!empty(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''))) {
|
||||
$validationStart = \microtime(true);
|
||||
$validator = new DNS(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''), DNS::RECORD_CAA);
|
||||
if (!$validator->isValid($domain->get())) {
|
||||
$log->addExtra('dnsTimingCaa', \strval(\microtime(true) - $validationStart));
|
||||
$log->addTag('dnsDomain', $domain->get());
|
||||
$error = $validator->getDescription();
|
||||
$log->addExtra('dnsResponse', \is_array($error) ? \json_encode($error) : \strval($error));
|
||||
throw new Exception(Exception::RULE_VERIFICATION_FAILED, 'Domain verification failed because CAA records do not allow Appwrite\'s certificate issuer.');
|
||||
}
|
||||
}
|
||||
|
||||
$dbForPlatform->updateDocument('rules', $rule->getId(), $rule->setAttribute('status', 'verifying'));
|
||||
|
||||
// Issue a TLS certificate when domain is verified
|
||||
|
||||
@@ -18,6 +18,7 @@ use Appwrite\Utopia\Database\Validator\Queries\Buckets;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Files;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
@@ -953,17 +954,20 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
->param('output', '', new WhiteList(\array_keys(Config::getParam('storage-outputs')), true), 'Output format type (jpeg, jpg, png, gif and webp).', true)
|
||||
// NOTE: this is only for the sdk generator and is not used in the action below and is utilised in `resources.php` for `resourceToken`.
|
||||
->param('token', '', new Text(512), 'File token for accessing this file.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('resourceToken')
|
||||
->inject('deviceForFiles')
|
||||
->inject('deviceForLocal')
|
||||
->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, ?string $token, Response $response, Database $dbForProject, Document $resourceToken, Device $deviceForFiles, Device $deviceForLocal) {
|
||||
->inject('project')
|
||||
->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, ?string $token, Request $request, Response $response, Database $dbForProject, Document $resourceToken, Device $deviceForFiles, Device $deviceForLocal, Document $project) {
|
||||
|
||||
if (!\extension_loaded('imagick')) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
|
||||
}
|
||||
|
||||
/* @type Document $bucket */
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
@@ -984,6 +988,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
if ($fileSecurity && !$valid && !$isToken) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
/* @type Document $file */
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
|
||||
@@ -1035,8 +1040,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
$output = empty($type) ? (array_search($mime, $outputs) ?? 'jpg') : $type;
|
||||
}
|
||||
|
||||
$startTime = \microtime(true);
|
||||
|
||||
$source = $deviceForFiles->read($path);
|
||||
|
||||
$downloadTime = \microtime(true) - $startTime;
|
||||
|
||||
if (!empty($cipher)) { // Decrypt
|
||||
$source = OpenSSL::decrypt(
|
||||
$source,
|
||||
@@ -1048,6 +1057,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
);
|
||||
}
|
||||
|
||||
$decryptionTime = \microtime(true) - $startTime - $downloadTime;
|
||||
|
||||
switch ($algorithm) {
|
||||
case Compression::ZSTD:
|
||||
$compressor = new Zstd();
|
||||
@@ -1059,6 +1070,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
break;
|
||||
}
|
||||
|
||||
$decompressionTime = \microtime(true) - $startTime - $downloadTime - $decryptionTime;
|
||||
|
||||
try {
|
||||
$image = new Image($source);
|
||||
} catch (ImagickException $e) {
|
||||
@@ -1089,6 +1102,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
|
||||
$data = $image->output($output, $quality);
|
||||
|
||||
$renderingTime = \microtime(true) - $startTime - $downloadTime - $decryptionTime - $decompressionTime;
|
||||
|
||||
$totalTime = \microtime(true) - $startTime;
|
||||
|
||||
Console::info("File preview rendered,project=" . $project->getId() . ",bucket=" . $bucketId . ",file=" . $file->getId() . ",uri=" . $request->getURI() . ",total=" . $totalTime . ",rendering=" . $renderingTime . ",decryption=" . $decryptionTime . ",decompression=" . $decompressionTime . ",download=" . $downloadTime);
|
||||
|
||||
$contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg'];
|
||||
|
||||
//Do not update transformedAt if it's a console user
|
||||
@@ -1140,7 +1159,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
->inject('resourceToken')
|
||||
->inject('deviceForFiles')
|
||||
->action(function (string $bucketId, string $fileId, ?string $token, Request $request, Response $response, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
|
||||
|
||||
/* @type Document $bucket */
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
@@ -1158,9 +1177,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($fileSecurity && !$valid) {
|
||||
if ($fileSecurity && !$valid && !$isToken) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
/* @type Document $file */
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
|
||||
@@ -1300,6 +1320,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
->inject('resourceToken')
|
||||
->inject('deviceForFiles')
|
||||
->action(function (string $bucketId, string $fileId, ?string $token, Response $response, Request $request, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
|
||||
/* @type Document $bucket */
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
@@ -1317,9 +1338,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($fileSecurity && !$valid) {
|
||||
if ($fileSecurity && !$valid && !$isToken) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
/* @type Document $file */
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
|
||||
@@ -1848,7 +1870,7 @@ App::get('/v1/storage/usage')
|
||||
$total = [];
|
||||
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
|
||||
foreach ($metrics as $metric) {
|
||||
$result = $dbForProject->findOne('stats', [
|
||||
$result = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
]);
|
||||
@@ -1877,7 +1899,7 @@ App::get('/v1/storage/usage')
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
$usage[$metric]['total'] = $stats[$metric]['total'];
|
||||
$usage[$metric]['total'] = $stats[$metric]['total'];
|
||||
$usage[$metric]['data'] = [];
|
||||
$leap = time() - ($days['limit'] * $days['factor']);
|
||||
while ($leap < time()) {
|
||||
@@ -1895,8 +1917,8 @@ App::get('/v1/storage/usage')
|
||||
'filesTotal' => $usage[$metrics[1]]['total'],
|
||||
'filesStorageTotal' => $usage[$metrics[2]]['total'],
|
||||
'buckets' => $usage[$metrics[0]]['data'],
|
||||
'files' => $usage[$metrics[1]]['data'],
|
||||
'storage' => $usage[$metrics[2]]['data'],
|
||||
'files' => $usage[$metrics[1]]['data'],
|
||||
'storage' => $usage[$metrics[2]]['data'],
|
||||
]), Response::MODEL_USAGE_STORAGE);
|
||||
});
|
||||
|
||||
@@ -1948,7 +1970,7 @@ App::get('/v1/storage/:bucketId/usage')
|
||||
? $dbForLogs
|
||||
: $dbForProject;
|
||||
|
||||
$result = $db->findOne('stats', [
|
||||
$result = $db->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
]);
|
||||
@@ -1978,7 +2000,7 @@ App::get('/v1/storage/:bucketId/usage')
|
||||
};
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
$usage[$metric]['total'] = $stats[$metric]['total'];
|
||||
$usage[$metric]['total'] = $stats[$metric]['total'];
|
||||
$usage[$metric]['data'] = [];
|
||||
$leap = time() - ($days['limit'] * $days['factor']);
|
||||
while ($leap < time()) {
|
||||
|
||||
@@ -11,6 +11,7 @@ use Appwrite\Event\Messaging;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Redirect;
|
||||
use Appwrite\Platform\Workers\Deletes;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
@@ -50,7 +51,6 @@ use Utopia\Locale\Locale;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Host;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\URL;
|
||||
use Utopia\Validator\WhiteList;
|
||||
@@ -466,7 +466,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
}
|
||||
return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}, 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project'])
|
||||
->param('url', '', fn ($clients, $devKey) => $devKey->isEmpty() ? new Host($clients) : new URL(), 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients', 'devKey']) // TODO add our own built-in confirm page
|
||||
->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey']) // TODO add our own built-in confirm page
|
||||
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
@@ -657,6 +657,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
$projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]');
|
||||
|
||||
$body = $locale->getText("emails.invitation.body");
|
||||
$preview = $locale->getText("emails.invitation.preview");
|
||||
$subject = \sprintf($locale->getText("emails.invitation.subject"), $team->getAttribute('name'), $projectName);
|
||||
$customTemplate = $project->getAttribute('templates', [])['email.invitation-' . $locale->default] ?? [];
|
||||
|
||||
@@ -729,6 +730,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
$queueForMails
|
||||
->setSubject($subject)
|
||||
->setBody($body)
|
||||
->setPreview($preview)
|
||||
->setRecipient($invitee->getAttribute('email'))
|
||||
->setName($invitee->getAttribute('name', ''))
|
||||
->setVariables($emailVariables)
|
||||
|
||||
@@ -649,6 +649,8 @@ App::get('/v1/users')
|
||||
$total = $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'users' => $users,
|
||||
|
||||
+20
-12
@@ -4,6 +4,7 @@ use Appwrite\Auth\OAuth2\Github as OAuth2Github;
|
||||
use Appwrite\Event\Build;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Network\Validator\Redirect;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
@@ -53,7 +54,6 @@ use Utopia\Detector\Detector\Runtime;
|
||||
use Utopia\Detector\Detector\Strategy;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Host;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
@@ -116,8 +116,10 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
}
|
||||
|
||||
$commentStatus = $isAuthorized ? 'waiting' : 'failed';
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
|
||||
$authorizeUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}";
|
||||
$authorizeUrl = $protocol . '://' . $hostname . "/console/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}";
|
||||
|
||||
$action = $isAuthorized ? ['type' => 'logs'] : ['type' => 'authorize', 'url' => $authorizeUrl];
|
||||
|
||||
@@ -378,7 +380,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
}
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
|
||||
$providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/$resourceCollection/$resourceType-$resourceId";
|
||||
$providerTargetUrl = $protocol . '://' . $hostname . "/console/project-$region-$projectId/$resourceCollection/$resourceType-$resourceId";
|
||||
$github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'pending', $message, $providerTargetUrl, $name);
|
||||
}
|
||||
|
||||
@@ -424,8 +426,8 @@ App::get('/v1/vcs/github/authorize')
|
||||
type: MethodType::WEBAUTH,
|
||||
hide: true,
|
||||
))
|
||||
->param('success', '', fn ($clients) => new Host($clients), 'URL to redirect back to console after a successful installation attempt.', true, ['clients'])
|
||||
->param('failure', '', fn ($clients) => new Host($clients), 'URL to redirect back to console after a failed installation attempt.', true, ['clients'])
|
||||
->param('success', '', fn ($platforms) => new Redirect($platforms), 'URL to redirect back to console after a successful installation attempt.', true, ['platforms'])
|
||||
->param('failure', '', fn ($platforms) => new Redirect($platforms), 'URL to redirect back to console after a failed installation attempt.', true, ['platforms'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
@@ -437,6 +439,8 @@ App::get('/v1/vcs/github/authorize')
|
||||
]);
|
||||
|
||||
$appName = System::getEnv('_APP_VCS_GITHUB_APP_NAME');
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
|
||||
if (empty($appName)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'GitHub App name is not configured. Please configure VCS (Version Control System) variables in .env file.');
|
||||
@@ -444,7 +448,7 @@ App::get('/v1/vcs/github/authorize')
|
||||
|
||||
$url = "https://github.com/apps/$appName/installations/new?" . \http_build_query([
|
||||
'state' => $state,
|
||||
'redirect_uri' => $request->getProtocol() . '://' . $request->getHostname() . "/v1/vcs/github/callback"
|
||||
'redirect_uri' => $protocol . '://' . $hostname . "/v1/vcs/github/callback"
|
||||
]);
|
||||
|
||||
$response
|
||||
@@ -494,10 +498,12 @@ App::get('/v1/vcs/github/callback')
|
||||
}
|
||||
|
||||
$region = $project->getAttribute('region', 'default');
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
|
||||
$defaultState = [
|
||||
'success' => $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/settings/git-installations",
|
||||
'failure' => $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/settings/git-installations",
|
||||
'success' => $protocol . '://' . $hostname . "/console/project-$region-$projectId/settings/git-installations",
|
||||
'failure' => $protocol . '://' . $hostname . "/console/project-$region-$projectId/settings/git-installations",
|
||||
];
|
||||
|
||||
$state = \array_merge($defaultState, $state ?? []);
|
||||
@@ -1131,6 +1137,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
$repository['pushedAt'] = $repository['pushed_at'] ?? '';
|
||||
$repository['organization'] = $installation->getAttribute('organization', '');
|
||||
$repository['provider'] = $installation->getAttribute('provider', '');
|
||||
$repository['defaultBranch'] = $repository['default_branch'] ?? '';
|
||||
|
||||
$response->dynamic(new Document($repository), Response::MODEL_PROVIDER_REPOSITORY);
|
||||
});
|
||||
@@ -1228,7 +1235,8 @@ App::post('/v1/vcs/github/events')
|
||||
$providerRepositoryUrl = $parsedPayload["repositoryUrl"] ?? '';
|
||||
$providerCommitHash = $parsedPayload["commitHash"] ?? '';
|
||||
$providerRepositoryOwner = $parsedPayload["owner"] ?? '';
|
||||
$providerCommitAuthor = $parsedPayload["headCommitAuthor"] ?? '';
|
||||
$providerCommitAuthorName = $parsedPayload["headCommitAuthorName"] ?? '';
|
||||
$providerCommitAuthorEmail = $parsedPayload["headCommitAuthorEmail"] ?? '';
|
||||
$providerCommitAuthorUrl = $parsedPayload["authorUrl"] ?? '';
|
||||
$providerCommitMessage = $parsedPayload["headCommitMessage"] ?? '';
|
||||
$providerCommitUrl = $parsedPayload["headCommitUrl"] ?? '';
|
||||
@@ -1241,9 +1249,9 @@ App::post('/v1/vcs/github/events')
|
||||
Query::limit(100),
|
||||
]));
|
||||
|
||||
// create new deployment only on push and not when branch is created or deleted
|
||||
if (!$providerBranchCreated && !$providerBranchDeleted) {
|
||||
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForPlatform, $queueForBuilds, $getProjectDB, $request);
|
||||
// create new deployment only on push (not committed by us) and not when branch is created or deleted
|
||||
if ($providerCommitAuthorEmail !== APP_VCS_GITHUB_EMAIL && !$providerBranchCreated && !$providerBranchDeleted) {
|
||||
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthorName, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForPlatform, $queueForBuilds, $getProjectDB, $request);
|
||||
}
|
||||
} elseif ($event == $github::EVENT_INSTALLATION) {
|
||||
if ($parsedPayload["action"] == "deleted") {
|
||||
|
||||
+41
-55
@@ -49,14 +49,13 @@ use Utopia\Logger\Log\User;
|
||||
use Utopia\Logger\Logger;
|
||||
use Utopia\Platform\Service;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Hostname;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
Config::setParam('domainVerification', false);
|
||||
Config::setParam('cookieDomain', 'localhost');
|
||||
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
|
||||
|
||||
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey)
|
||||
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey)
|
||||
{
|
||||
$host = $request->getHostname() ?? '';
|
||||
if (!empty($previewHostname)) {
|
||||
@@ -76,7 +75,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
}
|
||||
|
||||
$errorView = __DIR__ . '/../views/general/error.phtml';
|
||||
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . '://' . System::getEnv('_APP_DOMAIN', '');
|
||||
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . '://' . System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
|
||||
if ($rule->isEmpty()) {
|
||||
$appDomainFunctionsFallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
|
||||
@@ -118,6 +117,11 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
$project->setAttribute('accessedAt', DateTime::now());
|
||||
Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set projectId to update the Error hook logger, since x-appwrite-project is not available when executing custom domain function
|
||||
*/
|
||||
$log->addTag('projectId', $project->getId());
|
||||
}
|
||||
|
||||
if (array_key_exists('proxy', $project->getAttribute('services', []))) {
|
||||
@@ -262,7 +266,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
}
|
||||
|
||||
if (!$authorized) {
|
||||
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . "://" . System::getEnv('_APP_DOMAIN');
|
||||
$url = (System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https') . "://" . System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
@@ -448,7 +452,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
|
||||
}
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
$endpoint = $protocol . '://' . $hostname . "/v1";
|
||||
|
||||
@@ -787,13 +791,14 @@ App::init()
|
||||
->inject('swooleRequest')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('log')
|
||||
->inject('console')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->inject('getProjectDB')
|
||||
->inject('locale')
|
||||
->inject('localeCodes')
|
||||
->inject('clients')
|
||||
->inject('platforms')
|
||||
->inject('geodb')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForEvents')
|
||||
@@ -804,7 +809,9 @@ App::init()
|
||||
->inject('previewHostname')
|
||||
->inject('devKey')
|
||||
->inject('apiKey')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, Executor $executor, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey) {
|
||||
->inject('httpReferrer')
|
||||
->inject('httpReferrerSafe')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $platforms, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, Executor $executor, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, string $httpReferrer, string $httpReferrerSafe) {
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
@@ -812,7 +819,7 @@ App::init()
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
// Only run Router when external domain
|
||||
if ($host !== $mainDomain || !empty($previewHostname)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
|
||||
$utopia->getRoute()?->label('router', true);
|
||||
}
|
||||
}
|
||||
@@ -936,42 +943,9 @@ App::init()
|
||||
$locale->setDefault($localeParam);
|
||||
}
|
||||
|
||||
$referrer = $request->getReferer();
|
||||
$origin = \parse_url($request->getOrigin($referrer), PHP_URL_HOST);
|
||||
$protocol = \parse_url($request->getOrigin($referrer), PHP_URL_SCHEME);
|
||||
$port = \parse_url($request->getOrigin($referrer), PHP_URL_PORT);
|
||||
|
||||
$refDomainOrigin = 'localhost';
|
||||
$validator = new Hostname($clients);
|
||||
if ($validator->isValid($origin)) {
|
||||
$refDomainOrigin = $origin;
|
||||
} elseif (!empty($origin)) {
|
||||
// Auto-allow domains with linked rule
|
||||
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
|
||||
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
|
||||
} else {
|
||||
$rule = Authorization::skip(
|
||||
fn () => $dbForPlatform->find('rules', [
|
||||
Query::equal('domain', [$origin]),
|
||||
Query::limit(1)
|
||||
])
|
||||
)[0] ?? new Document();
|
||||
}
|
||||
|
||||
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) {
|
||||
$refDomainOrigin = $origin;
|
||||
}
|
||||
}
|
||||
|
||||
$refDomain = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $refDomainOrigin . (!empty($port) ? ':' . $port : '');
|
||||
|
||||
$refDomain = (!$route->getLabel('origin', false)) // This route is publicly accessible
|
||||
? $refDomain
|
||||
: (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
|
||||
|
||||
$origin = \parse_url($request->getOrigin($httpReferrer), PHP_URL_HOST);
|
||||
$selfDomain = new Domain($request->getHostname());
|
||||
$endDomain = new Domain((string)$origin);
|
||||
|
||||
Config::setParam(
|
||||
'domainVerification',
|
||||
($selfDomain->getRegisterable() === $endDomain->getRegisterable()) &&
|
||||
@@ -1043,7 +1017,7 @@ App::init()
|
||||
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
|
||||
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Dev-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-Appwrite-JWT, X-Appwrite-Response-Format, X-Appwrite-Timeout, X-SDK-Version, X-SDK-Name, X-SDK-Language, X-SDK-Platform, X-SDK-GraphQL, X-Appwrite-ID, X-Appwrite-Timestamp, Content-Range, Range, Cache-Control, Expires, Pragma, X-Forwarded-For, X-Forwarded-User-Agent')
|
||||
->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies')
|
||||
->addHeader('Access-Control-Allow-Origin', $refDomain)
|
||||
->addHeader('Access-Control-Allow-Origin', $httpReferrerSafe)
|
||||
->addHeader('Access-Control-Allow-Credentials', 'true');
|
||||
|
||||
if (!$devKey->isEmpty()) {
|
||||
@@ -1056,14 +1030,16 @@ App::init()
|
||||
* Skip this check for non-web platforms which are not required to send an origin header
|
||||
*/
|
||||
$origin = $request->getOrigin($request->getReferer(''));
|
||||
$originValidator = new Origin(\array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
|
||||
$originValidator = new Origin($platforms);
|
||||
|
||||
if (
|
||||
!$originValidator->isValid($origin)
|
||||
&& $devKey->isEmpty()
|
||||
$devKey->isEmpty()
|
||||
&& !empty($origin)
|
||||
&& !$originValidator->isValid($origin)
|
||||
&& \in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE])
|
||||
&& $route->getLabel('origin', false) !== '*'
|
||||
&& empty($request->getHeader('x-appwrite-key', ''))
|
||||
&& \parse_url($httpReferrerSafe, PHP_URL_HOST) === 'localhost'
|
||||
) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_UNKNOWN_ORIGIN, $originValidator->getDescription());
|
||||
}
|
||||
@@ -1074,6 +1050,7 @@ App::options()
|
||||
->inject('swooleRequest')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('log')
|
||||
->inject('dbForPlatform')
|
||||
->inject('getProjectDB')
|
||||
->inject('queueForEvents')
|
||||
@@ -1086,7 +1063,7 @@ App::options()
|
||||
->inject('project')
|
||||
->inject('devKey')
|
||||
->inject('apiKey')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey) {
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey) {
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
@@ -1094,7 +1071,7 @@ App::options()
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
// Only run Router when external domain
|
||||
if ($host !== $mainDomain || !empty($previewHostname)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
|
||||
$utopia->getRoute()?->label('router', true);
|
||||
}
|
||||
}
|
||||
@@ -1267,7 +1244,12 @@ App::error()
|
||||
$log->addTag('url', $request->getURI());
|
||||
$log->addTag('verboseType', get_class($error));
|
||||
$log->addTag('code', $error->getCode());
|
||||
$log->addTag('projectId', $project->getId());
|
||||
|
||||
$tags = $log->getTags();
|
||||
if (!isset($tags['projectId'])) {
|
||||
$log->addTag('projectId', $project->getId());
|
||||
}
|
||||
|
||||
$log->addTag('hostname', $request->getHostname());
|
||||
$log->addTag('locale', (string)$request->getParam('locale', $request->getHeader('x-appwrite-locale', '')));
|
||||
|
||||
@@ -1385,6 +1367,7 @@ App::get('/robots.txt')
|
||||
->inject('swooleRequest')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('log')
|
||||
->inject('dbForPlatform')
|
||||
->inject('getProjectDB')
|
||||
->inject('queueForEvents')
|
||||
@@ -1395,15 +1378,16 @@ App::get('/robots.txt')
|
||||
->inject('isResourceBlocked')
|
||||
->inject('previewHostname')
|
||||
->inject('apiKey')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
|
||||
$host = $request->getHostname() ?? '';
|
||||
$consoleDomain = System::getEnv('_APP_CONSOLE_DOMAIN', '');
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
|
||||
if (($host === $mainDomain || $host === 'localhost') && empty($previewHostname)) {
|
||||
if (($host === $consoleDomain || $host === $mainDomain || $host === 'localhost') && empty($previewHostname)) {
|
||||
$template = new View(__DIR__ . '/../views/general/robots.phtml');
|
||||
$response->text($template->render(false));
|
||||
} else {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
|
||||
$utopia->getRoute()?->label('router', true);
|
||||
}
|
||||
}
|
||||
@@ -1417,6 +1401,7 @@ App::get('/humans.txt')
|
||||
->inject('swooleRequest')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('log')
|
||||
->inject('dbForPlatform')
|
||||
->inject('getProjectDB')
|
||||
->inject('queueForEvents')
|
||||
@@ -1427,15 +1412,16 @@ App::get('/humans.txt')
|
||||
->inject('isResourceBlocked')
|
||||
->inject('previewHostname')
|
||||
->inject('apiKey')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
|
||||
$host = $request->getHostname() ?? '';
|
||||
$consoleDomain = System::getEnv('_APP_CONSOLE_DOMAIN', '');
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
|
||||
if (($host === $mainDomain || $host === 'localhost') && empty($previewHostname)) {
|
||||
if (($host === $consoleDomain || $host === $mainDomain || $host === 'localhost') && empty($previewHostname)) {
|
||||
$template = new View(__DIR__ . '/../views/general/humans.phtml');
|
||||
$response->text($template->render(false));
|
||||
} else {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname, $apiKey)) {
|
||||
$utopia->getRoute()?->label('router', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,32 +129,6 @@ App::get('/v1/mock/tests/general/oauth2/failure')
|
||||
]);
|
||||
});
|
||||
|
||||
App::patch('/v1/mock/functions-v2')
|
||||
->desc('Update Function Version to V2 (outdated code syntax)')
|
||||
->groups(['mock', 'api', 'functions'])
|
||||
->label('scope', 'functions.write')
|
||||
->label('docs', false)
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $functionId, Response $response, Database $dbForProject) {
|
||||
$isDevelopment = System::getEnv('_APP_ENV', 'development') === 'development';
|
||||
|
||||
if (!$isDevelopment) {
|
||||
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('version', 'v2'));
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::post('/v1/mock/api-key-unprefixed')
|
||||
->desc('Create API Key (without standard prefix)')
|
||||
->groups(['mock', 'api', 'projects'])
|
||||
|
||||
@@ -22,6 +22,7 @@ use Utopia\Abuse\Abuse;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Adapter\Filesystem;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
@@ -30,6 +31,7 @@ use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Queue\Publisher;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter as Telemetry;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
$parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) {
|
||||
@@ -420,7 +422,8 @@ App::init()
|
||||
->inject('apiKey')
|
||||
->inject('plan')
|
||||
->inject('devKey')
|
||||
->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, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey) use ($usageDatabaseListener, $eventDatabaseListener) {
|
||||
->inject('telemetry')
|
||||
->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, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry) use ($usageDatabaseListener, $eventDatabaseListener) {
|
||||
|
||||
$route = $utopia->getRoute();
|
||||
|
||||
@@ -553,6 +556,7 @@ App::init()
|
||||
));
|
||||
|
||||
$useCache = $route->getLabel('cache', false);
|
||||
$storageCacheOperationsCounter = $telemetry->createCounter('storage.cache.operations.load');
|
||||
if ($useCache) {
|
||||
$route = $utopia->match($request);
|
||||
$isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview';
|
||||
@@ -563,7 +567,7 @@ App::init()
|
||||
$cache = new Cache(
|
||||
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
||||
);
|
||||
$timestamp = 60 * 60 * 24 * 30;
|
||||
$timestamp = 60 * 60 * 24 * 180; // Temporarily increase the TTL to 180 day to ensure files in the cache are still fetched.
|
||||
$data = $cache->load($key, $timestamp);
|
||||
|
||||
if (!empty($data) && !$cacheLog->isEmpty()) {
|
||||
@@ -575,7 +579,6 @@ App::init()
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
|
||||
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) {
|
||||
@@ -619,10 +622,12 @@ App::init()
|
||||
->addHeader('Cache-Control', sprintf('private, max-age=%d', $timestamp))
|
||||
->addHeader('X-Appwrite-Cache', 'hit')
|
||||
->setContentType($cacheLog->getAttribute('mimeType'));
|
||||
$storageCacheOperationsCounter->add(1, ['result' => 'hit']);
|
||||
if (!$isImageTransformation || !$isDisabled) {
|
||||
$response->send($data);
|
||||
}
|
||||
} else {
|
||||
$storageCacheOperationsCounter->add(1, ['result' => 'miss']);
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
@@ -797,6 +802,12 @@ App::shutdown()
|
||||
}
|
||||
|
||||
if (!empty($queueForDatabase->getType())) {
|
||||
Console::info("Triggering database event: \n" . \json_encode([
|
||||
'projectId' => $project->getId(),
|
||||
'databaseId' => $queueForDatabase->getDatabase()?->getId(),
|
||||
'collectionId' => $queueForDatabase->getCollection()?->getId(),
|
||||
'documentId' => $queueForDatabase->getDocument()?->getId(),
|
||||
]));
|
||||
$queueForDatabase->trigger();
|
||||
}
|
||||
|
||||
@@ -824,6 +835,10 @@ App::shutdown()
|
||||
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
|
||||
}
|
||||
|
||||
$cache = new Cache(
|
||||
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
||||
);
|
||||
|
||||
$key = $request->cacheIdentifier();
|
||||
$signature = md5($data['payload']);
|
||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||
@@ -841,12 +856,11 @@ App::shutdown()
|
||||
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
|
||||
$cacheLog->setAttribute('accessedAt', $now);
|
||||
Authorization::skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
|
||||
// Overwrite the file every APP_CACHE_UPDATE seconds to update the file modified time that is used in the TTL checks in cache->load()
|
||||
$cache->save($key, $data['payload']);
|
||||
}
|
||||
|
||||
if ($signature !== $cacheLog->getAttribute('signature')) {
|
||||
$cache = new Cache(
|
||||
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
||||
);
|
||||
$cache->save($key, $data['payload']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ const APP_COMPUTE_SPECIFICATION_DEFAULT = Specification::S_1VCPU_512MB;
|
||||
const APP_PLATFORM_SERVER = 'server';
|
||||
const APP_PLATFORM_CLIENT = 'client';
|
||||
const APP_PLATFORM_CONSOLE = 'console';
|
||||
const APP_VCS_GITHUB_USERNAME = 'Appwrite';
|
||||
const APP_VCS_GITHUB_EMAIL = 'team@appwrite.io';
|
||||
|
||||
// Database Reconnect
|
||||
const DATABASE_RECONNECT_SLEEP = 2;
|
||||
|
||||
@@ -36,7 +36,8 @@ App::setMode(System::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
|
||||
if (!App::isProduction()) {
|
||||
// Allow specific domains to skip public domain validation in dev environment
|
||||
// Useful for existing tests involving webhooks
|
||||
PublicDomain::allow(['request-catcher']);
|
||||
PublicDomain::allow(['request-catcher-sms']);
|
||||
PublicDomain::allow(['request-catcher-webhook']);
|
||||
}
|
||||
$register->set('logger', function () {
|
||||
// Register error logger
|
||||
|
||||
+97
-32
@@ -19,6 +19,7 @@ use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\GraphQL\Schema;
|
||||
use Appwrite\Network\Platform;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Executor\Executor;
|
||||
@@ -79,9 +80,27 @@ App::setResource('localeCodes', function () {
|
||||
App::setResource('publisher', function (Group $pools) {
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
App::setResource('publisherDatabases', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherMigrations', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherStatsUsage', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('consumer', function (Group $pools) {
|
||||
return new BrokerPool(consumer: $pools->get('consumer'));
|
||||
}, ['pools']);
|
||||
App::setResource('consumerDatabases', function (BrokerPool $consumer) {
|
||||
return $consumer;
|
||||
}, ['consumer']);
|
||||
App::setResource('consumerMigrations', function (BrokerPool $consumer) {
|
||||
return $consumer;
|
||||
}, ['publisher']);
|
||||
App::setResource('consumerStatsUsage', function (BrokerPool $consumer) {
|
||||
return $consumer;
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForMessaging', function (Publisher $publisher) {
|
||||
return new Messaging($publisher);
|
||||
}, ['publisher']);
|
||||
@@ -121,11 +140,11 @@ App::setResource('queueForCertificates', function (Publisher $publisher) {
|
||||
App::setResource('queueForMigrations', function (Publisher $publisher) {
|
||||
return new Migration($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('clients', function ($request, $console, $project) {
|
||||
App::setResource('platforms', function (Request $request, Document $console, Document $project) {
|
||||
$console->setAttribute('platforms', [ // Always allow current host
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'name' => 'Current Host',
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'type' => Platform::TYPE_WEB,
|
||||
'hostname' => $request->getHostname(),
|
||||
], Document::SET_TYPE_APPEND);
|
||||
|
||||
@@ -138,39 +157,32 @@ App::setResource('clients', function ($request, $console, $project) {
|
||||
}
|
||||
$console->setAttribute('platforms', [
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'type' => Origin::CLIENT_TYPE_WEB,
|
||||
'type' => Platform::TYPE_WEB,
|
||||
'name' => $hostname,
|
||||
'hostname' => $hostname,
|
||||
], Document::SET_TYPE_APPEND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get All verified client URLs for both console and current projects
|
||||
* + Filter for duplicated entries
|
||||
*/
|
||||
$clientsConsole = \array_map(
|
||||
fn ($node) => $node['hostname'],
|
||||
\array_filter(
|
||||
$console->getAttribute('platforms', []),
|
||||
fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname']))
|
||||
)
|
||||
);
|
||||
|
||||
$clients = $clientsConsole;
|
||||
$platforms = $project->getAttribute('platforms', []);
|
||||
|
||||
foreach ($platforms as $node) {
|
||||
if (
|
||||
isset($node['type']) &&
|
||||
($node['type'] === Origin::CLIENT_TYPE_WEB ||
|
||||
$node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) &&
|
||||
!empty($node['hostname'])
|
||||
) {
|
||||
$clients[] = $node['hostname'];
|
||||
}
|
||||
// Add `exp` and `appwrite-callback-{projectId}` schemes
|
||||
if (!$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$project->setAttribute('platforms', [
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'type' => Platform::TYPE_SCHEME,
|
||||
'name' => 'Expo',
|
||||
'key' => 'exp',
|
||||
], Document::SET_TYPE_APPEND);
|
||||
$project->setAttribute('platforms', [
|
||||
'$collection' => ID::custom('platforms'),
|
||||
'type' => Platform::TYPE_SCHEME,
|
||||
'name' => 'Appwrite Callback',
|
||||
'key' => 'appwrite-callback-' . $project->getId(),
|
||||
], Document::SET_TYPE_APPEND);
|
||||
}
|
||||
|
||||
return \array_unique($clients);
|
||||
return [
|
||||
...$console->getAttribute('platforms', []),
|
||||
...$project->getAttribute('platforms', []),
|
||||
];
|
||||
}, ['request', 'console', 'project']);
|
||||
|
||||
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) {
|
||||
@@ -222,7 +234,9 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
||||
Auth::$unique = $session['id'] ?? '';
|
||||
Auth::$secret = $session['secret'] ?? '';
|
||||
|
||||
if (APP_MODE_ADMIN !== $mode) {
|
||||
if ($mode === APP_MODE_ADMIN) {
|
||||
$user = $dbForPlatform->getDocument('users', Auth::$unique);
|
||||
} else {
|
||||
if ($project->isEmpty()) {
|
||||
$user = new Document([]);
|
||||
} else {
|
||||
@@ -232,8 +246,6 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
||||
$user = $dbForProject->getDocument('users', Auth::$unique);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$user = $dbForPlatform->getDocument('users', Auth::$unique);
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -264,7 +276,11 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
||||
|
||||
$jwtUserId = $payload['userId'] ?? '';
|
||||
if (!empty($jwtUserId)) {
|
||||
$user = $dbForProject->getDocument('users', $jwtUserId);
|
||||
if ($mode === APP_MODE_ADMIN) {
|
||||
$user = $dbForPlatform->getDocument('users', $jwtUserId);
|
||||
} else {
|
||||
$user = $dbForProject->getDocument('users', $jwtUserId);
|
||||
}
|
||||
}
|
||||
|
||||
$jwtSessionId = $payload['sessionId'] ?? '';
|
||||
@@ -944,3 +960,52 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
|
||||
}
|
||||
return new Document([]);
|
||||
}, ['project', 'dbForProject', 'request']);
|
||||
|
||||
App::setResource('httpReferrer', function (Request $request): string {
|
||||
$referrer = $request->getReferer();
|
||||
return $referrer;
|
||||
}, ['request']);
|
||||
|
||||
App::setResource('httpReferrerSafe', function (Request $request, string $httpReferrer, array $platforms, Database $dbForPlatform, Document $project, App $utopia): string {
|
||||
$origin = \parse_url($request->getOrigin($httpReferrer), PHP_URL_HOST);
|
||||
$protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME);
|
||||
$port = \parse_url($request->getOrigin($httpReferrer), PHP_URL_PORT);
|
||||
$referrer = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
|
||||
|
||||
// Safe if route is publicly accessible
|
||||
$route = $utopia->getRoute();
|
||||
if ($route->getLabel('origin', false)) {
|
||||
return $referrer;
|
||||
}
|
||||
|
||||
// Safe if added as web platform
|
||||
$originValidator = new Origin($platforms);
|
||||
if ($originValidator->isValid($request->getOrigin($httpReferrer))) {
|
||||
return $referrer;
|
||||
}
|
||||
|
||||
// Safe if rule with same project ID exists
|
||||
if (!empty($origin)) {
|
||||
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
|
||||
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
|
||||
} else {
|
||||
$rule = Authorization::skip(
|
||||
fn () => $dbForPlatform->find('rules', [
|
||||
Query::equal('domain', [$origin]),
|
||||
Query::limit(1)
|
||||
])
|
||||
)[0] ?? new Document();
|
||||
}
|
||||
|
||||
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) {
|
||||
return $referrer;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe; Localhost is always safe for ease of local development
|
||||
$origin = 'localhost';
|
||||
$protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME);
|
||||
$port = \parse_url($request->getOrigin($httpReferrer), PHP_URL_PORT);
|
||||
$referrer = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
|
||||
return $referrer;
|
||||
}, ['request', 'httpReferrer', 'platforms', 'dbForPlatform', 'project', 'utopia']);
|
||||
|
||||
+3
-3
@@ -532,7 +532,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
}
|
||||
|
||||
$timelimit = $app->getResource('timelimit');
|
||||
$console = $app->getResource('console'); /** @var Document $console */
|
||||
$platforms = $app->getResource('platforms');
|
||||
$user = $app->getResource('user'); /** @var Document $user */
|
||||
|
||||
/*
|
||||
@@ -557,9 +557,9 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
* Skip this check for non-web platforms which are not required to send an origin header.
|
||||
*/
|
||||
$origin = $request->getOrigin();
|
||||
$originValidator = new Origin(array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
|
||||
$originValidator = new Origin($platforms);
|
||||
|
||||
if (!$originValidator->isValid($origin) && $project->getId() !== 'console') {
|
||||
if (!empty($origin) && !$originValidator->isValid($origin) && $project->getId() !== 'console') {
|
||||
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription());
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ $labelClass = '';
|
||||
$buttons = [];
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
$hostname = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
|
||||
// TODO: remove this later
|
||||
if (System::getEnv('_APP_ENV') === 'development') {
|
||||
$hostname = 'localhost';
|
||||
|
||||
@@ -95,6 +95,8 @@ $image = $this->getParam('image', '');
|
||||
- _APP_DOMAIN_TARGET_CNAME
|
||||
- _APP_DOMAIN_TARGET_AAAA
|
||||
- _APP_DOMAIN_TARGET_A
|
||||
- _APP_DOMAIN_TARGET_CAA
|
||||
- _APP_DNS
|
||||
- _APP_DOMAIN_FUNCTIONS
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
@@ -472,6 +474,8 @@ $image = $this->getParam('image', '');
|
||||
- _APP_DOMAIN_TARGET_CNAME
|
||||
- _APP_DOMAIN_TARGET_AAAA
|
||||
- _APP_DOMAIN_TARGET_A
|
||||
- _APP_DOMAIN_TARGET_CAA
|
||||
- _APP_DNS
|
||||
- _APP_DOMAIN_FUNCTIONS
|
||||
- _APP_EMAIL_CERTIFICATES
|
||||
- _APP_REDIS_HOST
|
||||
@@ -629,6 +633,8 @@ $image = $this->getParam('image', '');
|
||||
- _APP_DOMAIN_TARGET_CNAME
|
||||
- _APP_DOMAIN_TARGET_AAAA
|
||||
- _APP_DOMAIN_TARGET_A
|
||||
- _APP_DOMAIN_TARGET_CAA
|
||||
- _APP_DNS
|
||||
- _APP_EMAIL_SECURITY
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
@@ -660,6 +666,8 @@ $image = $this->getParam('image', '');
|
||||
- _APP_DOMAIN_TARGET_CNAME
|
||||
- _APP_DOMAIN_TARGET_AAAA
|
||||
- _APP_DOMAIN_TARGET_A
|
||||
- _APP_DOMAIN_TARGET_CAA
|
||||
- _APP_DNS
|
||||
- _APP_DOMAIN_FUNCTIONS
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_REDIS_HOST
|
||||
|
||||
@@ -247,10 +247,34 @@ Server::setResource('publisher', function (Group $pools) {
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
|
||||
Server::setResource('publisherDatabases', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('publisherMigrations', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('publisherStatsUsage', function (BrokerPool $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('consumer', function (Group $pools) {
|
||||
return new BrokerPool(consumer: $pools->get('consumer'));
|
||||
}, ['pools']);
|
||||
|
||||
Server::setResource('consumerDatabases', function (BrokerPool $consumer) {
|
||||
return $consumer;
|
||||
}, ['consumer']);
|
||||
|
||||
Server::setResource('consumerMigrations', function (BrokerPool $consumer) {
|
||||
return $consumer;
|
||||
}, ['consumer']);
|
||||
|
||||
Server::setResource('consumerStatsUsage', function (BrokerPool $consumer) {
|
||||
return $consumer;
|
||||
}, ['consumer']);
|
||||
|
||||
Server::setResource('queueForStatsUsage', function (Publisher $publisher) {
|
||||
return new StatsUsage($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
+5
-3
@@ -55,6 +55,7 @@
|
||||
"utopia-php/database": "0.71.*",
|
||||
"utopia-php/detector": "0.1.*",
|
||||
"utopia-php/domains": "0.8.*",
|
||||
"utopia-php/dns": "0.3.*",
|
||||
"utopia-php/dsn": "0.2.1",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
"utopia-php/fetch": "0.4.*",
|
||||
@@ -62,7 +63,7 @@
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/logger": "0.6.*",
|
||||
"utopia-php/messaging": "0.18.*",
|
||||
"utopia-php/migration": "0.10.*",
|
||||
"utopia-php/migration": "0.11.*",
|
||||
"utopia-php/orchestration": "0.9.*",
|
||||
"utopia-php/platform": "0.7.*",
|
||||
"utopia-php/pools": "0.8.*",
|
||||
@@ -73,7 +74,7 @@
|
||||
"utopia-php/swoole": "0.8.*",
|
||||
"utopia-php/system": "0.9.*",
|
||||
"utopia-php/telemetry": "0.1.*",
|
||||
"utopia-php/vcs": "0.10.*",
|
||||
"utopia-php/vcs": "0.11.*",
|
||||
"utopia-php/websocket": "0.3.*",
|
||||
"matomo/device-detector": "6.1.*",
|
||||
"dragonmantank/cron-expression": "3.3.*",
|
||||
@@ -82,7 +83,8 @@
|
||||
"adhocore/jwt": "1.1.*",
|
||||
"spomky-labs/otphp": "^10.0",
|
||||
"webonyx/graphql-php": "14.11.*",
|
||||
"league/csv": "9.14.*"
|
||||
"league/csv": "9.14.*",
|
||||
"enshrined/svg-sanitize": "0.21.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-fileinfo": "*",
|
||||
|
||||
Generated
+277
-146
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "55bc52686a08d64930e6af7411ac0654",
|
||||
"content-hash": "761a7e17b49381e68038c92873888125",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -69,16 +69,16 @@
|
||||
},
|
||||
{
|
||||
"name": "appwrite/appwrite",
|
||||
"version": "11.1.0",
|
||||
"version": "15.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-for-php.git",
|
||||
"reference": "1d043f543acdb17b9fdb440b1b2dd208e400bad3"
|
||||
"reference": "c438b3885071ac7c0329199dce5e6f6a24dd215b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/1d043f543acdb17b9fdb440b1b2dd208e400bad3",
|
||||
"reference": "1d043f543acdb17b9fdb440b1b2dd208e400bad3",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/c438b3885071ac7c0329199dce5e6f6a24dd215b",
|
||||
"reference": "c438b3885071ac7c0329199dce5e6f6a24dd215b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -104,10 +104,10 @@
|
||||
"support": {
|
||||
"email": "team@appwrite.io",
|
||||
"issues": "https://github.com/appwrite/sdk-for-php/issues",
|
||||
"source": "https://github.com/appwrite/sdk-for-php/tree/11.1.0",
|
||||
"source": "https://github.com/appwrite/sdk-for-php/tree/15.1.0",
|
||||
"url": "https://appwrite.io/support"
|
||||
},
|
||||
"time": "2024-06-26T07:03:23+00:00"
|
||||
"time": "2025-08-01T04:50:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "appwrite/php-clamav",
|
||||
@@ -628,6 +628,51 @@
|
||||
],
|
||||
"time": "2023-08-10T19:36:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "enshrined/svg-sanitize",
|
||||
"version": "0.21.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/darylldoyle/svg-sanitizer.git",
|
||||
"reference": "5e477468fac5c5ce933dce53af3e8e4e58dcccc9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/5e477468fac5c5ce933dce53af3e8e4e58dcccc9",
|
||||
"reference": "5e477468fac5c5ce933dce53af3e8e4e58dcccc9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.5 || ^8.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"enshrined\\svgSanitize\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"GPL-2.0-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Daryll Doyle",
|
||||
"email": "daryll@enshrined.co.uk"
|
||||
}
|
||||
],
|
||||
"description": "An SVG sanitizer for PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/darylldoyle/svg-sanitizer/issues",
|
||||
"source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.21.0"
|
||||
},
|
||||
"time": "2025-01-13T09:32:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "giggsey/libphonenumber-for-php-lite",
|
||||
"version": "8.13.36",
|
||||
@@ -1113,16 +1158,16 @@
|
||||
},
|
||||
{
|
||||
"name": "open-telemetry/api",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/opentelemetry-php/api.git",
|
||||
"reference": "4e3bb38e069876fb73c2ce85c89583bf2b28cd86"
|
||||
"reference": "b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/4e3bb38e069876fb73c2ce85c89583bf2b28cd86",
|
||||
"reference": "4e3bb38e069876fb73c2ce85c89583bf2b28cd86",
|
||||
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7",
|
||||
"reference": "b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1142,7 +1187,7 @@
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "1.1.x-dev"
|
||||
"dev-main": "1.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1179,7 +1224,7 @@
|
||||
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
|
||||
"source": "https://github.com/open-telemetry/opentelemetry-php"
|
||||
},
|
||||
"time": "2025-05-07T12:32:21+00:00"
|
||||
"time": "2025-06-19T23:36:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "open-telemetry/context",
|
||||
@@ -1242,16 +1287,16 @@
|
||||
},
|
||||
{
|
||||
"name": "open-telemetry/exporter-otlp",
|
||||
"version": "1.3.1",
|
||||
"version": "1.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/opentelemetry-php/exporter-otlp.git",
|
||||
"reference": "8b3ca1f86d01429c73b407bf1a2075d9c187001e"
|
||||
"reference": "196f3a1dbce3b2c0f8110d164232c11ac00ddbb2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/8b3ca1f86d01429c73b407bf1a2075d9c187001e",
|
||||
"reference": "8b3ca1f86d01429c73b407bf1a2075d9c187001e",
|
||||
"url": "https://api.github.com/repos/opentelemetry-php/exporter-otlp/zipball/196f3a1dbce3b2c0f8110d164232c11ac00ddbb2",
|
||||
"reference": "196f3a1dbce3b2c0f8110d164232c11ac00ddbb2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1302,7 +1347,7 @@
|
||||
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
|
||||
"source": "https://github.com/open-telemetry/opentelemetry-php"
|
||||
},
|
||||
"time": "2025-05-21T12:02:20+00:00"
|
||||
"time": "2025-06-16T00:24:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "open-telemetry/gen-otlp-protobuf",
|
||||
@@ -1369,22 +1414,22 @@
|
||||
},
|
||||
{
|
||||
"name": "open-telemetry/sdk",
|
||||
"version": "1.5.0",
|
||||
"version": "1.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/opentelemetry-php/sdk.git",
|
||||
"reference": "cd0d7367599717fc29e04eb8838ec061e6c2c657"
|
||||
"reference": "1c0371794e4c0700afd4a9d4d8511cb5e3f78e6a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/cd0d7367599717fc29e04eb8838ec061e6c2c657",
|
||||
"reference": "cd0d7367599717fc29e04eb8838ec061e6c2c657",
|
||||
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/1c0371794e4c0700afd4a9d4d8511cb5e3f78e6a",
|
||||
"reference": "1c0371794e4c0700afd4a9d4d8511cb5e3f78e6a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"nyholm/psr7-server": "^1.1",
|
||||
"open-telemetry/api": "~1.0 || ~1.1",
|
||||
"open-telemetry/api": "~1.4.0",
|
||||
"open-telemetry/context": "^1.0",
|
||||
"open-telemetry/sem-conv": "^1.0",
|
||||
"php": "^8.1",
|
||||
@@ -1407,6 +1452,10 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"spi": {
|
||||
"OpenTelemetry\\API\\Configuration\\ConfigEnv\\EnvComponentLoader": [
|
||||
"OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderHttpConfig",
|
||||
"OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderPeerConfig"
|
||||
],
|
||||
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [
|
||||
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager"
|
||||
]
|
||||
@@ -1455,20 +1504,20 @@
|
||||
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
|
||||
"source": "https://github.com/open-telemetry/opentelemetry-php"
|
||||
},
|
||||
"time": "2025-05-22T02:33:34+00:00"
|
||||
"time": "2025-06-19T23:36:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "open-telemetry/sem-conv",
|
||||
"version": "1.32.0",
|
||||
"version": "1.32.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/opentelemetry-php/sem-conv.git",
|
||||
"reference": "16585cc0dbc3032a318e274043454679430d2ebf"
|
||||
"reference": "94daa85ea61a8e2b7e1b0af6be0e875bedda7c22"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/16585cc0dbc3032a318e274043454679430d2ebf",
|
||||
"reference": "16585cc0dbc3032a318e274043454679430d2ebf",
|
||||
"url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/94daa85ea61a8e2b7e1b0af6be0e875bedda7c22",
|
||||
"reference": "94daa85ea61a8e2b7e1b0af6be0e875bedda7c22",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1512,7 +1561,7 @@
|
||||
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
|
||||
"source": "https://github.com/open-telemetry/opentelemetry-php"
|
||||
},
|
||||
"time": "2025-05-05T03:58:53+00:00"
|
||||
"time": "2025-06-24T02:32:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/constant_time_encoding",
|
||||
@@ -1874,16 +1923,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "3.0.44",
|
||||
"version": "3.0.46",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "1d0b5e7e1434678411787c5a0535e68907cf82d9"
|
||||
"reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/1d0b5e7e1434678411787c5a0535e68907cf82d9",
|
||||
"reference": "1d0b5e7e1434678411787c5a0535e68907cf82d9",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
|
||||
"reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1964,7 +2013,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.44"
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.46"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1980,7 +2029,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-06-15T09:59:26+00:00"
|
||||
"time": "2025-06-26T16:29:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
@@ -2323,21 +2372,20 @@
|
||||
},
|
||||
{
|
||||
"name": "ramsey/uuid",
|
||||
"version": "4.8.1",
|
||||
"version": "4.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/uuid.git",
|
||||
"reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28"
|
||||
"reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
|
||||
"reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0",
|
||||
"reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13",
|
||||
"ext-json": "*",
|
||||
"php": "^8.0",
|
||||
"ramsey/collection": "^1.2 || ^2.0"
|
||||
},
|
||||
@@ -2396,9 +2444,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ramsey/uuid/issues",
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.8.1"
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.9.0"
|
||||
},
|
||||
"time": "2025-06-01T06:28:46+00:00"
|
||||
"time": "2025-06-25T14:20:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spomky-labs/otphp",
|
||||
@@ -2544,16 +2592,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client",
|
||||
"version": "v7.3.0",
|
||||
"version": "v7.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-client.git",
|
||||
"reference": "57e4fb86314015a695a750ace358d07a7e37b8a9"
|
||||
"reference": "1c064a0c67749923483216b081066642751cc2c7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/57e4fb86314015a695a750ace358d07a7e37b8a9",
|
||||
"reference": "57e4fb86314015a695a750ace358d07a7e37b8a9",
|
||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/1c064a0c67749923483216b081066642751cc2c7",
|
||||
"reference": "1c064a0c67749923483216b081066642751cc2c7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2565,6 +2613,7 @@
|
||||
},
|
||||
"conflict": {
|
||||
"amphp/amp": "<2.5",
|
||||
"amphp/socket": "<1.1",
|
||||
"php-http/discovery": "<1.15",
|
||||
"symfony/http-foundation": "<6.4"
|
||||
},
|
||||
@@ -2577,7 +2626,6 @@
|
||||
"require-dev": {
|
||||
"amphp/http-client": "^4.2.1|^5.0",
|
||||
"amphp/http-tunnel": "^1.0|^2.0",
|
||||
"amphp/socket": "^1.1",
|
||||
"guzzlehttp/promises": "^1.4|^2.0",
|
||||
"nyholm/psr7": "^1.0",
|
||||
"php-http/httplug": "^1.0|^2.0",
|
||||
@@ -2619,7 +2667,7 @@
|
||||
"http"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.3.0"
|
||||
"source": "https://github.com/symfony/http-client/tree/v7.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2630,12 +2678,16 @@
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-05-02T08:23:16+00:00"
|
||||
"time": "2025-07-15T11:36:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-client-contracts",
|
||||
@@ -2957,16 +3009,16 @@
|
||||
},
|
||||
{
|
||||
"name": "tbachert/spi",
|
||||
"version": "v1.0.3",
|
||||
"version": "v1.0.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Nevay/spi.git",
|
||||
"reference": "506a79c98e1a51522e76ee921ccb6c62d52faf3a"
|
||||
"reference": "e7078767866d0a9e0f91d3f9d42a832df5e39002"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Nevay/spi/zipball/506a79c98e1a51522e76ee921ccb6c62d52faf3a",
|
||||
"reference": "506a79c98e1a51522e76ee921ccb6c62d52faf3a",
|
||||
"url": "https://api.github.com/repos/Nevay/spi/zipball/e7078767866d0a9e0f91d3f9d42a832df5e39002",
|
||||
"reference": "e7078767866d0a9e0f91d3f9d42a832df5e39002",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2984,7 +3036,7 @@
|
||||
"extra": {
|
||||
"class": "Nevay\\SPI\\Composer\\Plugin",
|
||||
"branch-alias": {
|
||||
"dev-main": "0.2.x-dev"
|
||||
"dev-main": "1.0.x-dev"
|
||||
},
|
||||
"plugin-optional": true
|
||||
},
|
||||
@@ -3003,9 +3055,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Nevay/spi/issues",
|
||||
"source": "https://github.com/Nevay/spi/tree/v1.0.3"
|
||||
"source": "https://github.com/Nevay/spi/tree/v1.0.5"
|
||||
},
|
||||
"time": "2025-04-02T19:38:14+00:00"
|
||||
"time": "2025-06-29T15:42:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "thecodingmachine/safe",
|
||||
@@ -3490,16 +3542,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.71.7",
|
||||
"version": "0.71.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "aa0116b2380125907fc18c82662be8e74c54091f"
|
||||
"reference": "644ed827aace63cbdf8c6c64a3998c11b43e3383"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/aa0116b2380125907fc18c82662be8e74c54091f",
|
||||
"reference": "aa0116b2380125907fc18c82662be8e74c54091f",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/644ed827aace63cbdf8c6c64a3998c11b43e3383",
|
||||
"reference": "644ed827aace63cbdf8c6c64a3998c11b43e3383",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3540,9 +3592,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/0.71.7"
|
||||
"source": "https://github.com/utopia-php/database/tree/0.71.11"
|
||||
},
|
||||
"time": "2025-06-17T23:59:10+00:00"
|
||||
"time": "2025-08-05T08:35:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/detector",
|
||||
@@ -3589,6 +3641,62 @@
|
||||
},
|
||||
"time": "2025-05-19T11:01:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/dns",
|
||||
"version": "0.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/dns.git",
|
||||
"reference": "8fd4161bc3a8021a670c1101b40f6b09a97f1a54"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/dns/zipball/8fd4161bc3a8021a670c1101b40f6b09a97f1a54",
|
||||
"reference": "8fd4161bc3a8021a670c1101b40f6b09a97f1a54",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/telemetry": "^0.1.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "1.2.*",
|
||||
"phpstan/phpstan": "1.8.*",
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"rregeer/phpunit-coverage-check": "^0.3.1",
|
||||
"swoole/ide-helper": "4.6.6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Utopia\\DNS\\": "src/DNS"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Eldad Fux",
|
||||
"email": "eldad@appwrite.io"
|
||||
}
|
||||
],
|
||||
"description": "Lite & fast micro PHP DNS server abstraction that is **easy to use**.",
|
||||
"keywords": [
|
||||
"dns",
|
||||
"framework",
|
||||
"php",
|
||||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/dns/issues",
|
||||
"source": "https://github.com/utopia-php/dns/tree/0.3.0"
|
||||
},
|
||||
"time": "2025-08-04T11:05:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
"version": "0.8.0",
|
||||
@@ -3939,16 +4047,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/messaging",
|
||||
"version": "0.18.0",
|
||||
"version": "0.18.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/messaging.git",
|
||||
"reference": "c151aa5d4d475c788ca15c210b5b2017e21c41d6"
|
||||
"reference": "0d364edacf4d4867964c7e17f653031dd39394bf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/c151aa5d4d475c788ca15c210b5b2017e21c41d6",
|
||||
"reference": "c151aa5d4d475c788ca15c210b5b2017e21c41d6",
|
||||
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/0d364edacf4d4867964c7e17f653031dd39394bf",
|
||||
"reference": "0d364edacf4d4867964c7e17f653031dd39394bf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3984,26 +4092,26 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/messaging/issues",
|
||||
"source": "https://github.com/utopia-php/messaging/tree/0.18.0"
|
||||
"source": "https://github.com/utopia-php/messaging/tree/0.18.2"
|
||||
},
|
||||
"time": "2025-05-15T05:00:03+00:00"
|
||||
"time": "2025-07-21T18:27:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
"version": "0.10.1",
|
||||
"version": "0.11.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "ea1c585df7ec5f346f061a11581fc9a91679966f"
|
||||
"reference": "fb14e0e35061bfec2656200e3d02a3bdf569a0ac"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/ea1c585df7ec5f346f061a11581fc9a91679966f",
|
||||
"reference": "ea1c585df7ec5f346f061a11581fc9a91679966f",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/fb14e0e35061bfec2656200e3d02a3bdf569a0ac",
|
||||
"reference": "fb14e0e35061bfec2656200e3d02a3bdf569a0ac",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"appwrite/appwrite": "11.*",
|
||||
"appwrite/appwrite": "15.*",
|
||||
"ext-curl": "*",
|
||||
"ext-openssl": "*",
|
||||
"php": ">=8.1",
|
||||
@@ -4040,9 +4148,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.10.1"
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.11.3"
|
||||
},
|
||||
"time": "2025-05-26T15:29:19+00:00"
|
||||
"time": "2025-07-30T11:20:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/orchestration",
|
||||
@@ -4584,16 +4692,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/vcs",
|
||||
"version": "0.10.5",
|
||||
"version": "0.11.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/vcs.git",
|
||||
"reference": "b358439dc387f6097019eb83ebb9fc258fe9da05"
|
||||
"reference": "0e665eaa7d906168525bf6aac50b6bcc3e4fe528"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/b358439dc387f6097019eb83ebb9fc258fe9da05",
|
||||
"reference": "b358439dc387f6097019eb83ebb9fc258fe9da05",
|
||||
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/0e665eaa7d906168525bf6aac50b6bcc3e4fe528",
|
||||
"reference": "0e665eaa7d906168525bf6aac50b6bcc3e4fe528",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4627,9 +4735,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/vcs/issues",
|
||||
"source": "https://github.com/utopia-php/vcs/tree/0.10.5"
|
||||
"source": "https://github.com/utopia-php/vcs/tree/0.11.0"
|
||||
},
|
||||
"time": "2025-06-10T15:01:16+00:00"
|
||||
"time": "2025-07-23T13:54:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/websocket",
|
||||
@@ -4807,16 +4915,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "0.41.8",
|
||||
"version": "0.41.28",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "93ffb24b25b376ca4423e3a5caf6f916673af4b2"
|
||||
"reference": "8eace11070264c62c8da3c69498fb8dc98fcfaf7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/93ffb24b25b376ca4423e3a5caf6f916673af4b2",
|
||||
"reference": "93ffb24b25b376ca4423e3a5caf6f916673af4b2",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/8eace11070264c62c8da3c69498fb8dc98fcfaf7",
|
||||
"reference": "8eace11070264c62c8da3c69498fb8dc98fcfaf7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4852,9 +4960,9 @@
|
||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.41.8"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.41.28"
|
||||
},
|
||||
"time": "2025-06-18T13:20:45+00:00"
|
||||
"time": "2025-08-01T11:06:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
@@ -5081,16 +5189,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/pint",
|
||||
"version": "v1.22.1",
|
||||
"version": "v1.24.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/pint.git",
|
||||
"reference": "941d1927c5ca420c22710e98420287169c7bcaf7"
|
||||
"reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/pint/zipball/941d1927c5ca420c22710e98420287169c7bcaf7",
|
||||
"reference": "941d1927c5ca420c22710e98420287169c7bcaf7",
|
||||
"url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a",
|
||||
"reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5101,10 +5209,10 @@
|
||||
"php": "^8.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.75.0",
|
||||
"illuminate/view": "^11.44.7",
|
||||
"larastan/larastan": "^3.4.0",
|
||||
"laravel-zero/framework": "^11.36.1",
|
||||
"friendsofphp/php-cs-fixer": "^3.82.2",
|
||||
"illuminate/view": "^11.45.1",
|
||||
"larastan/larastan": "^3.5.0",
|
||||
"laravel-zero/framework": "^11.45.0",
|
||||
"mockery/mockery": "^1.6.12",
|
||||
"nunomaduro/termwind": "^2.3.1",
|
||||
"pestphp/pest": "^2.36.0"
|
||||
@@ -5114,6 +5222,9 @@
|
||||
],
|
||||
"type": "project",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"overrides/Runner/Parallel/ProcessFactory.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
"Database\\Seeders\\": "database/seeders/",
|
||||
@@ -5143,20 +5254,20 @@
|
||||
"issues": "https://github.com/laravel/pint/issues",
|
||||
"source": "https://github.com/laravel/pint"
|
||||
},
|
||||
"time": "2025-05-08T08:38:12+00:00"
|
||||
"time": "2025-07-10T18:09:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "matthiasmullie/minify",
|
||||
"version": "1.3.74",
|
||||
"version": "1.3.75",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/matthiasmullie/minify.git",
|
||||
"reference": "a2593286a4135d03c6a6a9e9aeded5d41e931ce4"
|
||||
"reference": "76ba4a5f555fd7bf4aa408af608e991569076671"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/a2593286a4135d03c6a6a9e9aeded5d41e931ce4",
|
||||
"reference": "a2593286a4135d03c6a6a9e9aeded5d41e931ce4",
|
||||
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/76ba4a5f555fd7bf4aa408af608e991569076671",
|
||||
"reference": "76ba4a5f555fd7bf4aa408af608e991569076671",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5205,7 +5316,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/matthiasmullie/minify/issues",
|
||||
"source": "https://github.com/matthiasmullie/minify/tree/1.3.74"
|
||||
"source": "https://github.com/matthiasmullie/minify/tree/1.3.75"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5213,7 +5324,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-06-12T08:06:04+00:00"
|
||||
"time": "2025-06-25T09:56:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "matthiasmullie/path-converter",
|
||||
@@ -5270,16 +5381,16 @@
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.13.1",
|
||||
"version": "1.13.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c"
|
||||
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c",
|
||||
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
|
||||
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5318,7 +5429,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.1"
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5326,20 +5437,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-29T12:36:36+00:00"
|
||||
"time": "2025-08-01T08:46:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v5.5.0",
|
||||
"version": "v5.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "ae59794362fe85e051a58ad36b289443f57be7a9"
|
||||
"reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9",
|
||||
"reference": "ae59794362fe85e051a58ad36b289443f57be7a9",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56",
|
||||
"reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5382,9 +5493,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0"
|
||||
},
|
||||
"time": "2025-05-31T08:24:38+00:00"
|
||||
"time": "2025-07-27T20:03:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@@ -7252,16 +7363,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.3.0",
|
||||
"version": "v7.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44"
|
||||
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/66c1440edf6f339fd82ed6c7caa76cb006211b44",
|
||||
"reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1",
|
||||
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7326,7 +7437,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v7.3.0"
|
||||
"source": "https://github.com/symfony/console/tree/v7.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7337,25 +7448,29 @@
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-05-24T10:34:04+00:00"
|
||||
"time": "2025-07-30T17:13:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v7.3.0",
|
||||
"version": "v7.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb"
|
||||
"reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb",
|
||||
"reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd",
|
||||
"reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7392,7 +7507,7 @@
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v7.3.0"
|
||||
"source": "https://github.com/symfony/filesystem/tree/v7.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7403,25 +7518,29 @@
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-25T15:15:23+00:00"
|
||||
"time": "2025-07-07T08:17:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v7.3.0",
|
||||
"version": "v7.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d"
|
||||
"reference": "2a6614966ba1074fa93dae0bc804227422df4dfe"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d",
|
||||
"reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe",
|
||||
"reference": "2a6614966ba1074fa93dae0bc804227422df4dfe",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7456,7 +7575,7 @@
|
||||
"description": "Finds files and directories via an intuitive fluent interface",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/finder/tree/v7.3.0"
|
||||
"source": "https://github.com/symfony/finder/tree/v7.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7467,25 +7586,29 @@
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-30T19:00:26+00:00"
|
||||
"time": "2025-07-15T13:41:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
"version": "v7.3.0",
|
||||
"version": "v7.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/options-resolver.git",
|
||||
"reference": "afb9a8038025e5dbc657378bfab9198d75f10fca"
|
||||
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/afb9a8038025e5dbc657378bfab9198d75f10fca",
|
||||
"reference": "afb9a8038025e5dbc657378bfab9198d75f10fca",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37",
|
||||
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7523,7 +7646,7 @@
|
||||
"options"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v7.3.0"
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v7.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7534,12 +7657,16 @@
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-04T13:12:05+00:00"
|
||||
"time": "2025-07-15T11:36:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
@@ -7918,16 +8045,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v7.3.0",
|
||||
"version": "v7.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125"
|
||||
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125",
|
||||
"reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca",
|
||||
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7985,7 +8112,7 @@
|
||||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v7.3.0"
|
||||
"source": "https://github.com/symfony/string/tree/v7.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7996,12 +8123,16 @@
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-20T20:19:01+00:00"
|
||||
"time": "2025-07-10T08:47:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "textalk/websocket",
|
||||
@@ -8233,7 +8364,7 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {},
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
@@ -8257,5 +8388,5 @@
|
||||
"platform-overrides": {
|
||||
"php": "8.3"
|
||||
},
|
||||
"plugin-api-version": "2.6.0"
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
|
||||
+26
-5
@@ -116,9 +116,12 @@ services:
|
||||
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DOMAIN
|
||||
- _APP_CONSOLE_DOMAIN
|
||||
- _APP_DOMAIN_TARGET_CNAME
|
||||
- _APP_DOMAIN_TARGET_AAAA
|
||||
- _APP_DOMAIN_TARGET_A
|
||||
- _APP_DOMAIN_TARGET_CAA
|
||||
- _APP_DNS
|
||||
- _APP_DOMAIN_FUNCTIONS
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
@@ -213,7 +216,7 @@ services:
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:6.0.41
|
||||
image: appwrite/console:6.1.12
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -323,7 +326,8 @@ services:
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
- request-catcher
|
||||
- request-catcher-sms
|
||||
- request-catcher-webhook
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
@@ -454,6 +458,7 @@ services:
|
||||
- redis
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_BROWSER_HOST
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
@@ -481,6 +486,7 @@ services:
|
||||
- _APP_OPTIONS_FORCE_HTTPS
|
||||
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
|
||||
- _APP_DOMAIN
|
||||
- _APP_CONSOLE_DOMAIN
|
||||
- _APP_STORAGE_DEVICE
|
||||
- _APP_STORAGE_S3_ACCESS_KEY
|
||||
- _APP_STORAGE_S3_SECRET
|
||||
@@ -531,6 +537,8 @@ services:
|
||||
- _APP_DOMAIN_TARGET_CNAME
|
||||
- _APP_DOMAIN_TARGET_AAAA
|
||||
- _APP_DOMAIN_TARGET_A
|
||||
- _APP_DOMAIN_TARGET_CAA
|
||||
- _APP_DNS
|
||||
- _APP_DOMAIN_FUNCTIONS
|
||||
- _APP_EMAIL_CERTIFICATES
|
||||
- _APP_REDIS_HOST
|
||||
@@ -700,6 +708,8 @@ services:
|
||||
- _APP_DOMAIN_TARGET_CNAME
|
||||
- _APP_DOMAIN_TARGET_AAAA
|
||||
- _APP_DOMAIN_TARGET_A
|
||||
- _APP_DOMAIN_TARGET_CAA
|
||||
- _APP_DNS
|
||||
- _APP_EMAIL_SECURITY
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
@@ -734,6 +744,8 @@ services:
|
||||
- _APP_DOMAIN_TARGET_CNAME
|
||||
- _APP_DOMAIN_TARGET_AAAA
|
||||
- _APP_DOMAIN_TARGET_A
|
||||
- _APP_DOMAIN_TARGET_CAA
|
||||
- _APP_DNS
|
||||
- _APP_DOMAIN_FUNCTIONS
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_REDIS_HOST
|
||||
@@ -974,7 +986,7 @@ services:
|
||||
- OPR_EXECUTOR_ENV=$_APP_ENV
|
||||
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES,$_APP_SITES_RUNTIMES
|
||||
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
|
||||
- OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v5
|
||||
- OPR_EXECUTOR_RUNTIME_VERSIONS=v5
|
||||
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
|
||||
- OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE
|
||||
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY
|
||||
@@ -1075,15 +1087,24 @@ services:
|
||||
networks:
|
||||
- appwrite
|
||||
|
||||
request-catcher: # used mainly for dev tests
|
||||
request-catcher-webhook: # used mainly for dev tests (mock HTTP webhook)
|
||||
image: appwrite/requestcatcher:1.0.0
|
||||
container_name: appwrite-requestcatcher
|
||||
container_name: appwrite-requestcatcher-webhook
|
||||
<<: *x-logging
|
||||
ports:
|
||||
- "9504:5000"
|
||||
networks:
|
||||
- appwrite
|
||||
|
||||
request-catcher-sms: # used mainly for dev tests (mock SMS auth secret)
|
||||
image: appwrite/requestcatcher:1.0.0
|
||||
container_name: appwrite-requestcatcher-sms
|
||||
<<: *x-logging
|
||||
ports:
|
||||
- "9507:5000"
|
||||
networks:
|
||||
- appwrite
|
||||
|
||||
adminer:
|
||||
image: adminer
|
||||
container_name: appwrite-adminer
|
||||
|
||||
@@ -4,9 +4,7 @@ import io.appwrite.services.Databases;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setSession("") // The user session to authenticate with
|
||||
.setKey("") //
|
||||
.setJWT("<YOUR_JWT>"); // Your secret JSON Web Token
|
||||
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
|
||||
|
||||
Databases databases = new Databases(client);
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.Databases;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
|
||||
|
||||
Databases databases = new Databases(client);
|
||||
|
||||
databases.decrementDocumentAttribute(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<COLLECTION_ID>", // collectionId
|
||||
"<DOCUMENT_ID>", // documentId
|
||||
"", // attribute
|
||||
0, // value (optional)
|
||||
0, // min (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d("Appwrite", result.toString());
|
||||
})
|
||||
);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.Databases;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
|
||||
|
||||
Databases databases = new Databases(client);
|
||||
|
||||
databases.incrementDocumentAttribute(
|
||||
"<DATABASE_ID>", // databaseId
|
||||
"<COLLECTION_ID>", // collectionId
|
||||
"<DOCUMENT_ID>", // documentId
|
||||
"", // attribute
|
||||
0, // value (optional)
|
||||
0, // max (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d("Appwrite", result.toString());
|
||||
})
|
||||
);
|
||||
|
||||
@@ -4,9 +4,7 @@ import io.appwrite.services.Databases
|
||||
|
||||
val client = Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setSession("") // The user session to authenticate with
|
||||
.setKey("") //
|
||||
.setJWT("<YOUR_JWT>") // Your secret JSON Web Token
|
||||
.setProject("<YOUR_PROJECT_ID>") // Your project ID
|
||||
|
||||
val databases = Databases(client)
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.coroutines.CoroutineCallback
|
||||
import io.appwrite.services.Databases
|
||||
|
||||
val client = Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("<YOUR_PROJECT_ID>") // Your project ID
|
||||
|
||||
val databases = Databases(client)
|
||||
|
||||
val result = databases.decrementDocumentAttribute(
|
||||
databaseId = "<DATABASE_ID>",
|
||||
collectionId = "<COLLECTION_ID>",
|
||||
documentId = "<DOCUMENT_ID>",
|
||||
attribute = "",
|
||||
value = 0, // (optional)
|
||||
min = 0, // (optional)
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.coroutines.CoroutineCallback
|
||||
import io.appwrite.services.Databases
|
||||
|
||||
val client = Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("<YOUR_PROJECT_ID>") // Your project ID
|
||||
|
||||
val databases = Databases(client)
|
||||
|
||||
val result = databases.incrementDocumentAttribute(
|
||||
databaseId = "<DATABASE_ID>",
|
||||
collectionId = "<COLLECTION_ID>",
|
||||
documentId = "<DOCUMENT_ID>",
|
||||
attribute = "",
|
||||
value = 0, // (optional)
|
||||
max = 0, // (optional)
|
||||
)
|
||||
@@ -2,9 +2,7 @@ import Appwrite
|
||||
|
||||
let client = Client()
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setSession("") // The user session to authenticate with
|
||||
.setKey("") //
|
||||
.setJWT("<YOUR_JWT>") // Your secret JSON Web Token
|
||||
.setProject("<YOUR_PROJECT_ID>") // Your project ID
|
||||
|
||||
let databases = Databases(client)
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import Appwrite
|
||||
|
||||
let client = Client()
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("<YOUR_PROJECT_ID>") // Your project ID
|
||||
|
||||
let databases = Databases(client)
|
||||
|
||||
let document = try await databases.decrementDocumentAttribute(
|
||||
databaseId: "<DATABASE_ID>",
|
||||
collectionId: "<COLLECTION_ID>",
|
||||
documentId: "<DOCUMENT_ID>",
|
||||
attribute: "",
|
||||
value: 0, // optional
|
||||
min: 0 // optional
|
||||
)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import Appwrite
|
||||
|
||||
let client = Client()
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("<YOUR_PROJECT_ID>") // Your project ID
|
||||
|
||||
let databases = Databases(client)
|
||||
|
||||
let document = try await databases.incrementDocumentAttribute(
|
||||
databaseId: "<DATABASE_ID>",
|
||||
collectionId: "<COLLECTION_ID>",
|
||||
documentId: "<DOCUMENT_ID>",
|
||||
attribute: "",
|
||||
value: 0, // optional
|
||||
max: 0 // optional
|
||||
)
|
||||
|
||||
@@ -2,9 +2,7 @@ import 'package:appwrite/appwrite.dart';
|
||||
|
||||
Client client = Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setSession('') // The user session to authenticate with
|
||||
.setKey('') //
|
||||
.setJWT('<YOUR_JWT>'); // Your secret JSON Web Token
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
Databases databases = Databases(client);
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:appwrite/appwrite.dart';
|
||||
|
||||
Client client = Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
Databases databases = Databases(client);
|
||||
|
||||
Document result = await databases.decrementDocumentAttribute(
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
attribute: '',
|
||||
value: 0, // optional
|
||||
min: 0, // optional
|
||||
);
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:appwrite/appwrite.dart';
|
||||
|
||||
Client client = Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
Databases databases = Databases(client);
|
||||
|
||||
Document result = await databases.incrementDocumentAttribute(
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
attribute: '',
|
||||
value: 0, // optional
|
||||
max: 0, // optional
|
||||
);
|
||||
@@ -7,6 +7,7 @@ mutation {
|
||||
permissions: ["read("any")"]
|
||||
) {
|
||||
_id
|
||||
_sequence
|
||||
_collectionId
|
||||
_databaseId
|
||||
_createdAt
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
mutation {
|
||||
databasesDecrementDocumentAttribute(
|
||||
databaseId: "<DATABASE_ID>",
|
||||
collectionId: "<COLLECTION_ID>",
|
||||
documentId: "<DOCUMENT_ID>",
|
||||
attribute: "",
|
||||
value: 0,
|
||||
min: 0
|
||||
) {
|
||||
_id
|
||||
_sequence
|
||||
_collectionId
|
||||
_databaseId
|
||||
_createdAt
|
||||
_updatedAt
|
||||
_permissions
|
||||
data
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
mutation {
|
||||
databasesIncrementDocumentAttribute(
|
||||
databaseId: "<DATABASE_ID>",
|
||||
collectionId: "<COLLECTION_ID>",
|
||||
documentId: "<DOCUMENT_ID>",
|
||||
attribute: "",
|
||||
value: 0,
|
||||
max: 0
|
||||
) {
|
||||
_id
|
||||
_sequence
|
||||
_collectionId
|
||||
_databaseId
|
||||
_createdAt
|
||||
_updatedAt
|
||||
_permissions
|
||||
data
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ mutation {
|
||||
permissions: ["read("any")"]
|
||||
) {
|
||||
_id
|
||||
_sequence
|
||||
_collectionId
|
||||
_databaseId
|
||||
_createdAt
|
||||
|
||||
@@ -7,6 +7,7 @@ mutation {
|
||||
permissions: ["read("any")"]
|
||||
) {
|
||||
_id
|
||||
_sequence
|
||||
_collectionId
|
||||
_databaseId
|
||||
_createdAt
|
||||
|
||||
@@ -2,9 +2,7 @@ import { Client, Databases } from "react-native-appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setSession('') // The user session to authenticate with
|
||||
.setKey('') //
|
||||
.setJWT('<YOUR_JWT>'); // Your secret JSON Web Token
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { Client, Databases } from "react-native-appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.decrementDocumentAttribute(
|
||||
'<DATABASE_ID>', // databaseId
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'<DOCUMENT_ID>', // documentId
|
||||
'', // attribute
|
||||
null, // value (optional)
|
||||
null // min (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { Client, Databases } from "react-native-appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.incrementDocumentAttribute(
|
||||
'<DATABASE_ID>', // databaseId
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'<DOCUMENT_ID>', // documentId
|
||||
'', // attribute
|
||||
null, // value (optional)
|
||||
null // max (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
@@ -0,0 +1,12 @@
|
||||
PATCH /v1/databases/{databaseId}/collections/{collectionId}/documents/{documentId}/{attribute}/decrement HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.7.0
|
||||
X-Appwrite-Project: <YOUR_PROJECT_ID>
|
||||
X-Appwrite-Session:
|
||||
X-Appwrite-JWT: <YOUR_JWT>
|
||||
|
||||
{
|
||||
"value": 0,
|
||||
"min": 0
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
PATCH /v1/databases/{databaseId}/collections/{collectionId}/documents/{documentId}/{attribute}/increment HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
Content-Type: application/json
|
||||
X-Appwrite-Response-Format: 1.7.0
|
||||
X-Appwrite-Project: <YOUR_PROJECT_ID>
|
||||
X-Appwrite-Session:
|
||||
X-Appwrite-JWT: <YOUR_JWT>
|
||||
|
||||
{
|
||||
"value": 0,
|
||||
"max": 0
|
||||
}
|
||||
@@ -2,9 +2,7 @@ import { Client, Databases } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setSession('') // The user session to authenticate with
|
||||
.setKey('') //
|
||||
.setJWT('<YOUR_JWT>'); // Your secret JSON Web Token
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Client, Databases } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.decrementDocumentAttribute(
|
||||
'<DATABASE_ID>', // databaseId
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'<DOCUMENT_ID>', // documentId
|
||||
'', // attribute
|
||||
null, // value (optional)
|
||||
null // min (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Client, Databases } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.incrementDocumentAttribute(
|
||||
'<DATABASE_ID>', // databaseId
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'<DOCUMENT_ID>', // documentId
|
||||
'', // attribute
|
||||
null, // value (optional)
|
||||
null // max (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
@@ -0,0 +1,7 @@
|
||||
appwrite databases decrementDocumentAttribute \
|
||||
--databaseId <DATABASE_ID> \
|
||||
--collectionId <COLLECTION_ID> \
|
||||
--documentId <DOCUMENT_ID> \
|
||||
--attribute '' \
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
appwrite databases incrementDocumentAttribute \
|
||||
--databaseId <DATABASE_ID> \
|
||||
--collectionId <COLLECTION_ID> \
|
||||
--documentId <DOCUMENT_ID> \
|
||||
--attribute '' \
|
||||
|
||||
|
||||
@@ -2,9 +2,7 @@ import { Client, Databases } from "@appwrite.io/console";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setSession('') //
|
||||
.setKey('<YOUR_API_KEY>') // Your secret API key
|
||||
.setJWT('<YOUR_JWT>'); // Your secret JSON Web Token
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Client, Databases } from "@appwrite.io/console";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Client, Databases } from "@appwrite.io/console";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.decrementDocumentAttribute(
|
||||
'<DATABASE_ID>', // databaseId
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'<DOCUMENT_ID>', // documentId
|
||||
'', // attribute
|
||||
null, // value (optional)
|
||||
null // min (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Client, Databases } from "@appwrite.io/console";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.incrementDocumentAttribute(
|
||||
'<DATABASE_ID>', // databaseId
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'<DOCUMENT_ID>', // documentId
|
||||
'', // attribute
|
||||
null, // value (optional)
|
||||
null // max (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Client, Databases } from "@appwrite.io/console";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.upsertDocument(
|
||||
'<DATABASE_ID>', // databaseId
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
'<DOCUMENT_ID>', // documentId
|
||||
{}, // data
|
||||
["read("any")"] // permissions (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
@@ -9,7 +9,7 @@ const databases = new Databases(client);
|
||||
const result = await databases.upsertDocuments(
|
||||
'<DATABASE_ID>', // databaseId
|
||||
'<COLLECTION_ID>', // collectionId
|
||||
[] // documents (optional)
|
||||
[] // documents
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Client, Proxy, } from "@appwrite.io/console";
|
||||
import { Client, Proxy, , ProxyResourceType } from "@appwrite.io/console";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
@@ -9,7 +9,9 @@ const proxy = new Proxy(client);
|
||||
const result = await proxy.createRedirectRule(
|
||||
'', // domain
|
||||
'https://example.com', // url
|
||||
.MovedPermanently301 // statusCode
|
||||
.MovedPermanently301, // statusCode
|
||||
'<RESOURCE_ID>', // resourceId
|
||||
ProxyResourceType.Site // resourceType
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -9,7 +9,8 @@ const vcs = new Vcs(client);
|
||||
const result = await vcs.getRepositoryContents(
|
||||
'<INSTALLATION_ID>', // installationId
|
||||
'<PROVIDER_REPOSITORY_ID>', // providerRepositoryId
|
||||
'<PROVIDER_ROOT_DIRECTORY>' // providerRootDirectory (optional)
|
||||
'<PROVIDER_ROOT_DIRECTORY>', // providerRootDirectory (optional)
|
||||
'<PROVIDER_REFERENCE>' // providerReference (optional)
|
||||
);
|
||||
|
||||
console.log(result);
|
||||
|
||||
@@ -2,9 +2,8 @@ import 'package:dart_appwrite/dart_appwrite.dart';
|
||||
|
||||
Client client = Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setSession('') // The user session to authenticate with
|
||||
.setKey('<YOUR_API_KEY>') // Your secret API key
|
||||
.setJWT('<YOUR_JWT>'); // Your secret JSON Web Token
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setSession(''); // The user session to authenticate with
|
||||
|
||||
Databases databases = Databases(client);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:dart_appwrite/dart_appwrite.dart';
|
||||
|
||||
Client client = Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
Databases databases = Databases(client);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:dart_appwrite/dart_appwrite.dart';
|
||||
|
||||
Client client = Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
Databases databases = Databases(client);
|
||||
|
||||
Document result = await databases.decrementDocumentAttribute(
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
attribute: '',
|
||||
value: 0, // (optional)
|
||||
min: 0, // (optional)
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:dart_appwrite/dart_appwrite.dart';
|
||||
|
||||
Client client = Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
Databases databases = Databases(client);
|
||||
|
||||
Document result = await databases.incrementDocumentAttribute(
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
attribute: '',
|
||||
value: 0, // (optional)
|
||||
max: 0, // (optional)
|
||||
);
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:dart_appwrite/dart_appwrite.dart';
|
||||
|
||||
Client client = Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setSession(''); // The user session to authenticate with
|
||||
|
||||
Databases databases = Databases(client);
|
||||
|
||||
Document result = await databases.upsertDocument(
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
data: {},
|
||||
permissions: ["read("any")"], // (optional)
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user