mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
542
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5c5f76acc | ||
|
|
fd952f20d3 | ||
|
|
1d0af0b27d | ||
|
|
81f07d4e98 | ||
|
|
c277408543 | ||
|
|
2df4dea0fc | ||
|
|
c1f46fc20b | ||
|
|
d70e838816 | ||
|
|
1eb5255998 | ||
|
|
ad9c1ec4a8 | ||
|
|
9b1f2dfb8b | ||
|
|
89b559dd8f | ||
|
|
7ba8a28c54 | ||
|
|
3b629e6e37 | ||
|
|
2cbc399fd5 | ||
|
|
528b4f459c | ||
|
|
55435216c8 | ||
|
|
8a1ffca9f9 | ||
|
|
06e382be08 | ||
|
|
5934ad459f | ||
|
|
f4434264cd | ||
|
|
6ff6e0a93d | ||
|
|
727ad6832b | ||
|
|
d84151192e | ||
|
|
178a8ff8e9 | ||
|
|
cc0dfd9e69 | ||
|
|
5d62613cbc | ||
|
|
119bf71e1a | ||
|
|
dc881f46ed | ||
|
|
5fceab8926 | ||
|
|
149ad1a0f3 | ||
|
|
227c4fde13 | ||
|
|
f31a7c007e | ||
|
|
c4e7861358 | ||
|
|
3729c6b1c2 | ||
|
|
65ac334e50 | ||
|
|
3ca9599f73 | ||
|
|
e704e96290 | ||
|
|
a21ccfd694 | ||
|
|
89f0c28e26 | ||
|
|
ea33588f26 | ||
|
|
384b423129 | ||
|
|
15f81d29f3 | ||
|
|
006153e14a | ||
|
|
9196ae7908 | ||
|
|
b46a4dfd3e | ||
|
|
0c163baf80 | ||
|
|
ac2acbb770 | ||
|
|
e54a157ca8 | ||
|
|
050a563db1 | ||
|
|
b096af22a7 | ||
|
|
c8854e01de | ||
|
|
089a80e9a3 | ||
|
|
52f83c4015 | ||
|
|
9dd5898dfd | ||
|
|
7b322f8502 | ||
|
|
160c937dc2 | ||
|
|
c510af0a90 | ||
|
|
44a5a3eca1 | ||
|
|
f09042dfb1 | ||
|
|
d01f1c1a7c | ||
|
|
4704f6f0b0 | ||
|
|
3f14c27c9c | ||
|
|
3ce2ad188a | ||
|
|
481f1b48b2 | ||
|
|
221f15313c | ||
|
|
4099f8968a | ||
|
|
e7c0111229 | ||
|
|
fc0e1b607d | ||
|
|
bc7b310c51 | ||
|
|
cf830affc9 | ||
|
|
014036e9c1 | ||
|
|
ce67def030 | ||
|
|
5df2f5ff80 | ||
|
|
4d650114f2 | ||
|
|
bc41838c66 | ||
|
|
3d9e22a6df | ||
|
|
1b6d05a0fb | ||
|
|
176c085068 | ||
|
|
0487fae9b1 | ||
|
|
c06ff42f61 | ||
|
|
9f30b0263a | ||
|
|
5aa88be49c | ||
|
|
f6fa1aea99 | ||
|
|
df9a6d81fd | ||
|
|
6f493e7c29 | ||
|
|
d5bbb95f3b | ||
|
|
87c9f93fa7 | ||
|
|
517870dd45 | ||
|
|
7dd45b0644 | ||
|
|
7fb4dc5f07 | ||
|
|
e267283fa9 | ||
|
|
84f4a5dba0 | ||
|
|
0c8ae4f34f | ||
|
|
0e17aeaa57 | ||
|
|
78d542427a | ||
|
|
b4b28a9cf4 | ||
|
|
22f64040e3 | ||
|
|
93472ac40e | ||
|
|
d6829dfb34 | ||
|
|
4332adaf37 | ||
|
|
c14d40cc5a | ||
|
|
eab0faab83 | ||
|
|
851894947f | ||
|
|
077c80ca26 | ||
|
|
082309697c | ||
|
|
963d177b17 | ||
|
|
178dec01fd | ||
|
|
5f588b0c65 | ||
|
|
d067d215b1 | ||
|
|
a77eea6b55 | ||
|
|
d3af2e4735 | ||
|
|
a82bd62ef6 | ||
|
|
a95a651d30 | ||
|
|
8c6f0a4e7b | ||
|
|
52de9030c4 | ||
|
|
ff92b23d8e | ||
|
|
0554f9295b | ||
|
|
ffb27284e5 | ||
|
|
e1a291ff21 | ||
|
|
72090248e5 | ||
|
|
261adac306 | ||
|
|
a1ff96829d | ||
|
|
e8e2bb87f6 | ||
|
|
b1e11ab854 | ||
|
|
bc3e1990d0 | ||
|
|
2df28bdf19 | ||
|
|
357b290e36 | ||
|
|
45619e6093 | ||
|
|
5b6bc81b29 | ||
|
|
2fa5bc614c | ||
|
|
6ea61106f4 | ||
|
|
cf13a45188 | ||
|
|
91b1670e52 | ||
|
|
e6881c10d4 | ||
|
|
5585a75624 | ||
|
|
fd7d4db4c4 | ||
|
|
9fc2c5db69 | ||
|
|
bd4bb48da2 | ||
|
|
41ce945c02 | ||
|
|
c852cb3ed0 | ||
|
|
fa67144f32 | ||
|
|
2202b11a26 | ||
|
|
166b19b65c | ||
|
|
d6bcb46b36 | ||
|
|
11f31a15b6 | ||
|
|
2e0fb694d4 | ||
|
|
452aa708df | ||
|
|
e699c998bb | ||
|
|
564fe5323c | ||
|
|
387e8eaa2f | ||
|
|
17f0f51c46 | ||
|
|
0ee4f3c4ce | ||
|
|
e5dc4ac056 | ||
|
|
e3743f72ad | ||
|
|
9866e6fa2f | ||
|
|
66f7d1a3c9 | ||
|
|
c0fd5f6233 | ||
|
|
3a1cd22be5 | ||
|
|
aee3280a9c | ||
|
|
f9be8dc966 | ||
|
|
7522a3a56e | ||
|
|
fc14144a0b | ||
|
|
b47ab90665 | ||
|
|
41cda120d1 | ||
|
|
584ca3065f | ||
|
|
809d31bba2 | ||
|
|
2b44b95073 | ||
|
|
419b134703 | ||
|
|
9fb12fde35 | ||
|
|
d14eb3f12d | ||
|
|
71c54f5742 | ||
|
|
c19d123d48 | ||
|
|
b9579c8922 | ||
|
|
7ed1d7bd37 | ||
|
|
854304dd16 | ||
|
|
c449144926 | ||
|
|
c92a14d318 | ||
|
|
2514391007 | ||
|
|
7049a58f21 | ||
|
|
288f3b2cc8 | ||
|
|
e7963e6825 | ||
|
|
0ac6decc99 | ||
|
|
e48e4537de | ||
|
|
c57967c3b8 | ||
|
|
683eecfd2d | ||
|
|
cdd047b459 | ||
|
|
66194722a9 | ||
|
|
48d5ae0bca | ||
|
|
0467ebc1ff | ||
|
|
df67dd5576 | ||
|
|
a799a2a40b | ||
|
|
8716ef98ca | ||
|
|
7e4a3bb018 | ||
|
|
67cb3aad2f | ||
|
|
e1ee9ba89e | ||
|
|
d98c06abf2 | ||
|
|
8d5d9dc463 | ||
|
|
2af8b3046f | ||
|
|
1f5d2b0ec6 | ||
|
|
b189ba669e | ||
|
|
217e765f7b | ||
|
|
36bc41a748 | ||
|
|
4fc84bec7d | ||
|
|
f7f746a543 | ||
|
|
3063ac899c | ||
|
|
b309808592 | ||
|
|
65ae22d7cd | ||
|
|
2ef2c59d18 | ||
|
|
34150f0c38 | ||
|
|
48f26d85ca | ||
|
|
502b7df2d0 | ||
|
|
f6cd1d4220 | ||
|
|
972c9dde21 | ||
|
|
c770147755 | ||
|
|
0a80ffe4a7 | ||
|
|
7db4932084 | ||
|
|
7a34c7b8df | ||
|
|
9829ba2afb | ||
|
|
0097a0edcd | ||
|
|
c6b1209b8f | ||
|
|
d7653c68ce | ||
|
|
2b9b76f7bd | ||
|
|
823d8604bd | ||
|
|
c4510004ce | ||
|
|
7d284a9872 | ||
|
|
9dc179b022 | ||
|
|
8b53f1b5f5 | ||
|
|
601b6760a5 | ||
|
|
c9c2b575ba | ||
|
|
0d46d4d6e9 | ||
|
|
ebb05cba92 | ||
|
|
61f0e5b3f3 | ||
|
|
9cd521390f | ||
|
|
af6499bd47 | ||
|
|
814abddf93 | ||
|
|
707ef16f79 | ||
|
|
aa07ba0027 | ||
|
|
66ac440f9d | ||
|
|
320bf69208 | ||
|
|
7e6f4befa5 | ||
|
|
2de9b85d6b | ||
|
|
b7702340ba | ||
|
|
340211e3a8 | ||
|
|
501d29f696 | ||
|
|
675179def8 | ||
|
|
cedc4eb2df | ||
|
|
64cea46183 | ||
|
|
852c6fc3fe | ||
|
|
8a09a4b415 | ||
|
|
11725869e2 | ||
|
|
968b6302d0 | ||
|
|
9c62b45cd4 | ||
|
|
8e8402fbd8 | ||
|
|
4ffa89fe35 | ||
|
|
59c1f55183 | ||
|
|
dd25072cdf | ||
|
|
8df77a2295 | ||
|
|
6159e9caa7 | ||
|
|
2d29317179 | ||
|
|
3158c15111 | ||
|
|
021fb48dc1 | ||
|
|
35585fe864 | ||
|
|
deffe25c30 | ||
|
|
4fafb47508 | ||
|
|
08370c10ee | ||
|
|
51f6751b94 | ||
|
|
f6dd9b0d33 | ||
|
|
a68d49a405 | ||
|
|
d9a02fc0fa | ||
|
|
44e16ce98d | ||
|
|
2a5ecb2547 | ||
|
|
3f9b4e92e9 | ||
|
|
c7c9e057ab | ||
|
|
ab97b7cc54 | ||
|
|
b7d0c127a3 | ||
|
|
3adbe8a8f5 | ||
|
|
554d8365a6 | ||
|
|
64baf1071d | ||
|
|
96bef81ebc | ||
|
|
d5999fde53 | ||
|
|
856a9a714b | ||
|
|
f09bda97f8 | ||
|
|
86f7489640 | ||
|
|
c51cf2f3e4 | ||
|
|
a616aa0e93 | ||
|
|
9bb52cda93 | ||
|
|
760f26e044 | ||
|
|
568f6fd274 | ||
|
|
43d84c680e | ||
|
|
7531c64b31 | ||
|
|
ddf5b8e156 | ||
|
|
3f839e3807 | ||
|
|
24e0b3e8cb | ||
|
|
3bb5e9085e | ||
|
|
d3533840c8 | ||
|
|
aa9f554853 | ||
|
|
eb8da87eed | ||
|
|
17eab0ecb5 | ||
|
|
4c5094d3dc | ||
|
|
295e2773f3 | ||
|
|
341bcb5222 | ||
|
|
b932db34f3 | ||
|
|
7208729805 | ||
|
|
f9a74868ff | ||
|
|
0ec7dcf08d | ||
|
|
8ce46c791f | ||
|
|
d8904af909 | ||
|
|
28d2ed1baa | ||
|
|
a366feba80 | ||
|
|
484988fcd7 | ||
|
|
49f8369fdd | ||
|
|
6af9d1206d | ||
|
|
1a0e82b4e4 | ||
|
|
14fe7a3293 | ||
|
|
70feb72de0 | ||
|
|
1434e69b94 | ||
|
|
65a220b702 | ||
|
|
8bc5117259 | ||
|
|
ec77137768 | ||
|
|
281c52d58c | ||
|
|
18335ebf89 | ||
|
|
098f891f7c | ||
|
|
0b0a1c20e9 | ||
|
|
d27dba05d2 | ||
|
|
1a2a725cb0 | ||
|
|
a8fb467df7 | ||
|
|
bc778f507d | ||
|
|
2c7666e0df | ||
|
|
7861cb7c49 | ||
|
|
39d45bdca9 | ||
|
|
a413a613f5 | ||
|
|
5eab81cf03 | ||
|
|
b8cb6f281d | ||
|
|
7c760e4b07 | ||
|
|
e586afceb7 | ||
|
|
6ea3593921 | ||
|
|
73764941b6 | ||
|
|
14274d96aa | ||
|
|
587b84d04a | ||
|
|
fec814e0ec | ||
|
|
02426df1ca | ||
|
|
1d0577b07d | ||
|
|
1a4a10400b | ||
|
|
0cccb75273 | ||
|
|
6526e8d956 | ||
|
|
1e220766b2 | ||
|
|
70f7f71a95 | ||
|
|
57ee69ad9a | ||
|
|
04a9b0be34 | ||
|
|
b4f5af8461 | ||
|
|
f6bf779119 | ||
|
|
71e3446498 | ||
|
|
4adea70684 | ||
|
|
9321f585f5 | ||
|
|
91d729cc55 | ||
|
|
bd01d12db5 | ||
|
|
d2d9032ede | ||
|
|
b727b8c100 | ||
|
|
080cc4a93f | ||
|
|
9df0045024 | ||
|
|
3c74818591 | ||
|
|
a734ae163a | ||
|
|
67a5192705 | ||
|
|
128dc1d946 | ||
|
|
d283c820da | ||
|
|
86526fa1df | ||
|
|
c5f7a72409 | ||
|
|
e81e9d5d67 | ||
|
|
6b7071ca94 | ||
|
|
c00d032c5a | ||
|
|
83e62dc6e1 | ||
|
|
02de47f1dc | ||
|
|
241a0c88e1 | ||
|
|
2803693402 | ||
|
|
341775de2e | ||
|
|
eda1bf3207 | ||
|
|
30915b4803 | ||
|
|
d9bd867b38 | ||
|
|
86b64fdee8 | ||
|
|
8fec677579 | ||
|
|
27fae92b89 | ||
|
|
ba3f907028 | ||
|
|
eab446a828 | ||
|
|
d25800973c | ||
|
|
0024926e11 | ||
|
|
8734aeb314 | ||
|
|
3ec0f08db1 | ||
|
|
c4df1c8c4b | ||
|
|
61b20160f4 | ||
|
|
b8a744d088 | ||
|
|
82fd8553bf | ||
|
|
8796670c66 | ||
|
|
c96232f764 | ||
|
|
9fa6deae61 | ||
|
|
20f2cbffda | ||
|
|
fe5419401a | ||
|
|
cca95d5bd2 | ||
|
|
43aae24132 | ||
|
|
f534b9e460 | ||
|
|
d302718757 | ||
|
|
e5ad27c33a | ||
|
|
58c3232578 | ||
|
|
ac8105dea2 | ||
|
|
cbba893f53 | ||
|
|
dde0dd2218 | ||
|
|
3335f56cf6 | ||
|
|
815849aa64 | ||
|
|
727bac5f3d | ||
|
|
9f14f1152a | ||
|
|
bc34f95646 | ||
|
|
dbc66d2221 | ||
|
|
e8661e6d31 | ||
|
|
2ad4390958 | ||
|
|
fe763093df | ||
|
|
c5a08b69b0 | ||
|
|
00e3397b56 | ||
|
|
69adb689f8 | ||
|
|
0379086c48 | ||
|
|
bc5343b559 | ||
|
|
eb05e8c35c | ||
|
|
5e10670d22 | ||
|
|
455554a6a2 | ||
|
|
cb97679553 | ||
|
|
80e842444b | ||
|
|
6400e94d8b | ||
|
|
d710b48ef5 | ||
|
|
2f6a82f4d5 | ||
|
|
1a7982f855 | ||
|
|
149dfb9651 | ||
|
|
14873824ce | ||
|
|
2a99bd4bda | ||
|
|
534096cac6 | ||
|
|
9f50f0c04a | ||
|
|
b5d31d1b76 | ||
|
|
53e790d886 | ||
|
|
e9d0462548 | ||
|
|
d2b0048a7f | ||
|
|
4aeca149b0 | ||
|
|
12bb74a58d | ||
|
|
b2451c37c3 | ||
|
|
14fe7a7217 | ||
|
|
f59377e1bb | ||
|
|
24312e69f4 | ||
|
|
1177178885 | ||
|
|
3274a38c9b | ||
|
|
eb3b09cf0d | ||
|
|
e203ae7327 | ||
|
|
559b6024bc | ||
|
|
256aa684a2 | ||
|
|
0ec7ff2cee | ||
|
|
fc279cb30c | ||
|
|
abd2cead73 | ||
|
|
1de778443f | ||
|
|
08478b8d39 | ||
|
|
330c298c58 | ||
|
|
e7c9b3d900 | ||
|
|
6090ca50fc | ||
|
|
3ba0565e44 | ||
|
|
6dbb14bf7b | ||
|
|
b8b0db7a2f | ||
|
|
728b758db5 | ||
|
|
33dc7ea97c | ||
|
|
da811de615 | ||
|
|
c1315019cb | ||
|
|
1eb642aff0 | ||
|
|
908d80e8d9 | ||
|
|
c4f1a682fd | ||
|
|
55236524e3 | ||
|
|
4e9385f70b | ||
|
|
33fbab7e3d | ||
|
|
09152a08e1 | ||
|
|
af3388a51f | ||
|
|
16b2449787 | ||
|
|
d8cc770ba5 | ||
|
|
3e914a4f1b | ||
|
|
406630cb61 | ||
|
|
a4239fc5dc | ||
|
|
0f313afcb5 | ||
|
|
bc556c559f | ||
|
|
04539bff4a | ||
|
|
29647a9dc8 | ||
|
|
a3db080fcf | ||
|
|
5ba363b787 | ||
|
|
0a12b561c8 | ||
|
|
36c4ed1dca | ||
|
|
53133ba54f | ||
|
|
22174bec0e | ||
|
|
b00e7b804a | ||
|
|
50448126a9 | ||
|
|
e7e38c57f7 | ||
|
|
7750ada4d8 | ||
|
|
ea42b823d8 | ||
|
|
6f861a91ee | ||
|
|
0483c7efb5 | ||
|
|
fb29fe7b3c | ||
|
|
6ffe620c57 | ||
|
|
7b901b5d34 | ||
|
|
511cf72013 | ||
|
|
12b816178b | ||
|
|
1bf1766822 | ||
|
|
57edb4a385 | ||
|
|
17c555ab6c | ||
|
|
d6bd72cfd3 | ||
|
|
8c9123beaa | ||
|
|
3d967e695f | ||
|
|
a7d7e39dfd | ||
|
|
38cb95e940 | ||
|
|
b2b20c48b3 | ||
|
|
fed6579491 | ||
|
|
8cb85cafbd | ||
|
|
55e560bb41 | ||
|
|
dd9a40221e | ||
|
|
24300cd3bd | ||
|
|
4d5961c3ab | ||
|
|
afb40218d7 | ||
|
|
3daa3821ef | ||
|
|
84cec0e32c | ||
|
|
7bae15d4fe | ||
|
|
0180f72067 | ||
|
|
f537091eb2 | ||
|
|
c99c83a006 | ||
|
|
113828cd00 | ||
|
|
477add3022 | ||
|
|
8aa5714173 | ||
|
|
fdb83c51f7 | ||
|
|
c15739bba7 | ||
|
|
1ce84f1650 | ||
|
|
f618a8b563 | ||
|
|
9346efc24a | ||
|
|
a5e57a9a67 | ||
|
|
cc724218b0 | ||
|
|
706ce4b3b6 | ||
|
|
c08355df8d | ||
|
|
2d5904e3d0 | ||
|
|
d1318cf016 | ||
|
|
91921859a2 | ||
|
|
a058925252 | ||
|
|
fadb24f295 | ||
|
|
748119334d | ||
|
|
cb05b87dda | ||
|
|
aa36d944db |
+7
-1
@@ -8,4 +8,10 @@ reviews:
|
||||
base_branches:
|
||||
- main
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
high_level_summary: false
|
||||
poem: false
|
||||
sequence_diagrams: false
|
||||
collapse_walkthrough: true
|
||||
changed_files_summary: false
|
||||
|
||||
@@ -85,7 +85,7 @@ _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.29
|
||||
_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.32
|
||||
_APP_MAINTENANCE_INTERVAL=86400
|
||||
_APP_MAINTENANCE_START_TIME=12:00
|
||||
_APP_MAINTENANCE_RETENTION_CACHE=2592000
|
||||
|
||||
@@ -198,7 +198,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=devKeys
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys
|
||||
|
||||
e2e_shared_mode_test:
|
||||
name: E2E Shared Mode Service Test
|
||||
@@ -277,7 +277,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=devKeys
|
||||
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys
|
||||
|
||||
e2e_dev_keys:
|
||||
name: E2E Service Test (Dev Keys)
|
||||
|
||||
+5
-5
@@ -415,8 +415,8 @@ In addition, you will also need to add some logic to the `reduce()` method of th
|
||||
|
||||
```php
|
||||
case $document->getCollection() === 'buckets':
|
||||
$files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES)));
|
||||
$storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE)));
|
||||
$files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getSequence(), METRIC_BUCKET_ID_FILES)));
|
||||
$storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getSequence(), METRIC_BUCKET_ID_FILES_STORAGE)));
|
||||
|
||||
if (!empty($files['value'])) {
|
||||
$metrics[] = [
|
||||
@@ -463,9 +463,9 @@ $queueForStatsUsage
|
||||
->addMetric(METRIC_BUILDS, 1)
|
||||
->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0))
|
||||
->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getSequence(), METRIC_FUNCTION_ID_BUILDS), 1)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getSequence(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getSequence(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
```
|
||||
|
||||
+3
-3
@@ -72,7 +72,7 @@ docker run -it --rm \
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.7.0
|
||||
appwrite/appwrite:1.7.4
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -84,7 +84,7 @@ docker run -it --rm ^
|
||||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.7.0
|
||||
appwrite/appwrite:1.7.4
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
@@ -94,7 +94,7 @@ docker run -it --rm `
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.7.0
|
||||
appwrite/appwrite:1.7.4
|
||||
```
|
||||
|
||||
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
|
||||
|
||||
@@ -78,7 +78,7 @@ docker run -it --rm \
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.7.0
|
||||
appwrite/appwrite:1.7.4
|
||||
```
|
||||
|
||||
### Windows
|
||||
@@ -90,7 +90,7 @@ docker run -it --rm ^
|
||||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.7.0
|
||||
appwrite/appwrite:1.7.4
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
@@ -100,7 +100,7 @@ docker run -it --rm `
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.7.0
|
||||
appwrite/appwrite:1.7.4
|
||||
```
|
||||
|
||||
Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation.
|
||||
|
||||
+21
-34
@@ -10,12 +10,15 @@ use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
use Appwrite\Runtimes\Runtimes;
|
||||
use Executor\Executor;
|
||||
use Swoole\Runtime;
|
||||
use Swoole\Timer;
|
||||
use Utopia\Cache\Adapter\Pool as CachePool;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\CLI;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\Pool as DatabasePool;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
@@ -23,6 +26,7 @@ use Utopia\DSN\DSN;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Platform\Service;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Queue\Broker\Pool as BrokerPool;
|
||||
use Utopia\Queue\Publisher;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\System\System;
|
||||
@@ -45,10 +49,7 @@ CLI::setResource('cache', function ($pools) {
|
||||
$adapters = [];
|
||||
|
||||
foreach ($list as $value) {
|
||||
$adapters[] = $pools
|
||||
->get($value)
|
||||
->pop()
|
||||
->getResource();
|
||||
$adapters[] = new CachePool($pools->get($value));
|
||||
}
|
||||
|
||||
return new Cache(new Sharding($adapters));
|
||||
@@ -68,12 +69,8 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) {
|
||||
$attempts++;
|
||||
try {
|
||||
// Prepare database connection
|
||||
$dbAdapter = $pools
|
||||
->get('console')
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$dbForPlatform = new Database($dbAdapter, $cache);
|
||||
$adapter = new DatabasePool($pools->get('console'));
|
||||
$dbForPlatform = new Database($adapter, $cache);
|
||||
|
||||
$dbForPlatform
|
||||
->setNamespace('_console')
|
||||
@@ -91,7 +88,6 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) {
|
||||
$ready = true;
|
||||
} catch (\Throwable $err) {
|
||||
Console::warning($err->getMessage());
|
||||
$pools->get('console')->reclaim();
|
||||
sleep($sleep);
|
||||
}
|
||||
} while ($attempts < $maxAttempts && !$ready);
|
||||
@@ -129,37 +125,33 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getInternalId())
|
||||
->setTenant((int)$project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
return $database;
|
||||
}
|
||||
|
||||
$dbAdapter = $pools
|
||||
->get($dsn->getHost())
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database($dbAdapter, $cache);
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$database = new Database($adapter, $cache);
|
||||
$databases[$dsn->getHost()] = $database;
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getInternalId())
|
||||
->setTenant((int)$project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
$database
|
||||
@@ -172,21 +164,15 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
|
||||
|
||||
CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
$database = null;
|
||||
|
||||
return function (?Document $project = null) use ($pools, $cache, $database) {
|
||||
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getInternalId());
|
||||
$database->setTenant((int)$project->getSequence());
|
||||
return $database;
|
||||
}
|
||||
|
||||
$dbAdapter = $pools
|
||||
->get('logs')
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database(
|
||||
$dbAdapter,
|
||||
$cache
|
||||
);
|
||||
$adapter = new DatabasePool($pools->get('logs'));
|
||||
$database = new Database($adapter, $cache);
|
||||
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
@@ -196,7 +182,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
|
||||
// set tenant
|
||||
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getInternalId());
|
||||
$database->setTenant((int)$project->getSequence());
|
||||
}
|
||||
|
||||
return $database;
|
||||
@@ -210,7 +196,7 @@ CLI::setResource('queueForStatsResources', function (Publisher $publisher) {
|
||||
return new StatsResources($publisher);
|
||||
}, ['publisher']);
|
||||
CLI::setResource('publisher', function (Group $pools) {
|
||||
return $pools->get('publisher')->pop()->getResource();
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
CLI::setResource('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
@@ -265,7 +251,7 @@ CLI::setResource('logError', function (Registry $register) {
|
||||
};
|
||||
}, ['register']);
|
||||
|
||||
CLI::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
|
||||
CLI::setResource('executor', fn () => new Executor());
|
||||
|
||||
CLI::setResource('telemetry', fn () => new NoTelemetry());
|
||||
|
||||
@@ -298,4 +284,5 @@ $cli
|
||||
|
||||
$cli->shutdown()->action(fn () => Timer::clearAll());
|
||||
|
||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||
run($cli->run(...));
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
|
||||
@@ -173,7 +172,7 @@ return [
|
||||
'size' => 256,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => Auth::DEFAULT_ALGO,
|
||||
'default' => '',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
@@ -184,7 +183,7 @@ return [
|
||||
'size' => 65535,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
'default' => new \stdClass(),
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
@@ -1115,9 +1114,9 @@ return [
|
||||
[
|
||||
'$id' => ID::custom('expire'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'required' => false,
|
||||
'required' => true,
|
||||
'format' => '',
|
||||
'signed' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
@@ -1439,13 +1438,6 @@ return [
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_roles'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['roles'],
|
||||
'lengths' => [128],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -1286,6 +1286,7 @@ return [
|
||||
]
|
||||
],
|
||||
],
|
||||
|
||||
'deployments' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('deployments'),
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
* Initializes console project document.
|
||||
*/
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\System\System;
|
||||
|
||||
$console = [
|
||||
'$id' => ID::custom('console'),
|
||||
'$internalId' => ID::custom('console'),
|
||||
'$sequence' => ID::custom('console'),
|
||||
'name' => 'Appwrite',
|
||||
'$collection' => ID::custom('projects'),
|
||||
'description' => 'Appwrite core engine',
|
||||
@@ -38,7 +37,7 @@ $console = [
|
||||
'mockNumbers' => [],
|
||||
'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled',
|
||||
'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
|
||||
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
|
||||
'duration' => TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
|
||||
'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled'
|
||||
],
|
||||
'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
|
||||
|
||||
@@ -393,6 +393,16 @@ return [
|
||||
'description' => 'Membership is already confirmed.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::MEMBERSHIP_DELETION_PROHIBITED => [
|
||||
'name' => Exception::MEMBERSHIP_DELETION_PROHIBITED,
|
||||
'description' => 'Membership deletion is prohibited.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::MEMBERSHIP_DOWNGRADE_PROHIBITED => [
|
||||
'name' => Exception::MEMBERSHIP_DOWNGRADE_PROHIBITED,
|
||||
'description' => 'Membership role downgrade is prohibited.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Avatars */
|
||||
Exception::AVATAR_SET_NOT_FOUND => [
|
||||
|
||||
@@ -11,7 +11,7 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Web',
|
||||
'version' => '18.0.0',
|
||||
'version' => '18.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-web',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -59,7 +59,7 @@ return [
|
||||
[
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
'version' => '16.0.0',
|
||||
'version' => '17.0.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-flutter',
|
||||
'package' => 'https://pub.dev/packages/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -77,7 +77,7 @@ return [
|
||||
[
|
||||
'key' => 'apple',
|
||||
'name' => 'Apple',
|
||||
'version' => '10.0.0',
|
||||
'version' => '10.1.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'enabled' => true,
|
||||
@@ -112,7 +112,7 @@ return [
|
||||
[
|
||||
'key' => 'android',
|
||||
'name' => 'Android',
|
||||
'version' => '8.0.0',
|
||||
'version' => '8.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-android',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
|
||||
'enabled' => true,
|
||||
@@ -134,7 +134,7 @@ return [
|
||||
[
|
||||
'key' => 'react-native',
|
||||
'name' => 'React Native',
|
||||
'version' => '0.9.0',
|
||||
'version' => '0.10.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-react-native',
|
||||
'package' => 'https://npmjs.com/package/react-native-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -199,7 +199,7 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Console',
|
||||
'version' => '1.3.0',
|
||||
'version' => '1.7.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-console',
|
||||
'package' => '',
|
||||
'enabled' => true,
|
||||
@@ -217,7 +217,7 @@ return [
|
||||
[
|
||||
'key' => 'cli',
|
||||
'name' => 'Command Line',
|
||||
'version' => '6.2.3',
|
||||
'version' => '8.0.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
@@ -231,6 +231,11 @@ return [
|
||||
'gitRepoName' => 'sdk-for-cli',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'exclude' => [
|
||||
'services' => [
|
||||
['name' => 'assistant'],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -353,7 +358,7 @@ return [
|
||||
[
|
||||
'key' => 'dotnet',
|
||||
'name' => '.NET',
|
||||
'version' => '0.15.0',
|
||||
'version' => '0.13.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
||||
'package' => 'https://www.nuget.org/packages/Appwrite',
|
||||
'enabled' => true,
|
||||
@@ -411,7 +416,7 @@ return [
|
||||
[
|
||||
'key' => 'swift',
|
||||
'name' => 'Swift',
|
||||
'version' => '10.0.0',
|
||||
'version' => '10.1.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'enabled' => true,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
|
||||
$member = [
|
||||
'global',
|
||||
@@ -86,7 +85,7 @@ $admins = [
|
||||
];
|
||||
|
||||
return [
|
||||
Auth::USER_ROLE_GUESTS => [
|
||||
USER_ROLE_GUESTS => [
|
||||
'label' => 'Guests',
|
||||
'scopes' => [
|
||||
'global',
|
||||
@@ -104,23 +103,23 @@ return [
|
||||
'execution.write',
|
||||
],
|
||||
],
|
||||
Auth::USER_ROLE_USERS => [
|
||||
USER_ROLE_USERS => [
|
||||
'label' => 'Users',
|
||||
'scopes' => \array_merge($member),
|
||||
],
|
||||
Auth::USER_ROLE_ADMIN => [
|
||||
USER_ROLE_ADMIN => [
|
||||
'label' => 'Admin',
|
||||
'scopes' => \array_merge($admins),
|
||||
],
|
||||
Auth::USER_ROLE_DEVELOPER => [
|
||||
USER_ROLE_DEVELOPER => [
|
||||
'label' => 'Developer',
|
||||
'scopes' => \array_merge($admins),
|
||||
],
|
||||
Auth::USER_ROLE_OWNER => [
|
||||
USER_ROLE_OWNER => [
|
||||
'label' => 'Owner',
|
||||
'scopes' => \array_merge($member, $admins),
|
||||
],
|
||||
Auth::USER_ROLE_APPS => [
|
||||
USER_ROLE_APPS => [
|
||||
'label' => 'Applications',
|
||||
'scopes' => ['global', 'health.read', 'graphql'],
|
||||
],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"version": "1.7.0",
|
||||
"version": "1.7.4",
|
||||
"title": "Appwrite",
|
||||
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
|
||||
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
|
||||
@@ -4660,6 +4660,115 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"summary": "Upsert document",
|
||||
"operationId": "databasesUpsertDocument",
|
||||
"tags": [
|
||||
"databases"
|
||||
],
|
||||
"description": "**WARNING: Experimental Feature** - This endpoint is experimental and not yet officially supported. It may be subject to breaking changes or removal in future versions.\n\nCreate or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Document",
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"$ref": "#\/components\/schemas\/document"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-appwrite": {
|
||||
"method": "upsertDocument",
|
||||
"group": "documents",
|
||||
"weight": 114,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
"demo": "databases\/upsert-document.md",
|
||||
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md",
|
||||
"rate-limit": 120,
|
||||
"rate-time": 60,
|
||||
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
|
||||
"scope": "documents.write",
|
||||
"platforms": [
|
||||
"client",
|
||||
"server",
|
||||
"server"
|
||||
],
|
||||
"packaging": false,
|
||||
"auth": {
|
||||
"Project": []
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Project": [],
|
||||
"Session": [],
|
||||
"JWT": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "databaseId",
|
||||
"description": "Database ID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<DATABASE_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "collectionId",
|
||||
"description": "Collection ID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<COLLECTION_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "documentId",
|
||||
"description": "Document ID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<DOCUMENT_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"description": "Document data as JSON object. Include all required attributes of the document to be created or updated.",
|
||||
"x-example": "{}"
|
||||
},
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).",
|
||||
"x-example": "[\"read(\"any\")\"]",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"summary": "Update document",
|
||||
"operationId": "databasesUpdateDocument",
|
||||
@@ -4781,7 +4890,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteDocument",
|
||||
"group": "documents",
|
||||
"weight": 116,
|
||||
"weight": 117,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -4865,7 +4974,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listExecutions",
|
||||
"group": "executions",
|
||||
"weight": 391,
|
||||
"weight": 392,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -4940,7 +5049,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createExecution",
|
||||
"group": "executions",
|
||||
"weight": 389,
|
||||
"weight": 390,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5055,7 +5164,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getExecution",
|
||||
"group": "executions",
|
||||
"weight": 390,
|
||||
"weight": 391,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5129,7 +5238,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 305,
|
||||
"weight": 306,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
@@ -5181,7 +5290,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 304,
|
||||
"weight": 305,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
@@ -5233,7 +5342,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": null,
|
||||
"weight": 121,
|
||||
"weight": 122,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5285,7 +5394,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCodes",
|
||||
"group": null,
|
||||
"weight": 122,
|
||||
"weight": 123,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5337,7 +5446,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listContinents",
|
||||
"group": null,
|
||||
"weight": 126,
|
||||
"weight": 127,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5389,7 +5498,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountries",
|
||||
"group": null,
|
||||
"weight": 123,
|
||||
"weight": 124,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5441,7 +5550,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesEU",
|
||||
"group": null,
|
||||
"weight": 124,
|
||||
"weight": 125,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5493,7 +5602,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesPhones",
|
||||
"group": null,
|
||||
"weight": 125,
|
||||
"weight": 126,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5545,7 +5654,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCurrencies",
|
||||
"group": null,
|
||||
"weight": 127,
|
||||
"weight": 128,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5597,7 +5706,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listLanguages",
|
||||
"group": null,
|
||||
"weight": 128,
|
||||
"weight": 129,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5649,7 +5758,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 351,
|
||||
"weight": 352,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5732,7 +5841,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 355,
|
||||
"weight": 356,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5807,7 +5916,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"group": "files",
|
||||
"weight": 211,
|
||||
"weight": 212,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5893,7 +6002,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"group": "files",
|
||||
"weight": 210,
|
||||
"weight": 211,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"deprecated": false,
|
||||
@@ -5991,7 +6100,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"group": "files",
|
||||
"weight": 212,
|
||||
"weight": 213,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6063,7 +6172,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"group": "files",
|
||||
"weight": 217,
|
||||
"weight": 218,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6152,7 +6261,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"group": "files",
|
||||
"weight": 218,
|
||||
"weight": 219,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6219,7 +6328,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"group": "files",
|
||||
"weight": 214,
|
||||
"weight": 215,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
@@ -6297,7 +6406,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"group": "files",
|
||||
"weight": 213,
|
||||
"weight": 214,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
@@ -6486,7 +6595,8 @@
|
||||
"png",
|
||||
"webp",
|
||||
"heic",
|
||||
"avif"
|
||||
"avif",
|
||||
"gif"
|
||||
],
|
||||
"x-enum-name": "ImageFormat",
|
||||
"x-enum-keys": [],
|
||||
@@ -6524,7 +6634,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"group": "files",
|
||||
"weight": 215,
|
||||
"weight": 216,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
@@ -6609,7 +6719,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "list",
|
||||
"group": "teams",
|
||||
"weight": 222,
|
||||
"weight": 223,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6685,7 +6795,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "create",
|
||||
"group": "teams",
|
||||
"weight": 221,
|
||||
"weight": 222,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6770,7 +6880,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": "teams",
|
||||
"weight": 223,
|
||||
"weight": 224,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6832,7 +6942,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateName",
|
||||
"group": "teams",
|
||||
"weight": 225,
|
||||
"weight": 226,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6906,7 +7016,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "delete",
|
||||
"group": "teams",
|
||||
"weight": 227,
|
||||
"weight": 228,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6970,7 +7080,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMemberships",
|
||||
"group": "memberships",
|
||||
"weight": 229,
|
||||
"weight": 230,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7056,7 +7166,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMembership",
|
||||
"group": "memberships",
|
||||
"weight": 228,
|
||||
"weight": 229,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7167,7 +7277,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMembership",
|
||||
"group": "memberships",
|
||||
"weight": 230,
|
||||
"weight": 231,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7239,7 +7349,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembership",
|
||||
"group": "memberships",
|
||||
"weight": 231,
|
||||
"weight": 232,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7326,7 +7436,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMembership",
|
||||
"group": "memberships",
|
||||
"weight": 233,
|
||||
"weight": 234,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7400,7 +7510,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembershipStatus",
|
||||
"group": "memberships",
|
||||
"weight": 232,
|
||||
"weight": 233,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7498,7 +7608,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getPrefs",
|
||||
"group": "teams",
|
||||
"weight": 224,
|
||||
"weight": 225,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7559,7 +7669,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updatePrefs",
|
||||
"group": "teams",
|
||||
"weight": 226,
|
||||
"weight": 227,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -9752,6 +9862,12 @@
|
||||
"name": "X-Appwrite-Session",
|
||||
"description": "The user session to authenticate with",
|
||||
"in": "header"
|
||||
},
|
||||
"DevKey": {
|
||||
"type": "apiKey",
|
||||
"name": "X-Appwrite-Dev-Key",
|
||||
"description": "Your secret dev API key",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"version": "1.7.0",
|
||||
"version": "1.7.4",
|
||||
"title": "Appwrite",
|
||||
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
|
||||
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
|
||||
@@ -4457,6 +4457,7 @@
|
||||
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
|
||||
"scope": "documents.write",
|
||||
"platforms": [
|
||||
"console",
|
||||
"client",
|
||||
"server",
|
||||
"server"
|
||||
@@ -4466,6 +4467,7 @@
|
||||
{
|
||||
"name": "createDocument",
|
||||
"auth": {
|
||||
"Admin": [],
|
||||
"Session": [],
|
||||
"Key": [],
|
||||
"JWT": []
|
||||
@@ -4660,6 +4662,115 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"summary": "Upsert document",
|
||||
"operationId": "databasesUpsertDocument",
|
||||
"tags": [
|
||||
"databases"
|
||||
],
|
||||
"description": "**WARNING: Experimental Feature** - This endpoint is experimental and not yet officially supported. It may be subject to breaking changes or removal in future versions.\n\nCreate or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Document",
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"$ref": "#\/components\/schemas\/document"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-appwrite": {
|
||||
"method": "upsertDocument",
|
||||
"group": "documents",
|
||||
"weight": 114,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
"demo": "databases\/upsert-document.md",
|
||||
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md",
|
||||
"rate-limit": 120,
|
||||
"rate-time": 60,
|
||||
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
|
||||
"scope": "documents.write",
|
||||
"platforms": [
|
||||
"client",
|
||||
"server",
|
||||
"server"
|
||||
],
|
||||
"packaging": false,
|
||||
"auth": {
|
||||
"Project": []
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Project": [],
|
||||
"Session": [],
|
||||
"JWT": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "databaseId",
|
||||
"description": "Database ID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<DATABASE_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "collectionId",
|
||||
"description": "Collection ID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<COLLECTION_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "documentId",
|
||||
"description": "Document ID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<DOCUMENT_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"description": "Document data as JSON object. Include all required attributes of the document to be created or updated.",
|
||||
"x-example": "{}"
|
||||
},
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).",
|
||||
"x-example": "[\"read(\"any\")\"]",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"summary": "Update document",
|
||||
"operationId": "databasesUpdateDocument",
|
||||
@@ -4781,7 +4892,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteDocument",
|
||||
"group": "documents",
|
||||
"weight": 116,
|
||||
"weight": 119,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -4842,6 +4953,236 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/decrement": {
|
||||
"patch": {
|
||||
"summary": "Decrement document attribute",
|
||||
"operationId": "databasesDecrementDocumentAttribute",
|
||||
"tags": [
|
||||
"databases"
|
||||
],
|
||||
"description": "Decrement a specific attribute of a document by a given value.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Document",
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"$ref": "#\/components\/schemas\/document"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-appwrite": {
|
||||
"method": "decrementDocumentAttribute",
|
||||
"group": "documents",
|
||||
"weight": 116,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
"demo": "databases\/decrement-document-attribute.md",
|
||||
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/decrement-document-attribute.md",
|
||||
"rate-limit": 120,
|
||||
"rate-time": 60,
|
||||
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
|
||||
"scope": "documents.write",
|
||||
"platforms": [
|
||||
"console",
|
||||
"server",
|
||||
"client",
|
||||
"server"
|
||||
],
|
||||
"packaging": false,
|
||||
"auth": {
|
||||
"Project": []
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Project": [],
|
||||
"Session": [],
|
||||
"JWT": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "databaseId",
|
||||
"description": "Database ID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<DATABASE_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "collectionId",
|
||||
"description": "Collection ID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<COLLECTION_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "documentId",
|
||||
"description": "Document ID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<DOCUMENT_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "attribute",
|
||||
"description": "Attribute key.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "number",
|
||||
"description": "Value to decrement the attribute by. The value must be a number.",
|
||||
"x-example": null
|
||||
},
|
||||
"min": {
|
||||
"type": "number",
|
||||
"description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.",
|
||||
"x-example": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"\/databases\/{databaseId}\/collections\/{collectionId}\/documents\/{documentId}\/{attribute}\/increment": {
|
||||
"patch": {
|
||||
"summary": "Increment document attribute",
|
||||
"operationId": "databasesIncrementDocumentAttribute",
|
||||
"tags": [
|
||||
"databases"
|
||||
],
|
||||
"description": "Increment a specific attribute of a document by a given value.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Document",
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"$ref": "#\/components\/schemas\/document"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-appwrite": {
|
||||
"method": "incrementDocumentAttribute",
|
||||
"group": "documents",
|
||||
"weight": 115,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
"demo": "databases\/increment-document-attribute.md",
|
||||
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/increment-document-attribute.md",
|
||||
"rate-limit": 120,
|
||||
"rate-time": 60,
|
||||
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
|
||||
"scope": "documents.write",
|
||||
"platforms": [
|
||||
"console",
|
||||
"server",
|
||||
"client",
|
||||
"server"
|
||||
],
|
||||
"packaging": false,
|
||||
"auth": {
|
||||
"Project": []
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Project": [],
|
||||
"Session": [],
|
||||
"JWT": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "databaseId",
|
||||
"description": "Database ID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<DATABASE_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "collectionId",
|
||||
"description": "Collection ID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<COLLECTION_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "documentId",
|
||||
"description": "Document ID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<DOCUMENT_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "attribute",
|
||||
"description": "Attribute key.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "path"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "number",
|
||||
"description": "Value to increment the attribute by. The value must be a number.",
|
||||
"x-example": null
|
||||
},
|
||||
"max": {
|
||||
"type": "number",
|
||||
"description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.",
|
||||
"x-example": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"\/functions\/{functionId}\/executions": {
|
||||
"get": {
|
||||
"summary": "List executions",
|
||||
@@ -4865,7 +5206,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listExecutions",
|
||||
"group": "executions",
|
||||
"weight": 391,
|
||||
"weight": 394,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -4940,7 +5281,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createExecution",
|
||||
"group": "executions",
|
||||
"weight": 389,
|
||||
"weight": 392,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5055,7 +5396,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getExecution",
|
||||
"group": "executions",
|
||||
"weight": 390,
|
||||
"weight": 393,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5129,7 +5470,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 305,
|
||||
"weight": 308,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
@@ -5181,7 +5522,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 304,
|
||||
"weight": 307,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
@@ -5233,7 +5574,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": null,
|
||||
"weight": 121,
|
||||
"weight": 124,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5285,7 +5626,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCodes",
|
||||
"group": null,
|
||||
"weight": 122,
|
||||
"weight": 125,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5337,7 +5678,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listContinents",
|
||||
"group": null,
|
||||
"weight": 126,
|
||||
"weight": 129,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5389,7 +5730,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountries",
|
||||
"group": null,
|
||||
"weight": 123,
|
||||
"weight": 126,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5441,7 +5782,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesEU",
|
||||
"group": null,
|
||||
"weight": 124,
|
||||
"weight": 127,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5493,7 +5834,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesPhones",
|
||||
"group": null,
|
||||
"weight": 125,
|
||||
"weight": 128,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5545,7 +5886,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCurrencies",
|
||||
"group": null,
|
||||
"weight": 127,
|
||||
"weight": 130,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5597,7 +5938,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listLanguages",
|
||||
"group": null,
|
||||
"weight": 128,
|
||||
"weight": 131,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5649,7 +5990,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 351,
|
||||
"weight": 354,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5732,7 +6073,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 355,
|
||||
"weight": 358,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5807,7 +6148,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"group": "files",
|
||||
"weight": 211,
|
||||
"weight": 214,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5893,7 +6234,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"group": "files",
|
||||
"weight": 210,
|
||||
"weight": 213,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"deprecated": false,
|
||||
@@ -5991,7 +6332,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"group": "files",
|
||||
"weight": 212,
|
||||
"weight": 215,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6063,7 +6404,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"group": "files",
|
||||
"weight": 217,
|
||||
"weight": 220,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6152,7 +6493,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"group": "files",
|
||||
"weight": 218,
|
||||
"weight": 221,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6219,7 +6560,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"group": "files",
|
||||
"weight": 214,
|
||||
"weight": 217,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
@@ -6297,7 +6638,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"group": "files",
|
||||
"weight": 213,
|
||||
"weight": 216,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
@@ -6486,7 +6827,8 @@
|
||||
"png",
|
||||
"webp",
|
||||
"heic",
|
||||
"avif"
|
||||
"avif",
|
||||
"gif"
|
||||
],
|
||||
"x-enum-name": "ImageFormat",
|
||||
"x-enum-keys": [],
|
||||
@@ -6524,7 +6866,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"group": "files",
|
||||
"weight": 215,
|
||||
"weight": 218,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
@@ -6609,7 +6951,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "list",
|
||||
"group": "teams",
|
||||
"weight": 222,
|
||||
"weight": 225,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6685,7 +7027,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "create",
|
||||
"group": "teams",
|
||||
"weight": 221,
|
||||
"weight": 224,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6770,7 +7112,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": "teams",
|
||||
"weight": 223,
|
||||
"weight": 226,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6832,7 +7174,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateName",
|
||||
"group": "teams",
|
||||
"weight": 225,
|
||||
"weight": 228,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6906,7 +7248,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "delete",
|
||||
"group": "teams",
|
||||
"weight": 227,
|
||||
"weight": 230,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6970,7 +7312,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMemberships",
|
||||
"group": "memberships",
|
||||
"weight": 229,
|
||||
"weight": 232,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7056,7 +7398,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMembership",
|
||||
"group": "memberships",
|
||||
"weight": 228,
|
||||
"weight": 231,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7167,7 +7509,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMembership",
|
||||
"group": "memberships",
|
||||
"weight": 230,
|
||||
"weight": 233,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7239,7 +7581,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembership",
|
||||
"group": "memberships",
|
||||
"weight": 231,
|
||||
"weight": 234,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7326,7 +7668,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMembership",
|
||||
"group": "memberships",
|
||||
"weight": 233,
|
||||
"weight": 236,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7400,7 +7742,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembershipStatus",
|
||||
"group": "memberships",
|
||||
"weight": 232,
|
||||
"weight": 235,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7498,7 +7840,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getPrefs",
|
||||
"group": "teams",
|
||||
"weight": 224,
|
||||
"weight": 227,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7559,7 +7901,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updatePrefs",
|
||||
"group": "teams",
|
||||
"weight": 226,
|
||||
"weight": 229,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -8073,6 +8415,12 @@
|
||||
"description": "Document ID.",
|
||||
"x-example": "5e5ea5c16897e"
|
||||
},
|
||||
"$sequence": {
|
||||
"type": "integer",
|
||||
"description": "Document automatically incrementing ID.",
|
||||
"x-example": 1,
|
||||
"format": "int32"
|
||||
},
|
||||
"$collectionId": {
|
||||
"type": "string",
|
||||
"description": "Collection ID.",
|
||||
@@ -8107,6 +8455,7 @@
|
||||
"additionalProperties": true,
|
||||
"required": [
|
||||
"$id",
|
||||
"$sequence",
|
||||
"$collectionId",
|
||||
"$databaseId",
|
||||
"$createdAt",
|
||||
@@ -9752,6 +10101,12 @@
|
||||
"name": "X-Appwrite-Session",
|
||||
"description": "The user session to authenticate with",
|
||||
"in": "header"
|
||||
},
|
||||
"DevKey": {
|
||||
"type": "apiKey",
|
||||
"name": "X-Appwrite-Dev-Key",
|
||||
"description": "Your secret dev API key",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"version": "1.7.0",
|
||||
"version": "1.7.4",
|
||||
"title": "Appwrite",
|
||||
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
|
||||
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
|
||||
@@ -61,6 +61,12 @@
|
||||
"name": "X-Appwrite-Session",
|
||||
"description": "The user session to authenticate with",
|
||||
"in": "header"
|
||||
},
|
||||
"DevKey": {
|
||||
"type": "apiKey",
|
||||
"name": "X-Appwrite-Dev-Key",
|
||||
"description": "Your secret dev API key",
|
||||
"in": "header"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
@@ -4791,6 +4797,111 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"summary": "Upsert document",
|
||||
"operationId": "databasesUpsertDocument",
|
||||
"consumes": [
|
||||
"application\/json"
|
||||
],
|
||||
"produces": [
|
||||
"application\/json"
|
||||
],
|
||||
"tags": [
|
||||
"databases"
|
||||
],
|
||||
"description": "**WARNING: Experimental Feature** - This endpoint is experimental and not yet officially supported. It may be subject to breaking changes or removal in future versions.\n\nCreate or update a Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Document",
|
||||
"schema": {
|
||||
"$ref": "#\/definitions\/document"
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-appwrite": {
|
||||
"method": "upsertDocument",
|
||||
"group": "documents",
|
||||
"weight": 114,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
"demo": "databases\/upsert-document.md",
|
||||
"edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/databases\/upsert-document.md",
|
||||
"rate-limit": 120,
|
||||
"rate-time": 60,
|
||||
"rate-key": "ip:{ip},method:{method},url:{url},userId:{userId}",
|
||||
"scope": "documents.write",
|
||||
"platforms": [
|
||||
"client",
|
||||
"server",
|
||||
"server"
|
||||
],
|
||||
"packaging": false,
|
||||
"auth": {
|
||||
"Project": []
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Project": [],
|
||||
"Session": [],
|
||||
"JWT": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "databaseId",
|
||||
"description": "Database ID.",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"x-example": "<DATABASE_ID>",
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "collectionId",
|
||||
"description": "Collection ID.",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"x-example": "<COLLECTION_ID>",
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "documentId",
|
||||
"description": "Document ID.",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"x-example": "<DOCUMENT_ID>",
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"description": "Document data as JSON object. Include all required attributes of the document to be created or updated.",
|
||||
"default": {},
|
||||
"x-example": "{}"
|
||||
},
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).",
|
||||
"default": null,
|
||||
"x-example": "[\"read(\"any\")\"]",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"patch": {
|
||||
"summary": "Update document",
|
||||
"operationId": "databasesUpdateDocument",
|
||||
@@ -4912,7 +5023,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteDocument",
|
||||
"group": "documents",
|
||||
"weight": 116,
|
||||
"weight": 117,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -4990,7 +5101,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listExecutions",
|
||||
"group": "executions",
|
||||
"weight": 391,
|
||||
"weight": 392,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5064,7 +5175,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createExecution",
|
||||
"group": "executions",
|
||||
"weight": 389,
|
||||
"weight": 390,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5181,7 +5292,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getExecution",
|
||||
"group": "executions",
|
||||
"weight": 390,
|
||||
"weight": 391,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5253,7 +5364,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 305,
|
||||
"weight": 306,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
@@ -5327,7 +5438,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 304,
|
||||
"weight": 305,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
@@ -5399,7 +5510,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": null,
|
||||
"weight": 121,
|
||||
"weight": 122,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5451,7 +5562,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCodes",
|
||||
"group": null,
|
||||
"weight": 122,
|
||||
"weight": 123,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5503,7 +5614,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listContinents",
|
||||
"group": null,
|
||||
"weight": 126,
|
||||
"weight": 127,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5555,7 +5666,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountries",
|
||||
"group": null,
|
||||
"weight": 123,
|
||||
"weight": 124,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5607,7 +5718,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesEU",
|
||||
"group": null,
|
||||
"weight": 124,
|
||||
"weight": 125,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5659,7 +5770,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCountriesPhones",
|
||||
"group": null,
|
||||
"weight": 125,
|
||||
"weight": 126,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5711,7 +5822,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listCurrencies",
|
||||
"group": null,
|
||||
"weight": 127,
|
||||
"weight": 128,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5763,7 +5874,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listLanguages",
|
||||
"group": null,
|
||||
"weight": 128,
|
||||
"weight": 129,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5817,7 +5928,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 351,
|
||||
"weight": 352,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5902,7 +6013,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 355,
|
||||
"weight": 356,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -5973,7 +6084,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"group": "files",
|
||||
"weight": 211,
|
||||
"weight": 212,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6056,7 +6167,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"group": "files",
|
||||
"weight": 210,
|
||||
"weight": 211,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"deprecated": false,
|
||||
@@ -6146,7 +6257,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"group": "files",
|
||||
"weight": 212,
|
||||
"weight": 213,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6216,7 +6327,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"group": "files",
|
||||
"weight": 217,
|
||||
"weight": 218,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6305,7 +6416,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"group": "files",
|
||||
"weight": 218,
|
||||
"weight": 219,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6375,7 +6486,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"group": "files",
|
||||
"weight": 214,
|
||||
"weight": 215,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
@@ -6454,7 +6565,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"group": "files",
|
||||
"weight": 213,
|
||||
"weight": 214,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
@@ -6618,7 +6729,8 @@
|
||||
"png",
|
||||
"webp",
|
||||
"heic",
|
||||
"avif"
|
||||
"avif",
|
||||
"gif"
|
||||
],
|
||||
"x-enum-name": "ImageFormat",
|
||||
"x-enum-keys": [],
|
||||
@@ -6660,7 +6772,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"group": "files",
|
||||
"weight": 215,
|
||||
"weight": 216,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
@@ -6739,7 +6851,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "list",
|
||||
"group": "teams",
|
||||
"weight": 222,
|
||||
"weight": 223,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6814,7 +6926,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "create",
|
||||
"group": "teams",
|
||||
"weight": 221,
|
||||
"weight": 222,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6904,7 +7016,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"group": "teams",
|
||||
"weight": 223,
|
||||
"weight": 224,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -6966,7 +7078,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateName",
|
||||
"group": "teams",
|
||||
"weight": 225,
|
||||
"weight": 226,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7041,7 +7153,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "delete",
|
||||
"group": "teams",
|
||||
"weight": 227,
|
||||
"weight": 228,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7103,7 +7215,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMemberships",
|
||||
"group": "memberships",
|
||||
"weight": 229,
|
||||
"weight": 230,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7186,7 +7298,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMembership",
|
||||
"group": "memberships",
|
||||
"weight": 228,
|
||||
"weight": 229,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7299,7 +7411,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMembership",
|
||||
"group": "memberships",
|
||||
"weight": 230,
|
||||
"weight": 231,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7369,7 +7481,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembership",
|
||||
"group": "memberships",
|
||||
"weight": 231,
|
||||
"weight": 232,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7455,7 +7567,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMembership",
|
||||
"group": "memberships",
|
||||
"weight": 233,
|
||||
"weight": 234,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7527,7 +7639,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMembershipStatus",
|
||||
"group": "memberships",
|
||||
"weight": 232,
|
||||
"weight": 233,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7621,7 +7733,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getPrefs",
|
||||
"group": "teams",
|
||||
"weight": 224,
|
||||
"weight": 225,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
@@ -7682,7 +7794,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updatePrefs",
|
||||
"group": "teams",
|
||||
"weight": 226,
|
||||
"weight": 227,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because 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
@@ -7,4 +7,5 @@ return [
|
||||
"png" => "image/png",
|
||||
"heic" => "image/heic",
|
||||
"webp" => "image/webp",
|
||||
"gif" => "image/gif",
|
||||
];
|
||||
|
||||
@@ -8,4 +8,5 @@ return [
|
||||
"webp" => "image/webp",
|
||||
"heic" => "image/heic",
|
||||
"avif" => "image/avif",
|
||||
"gif" => "image/gif",
|
||||
];
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Utopia\Image\Image;
|
||||
use Utopia\System\System;
|
||||
|
||||
Image::setResourceLimit('memory', intval(System::getEnv('_APP_IMAGES_RESOURCE_LIMIT_MEMORY', 1024*1024*64)));
|
||||
@@ -14,7 +14,7 @@ return [
|
||||
],
|
||||
'DART' => [
|
||||
'name' => 'dart',
|
||||
'versions' => ['3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16']
|
||||
'versions' => ['3.8', '3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16']
|
||||
],
|
||||
'GO' => [
|
||||
'name' => 'go',
|
||||
@@ -38,6 +38,6 @@ return [
|
||||
],
|
||||
'FLUTTER' => [
|
||||
'name' => 'flutter',
|
||||
'versions' => ['3.24']
|
||||
'versions' => ['3.32', '3.24']
|
||||
],
|
||||
];
|
||||
|
||||
@@ -1351,4 +1351,23 @@ return [
|
||||
'providerVersion' => '0.3.*',
|
||||
'variables' => [],
|
||||
],
|
||||
[
|
||||
'key' => 'gallery-for-lynx',
|
||||
'name' => 'Lynx gallery',
|
||||
'tagline' => 'A Lynx website showcasing gallery with smooth animations.',
|
||||
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'screenshotDark' => $url . '/images/sites/templates/gallery-for-lynx-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/gallery-for-lynx-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('LYNX', [
|
||||
'providerRootDirectory' => './lynx/gallery',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.3.*',
|
||||
'variables' => []
|
||||
],
|
||||
];
|
||||
|
||||
@@ -106,6 +106,15 @@ return [
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_DOMAIN_SITES',
|
||||
'description' => 'A domain to use for site preview URLs.',
|
||||
'introduction' => '',
|
||||
'default' => 'sites.localhost',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_DOMAIN_TARGET',
|
||||
'description' => 'Deprecated since 1.7.0. A DNS A record hostname to serve as a CNAME target for your Appwrite custom domains. You can use the same value as used for the Appwrite \'_APP_DOMAIN\' variable. The default value is \'localhost\'.',
|
||||
|
||||
+273
-196
File diff suppressed because it is too large
Load Diff
@@ -669,7 +669,7 @@ App::get('/v1/cards/cloud')
|
||||
}
|
||||
}
|
||||
|
||||
$isPlatinum = $user->getInternalId() % 100 === 0;
|
||||
$isPlatinum = $user->getSequence() % 100 === 0;
|
||||
} else {
|
||||
$name = $mock === 'normal-long' ? 'Sir First Walter O\'Brian Junior' : 'Walter O\'Brian';
|
||||
$createdAt = new \DateTime('now');
|
||||
@@ -859,7 +859,7 @@ App::get('/v1/cards/cloud-back')
|
||||
$isEmployee = \array_key_exists($email, $employees);
|
||||
|
||||
$isGolden = $isEmployee || $isHero || $isContributor;
|
||||
$isPlatinum = $user->getInternalId() % 100 === 0;
|
||||
$isPlatinum = $user->getSequence() % 100 === 0;
|
||||
} else {
|
||||
$userId = '63e0bcf3c3eb803ba530';
|
||||
|
||||
@@ -926,9 +926,9 @@ App::get('/v1/cards/cloud-og')
|
||||
}
|
||||
|
||||
if (!$mock) {
|
||||
$internalId = $user->getInternalId();
|
||||
$bgVariation = $internalId % 3 === 0 ? '1' : ($internalId % 3 === 1 ? '2' : '3');
|
||||
$cardVariation = $internalId % 3 === 0 ? '1' : ($internalId % 3 === 1 ? '2' : '3');
|
||||
$sequence = $user->getSequence();
|
||||
$bgVariation = $sequence % 3 === 0 ? '1' : ($sequence % 3 === 1 ? '2' : '3');
|
||||
$cardVariation = $sequence % 3 === 0 ? '1' : ($sequence % 3 === 1 ? '2' : '3');
|
||||
|
||||
$name = $user->getAttribute('name', 'Anonymous');
|
||||
$email = $user->getAttribute('email', '');
|
||||
@@ -958,7 +958,7 @@ App::get('/v1/cards/cloud-og')
|
||||
}
|
||||
}
|
||||
|
||||
$isPlatinum = $user->getInternalId() % 100 === 0;
|
||||
$isPlatinum = $user->getSequence() % 100 === 0;
|
||||
} else {
|
||||
$bgVariation = \str_ends_with($mock, '-bg2') ? '2' : (\str_ends_with($mock, '-bg3') ? '3' : '1');
|
||||
$cardVariation = \str_ends_with($mock, '-right') ? '2' : (\str_ends_with($mock, '-middle') ? '3' : '1');
|
||||
|
||||
+550
-135
File diff suppressed because it is too large
Load Diff
@@ -3,13 +3,16 @@
|
||||
use Appwrite\ClamAV\Network;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\PubSub\Adapter\Pool as PubSubPool;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Adapter\Pool as CachePool;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\Pool as DatabasePool;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Domains\Validator\PublicDomain;
|
||||
use Utopia\Pools\Group;
|
||||
@@ -34,8 +37,8 @@ App::get('/v1/health')
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'get',
|
||||
auth: [AuthType::KEY],
|
||||
description: '/docs/references/health/get.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -70,11 +73,11 @@ App::get('/v1/health/db')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'getDB',
|
||||
description: '/docs/references/health/get-db.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -86,8 +89,8 @@ App::get('/v1/health/db')
|
||||
->inject('response')
|
||||
->inject('pools')
|
||||
->action(function (Response $response, Group $pools) {
|
||||
|
||||
$output = [];
|
||||
$failures = [];
|
||||
|
||||
$configs = [
|
||||
'Console.DB' => Config::getParam('pools-console'),
|
||||
@@ -97,7 +100,7 @@ App::get('/v1/health/db')
|
||||
foreach ($configs as $key => $config) {
|
||||
foreach ($config as $database) {
|
||||
try {
|
||||
$adapter = $pools->get($database)->pop()->getResource();
|
||||
$adapter = new DatabasePool($pools->get($database));
|
||||
|
||||
$checkStart = \microtime(true);
|
||||
|
||||
@@ -108,16 +111,16 @@ App::get('/v1/health/db')
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
} else {
|
||||
$failure[] = $database;
|
||||
$failures[] = $database;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$failure[] = $database;
|
||||
} catch (\Throwable) {
|
||||
$failures[] = $database;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($failure)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'DB failure on: ' . implode(", ", $failure));
|
||||
if (!empty($failures)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'DB failure on: ' . implode(", ", $failures));
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
@@ -131,11 +134,11 @@ App::get('/v1/health/cache')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'getCache',
|
||||
description: '/docs/references/health/get-cache.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -147,44 +150,39 @@ App::get('/v1/health/cache')
|
||||
->inject('response')
|
||||
->inject('pools')
|
||||
->action(function (Response $response, Group $pools) {
|
||||
|
||||
$output = [];
|
||||
$failures = [];
|
||||
|
||||
$configs = [
|
||||
'Cache' => Config::getParam('pools-cache'),
|
||||
];
|
||||
|
||||
foreach ($configs as $key => $config) {
|
||||
foreach ($config as $database) {
|
||||
foreach ($config as $cache) {
|
||||
try {
|
||||
/** @var \Utopia\Cache\Adapter $adapter */
|
||||
$adapter = $pools->get($database)->pop()->getResource();
|
||||
$adapter = new CachePool($pools->get($cache));
|
||||
|
||||
$checkStart = \microtime(true);
|
||||
|
||||
if ($adapter->ping()) {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'name' => $key . " ($cache)",
|
||||
'status' => 'pass',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
} else {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'fail',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
$failures[] = $cache;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'fail',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
} catch (\Throwable) {
|
||||
$failures[] = $cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($failures)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Cache failure on: ' . implode(", ", $failures));
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'statuses' => $output,
|
||||
'total' => count($output),
|
||||
@@ -196,11 +194,11 @@ App::get('/v1/health/pubsub')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'getPubSub',
|
||||
description: '/docs/references/health/get-pubsub.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -212,44 +210,39 @@ App::get('/v1/health/pubsub')
|
||||
->inject('response')
|
||||
->inject('pools')
|
||||
->action(function (Response $response, Group $pools) {
|
||||
|
||||
$output = [];
|
||||
$failures = [];
|
||||
|
||||
$configs = [
|
||||
'PubSub' => Config::getParam('pools-pubsub'),
|
||||
];
|
||||
|
||||
foreach ($configs as $key => $config) {
|
||||
foreach ($config as $database) {
|
||||
foreach ($config as $pubsub) {
|
||||
try {
|
||||
/** @var \Appwrite\PubSub\Adapter $adapter */
|
||||
$adapter = $pools->get($database)->pop()->getResource();
|
||||
$adapter = new PubSubPool($pools->get($pubsub));
|
||||
|
||||
$checkStart = \microtime(true);
|
||||
|
||||
if ($adapter->ping()) {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'name' => $key . " ($pubsub)",
|
||||
'status' => 'pass',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
} else {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'fail',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
$failures[] = $pubsub;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$output[] = new Document([
|
||||
'name' => $key . " ($database)",
|
||||
'status' => 'fail',
|
||||
'ping' => \round((\microtime(true) - $checkStart) / 1000)
|
||||
]);
|
||||
} catch (\Throwable) {
|
||||
$failures[] = $pubsub;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($failures)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Pubsub failure on: ' . implode(", ", $failures));
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'statuses' => $output,
|
||||
'total' => count($output),
|
||||
@@ -261,11 +254,11 @@ App::get('/v1/health/time')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'getTime',
|
||||
description: '/docs/references/health/get-time.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -325,11 +318,11 @@ App::get('/v1/health/queue/webhooks')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueWebhooks',
|
||||
description: '/docs/references/health/get-queue-webhooks.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -351,18 +344,18 @@ App::get('/v1/health/queue/webhooks')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue/logs')
|
||||
->desc('Get logs queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueLogs',
|
||||
description: '/docs/references/health/get-queue-logs.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -384,18 +377,18 @@ App::get('/v1/health/queue/logs')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
});
|
||||
|
||||
App::get('/v1/health/certificate')
|
||||
->desc('Get the SSL certificate for a domain')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'getCertificate',
|
||||
description: '/docs/references/health/get-certificate.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -441,18 +434,18 @@ App::get('/v1/health/certificate')
|
||||
'validTo' => $certificatePayload['validTo_time_t'],
|
||||
'signatureTypeSN' => $certificatePayload['signatureTypeSN'],
|
||||
]), Response::MODEL_HEALTH_CERTIFICATE);
|
||||
}, ['response']);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue/certificates')
|
||||
->desc('Get certificates queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueCertificates',
|
||||
description: '/docs/references/health/get-queue-certificates.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -474,18 +467,18 @@ App::get('/v1/health/queue/certificates')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue/builds')
|
||||
->desc('Get builds queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueBuilds',
|
||||
description: '/docs/references/health/get-queue-builds.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -507,18 +500,18 @@ App::get('/v1/health/queue/builds')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue/databases')
|
||||
->desc('Get databases queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueDatabases',
|
||||
description: '/docs/references/health/get-queue-databases.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -541,18 +534,18 @@ App::get('/v1/health/queue/databases')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue/deletes')
|
||||
->desc('Get deletes queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueDeletes',
|
||||
description: '/docs/references/health/get-queue-deletes.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -574,18 +567,18 @@ App::get('/v1/health/queue/deletes')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue/mails')
|
||||
->desc('Get mails queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueMails',
|
||||
description: '/docs/references/health/get-queue-mails.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -607,18 +600,18 @@ App::get('/v1/health/queue/mails')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue/messaging')
|
||||
->desc('Get messaging queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueMessaging',
|
||||
description: '/docs/references/health/get-queue-messaging.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -640,18 +633,18 @@ App::get('/v1/health/queue/messaging')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue/migrations')
|
||||
->desc('Get migrations queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueMigrations',
|
||||
description: '/docs/references/health/get-queue-migrations.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -673,18 +666,18 @@ App::get('/v1/health/queue/migrations')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue/functions')
|
||||
->desc('Get functions queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueFunctions',
|
||||
description: '/docs/references/health/get-queue-functions.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -706,18 +699,18 @@ App::get('/v1/health/queue/functions')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
}, ['response']);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue/stats-resources')
|
||||
->desc('Get stats resources queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueStatsResources',
|
||||
description: '/docs/references/health/get-queue-stats-resources.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -746,11 +739,11 @@ App::get('/v1/health/queue/stats-usage')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getQueueUsage',
|
||||
description: '/docs/references/health/get-queue-stats-usage.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -779,11 +772,11 @@ App::get('/v1/health/storage/local')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'storage',
|
||||
name: 'getStorageLocal',
|
||||
description: '/docs/references/health/get-storage-local.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -829,11 +822,11 @@ App::get('/v1/health/storage')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'storage',
|
||||
name: 'getStorage',
|
||||
description: '/docs/references/health/get-storage.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -852,15 +845,18 @@ App::get('/v1/health/storage')
|
||||
$checkStart = \microtime(true);
|
||||
|
||||
foreach ($devices as $device) {
|
||||
if (!$device->write($device->getPath('health.txt'), 'test', 'text/plain')) {
|
||||
$uniqueFileName = \uniqid('health', true);
|
||||
$filePath = $device->getPath($uniqueFileName);
|
||||
|
||||
if (!$device->write($filePath, 'test', 'text/plain')) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed writing test file to ' . $device->getRoot());
|
||||
}
|
||||
|
||||
if ($device->read($device->getPath('health.txt')) !== 'test') {
|
||||
if ($device->read($filePath) !== 'test') {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed reading test file from ' . $device->getRoot());
|
||||
}
|
||||
|
||||
if (!$device->delete($device->getPath('health.txt'))) {
|
||||
if (!$device->delete($filePath)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed deleting test file from ' . $device->getRoot());
|
||||
}
|
||||
}
|
||||
@@ -878,11 +874,11 @@ App::get('/v1/health/anti-virus')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'getAntivirus',
|
||||
description: '/docs/references/health/get-storage-anti-virus.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -924,11 +920,11 @@ App::get('/v1/health/queue/failed/:name')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
group: 'queue',
|
||||
name: 'getFailedJobs',
|
||||
description: '/docs/references/health/get-failed-queue-jobs.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
|
||||
@@ -2511,11 +2511,11 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
],
|
||||
'topicId' => $topicId,
|
||||
'topicInternalId' => $topic->getInternalId(),
|
||||
'topicInternalId' => $topic->getSequence(),
|
||||
'targetId' => $targetId,
|
||||
'targetInternalId' => $target->getInternalId(),
|
||||
'targetInternalId' => $target->getSequence(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'userInternalId' => $user->getSequence(),
|
||||
'providerType' => $target->getAttribute('providerType'),
|
||||
'search' => implode(' ', [
|
||||
$subscriberId,
|
||||
@@ -2597,7 +2597,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||
throw new Exception(Exception::TOPIC_NOT_FOUND);
|
||||
}
|
||||
|
||||
$queries[] = Query::equal('topicInternalId', [$topic->getInternalId()]);
|
||||
$queries[] = Query::equal('topicInternalId', [$topic->getSequence()]);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
@@ -2947,7 +2947,7 @@ App::post('/v1/messaging/messages/email')
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
|
||||
if ($file->isEmpty()) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
@@ -2989,7 +2989,7 @@ App::post('/v1/messaging/messages/email')
|
||||
'region' => $project->getAttribute('region'),
|
||||
'resourceType' => 'message',
|
||||
'resourceId' => $message->getId(),
|
||||
'resourceInternalId' => $message->getInternalId(),
|
||||
'resourceInternalId' => $message->getSequence(),
|
||||
'resourceUpdatedAt' => DateTime::now(),
|
||||
'projectId' => $project->getId(),
|
||||
'schedule' => $scheduledAt,
|
||||
@@ -3112,7 +3112,7 @@ App::post('/v1/messaging/messages/sms')
|
||||
'region' => $project->getAttribute('region'),
|
||||
'resourceType' => 'message',
|
||||
'resourceId' => $message->getId(),
|
||||
'resourceInternalId' => $message->getInternalId(),
|
||||
'resourceInternalId' => $message->getSequence(),
|
||||
'resourceUpdatedAt' => DateTime::now(),
|
||||
'projectId' => $project->getId(),
|
||||
'schedule' => $scheduledAt,
|
||||
@@ -3232,7 +3232,7 @@ App::post('/v1/messaging/messages/push')
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
if ($file->isEmpty()) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
@@ -3330,7 +3330,7 @@ App::post('/v1/messaging/messages/push')
|
||||
'region' => $project->getAttribute('region'),
|
||||
'resourceType' => 'message',
|
||||
'resourceId' => $message->getId(),
|
||||
'resourceInternalId' => $message->getInternalId(),
|
||||
'resourceInternalId' => $message->getSequence(),
|
||||
'resourceUpdatedAt' => DateTime::now(),
|
||||
'projectId' => $project->getId(),
|
||||
'schedule' => $scheduledAt,
|
||||
@@ -3731,7 +3731,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
||||
'region' => $project->getAttribute('region'),
|
||||
'resourceType' => 'message',
|
||||
'resourceId' => $message->getId(),
|
||||
'resourceInternalId' => $message->getInternalId(),
|
||||
'resourceInternalId' => $message->getSequence(),
|
||||
'resourceUpdatedAt' => DateTime::now(),
|
||||
'projectId' => $project->getId(),
|
||||
'schedule' => $scheduledAt,
|
||||
@@ -3796,7 +3796,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
|
||||
if ($file->isEmpty()) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
@@ -3933,7 +3933,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
||||
'region' => $project->getAttribute('region'),
|
||||
'resourceType' => 'message',
|
||||
'resourceId' => $message->getId(),
|
||||
'resourceInternalId' => $message->getInternalId(),
|
||||
'resourceInternalId' => $message->getSequence(),
|
||||
'resourceUpdatedAt' => DateTime::now(),
|
||||
'projectId' => $project->getId(),
|
||||
'schedule' => $scheduledAt,
|
||||
@@ -4107,7 +4107,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||
'region' => $project->getAttribute('region'),
|
||||
'resourceType' => 'message',
|
||||
'resourceId' => $message->getId(),
|
||||
'resourceInternalId' => $message->getInternalId(),
|
||||
'resourceInternalId' => $message->getSequence(),
|
||||
'resourceUpdatedAt' => DateTime::now(),
|
||||
'projectId' => $project->getId(),
|
||||
'schedule' => $scheduledAt,
|
||||
@@ -4210,7 +4210,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
if ($file->isEmpty()) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ App::post('/v1/migrations/csv')
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
if ($file->isEmpty()) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ App::get('/v1/project/usage')
|
||||
$executionsBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS);
|
||||
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS);
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
@@ -166,7 +166,7 @@ App::get('/v1/project/usage')
|
||||
$executionsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS);
|
||||
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS);
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
@@ -182,7 +182,7 @@ App::get('/v1/project/usage')
|
||||
$buildsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS);
|
||||
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS);
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
@@ -198,7 +198,7 @@ App::get('/v1/project/usage')
|
||||
$bucketsBreakdown = array_map(function ($bucket) use ($dbForProject) {
|
||||
$id = $bucket->getId();
|
||||
$name = $bucket->getAttribute('name');
|
||||
$metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE);
|
||||
$metric = str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_STORAGE);
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
@@ -214,7 +214,7 @@ App::get('/v1/project/usage')
|
||||
$databasesStorageBreakdown = array_map(function ($database) use ($dbForProject) {
|
||||
$id = $database->getId();
|
||||
$name = $database->getAttribute('name');
|
||||
$metric = str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE);
|
||||
$metric = str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_STORAGE);
|
||||
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
@@ -231,13 +231,13 @@ App::get('/v1/project/usage')
|
||||
$functionsStorageBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$deploymentMetric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE);
|
||||
$deploymentMetric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE);
|
||||
$deploymentValue = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$deploymentMetric]),
|
||||
Query::equal('period', ['inf'])
|
||||
]);
|
||||
|
||||
$buildMetric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE);
|
||||
$buildMetric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE);
|
||||
$buildValue = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$buildMetric]),
|
||||
Query::equal('period', ['inf'])
|
||||
@@ -255,7 +255,7 @@ App::get('/v1/project/usage')
|
||||
$executionsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS);
|
||||
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS);
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
@@ -271,7 +271,7 @@ App::get('/v1/project/usage')
|
||||
$buildsMbSecondsBreakdown = array_map(function ($function) use ($dbForProject) {
|
||||
$id = $function->getId();
|
||||
$name = $function->getAttribute('name');
|
||||
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS);
|
||||
$metric = str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS);
|
||||
$value = $dbForProject->findOne('stats', [
|
||||
Query::equal('metric', [$metric]),
|
||||
Query::equal('period', ['inf'])
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Validator\MockNumber;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Mail;
|
||||
@@ -24,6 +23,7 @@ use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\Pool as DatabasePool;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
@@ -119,7 +119,7 @@ App::post('/v1/projects')
|
||||
'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT,
|
||||
'passwordHistory' => 0,
|
||||
'passwordDictionary' => false,
|
||||
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
'duration' => TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
'personalDataCheck' => false,
|
||||
'mockNumbers' => [],
|
||||
'sessionAlerts' => false,
|
||||
@@ -181,7 +181,7 @@ App::post('/v1/projects')
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
],
|
||||
'name' => $name,
|
||||
'teamInternalId' => $team->getInternalId(),
|
||||
'teamInternalId' => $team->getSequence(),
|
||||
'teamId' => $team->getId(),
|
||||
'region' => $region,
|
||||
'description' => $description,
|
||||
@@ -223,19 +223,19 @@ App::post('/v1/projects')
|
||||
$sharedTables = $sharedTablesV1 || $sharedTablesV2;
|
||||
|
||||
if (!$sharedTablesV2) {
|
||||
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$dbForProject = new Database($adapter, $cache);
|
||||
|
||||
if ($sharedTables) {
|
||||
$dbForProject
|
||||
->setSharedTables(true)
|
||||
->setTenant($sharedTablesV1 ? $project->getInternalId() : null)
|
||||
->setTenant($sharedTablesV1 ? (int)$project->getSequence() : null)
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$dbForProject
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
$create = true;
|
||||
@@ -503,12 +503,12 @@ App::patch('/v1/projects/:projectId/team')
|
||||
|
||||
$project
|
||||
->setAttribute('teamId', $teamId)
|
||||
->setAttribute('teamInternalId', $team->getInternalId())
|
||||
->setAttribute('teamInternalId', $team->getSequence())
|
||||
->setAttribute('$permissions', $permissions);
|
||||
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project);
|
||||
|
||||
$installations = $dbForPlatform->find('installations', [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
foreach ($installations as $installation) {
|
||||
$installation->getAttribute('$permissions', $permissions);
|
||||
@@ -516,7 +516,7 @@ App::patch('/v1/projects/:projectId/team')
|
||||
}
|
||||
|
||||
$repositories = $dbForPlatform->find('repositories', [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
foreach ($repositories as $repository) {
|
||||
$repository->getAttribute('$permissions', $permissions);
|
||||
@@ -524,7 +524,7 @@ App::patch('/v1/projects/:projectId/team')
|
||||
}
|
||||
|
||||
$vcsComments = $dbForPlatform->find('vcsComments', [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
foreach ($vcsComments as $vcsComment) {
|
||||
$vcsComment->getAttribute('$permissions', $permissions);
|
||||
@@ -1228,7 +1228,7 @@ App::post('/v1/projects/:projectId/webhooks')
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectInternalId' => $project->getSequence(),
|
||||
'projectId' => $project->getId(),
|
||||
'name' => $name,
|
||||
'events' => $events,
|
||||
@@ -1278,7 +1278,7 @@ App::get('/v1/projects/:projectId/webhooks')
|
||||
}
|
||||
|
||||
$webhooks = $dbForPlatform->find('webhooks', [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
Query::limit(5000),
|
||||
]);
|
||||
|
||||
@@ -1319,7 +1319,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
|
||||
$webhook = $dbForPlatform->findOne('webhooks', [
|
||||
Query::equal('$id', [$webhookId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($webhook->isEmpty()) {
|
||||
@@ -1369,7 +1369,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
|
||||
$webhook = $dbForPlatform->findOne('webhooks', [
|
||||
Query::equal('$id', [$webhookId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($webhook->isEmpty()) {
|
||||
@@ -1426,7 +1426,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
|
||||
|
||||
$webhook = $dbForPlatform->findOne('webhooks', [
|
||||
Query::equal('$id', [$webhookId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($webhook->isEmpty()) {
|
||||
@@ -1473,7 +1473,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
|
||||
$webhook = $dbForPlatform->findOne('webhooks', [
|
||||
Query::equal('$id', [$webhookId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($webhook->isEmpty()) {
|
||||
@@ -1527,7 +1527,7 @@ App::post('/v1/projects/:projectId/keys')
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectInternalId' => $project->getSequence(),
|
||||
'projectId' => $project->getId(),
|
||||
'name' => $name,
|
||||
'scopes' => $scopes,
|
||||
@@ -1575,7 +1575,7 @@ App::get('/v1/projects/:projectId/keys')
|
||||
}
|
||||
|
||||
$keys = $dbForPlatform->find('keys', [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
Query::limit(5000),
|
||||
]);
|
||||
|
||||
@@ -1616,7 +1616,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
|
||||
|
||||
$key = $dbForPlatform->findOne('keys', [
|
||||
Query::equal('$id', [$keyId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($key->isEmpty()) {
|
||||
@@ -1660,7 +1660,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||
|
||||
$key = $dbForPlatform->findOne('keys', [
|
||||
Query::equal('$id', [$keyId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($key->isEmpty()) {
|
||||
@@ -1711,7 +1711,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
||||
|
||||
$key = $dbForPlatform->findOne('keys', [
|
||||
Query::equal('$id', [$keyId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($key->isEmpty()) {
|
||||
@@ -1810,7 +1810,7 @@ App::post('/v1/projects/:projectId/platforms')
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectInternalId' => $project->getSequence(),
|
||||
'projectId' => $project->getId(),
|
||||
'type' => $type,
|
||||
'name' => $name,
|
||||
@@ -1857,7 +1857,7 @@ App::get('/v1/projects/:projectId/platforms')
|
||||
}
|
||||
|
||||
$platforms = $dbForPlatform->find('platforms', [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
Query::limit(5000),
|
||||
]);
|
||||
|
||||
@@ -1898,7 +1898,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
|
||||
|
||||
$platform = $dbForPlatform->findOne('platforms', [
|
||||
Query::equal('$id', [$platformId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($platform->isEmpty()) {
|
||||
@@ -1942,7 +1942,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
|
||||
|
||||
$platform = $dbForPlatform->findOne('platforms', [
|
||||
Query::equal('$id', [$platformId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($platform->isEmpty()) {
|
||||
@@ -1996,7 +1996,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||
|
||||
$platform = $dbForPlatform->findOne('platforms', [
|
||||
Query::equal('$id', [$platformId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
|
||||
if ($platform->isEmpty()) {
|
||||
|
||||
@@ -58,7 +58,7 @@ App::get('/v1/proxy/rules')
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
$queries[] = Query::equal('projectInternalId', [$project->getInternalId()]);
|
||||
$queries[] = Query::equal('projectInternalId', [$project->getSequence()]);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
@@ -124,7 +124,7 @@ App::get('/v1/proxy/rules/:ruleId')
|
||||
->action(function (string $ruleId, Response $response, Document $project, Database $dbForPlatform) {
|
||||
$rule = $dbForPlatform->getDocument('rules', $ruleId);
|
||||
|
||||
if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getInternalId()) {
|
||||
if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::RULE_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ App::delete('/v1/proxy/rules/:ruleId')
|
||||
->action(function (string $ruleId, Response $response, Document $project, Database $dbForPlatform, Delete $queueForDeletes, Event $queueForEvents) {
|
||||
$rule = $dbForPlatform->getDocument('rules', $ruleId);
|
||||
|
||||
if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getInternalId()) {
|
||||
if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::RULE_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -210,15 +210,46 @@ App::patch('/v1/proxy/rules/:ruleId/verification')
|
||||
->action(function (string $ruleId, Response $response, Certificate $queueForCertificates, Event $queueForEvents, Document $project, Database $dbForPlatform, Log $log) {
|
||||
$rule = $dbForPlatform->getDocument('rules', $ruleId);
|
||||
|
||||
if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getInternalId()) {
|
||||
if ($rule->isEmpty() || $rule->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::RULE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$validators = [];
|
||||
$targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', ''));
|
||||
if ($targetCNAME->isKnown() && !$targetCNAME->isTest()) {
|
||||
$validators[] = new DNS($targetCNAME->get(), DNS::RECORD_CNAME);
|
||||
$targetCNAME = null;
|
||||
switch ($rule->getAttribute('type', '')) {
|
||||
case 'api':
|
||||
// For example: fra.cloud.appwrite.io
|
||||
$targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', ''));
|
||||
break;
|
||||
case 'redirect':
|
||||
// For example: appwrite.network
|
||||
$targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', ''));
|
||||
break;
|
||||
case 'deployment':
|
||||
switch ($rule->getAttribute('deploymentResourceType', '')) {
|
||||
case 'function':
|
||||
// For example: fra.appwrite.run
|
||||
$targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_FUNCTIONS', ''));
|
||||
break;
|
||||
case 'site':
|
||||
// For example: appwrite.network
|
||||
$targetCNAME = new Domain(System::getEnv('_APP_DOMAIN_SITES', ''));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$validators = [];
|
||||
|
||||
if (!is_null($targetCNAME)) {
|
||||
if ($targetCNAME->isKnown() && !$targetCNAME->isTest()) {
|
||||
$validators[] = new DNS($targetCNAME->get(), DNS::RECORD_CNAME);
|
||||
}
|
||||
}
|
||||
|
||||
if ((new IP(IP::V4))->isValid(System::getEnv('_APP_DOMAIN_TARGET_A', ''))) {
|
||||
$validators[] = new DNS(System::getEnv('_APP_DOMAIN_TARGET_A', ''), DNS::RECORD_A);
|
||||
}
|
||||
@@ -260,7 +291,8 @@ App::patch('/v1/proxy/rules/:ruleId/verification')
|
||||
// Issue a TLS certificate when domain is verified
|
||||
$queueForCertificates
|
||||
->setDomain(new Document([
|
||||
'domain' => $rule->getAttribute('domain')
|
||||
'domain' => $rule->getAttribute('domain'),
|
||||
'domainType' => $rule->getAttribute('deploymentResourceType', $rule->getAttribute('type')),
|
||||
]))
|
||||
->trigger();
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ App::post('/v1/storage/buckets')
|
||||
|
||||
$bucket = $dbForProject->getDocument('buckets', $bucketId);
|
||||
|
||||
$dbForProject->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes, permissions: $permissions ?? [], documentSecurity: $fileSecurity);
|
||||
$dbForProject->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes, permissions: $permissions ?? [], documentSecurity: $fileSecurity);
|
||||
} catch (DuplicateException) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_ALREADY_EXISTS);
|
||||
}
|
||||
@@ -316,17 +316,17 @@ App::put('/v1/storage/buckets/:bucketId')
|
||||
$permissions = Permission::aggregate($permissions);
|
||||
|
||||
$bucket = $dbForProject->updateDocument('buckets', $bucket->getId(), $bucket
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('$permissions', $permissions)
|
||||
->setAttribute('maximumFileSize', $maximumFileSize)
|
||||
->setAttribute('allowedFileExtensions', $allowedFileExtensions)
|
||||
->setAttribute('fileSecurity', $fileSecurity)
|
||||
->setAttribute('enabled', $enabled)
|
||||
->setAttribute('encryption', $encryption)
|
||||
->setAttribute('compression', $compression)
|
||||
->setAttribute('antivirus', $antivirus));
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('$permissions', $permissions)
|
||||
->setAttribute('maximumFileSize', $maximumFileSize)
|
||||
->setAttribute('allowedFileExtensions', $allowedFileExtensions)
|
||||
->setAttribute('fileSecurity', $fileSecurity)
|
||||
->setAttribute('enabled', $enabled)
|
||||
->setAttribute('encryption', $encryption)
|
||||
->setAttribute('compression', $compression)
|
||||
->setAttribute('antivirus', $antivirus));
|
||||
|
||||
$dbForProject->updateCollection('bucket_' . $bucket->getInternalId(), $permissions, $fileSecurity);
|
||||
$dbForProject->updateCollection('bucket_' . $bucket->getSequence(), $permissions, $fileSecurity);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('bucketId', $bucket->getId());
|
||||
@@ -558,7 +558,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
$path = $deviceForFiles->getPath($fileId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
$path = str_ireplace($deviceForFiles->getRoot(), $deviceForFiles->getRoot() . DIRECTORY_SEPARATOR . $bucket->getId(), $path); // Add bucket id to path after root
|
||||
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
|
||||
$metadata = ['content_type' => $deviceForLocal->getFileMimeType($fileTmpName)];
|
||||
if (!$file->isEmpty()) {
|
||||
@@ -652,7 +652,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
'$id' => $fileId,
|
||||
'$permissions' => $permissions,
|
||||
'bucketId' => $bucket->getId(),
|
||||
'bucketInternalId' => $bucket->getInternalId(),
|
||||
'bucketInternalId' => $bucket->getSequence(),
|
||||
'name' => $fileName,
|
||||
'path' => $path,
|
||||
'signature' => $fileHash,
|
||||
@@ -671,7 +671,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
'metadata' => $metadata,
|
||||
]);
|
||||
|
||||
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
|
||||
$file = $dbForProject->createDocument('bucket_' . $bucket->getSequence(), $doc);
|
||||
} else {
|
||||
$file = $file
|
||||
->setAttribute('$permissions', $permissions)
|
||||
@@ -696,7 +696,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
if (!$validator->isValid($bucket->getCreate())) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
|
||||
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
|
||||
}
|
||||
} else {
|
||||
if ($file->isEmpty()) {
|
||||
@@ -704,7 +704,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
'$id' => ID::custom($fileId),
|
||||
'$permissions' => $permissions,
|
||||
'bucketId' => $bucket->getId(),
|
||||
'bucketInternalId' => $bucket->getInternalId(),
|
||||
'bucketInternalId' => $bucket->getSequence(),
|
||||
'name' => $fileName,
|
||||
'path' => $path,
|
||||
'signature' => '',
|
||||
@@ -720,7 +720,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
]);
|
||||
|
||||
try {
|
||||
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
|
||||
$file = $dbForProject->createDocument('bucket_' . $bucket->getSequence(), $doc);
|
||||
} catch (NotFoundException) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
@@ -741,7 +741,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
}
|
||||
|
||||
try {
|
||||
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
|
||||
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
|
||||
} catch (NotFoundException) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
@@ -826,9 +826,9 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||
$fileId = $cursor->getValue();
|
||||
|
||||
if ($fileSecurity && !$valid) {
|
||||
$cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
$cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
@@ -842,11 +842,11 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||
|
||||
try {
|
||||
if ($fileSecurity && !$valid) {
|
||||
$files = $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries);
|
||||
$total = $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT);
|
||||
$files = $dbForProject->find('bucket_' . $bucket->getSequence(), $queries);
|
||||
$total = $dbForProject->count('bucket_' . $bucket->getSequence(), $filterQueries, APP_LIMIT_COUNT);
|
||||
} else {
|
||||
$files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getInternalId(), $queries));
|
||||
$total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getInternalId(), $filterQueries, APP_LIMIT_COUNT));
|
||||
$files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getSequence(), $queries));
|
||||
$total = Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getSequence(), $filterQueries, APP_LIMIT_COUNT));
|
||||
}
|
||||
} catch (NotFoundException) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
@@ -902,9 +902,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
}
|
||||
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
|
||||
if ($file->isEmpty()) {
|
||||
@@ -973,7 +973,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getInternalId();
|
||||
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
@@ -982,12 +982,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
}
|
||||
|
||||
if ($fileSecurity && !$valid && !$isToken) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
|
||||
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getInternalId()) {
|
||||
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@@ -1004,7 +1004,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
$algorithm = $file->getAttribute('algorithm', Compression::NONE);
|
||||
$cipher = $file->getAttribute('openSSLCipher');
|
||||
$mime = $file->getAttribute('mimeType');
|
||||
if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) System::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) {
|
||||
if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) System::getEnv('_APP_STORAGE_PREVIEW_LIMIT', APP_STORAGE_READ_BUFFER)) {
|
||||
if (!\in_array($mime, $inputs)) {
|
||||
$path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default'];
|
||||
} else {
|
||||
@@ -1150,7 +1150,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getInternalId();
|
||||
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
@@ -1159,12 +1159,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
}
|
||||
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
|
||||
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getInternalId()) {
|
||||
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@@ -1178,13 +1178,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path);
|
||||
}
|
||||
|
||||
$response
|
||||
->setContentType($file->getAttribute('mimeType'))
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->addHeader('X-Peak', \memory_get_peak_usage())
|
||||
->addHeader('Content-Disposition', 'attachment; filename="' . $file->getAttribute('name', '') . '"')
|
||||
;
|
||||
|
||||
$size = $file->getAttribute('sizeOriginal', 0);
|
||||
|
||||
$rangeHeader = $request->getHeader('range');
|
||||
@@ -1193,7 +1186,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
$end = $request->getRangeEnd();
|
||||
$unit = $request->getRangeUnit();
|
||||
|
||||
if ($end === null) {
|
||||
if ($end === null || $end - $start > APP_STORAGE_READ_BUFFER) {
|
||||
$end = min(($start + MAX_OUTPUT_CHUNK_SIZE - 1), ($size - 1));
|
||||
}
|
||||
|
||||
@@ -1208,6 +1201,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT);
|
||||
}
|
||||
|
||||
$response
|
||||
->setContentType($file->getAttribute('mimeType'))
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->addHeader('X-Peak', \memory_get_peak_usage())
|
||||
->addHeader('Content-Disposition', 'attachment; filename="' . $file->getAttribute('name', '') . '"')
|
||||
;
|
||||
|
||||
$source = '';
|
||||
if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt
|
||||
$source = $deviceForFiles->read($path);
|
||||
@@ -1241,12 +1241,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
if (!empty($source)) {
|
||||
if (!empty($rangeHeader)) {
|
||||
$response->send(substr($source, $start, ($end - $start + 1)));
|
||||
return;
|
||||
}
|
||||
$response->send($source);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($rangeHeader)) {
|
||||
$response->send($deviceForFiles->read($path, $start, ($end - $start + 1)));
|
||||
return;
|
||||
}
|
||||
|
||||
if ($size > APP_STORAGE_READ_BUFFER) {
|
||||
@@ -1306,7 +1309,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getInternalId();
|
||||
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
|
||||
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
|
||||
$validator = new Authorization(Database::PERMISSION_READ);
|
||||
$valid = $validator->isValid($bucket->getRead());
|
||||
@@ -1315,12 +1318,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
}
|
||||
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
|
||||
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getInternalId()) {
|
||||
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@@ -1342,15 +1345,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
$contentType = $file->getAttribute('mimeType');
|
||||
}
|
||||
|
||||
$response
|
||||
->setContentType($contentType)
|
||||
->addHeader('Content-Security-Policy', 'script-src none;')
|
||||
->addHeader('X-Content-Type-Options', 'nosniff')
|
||||
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->addHeader('X-Peak', \memory_get_peak_usage())
|
||||
;
|
||||
|
||||
$size = $file->getAttribute('sizeOriginal', 0);
|
||||
|
||||
$rangeHeader = $request->getHeader('range');
|
||||
@@ -1359,8 +1353,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
$end = $request->getRangeEnd();
|
||||
$unit = $request->getRangeUnit();
|
||||
|
||||
if ($end === null) {
|
||||
$end = min(($start + 2000000 - 1), ($size - 1));
|
||||
if ($end === null || $end - $start > APP_STORAGE_READ_BUFFER) {
|
||||
$end = min(($start + APP_STORAGE_READ_BUFFER - 1), ($size - 1));
|
||||
}
|
||||
|
||||
if ($unit != 'bytes' || $start >= $end || $end >= $size) {
|
||||
@@ -1374,6 +1368,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT);
|
||||
}
|
||||
|
||||
$response
|
||||
->setContentType($contentType)
|
||||
->addHeader('Content-Security-Policy', 'script-src none;')
|
||||
->addHeader('X-Content-Type-Options', 'nosniff')
|
||||
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->addHeader('X-Peak', \memory_get_peak_usage())
|
||||
;
|
||||
|
||||
$source = '';
|
||||
if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt
|
||||
$source = $deviceForFiles->read($path);
|
||||
@@ -1407,6 +1410,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
if (!empty($source)) {
|
||||
if (!empty($rangeHeader)) {
|
||||
$response->send(substr($source, $start, ($end - $start + 1)));
|
||||
return;
|
||||
}
|
||||
$response->send($source);
|
||||
return;
|
||||
@@ -1474,7 +1478,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
|
||||
if ($file->isEmpty()) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
@@ -1494,14 +1498,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||
$contentType = $file->getAttribute('mimeType');
|
||||
}
|
||||
|
||||
$response
|
||||
->setContentType($contentType)
|
||||
->addHeader('Content-Security-Policy', 'script-src none;')
|
||||
->addHeader('X-Content-Type-Options', 'nosniff')
|
||||
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->addHeader('X-Peak', \memory_get_peak_usage());
|
||||
|
||||
$size = $file->getAttribute('sizeOriginal', 0);
|
||||
|
||||
$rangeHeader = $request->getHeader('range');
|
||||
@@ -1510,8 +1506,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||
$end = $request->getRangeEnd();
|
||||
$unit = $request->getRangeUnit();
|
||||
|
||||
if ($end === null) {
|
||||
$end = min(($start + 2000000 - 1), ($size - 1));
|
||||
if ($end === null || $end - $start > APP_STORAGE_READ_BUFFER) {
|
||||
$end = min(($start + APP_STORAGE_READ_BUFFER - 1), ($size - 1));
|
||||
}
|
||||
|
||||
if ($unit != 'bytes' || $start >= $end || $end >= $size) {
|
||||
@@ -1525,6 +1521,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||
->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT);
|
||||
}
|
||||
|
||||
$response
|
||||
->setContentType($contentType)
|
||||
->addHeader('Content-Security-Policy', 'script-src none;')
|
||||
->addHeader('X-Content-Type-Options', 'nosniff')
|
||||
->addHeader('Content-Disposition', 'inline; filename="' . $file->getAttribute('name', '') . '"')
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->addHeader('X-Peak', \memory_get_peak_usage());
|
||||
|
||||
$source = '';
|
||||
if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt
|
||||
$source = $deviceForFiles->read($path);
|
||||
@@ -1558,6 +1562,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||
if (!empty($source)) {
|
||||
if (!empty($rangeHeader)) {
|
||||
$response->send(substr($source, $start, ($end - $start + 1)));
|
||||
return;
|
||||
}
|
||||
$response->send($source);
|
||||
return;
|
||||
@@ -1638,7 +1643,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
}
|
||||
|
||||
// Read permission should not be required for update
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
|
||||
if ($file->isEmpty()) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
@@ -1684,9 +1689,9 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
|
||||
try {
|
||||
if ($fileSecurity && !$valid) {
|
||||
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
|
||||
$file = $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file);
|
||||
} else {
|
||||
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
|
||||
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
|
||||
}
|
||||
} catch (NotFoundException) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
@@ -1752,7 +1757,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
}
|
||||
|
||||
// Read permission should not be required for delete
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
|
||||
if ($file->isEmpty()) {
|
||||
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
|
||||
@@ -1782,9 +1787,9 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
|
||||
try {
|
||||
if ($fileSecurity && !$valid) {
|
||||
$deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
$deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
$deleted = Authorization::skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
$deleted = Authorization::skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
} catch (NotFoundException) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
@@ -1932,14 +1937,14 @@ App::get('/v1/storage/:bucketId/usage')
|
||||
$stats = $usage = [];
|
||||
$days = $periods[$range];
|
||||
$metrics = [
|
||||
str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES),
|
||||
str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE),
|
||||
str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED),
|
||||
str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES),
|
||||
str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_STORAGE),
|
||||
str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED),
|
||||
];
|
||||
|
||||
Authorization::skip(function () use ($dbForProject, $dbForLogs, $bucket, $days, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$db = ($metric === str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED))
|
||||
$db = ($metric === str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED))
|
||||
? $dbForLogs
|
||||
: $dbForProject;
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ use MaxMind\Db\Reader;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Auth\Proofs\Password;
|
||||
use Utopia\Auth\Proofs\Token;
|
||||
use Utopia\Auth\Store;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
@@ -123,9 +126,9 @@ App::post('/v1/teams')
|
||||
Permission::delete(Role::team($team->getId(), 'owner')),
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'userInternalId' => $user->getSequence(),
|
||||
'teamId' => $team->getId(),
|
||||
'teamInternalId' => $team->getInternalId(),
|
||||
'teamInternalId' => $team->getSequence(),
|
||||
'roles' => $roles,
|
||||
'invited' => DateTime::now(),
|
||||
'joined' => DateTime::now(),
|
||||
@@ -460,7 +463,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
;
|
||||
$roles = array_keys(Config::getParam('roles', []));
|
||||
array_filter($roles, function ($role) {
|
||||
return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]);
|
||||
return !in_array($role, [USER_ROLE_APPS, USER_ROLE_GUESTS, USER_ROLE_USERS]);
|
||||
});
|
||||
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}
|
||||
@@ -479,7 +482,9 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
->inject('timelimit')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('plan')
|
||||
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) {
|
||||
->inject('proofForPassword')
|
||||
->inject('proofForToken')
|
||||
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Password $proofForPassword, Token $proofForToken) {
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
|
||||
@@ -552,6 +557,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
|
||||
try {
|
||||
$userId = ID::unique();
|
||||
$hash = $proofForPassword->hash($proofForPassword->generate());
|
||||
$invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$permissions' => [
|
||||
@@ -565,9 +571,9 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
// TODO: Set password empty?
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator(), Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS),
|
||||
'hash' => Auth::DEFAULT_ALGO,
|
||||
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
'password' => $hash,
|
||||
'hash' => $proofForPassword->getHash()->getName(),
|
||||
'hashOptions' => $proofForPassword->getHash()->getOptions(),
|
||||
/**
|
||||
* Set the password update time to 0 for users created using
|
||||
* team invite and OAuth to allow password updates without an
|
||||
@@ -595,11 +601,11 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
}
|
||||
|
||||
$membership = $dbForProject->findOne('memberships', [
|
||||
Query::equal('userInternalId', [$invitee->getInternalId()]),
|
||||
Query::equal('teamInternalId', [$team->getInternalId()]),
|
||||
Query::equal('userInternalId', [$invitee->getSequence()]),
|
||||
Query::equal('teamInternalId', [$team->getSequence()]),
|
||||
]);
|
||||
|
||||
$secret = Auth::tokenGenerator();
|
||||
$secret = $proofForToken->generate();
|
||||
if ($membership->isEmpty()) {
|
||||
$membershipId = ID::unique();
|
||||
$membership = new Document([
|
||||
@@ -612,14 +618,14 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
Permission::delete(Role::team($team->getId(), 'owner')),
|
||||
],
|
||||
'userId' => $invitee->getId(),
|
||||
'userInternalId' => $invitee->getInternalId(),
|
||||
'userInternalId' => $invitee->getSequence(),
|
||||
'teamId' => $team->getId(),
|
||||
'teamInternalId' => $team->getInternalId(),
|
||||
'teamInternalId' => $team->getSequence(),
|
||||
'roles' => $roles,
|
||||
'invited' => DateTime::now(),
|
||||
'joined' => ($isPrivilegedUser || $isAppUser) ? DateTime::now() : null,
|
||||
'confirm' => ($isPrivilegedUser || $isAppUser),
|
||||
'secret' => Auth::hash($secret),
|
||||
'secret' => $proofForToken->hash($secret),
|
||||
'search' => implode(' ', [$membershipId, $invitee->getId()])
|
||||
]);
|
||||
|
||||
@@ -632,7 +638,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
}
|
||||
|
||||
} elseif ($membership->getAttribute('confirm') === false) {
|
||||
$membership->setAttribute('secret', Auth::hash($secret));
|
||||
$membership->setAttribute('secret', $proofForToken->hash($secret));
|
||||
$membership->setAttribute('invited', DateTime::now());
|
||||
|
||||
if ($isPrivilegedUser || $isAppUser) {
|
||||
@@ -842,7 +848,7 @@ App::get('/v1/teams/:teamId/memberships')
|
||||
}
|
||||
|
||||
// Set internal queries
|
||||
$queries[] = Query::equal('teamInternalId', [$team->getInternalId()]);
|
||||
$queries[] = Query::equal('teamInternalId', [$team->getSequence()]);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
@@ -1053,7 +1059,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
if ($project->getId() === 'console') {
|
||||
$roles = array_keys(Config::getParam('roles', []));
|
||||
array_filter($roles, function ($role) {
|
||||
return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]);
|
||||
return !in_array($role, [USER_ROLE_APPS, USER_ROLE_GUESTS, USER_ROLE_USERS]);
|
||||
});
|
||||
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}
|
||||
@@ -1092,15 +1098,18 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
collection: 'memberships',
|
||||
queries: [
|
||||
Query::contains('roles', ['owner']),
|
||||
Query::equal('teamInternalId', [$team->getInternalId()])
|
||||
Query::equal('teamInternalId', [$team->getSequence()])
|
||||
],
|
||||
max: 2
|
||||
);
|
||||
|
||||
// Is the role change being requested by the user on their own membership?
|
||||
$isCurrentUserAnOwner = $user->getSequence() === $membership->getAttribute('userInternalId');
|
||||
|
||||
// Prevent role change if there's only one owner left,
|
||||
// the requester is that owner, and the new `$roles` no longer include 'owner'!
|
||||
if ($ownersCount === 1 && $isOwner && !\in_array('owner', $roles)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'There must be at least one owner in the organization.');
|
||||
// the requester is that owner, and the new `$roles` no longer include 'owner'
|
||||
if ($ownersCount === 1 && $isOwner && $isCurrentUserAnOwner && !\in_array('owner', $roles)) {
|
||||
throw new Exception(Exception::MEMBERSHIP_DOWNGRADE_PROHIBITED, 'There must be at least one owner in the organization.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1165,7 +1174,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
->inject('project')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents) {
|
||||
->inject('store')
|
||||
->inject('proofForToken')
|
||||
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Store $store, Token $proofForToken) {
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||
@@ -1180,11 +1191,11 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($membership->getAttribute('teamInternalId') !== $team->getInternalId()) {
|
||||
if ($membership->getAttribute('teamInternalId') !== $team->getSequence()) {
|
||||
throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH);
|
||||
}
|
||||
|
||||
if (Auth::hash($secret) !== $membership->getAttribute('secret')) {
|
||||
if (!$proofForToken->verify($secret, $membership->getAttribute('secret'))) {
|
||||
throw new Exception(Exception::TEAM_INVALID_SECRET);
|
||||
}
|
||||
|
||||
@@ -1197,7 +1208,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
$user->setAttributes($dbForProject->getDocument('users', $userId)->getArrayCopy()); // Get user
|
||||
}
|
||||
|
||||
if ($membership->getAttribute('userInternalId') !== $user->getInternalId()) {
|
||||
if ($membership->getAttribute('userInternalId') !== $user->getSequence()) {
|
||||
throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user (' . $user->getAttribute('email') . ')');
|
||||
}
|
||||
|
||||
@@ -1218,9 +1229,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
$authDuration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$authDuration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$expire = DateTime::addSeconds(new \DateTime(), $authDuration);
|
||||
$secret = Auth::tokenGenerator();
|
||||
$secret = $proofForToken->generate();
|
||||
$session = new Document(array_merge([
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
@@ -1230,9 +1241,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_EMAIL,
|
||||
'provider' => SESSION_PROVIDER_EMAIL,
|
||||
'providerUid' => $user->getAttribute('email'),
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'secret' => $proofForToken->hash($secret), // One way hash encryption to protect DB leak
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
'ip' => $request->getIP(),
|
||||
'factors' => ['email'],
|
||||
@@ -1245,13 +1256,18 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
Authorization::setRole(Role::user($userId)->toString());
|
||||
|
||||
if (!Config::getParam('domainVerification')) {
|
||||
$response->addHeader('X-Fallback-Cookies', \json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]));
|
||||
$encoded = $store
|
||||
->setProperty('id', $user->getId())
|
||||
->setProperty('secret', $secret)
|
||||
->encode();
|
||||
|
||||
$response->addHeader('X-Fallback-Cookies', \json_encode([$store->getKey() => $encoded]));
|
||||
}
|
||||
|
||||
$response
|
||||
->addCookie(
|
||||
name: Auth::$cookieName . '_legacy',
|
||||
value: Auth::encodeSession($user->getId(), $secret),
|
||||
name: $store->getKey() . '_legacy',
|
||||
value: $encoded,
|
||||
expire: (new \DateTime($expire))->getTimestamp(),
|
||||
path: '/',
|
||||
domain: Config::getParam('cookieDomain'),
|
||||
@@ -1259,8 +1275,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
httponly: true
|
||||
)
|
||||
->addCookie(
|
||||
name: Auth::$cookieName,
|
||||
value: Auth::encodeSession($user->getId(), $secret),
|
||||
name: $store->getKey(),
|
||||
value: $encoded,
|
||||
expire: (new \DateTime($expire))->getTimestamp(),
|
||||
path: '/',
|
||||
domain: Config::getParam('cookieDomain'),
|
||||
@@ -1315,10 +1331,12 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
))
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('membershipId', '', new UID(), 'Membership ID.')
|
||||
->inject('user')
|
||||
->inject('project')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $teamId, string $membershipId, Document $user, Document $project, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
|
||||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||
|
||||
@@ -1326,9 +1344,9 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
throw new Exception(Exception::TEAM_INVITE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
|
||||
$profile = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
if ($profile->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -1338,10 +1356,33 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($membership->getAttribute('teamInternalId') !== $team->getInternalId()) {
|
||||
if ($membership->getAttribute('teamInternalId') !== $team->getSequence()) {
|
||||
throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH);
|
||||
}
|
||||
|
||||
if ($project->getId() === 'console') {
|
||||
// Quick check:
|
||||
// fetch up to 2 owners to determine if only one exists
|
||||
$ownersCount = $dbForProject->count(
|
||||
collection: 'memberships',
|
||||
queries: [
|
||||
Query::contains('roles', ['owner']),
|
||||
Query::equal('teamInternalId', [$team->getSequence()])
|
||||
],
|
||||
max: 2
|
||||
);
|
||||
|
||||
// Is the deletion being requested by the user on their own membership and they are also the owner?
|
||||
$isSelfOwner =
|
||||
in_array('owner', $membership->getAttribute('roles')) &&
|
||||
$membership->getAttribute('userInternalId') === $user->getSequence();
|
||||
|
||||
if ($ownersCount === 1 && $isSelfOwner) {
|
||||
/* Prevent removal if the user is the only owner. */
|
||||
throw new Exception(Exception::MEMBERSHIP_DELETION_PROHIBITED, 'There must be at least one owner in the organization.');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$dbForProject->deleteDocument('memberships', $membership->getId());
|
||||
} catch (AuthorizationException $exception) {
|
||||
@@ -1350,15 +1391,15 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove membership from DB');
|
||||
}
|
||||
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
$dbForProject->purgeCachedDocument('users', $profile->getId());
|
||||
|
||||
if ($membership->getAttribute('confirm')) { // Count only confirmed members
|
||||
Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0));
|
||||
}
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('teamId', $team->getId())
|
||||
->setParam('userId', $profile->getId())
|
||||
->setParam('membershipId', $membership->getId())
|
||||
->setPayload($response->output($membership, Response::MODEL_MEMBERSHIP))
|
||||
;
|
||||
|
||||
+120
-58
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\MFA\Type;
|
||||
use Appwrite\Auth\MFA\Type\TOTP;
|
||||
use Appwrite\Auth\Validator\Password;
|
||||
@@ -31,6 +30,18 @@ use Appwrite\Utopia\Response;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Auth\Hash;
|
||||
use Utopia\Auth\Hashes\Argon2;
|
||||
use Utopia\Auth\Hashes\Bcrypt;
|
||||
use Utopia\Auth\Hashes\MD5;
|
||||
use Utopia\Auth\Hashes\PHPass;
|
||||
use Utopia\Auth\Hashes\Plaintext;
|
||||
use Utopia\Auth\Hashes\Scrypt;
|
||||
use Utopia\Auth\Hashes\ScryptModified;
|
||||
use Utopia\Auth\Hashes\Sha;
|
||||
use Utopia\Auth\Proofs\Password as ProofsPassword;
|
||||
use Utopia\Auth\Proofs\Token;
|
||||
use Utopia\Auth\Store;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
@@ -59,10 +70,9 @@ use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
/** TODO: Remove function when we move to using utopia/platform */
|
||||
function createUser(string $hash, mixed $hashOptions, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Hooks $hooks): Document
|
||||
function createUser(Hash $hash, string $userId, ?string $email, ?string $password, ?string $phone, string $name, Document $project, Database $dbForProject, Hooks $hooks): Document
|
||||
{
|
||||
$plaintextPassword = $password;
|
||||
$hashOptionsObject = (\is_string($hashOptions)) ? \json_decode($hashOptions, true) : $hashOptions; // Cast to JSON array
|
||||
$passwordHistory = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
||||
|
||||
if (!empty($email)) {
|
||||
@@ -96,7 +106,18 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||
}
|
||||
}
|
||||
|
||||
$password = (!empty($password)) ? ($hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password) : null;
|
||||
$hashedPassword = null;
|
||||
|
||||
if (!empty($password)) {
|
||||
if ($hash instanceof Plaintext) { // Password was never hashed, hash it with the default hash
|
||||
$defaultHash = new ProofsPassword();
|
||||
$hashedPassword = $defaultHash->hash($password);
|
||||
$hash = $defaultHash->getHash();
|
||||
} else {
|
||||
$hashedPassword = $password;
|
||||
}
|
||||
}
|
||||
|
||||
$user = new Document([
|
||||
'$id' => $userId,
|
||||
'$permissions' => [
|
||||
@@ -110,11 +131,11 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||
'phoneVerification' => false,
|
||||
'status' => true,
|
||||
'labels' => [],
|
||||
'password' => $password,
|
||||
'passwordHistory' => is_null($password) || $passwordHistory === 0 ? [] : [$password],
|
||||
'passwordUpdate' => (!empty($password)) ? DateTime::now() : null,
|
||||
'hash' => $hash === 'plaintext' ? Auth::DEFAULT_ALGO : $hash,
|
||||
'hashOptions' => $hash === 'plaintext' ? Auth::DEFAULT_ALGO_OPTIONS : $hashOptionsObject + ['type' => $hash],
|
||||
'password' => $hashedPassword,
|
||||
'passwordHistory' => is_null($hashedPassword) || $passwordHistory === 0 ? [] : [$hashedPassword],
|
||||
'passwordUpdate' => (!empty($hashedPassword)) ? DateTime::now() : null,
|
||||
'hash' => $hash->getName(),
|
||||
'hashOptions' => $hash->getOptions(),
|
||||
'registration' => DateTime::now(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
@@ -125,7 +146,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||
'search' => implode(' ', [$userId, $email, $phone, $name]),
|
||||
]);
|
||||
|
||||
if ($hash === 'plaintext') {
|
||||
if ($hash instanceof Plaintext) {
|
||||
$hooks->trigger('passwordValidator', [$dbForProject, $project, $plaintextPassword, &$user, true]);
|
||||
}
|
||||
|
||||
@@ -140,7 +161,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'userInternalId' => $user->getSequence(),
|
||||
'providerType' => 'email',
|
||||
'identifier' => $email,
|
||||
]));
|
||||
@@ -164,7 +185,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'userInternalId' => $user->getSequence(),
|
||||
'providerType' => 'sms',
|
||||
'identifier' => $phone,
|
||||
]));
|
||||
@@ -216,7 +237,9 @@ App::post('/v1/users')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$user = createUser('plaintext', '{}', $userId, $email, $password, $phone, $name, $project, $dbForProject, $hooks);
|
||||
$plaintext = new Plaintext();
|
||||
|
||||
$user = createUser($plaintext, $userId, $email, $password, $phone, $name, $project, $dbForProject, $hooks);
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($user, Response::MODEL_USER);
|
||||
@@ -250,7 +273,10 @@ App::post('/v1/users/bcrypt')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$user = createUser('bcrypt', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$bcrypt = new Bcrypt();
|
||||
$bcrypt->setCost(8); // Default cost
|
||||
|
||||
$user = createUser($bcrypt, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -285,7 +311,9 @@ App::post('/v1/users/md5')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$user = createUser('md5', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$md5 = new MD5();
|
||||
|
||||
$user = createUser($md5, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -320,7 +348,13 @@ App::post('/v1/users/argon2')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$user = createUser('argon2', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$argon2 = new Argon2();
|
||||
$argon2
|
||||
->setMemoryCost(2048)
|
||||
->setTimeCost(4)
|
||||
->setThreads(3);
|
||||
|
||||
$user = createUser($argon2, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -356,13 +390,12 @@ App::post('/v1/users/sha')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $passwordVersion, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$options = '{}';
|
||||
|
||||
$sha = new Sha();
|
||||
if (!empty($passwordVersion)) {
|
||||
$options = '{"version":"' . $passwordVersion . '"}';
|
||||
$sha->setVersion($passwordVersion);
|
||||
}
|
||||
|
||||
$user = createUser('sha', $options, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$user = createUser($sha, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -397,7 +430,9 @@ App::post('/v1/users/phpass')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$user = createUser('phpass', '{}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$phpass = new PHPass();
|
||||
|
||||
$user = createUser($phpass, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -437,15 +472,15 @@ App::post('/v1/users/scrypt')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $passwordSalt, int $passwordCpu, int $passwordMemory, int $passwordParallel, int $passwordLength, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$options = [
|
||||
'salt' => $passwordSalt,
|
||||
'costCpu' => $passwordCpu,
|
||||
'costMemory' => $passwordMemory,
|
||||
'costParallel' => $passwordParallel,
|
||||
'length' => $passwordLength
|
||||
];
|
||||
$scrypt = new Scrypt();
|
||||
$scrypt
|
||||
->setSalt($passwordSalt)
|
||||
->setCpuCost($passwordCpu)
|
||||
->setMemoryCost($passwordMemory)
|
||||
->setParallelCost($passwordParallel)
|
||||
->setLength($passwordLength);
|
||||
|
||||
$user = createUser('scrypt', \json_encode($options), $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$user = createUser($scrypt, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -483,7 +518,13 @@ App::post('/v1/users/scrypt-modified')
|
||||
->inject('dbForProject')
|
||||
->inject('hooks')
|
||||
->action(function (string $userId, string $email, string $password, string $passwordSalt, string $passwordSaltSeparator, string $passwordSignerKey, string $name, Response $response, Document $project, Database $dbForProject, Hooks $hooks) {
|
||||
$user = createUser('scryptMod', '{"signerKey":"' . $passwordSignerKey . '","saltSeparator":"' . $passwordSaltSeparator . '","salt":"' . $passwordSalt . '"}', $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
$scryptModified = new ScryptModified();
|
||||
$scryptModified
|
||||
->setSalt($passwordSalt)
|
||||
->setSaltSeparator($passwordSaltSeparator)
|
||||
->setSignerKey($passwordSignerKey);
|
||||
|
||||
$user = createUser($scryptModified, $userId, $email, $password, null, $name, $project, $dbForProject, $hooks);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -564,10 +605,10 @@ App::post('/v1/users/:userId/targets')
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
],
|
||||
'providerId' => empty($provider->getId()) ? null : $provider->getId(),
|
||||
'providerInternalId' => $provider->isEmpty() ? null : $provider->getInternalId(),
|
||||
'providerInternalId' => $provider->isEmpty() ? null : $provider->getSequence(),
|
||||
'providerType' => $providerType,
|
||||
'userId' => $userId,
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'userInternalId' => $user->getSequence(),
|
||||
'identifier' => $identifier,
|
||||
'name' => ($name !== '') ? $name : null,
|
||||
]));
|
||||
@@ -846,7 +887,7 @@ App::get('/v1/users/:userId/memberships')
|
||||
}
|
||||
|
||||
// Set internal queries
|
||||
$queries[] = Query::equal('userInternalId', [$user->getInternalId()]);
|
||||
$queries[] = Query::equal('userInternalId', [$user->getSequence()]);
|
||||
|
||||
$memberships = array_map(function ($membership) use ($dbForProject, $user) {
|
||||
$team = $dbForProject->getDocument('teams', $membership->getAttribute('teamId'));
|
||||
@@ -910,7 +951,7 @@ App::get('/v1/users/:userId/logs')
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
|
||||
$logs = $audit->getLogsByUser($user->getInternalId(), $queries);
|
||||
$logs = $audit->getLogsByUser($user->getSequence(), $queries);
|
||||
|
||||
$output = [];
|
||||
|
||||
@@ -957,7 +998,7 @@ App::get('/v1/users/:userId/logs')
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => $audit->countLogsByUser($user->getInternalId(), $queries),
|
||||
'total' => $audit->countLogsByUser($user->getSequence(), $queries),
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
@@ -980,7 +1021,7 @@ App::get('/v1/users/:userId/targets')
|
||||
]
|
||||
))
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('queries', [], new Targets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Users::ALLOWED_ATTRIBUTES), true)
|
||||
->param('queries', [], new Targets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Targets::ALLOWED_ATTRIBUTES), true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $userId, array $queries, Response $response, Database $dbForProject) {
|
||||
@@ -1061,7 +1102,6 @@ App::get('/v1/users/identities')
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
@@ -1328,12 +1368,20 @@ App::patch('/v1/users/:userId/password')
|
||||
|
||||
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, true]);
|
||||
|
||||
$newPassword = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
|
||||
// Create Argon2 hasher with default settings
|
||||
$hasher = new Argon2();
|
||||
$hasher->setMemoryCost(2048);
|
||||
$hasher->setTimeCost(4);
|
||||
$hasher->setThreads(3);
|
||||
|
||||
$newPassword = $hasher->hash($password);
|
||||
|
||||
$hash = ProofsPassword::createHash($user->getAttribute('hash'), $user->getAttribute('hashOptions'));
|
||||
$historyLimit = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
||||
$history = $user->getAttribute('passwordHistory', []);
|
||||
|
||||
if ($historyLimit > 0) {
|
||||
$validator = new PasswordHistory($history, $user->getAttribute('hash'), $user->getAttribute('hashOptions'));
|
||||
$validator = new PasswordHistory($history, $hash);
|
||||
if (!$validator->isValid($password)) {
|
||||
throw new Exception(Exception::USER_PASSWORD_RECENTLY_USED);
|
||||
}
|
||||
@@ -1346,8 +1394,12 @@ App::patch('/v1/users/:userId/password')
|
||||
->setAttribute('password', $newPassword)
|
||||
->setAttribute('passwordHistory', $history)
|
||||
->setAttribute('passwordUpdate', DateTime::now())
|
||||
->setAttribute('hash', Auth::DEFAULT_ALGO)
|
||||
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS);
|
||||
->setAttribute('hash', 'argon2')
|
||||
->setAttribute('hashOptions', [
|
||||
'memoryCost' => 65536,
|
||||
'timeCost' => 4,
|
||||
'threads' => 3
|
||||
]);
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
@@ -1396,7 +1448,7 @@ App::patch('/v1/users/:userId/email')
|
||||
// Makes sure this email is not already used in another identity
|
||||
$identityWithMatchingEmail = $dbForProject->findOne('identities', [
|
||||
Query::equal('providerEmail', [$email]),
|
||||
Query::notEqual('userInternalId', $user->getInternalId()),
|
||||
Query::notEqual('userInternalId', $user->getSequence()),
|
||||
]);
|
||||
if (!$identityWithMatchingEmail->isEmpty()) {
|
||||
throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||
@@ -1440,7 +1492,7 @@ App::patch('/v1/users/:userId/email')
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'userInternalId' => $user->getSequence(),
|
||||
'providerType' => 'email',
|
||||
'identifier' => $email,
|
||||
]));
|
||||
@@ -1529,7 +1581,7 @@ App::patch('/v1/users/:userId/phone')
|
||||
Permission::delete(Role::user($user->getId())),
|
||||
],
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'userInternalId' => $user->getSequence(),
|
||||
'providerType' => 'sms',
|
||||
'identifier' => $number,
|
||||
]));
|
||||
@@ -1711,7 +1763,7 @@ App::patch('/v1/users/:userId/targets/:targetId')
|
||||
|
||||
$target
|
||||
->setAttribute('providerId', $provider->getId())
|
||||
->setAttribute('providerInternalId', $provider->getInternalId());
|
||||
->setAttribute('providerInternalId', $provider->getSequence());
|
||||
}
|
||||
|
||||
if ($name) {
|
||||
@@ -2034,17 +2086,19 @@ App::post('/v1/users/:userId/sessions')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $userId, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) {
|
||||
->inject('store')
|
||||
->inject('proofForToken')
|
||||
->action(function (string $userId, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Store $store, Token $proofForToken) {
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_SESSION);
|
||||
$secret = $proofForToken->generate();
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? Auth::TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration));
|
||||
|
||||
$session = new Document(array_merge(
|
||||
@@ -2052,8 +2106,8 @@ App::post('/v1/users/:userId/sessions')
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'provider' => Auth::SESSION_PROVIDER_SERVER,
|
||||
'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak
|
||||
'provider' => SESSION_PROVIDER_SERVER,
|
||||
'secret' => $proofForToken->hash($secret), // One way hash encryption to protect DB leak
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
'factors' => ['server'],
|
||||
'ip' => $request->getIP(),
|
||||
@@ -2077,8 +2131,13 @@ App::post('/v1/users/:userId/sessions')
|
||||
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
|
||||
$encoded = $store
|
||||
->setProperty('id', $user->getId())
|
||||
->setProperty('secret', $secret)
|
||||
->encode();
|
||||
|
||||
$session
|
||||
->setAttribute('secret', Auth::encodeSession($user->getId(), $secret))
|
||||
->setAttribute('secret', $encoded)
|
||||
->setAttribute('countryName', $countryName);
|
||||
|
||||
$queueForEvents
|
||||
@@ -2113,27 +2172,30 @@ App::post('/v1/users/:userId/tokens')
|
||||
))
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('length', 6, new Range(4, 128), 'Token length in characters. The default length is 6 characters', true)
|
||||
->param('expire', Auth::TOKEN_EXPIRATION_GENERIC, new Range(60, Auth::TOKEN_EXPIRATION_LOGIN_LONG), 'Token expiration period in seconds. The default expiration is 15 minutes.', true)
|
||||
->param('expire', TOKEN_EXPIRATION_GENERIC, new Range(60, TOKEN_EXPIRATION_LOGIN_LONG), 'Token expiration period in seconds. The default expiration is 15 minutes.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $userId, int $length, int $expire, Request $request, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->inject('proofForToken')
|
||||
->action(function (string $userId, int $length, int $expire, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Token $proofForToken) {
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$secret = Auth::tokenGenerator($length);
|
||||
$secret = $proofForToken
|
||||
->setLength($length)
|
||||
->generate();
|
||||
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $expire));
|
||||
|
||||
$token = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'userId' => $user->getId(),
|
||||
'userInternalId' => $user->getInternalId(),
|
||||
'type' => Auth::TOKEN_TYPE_GENERIC,
|
||||
'secret' => Auth::hash($secret),
|
||||
'type' => TOKEN_TYPE_GENERIC,
|
||||
'secret' => $proofForToken->hash($secret),
|
||||
'expire' => $expire,
|
||||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
'ip' => $request->getIP()
|
||||
@@ -2293,8 +2355,8 @@ App::delete('/v1/users/:userId')
|
||||
$clone = clone $user;
|
||||
|
||||
$dbForProject->deleteDocument('users', $userId);
|
||||
DeleteIdentities::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()]));
|
||||
DeleteTargets::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()]));
|
||||
DeleteIdentities::delete($dbForProject, Query::equal('userInternalId', [$user->getSequence()]));
|
||||
DeleteTargets::delete($dbForProject, Query::equal('userInternalId', [$user->getSequence()]));
|
||||
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
|
||||
+44
-34
@@ -78,11 +78,11 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
$resourceCollection = $resourceType === "function" ? 'functions' : 'sites';
|
||||
$resourceId = $repository->getAttribute('resourceId');
|
||||
$resource = Authorization::skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId));
|
||||
$resourceInternalId = $resource->getInternalId();
|
||||
$resourceInternalId = $resource->getSequence();
|
||||
|
||||
$deploymentId = ID::unique();
|
||||
$repositoryId = $repository->getId();
|
||||
$repositoryInternalId = $repository->getInternalId();
|
||||
$repositoryInternalId = $repository->getSequence();
|
||||
$providerRepositoryId = $repository->getAttribute('providerRepositoryId');
|
||||
$installationId = $repository->getAttribute('installationId');
|
||||
$installationInternalId = $repository->getAttribute('installationInternalId');
|
||||
@@ -157,7 +157,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
],
|
||||
'installationInternalId' => $installationInternalId,
|
||||
'installationId' => $installationId,
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectInternalId' => $project->getSequence(),
|
||||
'projectId' => $project->getId(),
|
||||
'providerRepositoryId' => $providerRepositoryId,
|
||||
'providerBranch' => $providerBranch,
|
||||
@@ -252,13 +252,12 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
'providerCommitUrl' => $providerCommitUrl,
|
||||
'providerCommentId' => \strval($latestCommentId),
|
||||
'providerBranch' => $providerBranch,
|
||||
'search' => implode(' ', [$deploymentId, $resource->getAttribute('entrypoint', '')]),
|
||||
'activate' => $activate,
|
||||
])));
|
||||
|
||||
$resource = $resource
|
||||
->setAttribute('latestDeploymentId', $deployment->getId())
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getInternalId())
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
Authorization::skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource));
|
||||
@@ -274,12 +273,12 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
fn () => $dbForPlatform->createDocument('rules', new Document([
|
||||
'$id' => $ruleId,
|
||||
'projectId' => $project->getId(),
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectInternalId' => $project->getSequence(),
|
||||
'domain' => $domain,
|
||||
'type' => 'deployment',
|
||||
'trigger' => 'deployment',
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'deploymentInternalId' => $deployment->getInternalId(),
|
||||
'deploymentInternalId' => $deployment->getSequence(),
|
||||
'deploymentResourceType' => 'site',
|
||||
'deploymentResourceId' => $resourceId,
|
||||
'deploymentResourceInternalId' => $resourceInternalId,
|
||||
@@ -294,19 +293,25 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
|
||||
// VCS branch preview
|
||||
if (!empty($providerBranch)) {
|
||||
$domain = "branch-{$providerBranch}-{$resource->getId()}-{$project->getId()}.{$sitesDomain}";
|
||||
$branchPrefix = substr($providerBranch, 0, 16);
|
||||
if (strlen($providerBranch) > 16) {
|
||||
$remainingChars = substr($providerBranch, 16);
|
||||
$branchPrefix .= '-' . substr(hash('sha256', $remainingChars), 0, 7);
|
||||
}
|
||||
$resourceProjectHash = substr(hash('sha256', $resource->getId() . $project->getId()), 0, 7);
|
||||
$domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}";
|
||||
$ruleId = md5($domain);
|
||||
try {
|
||||
Authorization::skip(
|
||||
fn () => $dbForPlatform->createDocument('rules', new Document([
|
||||
'$id' => $ruleId,
|
||||
'projectId' => $project->getId(),
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectInternalId' => $project->getSequence(),
|
||||
'domain' => $domain,
|
||||
'type' => 'deployment',
|
||||
'trigger' => 'deployment',
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'deploymentInternalId' => $deployment->getInternalId(),
|
||||
'deploymentInternalId' => $deployment->getSequence(),
|
||||
'deploymentResourceType' => 'site',
|
||||
'deploymentResourceId' => $resourceId,
|
||||
'deploymentResourceInternalId' => $resourceInternalId,
|
||||
@@ -325,19 +330,19 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
|
||||
// VCS commit preview
|
||||
if (!empty($providerCommitHash)) {
|
||||
$domain = "commit-{$providerCommitHash}-{$resource->getId()}-{$project->getId()}.{$sitesDomain}";
|
||||
$domain = "commit-" . substr($providerCommitHash, 0, 16) . ".{$sitesDomain}";
|
||||
$ruleId = md5($domain);
|
||||
try {
|
||||
Authorization::skip(
|
||||
fn () => $dbForPlatform->createDocument('rules', new Document([
|
||||
'$id' => $ruleId,
|
||||
'projectId' => $project->getId(),
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectInternalId' => $project->getSequence(),
|
||||
'domain' => $domain,
|
||||
'type' => 'deployment',
|
||||
'trigger' => 'deployment',
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'deploymentInternalId' => $deployment->getInternalId(),
|
||||
'deploymentInternalId' => $deployment->getSequence(),
|
||||
'deploymentResourceType' => 'site',
|
||||
'deploymentResourceId' => $resourceId,
|
||||
'deploymentResourceInternalId' => $resourceInternalId,
|
||||
@@ -358,6 +363,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
if (!empty($providerCommitHash) && $resource->getAttribute('providerSilentMode', false) === false) {
|
||||
$resourceName = $resource->getAttribute('name');
|
||||
$projectName = $project->getAttribute('name');
|
||||
$region = $project->getAttribute('region', 'default');
|
||||
$name = "{$resourceName} ({$projectName})";
|
||||
$message = 'Starting...';
|
||||
|
||||
@@ -372,7 +378,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
}
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
|
||||
$providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/$resourceCollection/$resourceType-$resourceId";
|
||||
$providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$region-$projectId/$resourceCollection/$resourceType-$resourceId";
|
||||
$github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'pending', $message, $providerTargetUrl, $name);
|
||||
}
|
||||
|
||||
@@ -471,16 +477,6 @@ App::get('/v1/vcs/github/callback')
|
||||
$state = \json_decode($state, true);
|
||||
$projectId = $state['projectId'] ?? '';
|
||||
|
||||
$defaultState = [
|
||||
'success' => $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/settings/git-installations",
|
||||
'failure' => $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/settings/git-installations",
|
||||
];
|
||||
|
||||
$state = \array_merge($defaultState, $state ?? []);
|
||||
|
||||
$redirectSuccess = $state['success'] ?? '';
|
||||
$redirectFailure = $state['failure'] ?? '';
|
||||
|
||||
$project = $dbForPlatform->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
@@ -497,6 +493,18 @@ App::get('/v1/vcs/github/callback')
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND, $error);
|
||||
}
|
||||
|
||||
$region = $project->getAttribute('region', 'default');
|
||||
|
||||
$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",
|
||||
];
|
||||
|
||||
$state = \array_merge($defaultState, $state ?? []);
|
||||
|
||||
$redirectSuccess = $state['success'] ?? '';
|
||||
$redirectFailure = $state['failure'] ?? '';
|
||||
|
||||
// Create / Update installation
|
||||
if (!empty($providerInstallationId)) {
|
||||
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||
@@ -504,7 +512,7 @@ App::get('/v1/vcs/github/callback')
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
$owner = $github->getOwnerName($providerInstallationId) ?? '';
|
||||
|
||||
$projectInternalId = $project->getInternalId();
|
||||
$projectInternalId = $project->getSequence();
|
||||
|
||||
$installation = $dbForPlatform->findOne('installations', [
|
||||
Query::equal('providerInstallationId', [$providerInstallationId]),
|
||||
@@ -600,11 +608,12 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->param('providerRepositoryId', '', new Text(256), 'Repository Id')
|
||||
->param('providerRootDirectory', '', new Text(256, 0), 'Path to get contents of nested directory', true)
|
||||
->param('providerReference', '', new Text(256, 0), 'Git reference (branch, tag, commit) to get contents from', true)
|
||||
->inject('gitHub')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $installationId, string $providerRepositoryId, string $providerRootDirectory, GitHub $github, Response $response, Document $project, Database $dbForPlatform) {
|
||||
->action(function (string $installationId, string $providerRepositoryId, string $providerRootDirectory, string $providerReference, GitHub $github, Response $response, Document $project, Database $dbForPlatform) {
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
@@ -626,7 +635,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
|
||||
$contents = $github->listRepositoryContents($owner, $repositoryName, $providerRootDirectory);
|
||||
$contents = $github->listRepositoryContents($owner, $repositoryName, $providerRootDirectory, $providerReference);
|
||||
|
||||
$vcsContents = [];
|
||||
foreach ($contents as $content) {
|
||||
@@ -1000,7 +1009,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
if (empty($accessToken) || empty($refreshToken) || empty($accessTokenExpiry)) {
|
||||
$identity = $dbForPlatform->findOne('identities', [
|
||||
Query::equal('provider', ['github']),
|
||||
Query::equal('userInternalId', [$user->getInternalId()]),
|
||||
Query::equal('userInternalId', [$user->getSequence()]),
|
||||
]);
|
||||
if ($identity->isEmpty()) {
|
||||
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
|
||||
@@ -1210,6 +1219,7 @@ App::post('/v1/vcs/github/events')
|
||||
|
||||
if ($event == $github::EVENT_PUSH) {
|
||||
$providerBranchCreated = $parsedPayload["branchCreated"] ?? false;
|
||||
$providerBranchDeleted = $parsedPayload["branchDeleted"] ?? false;
|
||||
$providerBranch = $parsedPayload["branch"] ?? '';
|
||||
$providerBranchUrl = $parsedPayload["branchUrl"] ?? '';
|
||||
$providerRepositoryId = $parsedPayload["repositoryId"] ?? '';
|
||||
@@ -1231,8 +1241,8 @@ App::post('/v1/vcs/github/events')
|
||||
Query::limit(100),
|
||||
]));
|
||||
|
||||
// create new deployment only on push and not when branch is created
|
||||
if (!$providerBranchCreated) {
|
||||
// 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);
|
||||
}
|
||||
} elseif ($event == $github::EVENT_INSTALLATION) {
|
||||
@@ -1247,7 +1257,7 @@ App::post('/v1/vcs/github/events')
|
||||
|
||||
foreach ($installations as $installation) {
|
||||
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
|
||||
Query::equal('installationInternalId', [$installation->getInternalId()]),
|
||||
Query::equal('installationInternalId', [$installation->getSequence()]),
|
||||
Query::limit(1000)
|
||||
]));
|
||||
|
||||
@@ -1350,7 +1360,7 @@ App::get('/v1/vcs/installations')
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$queries[] = Query::equal('projectInternalId', [$project->getInternalId()]);
|
||||
$queries[] = Query::equal('projectInternalId', [$project->getSequence()]);
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = Query::search('search', $search);
|
||||
@@ -1423,7 +1433,7 @@ App::get('/v1/vcs/installations/:installationId')
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($installation->getAttribute('projectInternalId') !== $project->getInternalId()) {
|
||||
if ($installation->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
@@ -1506,7 +1516,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
|
||||
}
|
||||
|
||||
$repository = Authorization::skip(fn () => $dbForPlatform->getDocument('repositories', $repositoryId, [
|
||||
Query::equal('projectInternalId', [$project->getInternalId()])
|
||||
Query::equal('projectInternalId', [$project->getSequence()])
|
||||
]));
|
||||
|
||||
if ($repository->isEmpty()) {
|
||||
|
||||
+74
-51
@@ -186,7 +186,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
$resourceId = $rule->getAttribute('deploymentResourceId', '');
|
||||
$type = ($resourceType === 'site') ? 'sites' : 'functions';
|
||||
$exception = new AppwriteException(AppwriteException::DEPLOYMENT_NOT_FOUND, view: $errorView);
|
||||
$exception->addCTA('View deployments', $url . '/console/project-' . $projectId . '/' . $type . '/' . $resourceType . '-' . $resourceId);
|
||||
$exception->addCTA('View deployments', $url . '/console/project-' . $project->getAttribute('region', 'default') . '-' . $projectId . '/' . $type . '/' . $resourceType . '-' . $resourceId);
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
@@ -319,21 +319,22 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
$allowAnyStatus = !\is_null($apiKey) && $apiKey->isDeploymentStatusIgnored();
|
||||
if (!$allowAnyStatus && $deployment->getAttribute('status') !== 'ready') {
|
||||
$status = $deployment->getAttribute('status');
|
||||
$region = $project->getAttribute('region', 'default');
|
||||
|
||||
switch ($status) {
|
||||
case 'failed':
|
||||
$exception = new AppwriteException(AppwriteException::BUILD_FAILED, view: $errorView);
|
||||
$ctaUrl = '/console/project-' . $project->getId() . '/sites/site-' . $resource->getId() . '/deployments/deployment-' . $deployment->getId();
|
||||
$ctaUrl = '/console/project-' . $region . '-' . $project->getId() . '/sites/site-' . $resource->getId() . '/deployments/deployment-' . $deployment->getId();
|
||||
$exception->addCTA('View logs', $url . $ctaUrl);
|
||||
break;
|
||||
case 'canceled':
|
||||
$exception = new AppwriteException(AppwriteException::BUILD_CANCELED, view: $errorView);
|
||||
$ctaUrl = '/console/project-' . $project->getId() . '/sites/site-' . $resource->getId() . '/deployments';
|
||||
$ctaUrl = '/console/project-' . $region . '-' . $project->getId() . '/sites/site-' . $resource->getId() . '/deployments';
|
||||
$exception->addCTA('View deployments', $url . $ctaUrl);
|
||||
break;
|
||||
default:
|
||||
$exception = new AppwriteException(AppwriteException::BUILD_NOT_READY, view: $errorView);
|
||||
$ctaUrl = '/console/project-' . $project->getId() . '/sites/site-' . $resource->getId() . '/deployments/deployment-' . $deployment->getId();
|
||||
$ctaUrl = '/console/project-' . $region . '-' . $project->getId() . '/sites/site-' . $resource->getId() . '/deployments/deployment-' . $deployment->getId();
|
||||
$exception->addCTA('Reload', '/');
|
||||
$exception->addCTA('View logs', $url . $ctaUrl);
|
||||
break;
|
||||
@@ -345,7 +346,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
$permissions = $resource->getAttribute('execute');
|
||||
if (!(\in_array('any', $permissions)) && !(\in_array('guests', $permissions))) {
|
||||
$exception = new AppwriteException(AppwriteException::FUNCTION_EXECUTE_PERMISSION_MISSING, view: $errorView);
|
||||
$exception->addCTA('View settings', $url . '/console/project-' . $project->getId() . '/functions/function-' . $resource->getId() . '/settings');
|
||||
$exception->addCTA('View settings', $url . '/console/project-' . $project->getAttribute('region', 'default') . '-' . $project->getId() . '/functions/function-' . $resource->getId() . '/settings');
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
@@ -391,9 +392,9 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
$execution = new Document([
|
||||
'$id' => $executionId,
|
||||
'$permissions' => [],
|
||||
'resourceInternalId' => $resource->getInternalId(),
|
||||
'resourceInternalId' => $resource->getSequence(),
|
||||
'resourceId' => $resource->getId(),
|
||||
'deploymentInternalId' => $deployment->getInternalId(),
|
||||
'deploymentInternalId' => $deployment->getSequence(),
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'responseStatusCode' => 0,
|
||||
'responseHeaders' => [],
|
||||
@@ -452,16 +453,33 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
$endpoint = $protocol . '://' . $hostname . "/v1";
|
||||
|
||||
// Appwrite vars
|
||||
if ($type === 'function') {
|
||||
$vars = \array_merge($vars, [
|
||||
'APPWRITE_FUNCTION_API_ENDPOINT' => $endpoint,
|
||||
'APPWRITE_FUNCTION_ID' => $resource->getId(),
|
||||
'APPWRITE_FUNCTION_NAME' => $resource->getAttribute('name'),
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
|
||||
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
|
||||
'APPWRITE_FUNCTION_CPUS' => $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT,
|
||||
'APPWRITE_FUNCTION_MEMORY' => $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT,
|
||||
]);
|
||||
} elseif ($type === 'site') {
|
||||
$vars = \array_merge($vars, [
|
||||
'APPWRITE_SITE_API_ENDPOINT' => $endpoint,
|
||||
'APPWRITE_SITE_ID' => $resource->getId(),
|
||||
'APPWRITE_SITE_NAME' => $resource->getAttribute('name'),
|
||||
'APPWRITE_SITE_DEPLOYMENT' => $deployment->getId(),
|
||||
'APPWRITE_SITE_PROJECT_ID' => $project->getId(),
|
||||
'APPWRITE_SITE_RUNTIME_NAME' => $runtime['name'] ?? '',
|
||||
'APPWRITE_SITE_RUNTIME_VERSION' => $runtime['version'] ?? '',
|
||||
'APPWRITE_SITE_CPUS' => $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT,
|
||||
'APPWRITE_SITE_MEMORY' => $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT,
|
||||
]);
|
||||
}
|
||||
|
||||
$vars = \array_merge($vars, [
|
||||
'APPWRITE_FUNCTION_API_ENDPOINT' => $endpoint,
|
||||
'APPWRITE_FUNCTION_ID' => $resource->getId(),
|
||||
'APPWRITE_FUNCTION_NAME' => $resource->getAttribute('name'),
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
|
||||
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
|
||||
'APPWRITE_FUNCTION_CPUS' => $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT,
|
||||
'APPWRITE_FUNCTION_MEMORY' => $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT,
|
||||
'APPWRITE_VERSION' => APP_VERSION_STABLE,
|
||||
'APPWRITE_REGION' => $project->getAttribute('region'),
|
||||
'APPWRITE_DEPLOYMENT_TYPE' => $deployment->getAttribute('type', ''),
|
||||
@@ -494,28 +512,27 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
'function' => $deployment->getAttribute('entrypoint', ''),
|
||||
'site' => '',
|
||||
};
|
||||
$source = $deployment->getAttribute('buildPath', '');
|
||||
$extension = str_ends_with($source, '.tar') ? 'tar' : 'tar.gz';
|
||||
|
||||
if ($type === 'function') {
|
||||
$runtimeEntrypoint = match ($version) {
|
||||
'v2' => '',
|
||||
default => 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $runtime['startCommand'] . '"'
|
||||
};
|
||||
} elseif ($type === 'site') {
|
||||
$startCommand = $runtime['startCommand'];
|
||||
if ($type === 'site') {
|
||||
$frameworks = Config::getParam('frameworks', []);
|
||||
$framework = $frameworks[$resource->getAttribute('framework', '')] ?? null;
|
||||
|
||||
$startCommand = $runtime['startCommand'];
|
||||
|
||||
if (!is_null($framework)) {
|
||||
$adapter = ($framework['adapters'] ?? [])[$deployment->getAttribute('adapter', '')] ?? null;
|
||||
if (!is_null($adapter) && isset($adapter['startCommand'])) {
|
||||
$startCommand = $adapter['startCommand'];
|
||||
}
|
||||
}
|
||||
|
||||
$runtimeEntrypoint = 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $startCommand . '"';
|
||||
}
|
||||
|
||||
$runtimeEntrypoint = match ($version) {
|
||||
'v2' => '',
|
||||
default => "cp /tmp/code.$extension /mnt/code/code.$extension && nohup helpers/start.sh \"$startCommand\"",
|
||||
};
|
||||
|
||||
$entrypoint = match ($type) {
|
||||
'function' => $deployment->getAttribute('entrypoint', ''),
|
||||
'site' => '',
|
||||
@@ -528,7 +545,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
variables: $vars,
|
||||
timeout: $resource->getAttribute('timeout', 30),
|
||||
image: $runtime['image'],
|
||||
source: $deployment->getAttribute('buildPath', ''),
|
||||
source: $source,
|
||||
entrypoint: $entrypoint,
|
||||
version: $version,
|
||||
path: $path,
|
||||
@@ -675,11 +692,11 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
}
|
||||
|
||||
$metricTypeExecutions = str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_EXECUTIONS);
|
||||
$metricTypeIdExecutions = str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS);
|
||||
$metricTypeIdExecutions = str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS);
|
||||
$metricTypeExecutionsCompute = str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_EXECUTIONS_COMPUTE);
|
||||
$metricTypeIdExecutionsCompute = str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE);
|
||||
$metricTypeIdExecutionsCompute = str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_COMPUTE);
|
||||
$metricTypeExecutionsMbSeconds = str_replace(['{resourceType}'], [$deployment->getAttribute('resourceType')], METRIC_RESOURCE_TYPE_EXECUTIONS_MB_SECONDS);
|
||||
$metricTypeIdExecutionsMBSeconds = str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getInternalId()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS);
|
||||
$metricTypeIdExecutionsMBSeconds = str_replace(['{resourceType}', '{resourceInternalId}'], [$deployment->getAttribute('resourceType'), $resource->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS);
|
||||
if ($deployment->getAttribute('resourceType') === 'sites') {
|
||||
$queueForStatsUsage
|
||||
->disableMetric(METRIC_NETWORK_REQUESTS)
|
||||
@@ -702,9 +719,9 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
->addMetric(METRIC_SITES_REQUESTS, 1)
|
||||
->addMetric(METRIC_SITES_INBOUND, $request->getSize() + $fileSize)
|
||||
->addMetric(METRIC_SITES_OUTBOUND, $response->getSize())
|
||||
->addMetric(str_replace('{siteInternalId}', $resource->getInternalId(), METRIC_SITES_ID_REQUESTS), 1)
|
||||
->addMetric(str_replace('{siteInternalId}', $resource->getInternalId(), METRIC_SITES_ID_INBOUND), $request->getSize() + $fileSize)
|
||||
->addMetric(str_replace('{siteInternalId}', $resource->getInternalId(), METRIC_SITES_ID_OUTBOUND), $response->getSize())
|
||||
->addMetric(str_replace('{siteInternalId}', $resource->getSequence(), METRIC_SITES_ID_REQUESTS), 1)
|
||||
->addMetric(str_replace('{siteInternalId}', $resource->getSequence(), METRIC_SITES_ID_INBOUND), $request->getSize() + $fileSize)
|
||||
->addMetric(str_replace('{siteInternalId}', $resource->getSequence(), METRIC_SITES_ID_OUTBOUND), $response->getSize())
|
||||
;
|
||||
}
|
||||
|
||||
@@ -732,12 +749,6 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
return false;
|
||||
} elseif ($type === 'redirect') {
|
||||
$url = $rule->getAttribute('redirectUrl', '');
|
||||
|
||||
$query = ($swooleRequest->server['query_string'] ?? '');
|
||||
if (!empty($query)) {
|
||||
$url .= '?' . $query;
|
||||
}
|
||||
|
||||
$response->redirect($url, \intval($rule->getAttribute('redirectStatusCode', 301)));
|
||||
return true;
|
||||
} else {
|
||||
@@ -897,7 +908,7 @@ App::init()
|
||||
'type' => 'api',
|
||||
'status' => 'verifying',
|
||||
'projectId' => $console->getId(),
|
||||
'projectInternalId' => $console->getInternalId(),
|
||||
'projectInternalId' => $console->getSequence(),
|
||||
'search' => implode(' ', [$ruleId, $domain->get()]),
|
||||
'owner' => $owner,
|
||||
'region' => $console->getAttribute('region')
|
||||
@@ -947,7 +958,7 @@ App::init()
|
||||
)[0] ?? new Document();
|
||||
}
|
||||
|
||||
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getInternalId()) {
|
||||
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) {
|
||||
$refDomainOrigin = $origin;
|
||||
}
|
||||
}
|
||||
@@ -1030,7 +1041,7 @@ App::init()
|
||||
->addHeader('Server', 'Appwrite')
|
||||
->addHeader('X-Content-Type-Options', 'nosniff')
|
||||
->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-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-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-Credentials', 'true');
|
||||
@@ -1093,7 +1104,7 @@ App::options()
|
||||
$response
|
||||
->addHeader('Server', 'Appwrite')
|
||||
->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-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-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent')
|
||||
->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-Appwrite-Session, X-Fallback-Cookies, X-Forwarded-For, X-Forwarded-User-Agent')
|
||||
->addHeader('Access-Control-Expose-Headers', 'X-Appwrite-Session, X-Fallback-Cookies')
|
||||
->addHeader('Access-Control-Allow-Origin', $origin)
|
||||
->addHeader('Access-Control-Allow-Credentials', 'true')
|
||||
@@ -1135,17 +1146,29 @@ App::error()
|
||||
$trace = $error->getTrace();
|
||||
|
||||
if (php_sapi_name() === 'cli') {
|
||||
Console::error('[Error] Timestamp: ' . date('c', time()));
|
||||
$logLevel = $code >= 500 || $code == 0 ? 'error' : 'warning';
|
||||
$logPrefix = $code >= 500 || $code == 0 ? '[Error]' : '[Warning]';
|
||||
|
||||
Console::$logLevel($logPrefix . ' Timestamp: ' . date('c', time()));
|
||||
|
||||
if ($route) {
|
||||
Console::error('[Error] Method: ' . $route->getMethod());
|
||||
Console::error('[Error] URL: ' . $route->getPath());
|
||||
Console::$logLevel($logPrefix . ' Status Code: ' . $code);
|
||||
Console::$logLevel($logPrefix . ' URL: ' . $route->getMethod() . ' ' . $route->getPath());
|
||||
}
|
||||
|
||||
Console::error('[Error] Type: ' . get_class($error));
|
||||
Console::error('[Error] Message: ' . $message);
|
||||
Console::error('[Error] File: ' . $file);
|
||||
Console::error('[Error] Line: ' . $line);
|
||||
Console::$logLevel($logPrefix . ' Type: ' . get_class($error));
|
||||
Console::$logLevel($logPrefix . ' Message: ' . $message);
|
||||
Console::$logLevel($logPrefix . ' File: ' . $file);
|
||||
Console::$logLevel($logPrefix . ' Line: ' . $line);
|
||||
Console::$logLevel($logPrefix . ' Trace:');
|
||||
foreach ($trace as $index => $entry) {
|
||||
$traceFile = $entry['file'] ?? 'unknown';
|
||||
$traceLine = $entry['line'] ?? 0;
|
||||
$traceFunction = $entry['function'] ?? 'unknown';
|
||||
$traceClass = $entry['class'] ?? '';
|
||||
$traceType = $entry['type'] ?? '';
|
||||
Console::$logLevel(" #{$index} {$traceFile}({$traceLine}): {$traceClass}{$traceType}{$traceFunction}()");
|
||||
}
|
||||
Console::$logLevel('');
|
||||
}
|
||||
|
||||
switch ($class) {
|
||||
|
||||
@@ -185,7 +185,7 @@ App::post('/v1/mock/api-key-unprefixed')
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'projectInternalId' => $project->getSequence(),
|
||||
'projectId' => $project->getId(),
|
||||
'name' => 'Outdated key',
|
||||
'scopes' => $scopes,
|
||||
@@ -235,7 +235,7 @@ App::get('/v1/mock/github/callback')
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
$owner = $github->getOwnerName($providerInstallationId) ?? '';
|
||||
|
||||
$projectInternalId = $project->getInternalId();
|
||||
$projectInternalId = $project->getSequence();
|
||||
|
||||
$teamId = $project->getAttribute('teamId', '');
|
||||
|
||||
|
||||
@@ -214,38 +214,93 @@ App::init()
|
||||
->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey) {
|
||||
$route = $utopia->getRoute();
|
||||
|
||||
/**
|
||||
* Handle user authentication and session validation.
|
||||
*
|
||||
* This function follows a series of steps to determine the appropriate user session
|
||||
* based on cookies, headers, and JWT tokens.
|
||||
*
|
||||
* Process:
|
||||
*
|
||||
* Project & Role Validation:
|
||||
* 1. Check if the project is empty. If so, throw an exception.
|
||||
* 2. Get the roles configuration.
|
||||
* 3. Determine the role for the user based on the user document.
|
||||
* 4. Get the scopes for the role.
|
||||
*
|
||||
* API Key Authentication:
|
||||
* 5. If there is an API key:
|
||||
* - Verify no user session exists simultaneously
|
||||
* - Check if key is expired
|
||||
* - Set role and scopes from API key
|
||||
* - Handle special app role case
|
||||
* - For standard keys, update last accessed time
|
||||
*
|
||||
* User Activity:
|
||||
* 6. If the project is not the console and user is not admin:
|
||||
* - Update user's last activity timestamp
|
||||
*
|
||||
* Access Control:
|
||||
* 7. Get the method from the route
|
||||
* 8. Validate namespace permissions
|
||||
* 9. Validate scope permissions
|
||||
* 10. Check if user is blocked
|
||||
*
|
||||
* Security Checks:
|
||||
* 11. Verify password status (check if reset required)
|
||||
* 12. Validate MFA requirements:
|
||||
* - Check if MFA is enabled
|
||||
* - Verify email status
|
||||
* - Verify phone status
|
||||
* - Verify authenticator status
|
||||
* 13. Handle Multi-Factor Authentication:
|
||||
* - Check remaining required factors
|
||||
* - Validate factor completion
|
||||
* - Throw exception if factors incomplete
|
||||
*/
|
||||
|
||||
// Step 1: Check if project is empty
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Step 2: Get roles configuration
|
||||
$roles = Config::getParam('roles', []);
|
||||
|
||||
// Step 3: Determine role for user
|
||||
// TODO get scopes from the identity instead of the user roles config. The identity will containn the scopes the user authorized for the access token.
|
||||
|
||||
$role = $user->isEmpty()
|
||||
? Role::guests()->toString()
|
||||
: Role::users()->toString();
|
||||
|
||||
// Step 4: Get scopes for the role
|
||||
$scopes = $roles[$role]['scopes'];
|
||||
|
||||
// API Key authentication
|
||||
// Step 5: API Key Authentication
|
||||
if (!empty($apiKey)) {
|
||||
// Verify no user session exists simultaneously
|
||||
if (!$user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_API_KEY_AND_SESSION_SET);
|
||||
}
|
||||
// Check if key is expired
|
||||
if ($apiKey->isExpired()) {
|
||||
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
|
||||
}
|
||||
|
||||
// Set role and scopes from API key
|
||||
$role = $apiKey->getRole();
|
||||
$scopes = $apiKey->getScopes();
|
||||
|
||||
// Disable authorization checks for API keys
|
||||
Authorization::setDefaultStatus(false);
|
||||
|
||||
if ($apiKey->getRole() === Auth::USER_ROLE_APPS) {
|
||||
// Handle special app role case
|
||||
if ($apiKey->getRole() === USER_ROLE_APPS) {
|
||||
$user = new Document([
|
||||
'$id' => '',
|
||||
'status' => true,
|
||||
'type' => Auth::ACTIVITY_TYPE_APP,
|
||||
'type' => ACTIVITY_TYPE_APP,
|
||||
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
|
||||
'password' => '',
|
||||
'name' => $apiKey->getName(),
|
||||
@@ -254,6 +309,7 @@ App::init()
|
||||
$queueForAudits->setUser($user);
|
||||
}
|
||||
|
||||
// For standard keys, update last accessed time
|
||||
if ($apiKey->getType() === API_KEY_STANDARD) {
|
||||
$dbKey = $project->find(
|
||||
key: 'secret',
|
||||
@@ -323,7 +379,7 @@ App::init()
|
||||
Authorization::setRole($authRole);
|
||||
}
|
||||
|
||||
// Update project last activity
|
||||
// Step 6: Update project and user last activity
|
||||
if (!$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$accessedAt = $project->getAttribute('accessedAt', 0);
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
|
||||
@@ -332,7 +388,6 @@ App::init()
|
||||
}
|
||||
}
|
||||
|
||||
// Update user last activity
|
||||
if (!empty($user->getId())) {
|
||||
$accessedAt = $user->getAttribute('accessedAt', 0);
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCESS)) > $accessedAt) {
|
||||
@@ -346,6 +401,7 @@ App::init()
|
||||
}
|
||||
}
|
||||
|
||||
// Steps 7-9: Access Control - Method, Namespace and Scope Validation
|
||||
/**
|
||||
* @var ?Method $method
|
||||
*/
|
||||
@@ -369,21 +425,23 @@ App::init()
|
||||
}
|
||||
}
|
||||
|
||||
// Do now allow access if scope is not allowed
|
||||
// Step 9: Validate scope permissions
|
||||
$scope = $route->getLabel('scope', 'none');
|
||||
if (!\in_array($scope, $scopes)) {
|
||||
throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')');
|
||||
}
|
||||
|
||||
// Do not allow access to blocked accounts
|
||||
// Step 10: Check if user is blocked
|
||||
if (false === $user->getAttribute('status')) { // Account is blocked
|
||||
throw new Exception(Exception::USER_BLOCKED);
|
||||
}
|
||||
|
||||
// Step 11: Verify password status
|
||||
if ($user->getAttribute('reset')) {
|
||||
throw new Exception(Exception::USER_PASSWORD_RESET_REQUIRED);
|
||||
}
|
||||
|
||||
// Step 12: Validate MFA requirements
|
||||
$mfaEnabled = $user->getAttribute('mfa', false);
|
||||
$hasVerifiedEmail = $user->getAttribute('emailVerification', false);
|
||||
$hasVerifiedPhone = $user->getAttribute('phoneVerification', false);
|
||||
@@ -391,6 +449,7 @@ App::init()
|
||||
$hasMoreFactors = $hasVerifiedEmail || $hasVerifiedPhone || $hasVerifiedAuthenticator;
|
||||
$minimumFactors = ($mfaEnabled && $hasMoreFactors) ? 2 : 1;
|
||||
|
||||
// Step 13: Handle Multi-Factor Authentication
|
||||
if (!in_array('mfa', $route->getGroups())) {
|
||||
if ($session && \count($session->getAttribute('factors', [])) < $minimumFactors) {
|
||||
throw new Exception(Exception::USER_MORE_FACTORS_REQUIRED);
|
||||
@@ -514,7 +573,7 @@ App::init()
|
||||
if (!$user->isEmpty()) {
|
||||
$userClone = clone $user;
|
||||
// $user doesn't support `type` and can cause unintended effects.
|
||||
$userClone->setAttribute('type', Auth::ACTIVITY_TYPE_USER);
|
||||
$userClone->setAttribute('type', ACTIVITY_TYPE_USER);
|
||||
$queueForAudits->setUser($userClone);
|
||||
}
|
||||
|
||||
@@ -574,7 +633,7 @@ App::init()
|
||||
$bucketId = $parts[1] ?? null;
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getInternalId();
|
||||
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
|
||||
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
|
||||
@@ -593,12 +652,12 @@ App::init()
|
||||
$fileId = $parts[1] ?? null;
|
||||
|
||||
if ($fileSecurity && !$valid && !$isToken) {
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId);
|
||||
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
|
||||
} else {
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId));
|
||||
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
|
||||
}
|
||||
|
||||
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getInternalId()) {
|
||||
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@@ -752,7 +811,7 @@ App::shutdown()
|
||||
if (!$user->isEmpty()) {
|
||||
$userClone = clone $user;
|
||||
// $user doesn't support `type` and can cause unintended effects.
|
||||
$userClone->setAttribute('type', Auth::ACTIVITY_TYPE_USER);
|
||||
$userClone->setAttribute('type', ACTIVITY_TYPE_USER);
|
||||
$queueForAudits->setUser($userClone);
|
||||
} elseif ($queueForAudits->getUser() === null || $queueForAudits->getUser()->isEmpty()) {
|
||||
/**
|
||||
@@ -766,7 +825,7 @@ App::shutdown()
|
||||
$user = new Document([
|
||||
'$id' => '',
|
||||
'status' => true,
|
||||
'type' => Auth::ACTIVITY_TYPE_GUEST,
|
||||
'type' => ACTIVITY_TYPE_GUEST,
|
||||
'email' => 'guest.' . $project->getId() . '@service.' . $request->getHostname(),
|
||||
'password' => '',
|
||||
'name' => 'Guest',
|
||||
|
||||
@@ -20,7 +20,7 @@ App::init()
|
||||
$lastUpdate = $session->getAttribute('mfaUpdatedAt');
|
||||
if (!empty($lastUpdate)) {
|
||||
$now = DateTime::now();
|
||||
$maxAllowedDate = DateTime::addSeconds(new \DateTime($lastUpdate), Auth::MFA_RECENT_DURATION); // Maximum date until session is considered safe before asking for another challenge
|
||||
$maxAllowedDate = DateTime::addSeconds(new \DateTime($lastUpdate), MFA_RECENT_DURATION); // Maximum date until session is considered safe before asking for another challenge
|
||||
|
||||
$isSessionFresh = DateTime::formatTz($maxAllowedDate) >= DateTime::formatTz($now);
|
||||
}
|
||||
|
||||
+9
-14
@@ -15,9 +15,11 @@ use Utopia\Audit\Audit;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Compression\Compression;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\Pool as DatabasePool;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
@@ -170,7 +172,7 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co
|
||||
$sleep = 1;
|
||||
$attempts = 0;
|
||||
|
||||
do {
|
||||
while (true) {
|
||||
try {
|
||||
$attempts++;
|
||||
$resource = $app->getResource($resourceKey);
|
||||
@@ -179,13 +181,12 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co
|
||||
break; // exit loop on success
|
||||
} catch (\Exception $e) {
|
||||
Console::warning(" └── Database not ready. Retrying connection ({$attempts})...");
|
||||
$pools->reclaim();
|
||||
if ($attempts >= $max) {
|
||||
throw new \Exception(' └── Failed to connect to database: ' . $e->getMessage());
|
||||
}
|
||||
sleep($sleep);
|
||||
}
|
||||
} while ($attempts < $max);
|
||||
}
|
||||
|
||||
Console::success("[Setup] - $dbName database init started...");
|
||||
|
||||
@@ -308,7 +309,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
|
||||
'orders' => $index['orders'],
|
||||
]), $files['indexes']);
|
||||
|
||||
$dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
|
||||
$dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes);
|
||||
}
|
||||
|
||||
if (Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) {
|
||||
@@ -356,7 +357,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
|
||||
'orders' => $index['orders'],
|
||||
]), $files['indexes']);
|
||||
|
||||
Authorization::skip(fn () => $dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes));
|
||||
Authorization::skip(fn () => $dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -368,11 +369,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
|
||||
$cache = $app->getResource('cache');
|
||||
|
||||
foreach ($sharedTablesV2 as $hostname) {
|
||||
$adapter = $pools
|
||||
->get($hostname)
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$adapter = new DatabasePool($pools->get($hostname));
|
||||
$dbForProject = (new Database($adapter, $cache))
|
||||
->setDatabase('appwrite')
|
||||
->setSharedTables(true)
|
||||
@@ -382,7 +379,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
|
||||
try {
|
||||
Console::success('[Setup] - Creating project database: ' . $hostname . '...');
|
||||
$dbForProject->create();
|
||||
} catch (Duplicate) {
|
||||
} catch (DuplicateException) {
|
||||
Console::success('[Setup] - Skip: metadata table already exists');
|
||||
}
|
||||
|
||||
@@ -408,7 +405,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
|
||||
}
|
||||
}
|
||||
|
||||
$pools->reclaim();
|
||||
Console::success('[Setup] - Server database init completed...');
|
||||
});
|
||||
|
||||
@@ -523,6 +519,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
Console::error('[Error] Line: ' . $th->getLine());
|
||||
Console::error('[Error] Trace: ' . $th->getTraceAsString());
|
||||
|
||||
$swooleResponse->setStatusCode(500);
|
||||
|
||||
@@ -540,8 +537,6 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
|
||||
];
|
||||
|
||||
$swooleResponse->end(\json_encode($output));
|
||||
} finally {
|
||||
$pools->reclaim();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
use Utopia\Config\Config;
|
||||
|
||||
require_once __DIR__ . '/../config/storage/resource_limits.php';
|
||||
|
||||
Config::load('template-runtimes', __DIR__ . '/../config/template-runtimes.php');
|
||||
Config::load('events', __DIR__ . '/../config/events.php');
|
||||
Config::load('auth', __DIR__ . '/../config/auth.php');
|
||||
|
||||
+69
-3
@@ -37,8 +37,8 @@ const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_RESOURCE_TOKEN_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_BUSTER = 4319;
|
||||
const APP_VERSION_STABLE = '1.7.0';
|
||||
const APP_CACHE_BUSTER = 4320;
|
||||
const APP_VERSION_STABLE = '1.7.4';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
@@ -51,6 +51,7 @@ const APP_DATABASE_TIMEOUT_MILLISECONDS_API = 15 * 1000; // 15 seconds
|
||||
const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes
|
||||
const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes
|
||||
const APP_DATABASE_QUERY_MAX_VALUES = 500;
|
||||
const APP_DATABASE_ENCRYPT_SIZE_MIN = 150;
|
||||
const APP_STORAGE_UPLOADS = '/storage/uploads';
|
||||
const APP_STORAGE_SITES = '/storage/sites';
|
||||
const APP_STORAGE_FUNCTIONS = '/storage/functions';
|
||||
@@ -80,6 +81,72 @@ const APP_PLATFORM_SERVER = 'server';
|
||||
const APP_PLATFORM_CLIENT = 'client';
|
||||
const APP_PLATFORM_CONSOLE = 'console';
|
||||
|
||||
// User Roles
|
||||
const USER_ROLE_ANY = 'any';
|
||||
const USER_ROLE_GUESTS = 'guests';
|
||||
const USER_ROLE_USERS = 'users';
|
||||
const USER_ROLE_ADMIN = 'admin';
|
||||
const USER_ROLE_DEVELOPER = 'developer';
|
||||
const USER_ROLE_OWNER = 'owner';
|
||||
const USER_ROLE_APPS = 'apps';
|
||||
const USER_ROLE_SYSTEM = 'system';
|
||||
|
||||
/**
|
||||
* Token Expiration times.
|
||||
*/
|
||||
const TOKEN_EXPIRATION_LOGIN_LONG = 31536000; /* 1 year */
|
||||
const TOKEN_EXPIRATION_LOGIN_SHORT = 3600; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_CONFIRM = 3600 * 1; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_OTP = 60 * 15; /* 15 minutes */
|
||||
const TOKEN_EXPIRATION_GENERIC = 60 * 15; /* 15 minutes */
|
||||
|
||||
/**
|
||||
* Token Lengths.
|
||||
*/
|
||||
const TOKEN_LENGTH_MAGIC_URL = 64;
|
||||
const TOKEN_LENGTH_VERIFICATION = 256;
|
||||
const TOKEN_LENGTH_RECOVERY = 256;
|
||||
const TOKEN_LENGTH_OAUTH2 = 64;
|
||||
const TOKEN_LENGTH_SESSION = 256;
|
||||
|
||||
/**
|
||||
* Token Types.
|
||||
*/
|
||||
const TOKEN_TYPE_LOGIN = 1; // Deprecated
|
||||
const TOKEN_TYPE_VERIFICATION = 2;
|
||||
const TOKEN_TYPE_RECOVERY = 3;
|
||||
const TOKEN_TYPE_INVITE = 4;
|
||||
const TOKEN_TYPE_MAGIC_URL = 5;
|
||||
const TOKEN_TYPE_PHONE = 6;
|
||||
const TOKEN_TYPE_OAUTH2 = 7;
|
||||
const TOKEN_TYPE_GENERIC = 8;
|
||||
const TOKEN_TYPE_EMAIL = 9; // OTP
|
||||
|
||||
/**
|
||||
* Session Providers.
|
||||
*/
|
||||
const SESSION_PROVIDER_EMAIL = 'email';
|
||||
const SESSION_PROVIDER_ANONYMOUS = 'anonymous';
|
||||
const SESSION_PROVIDER_MAGIC_URL = 'magic-url';
|
||||
const SESSION_PROVIDER_PHONE = 'phone';
|
||||
const SESSION_PROVIDER_OAUTH2 = 'oauth2';
|
||||
const SESSION_PROVIDER_TOKEN = 'token';
|
||||
const SESSION_PROVIDER_SERVER = 'server';
|
||||
|
||||
/**
|
||||
* Activity associated with user or the app.
|
||||
*/
|
||||
const ACTIVITY_TYPE_APP = 'app';
|
||||
const ACTIVITY_TYPE_USER = 'user';
|
||||
const ACTIVITY_TYPE_GUEST = 'guest';
|
||||
|
||||
/**
|
||||
* MFA
|
||||
*/
|
||||
const MFA_RECENT_DURATION = 1800; // 30 mins
|
||||
|
||||
|
||||
// Database Reconnect
|
||||
const DATABASE_RECONNECT_SLEEP = 2;
|
||||
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
|
||||
@@ -254,7 +321,6 @@ const METRIC_SITES_ID_INBOUND = 'sites.{siteInternalId}.inbound';
|
||||
const METRIC_SITES_ID_OUTBOUND = 'sites.{siteInternalId}.outbound';
|
||||
|
||||
// Resource types
|
||||
|
||||
const RESOURCE_TYPE_PROJECTS = 'projects';
|
||||
const RESOURCE_TYPE_FUNCTIONS = 'functions';
|
||||
const RESOURCE_TYPE_SITES = 'sites';
|
||||
|
||||
@@ -71,18 +71,27 @@ Database::addFilter(
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
$attributes = $database->find('attributes', [
|
||||
Query::equal('collectionInternalId', [$document->getInternalId()]),
|
||||
Query::equal('collectionInternalId', [$document->getSequence()]),
|
||||
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
|
||||
Query::limit($database->getLimitForAttributes()),
|
||||
]);
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) {
|
||||
$options = $attribute->getAttribute('options');
|
||||
foreach ($options as $key => $value) {
|
||||
$attribute->setAttribute($key, $value);
|
||||
}
|
||||
$attribute->removeAttribute('options');
|
||||
$attributeType = $attribute->getAttribute('type');
|
||||
|
||||
switch ($attributeType) {
|
||||
case Database::VAR_RELATIONSHIP:
|
||||
$options = $attribute->getAttribute('options');
|
||||
foreach ($options as $key => $value) {
|
||||
$attribute->setAttribute($key, $value);
|
||||
}
|
||||
$attribute->removeAttribute('options');
|
||||
break;
|
||||
|
||||
case Database::VAR_STRING:
|
||||
$filters = $attribute->getAttribute('filters', []);
|
||||
$attribute->setAttribute('encrypt', in_array('encrypt', $filters));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +107,7 @@ Database::addFilter(
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('indexes', [
|
||||
Query::equal('collectionInternalId', [$document->getInternalId()]),
|
||||
Query::equal('collectionInternalId', [$document->getSequence()]),
|
||||
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
|
||||
Query::limit($database->getLimitForIndexes()),
|
||||
]);
|
||||
@@ -113,7 +122,7 @@ Database::addFilter(
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('platforms', [
|
||||
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
@@ -127,7 +136,7 @@ Database::addFilter(
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('keys', [
|
||||
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
@@ -141,7 +150,7 @@ Database::addFilter(
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('devKeys', [
|
||||
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
@@ -155,7 +164,7 @@ Database::addFilter(
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('webhooks', [
|
||||
Query::equal('projectInternalId', [$document->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
}
|
||||
@@ -168,7 +177,7 @@ Database::addFilter(
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database->find('sessions', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::equal('userInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
@@ -182,7 +191,7 @@ Database::addFilter(
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database
|
||||
->find('tokens', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::equal('userInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
@@ -196,7 +205,7 @@ Database::addFilter(
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database
|
||||
->find('challenges', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::equal('userInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
@@ -210,7 +219,7 @@ Database::addFilter(
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database
|
||||
->find('authenticators', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::equal('userInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
@@ -224,7 +233,7 @@ Database::addFilter(
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database
|
||||
->find('memberships', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::equal('userInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]));
|
||||
}
|
||||
@@ -238,7 +247,7 @@ Database::addFilter(
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('variables', [
|
||||
Query::equal('resourceInternalId', [$document->getInternalId()]),
|
||||
Query::equal('resourceInternalId', [$document->getSequence()]),
|
||||
Query::equal('resourceType', ['function', 'site']),
|
||||
Query::limit(APP_LIMIT_SUBQUERY),
|
||||
]);
|
||||
@@ -316,7 +325,7 @@ Database::addFilter(
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database
|
||||
->find('targets', [
|
||||
Query::equal('userInternalId', [$document->getInternalId()]),
|
||||
Query::equal('userInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBQUERY)
|
||||
]));
|
||||
}
|
||||
@@ -331,13 +340,13 @@ Database::addFilter(
|
||||
$targetIds = Authorization::skip(fn () => \array_map(
|
||||
fn ($document) => $document->getAttribute('targetInternalId'),
|
||||
$database->find('subscribers', [
|
||||
Query::equal('topicInternalId', [$document->getInternalId()]),
|
||||
Query::equal('topicInternalId', [$document->getSequence()]),
|
||||
Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY)
|
||||
])
|
||||
));
|
||||
if (\count($targetIds) > 0) {
|
||||
return $database->skipValidation(fn () => $database->find('targets', [
|
||||
Query::equal('$internalId', $targetIds)
|
||||
Query::equal('$sequence', $targetIds)
|
||||
]));
|
||||
}
|
||||
return [];
|
||||
|
||||
@@ -216,13 +216,13 @@ $register->set('pools', function () {
|
||||
'mysql',
|
||||
'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
|
||||
return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
|
||||
return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array(
|
||||
return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, [
|
||||
\PDO::ATTR_TIMEOUT => 3, // Seconds
|
||||
\PDO::ATTR_PERSISTENT => false,
|
||||
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
|
||||
\PDO::ATTR_EMULATE_PREPARES => true,
|
||||
\PDO::ATTR_STRINGIFY_FETCHES => true
|
||||
));
|
||||
]);
|
||||
});
|
||||
},
|
||||
'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) {
|
||||
|
||||
+130
-91
@@ -24,10 +24,18 @@ use Appwrite\Utopia\Request;
|
||||
use Executor\Executor;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\App;
|
||||
use Utopia\Auth\Hashes\Argon2;
|
||||
use Utopia\Auth\Hashes\Sha;
|
||||
use Utopia\Auth\Proofs\Code;
|
||||
use Utopia\Auth\Proofs\Password;
|
||||
use Utopia\Auth\Proofs\Token;
|
||||
use Utopia\Auth\Store;
|
||||
use Utopia\Cache\Adapter\Pool as CachePool;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\Pool as DatabasePool;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime as DatabaseDateTime;
|
||||
use Utopia\Database\Document;
|
||||
@@ -38,6 +46,7 @@ use Utopia\DSN\DSN;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Queue\Broker\Pool as BrokerPool;
|
||||
use Utopia\Queue\Publisher;
|
||||
use Utopia\Storage\Device;
|
||||
use Utopia\Storage\Device\AWS;
|
||||
@@ -74,10 +83,10 @@ App::setResource('localeCodes', function () {
|
||||
|
||||
// Queues
|
||||
App::setResource('publisher', function (Group $pools) {
|
||||
return $pools->get('publisher')->pop()->getResource();
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
App::setResource('consumer', function (Group $pools) {
|
||||
return $pools->get('consumer')->pop()->getResource();
|
||||
return new BrokerPool(consumer: $pools->get('consumer'));
|
||||
}, ['pools']);
|
||||
App::setResource('queueForMessaging', function (Publisher $publisher) {
|
||||
return new Messaging($publisher);
|
||||
@@ -170,72 +179,89 @@ App::setResource('clients', function ($request, $console, $project) {
|
||||
return \array_unique($clients);
|
||||
}, ['request', 'console', 'project']);
|
||||
|
||||
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) {
|
||||
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform, Store $store, Token $proofForToken) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Database\Database $dbForPlatform */
|
||||
/** @var string $mode */
|
||||
/** @var Utopia\Auth\Store $store */
|
||||
|
||||
/**
|
||||
* Handles user authentication and session validation.
|
||||
*
|
||||
* This function follows a series of steps to determine the appropriate user session
|
||||
* based on cookies, headers, and JWT tokens.
|
||||
*
|
||||
* Process:
|
||||
* 1. Checks the cookie based on mode:
|
||||
* - If in admin mode, redirects to the console.
|
||||
* - Otherwise, retrieves the project ID from the cookie.
|
||||
* 2. If no cookie is found, attempts to retrieve the fallback header `x-fallback-cookies`.
|
||||
* - If this method is used, returns the header: `X-Debug-Fallback: true`.
|
||||
* 3. Fetches the user document from the appropriate database based on the mode.
|
||||
* 4. If the user document is empty or the session key cannot be verified, sets an empty user document.
|
||||
* 5. Regardless of the results from steps 1-4, attempts to fetch the JWT token.
|
||||
* 6. If the JWT user has a valid session ID, updates the user variable with the user from `projectDB`,
|
||||
* overwriting the previous value.
|
||||
*/
|
||||
|
||||
Authorization::setDefaultStatus(true);
|
||||
|
||||
Auth::setCookieName('a_session_' . $project->getId());
|
||||
$store->setKey('a_session_' . $project->getId());
|
||||
|
||||
if (APP_MODE_ADMIN === $mode) {
|
||||
Auth::setCookieName('a_session_' . $console->getId());
|
||||
$store->setKey('a_session_' . $console->getId());
|
||||
}
|
||||
|
||||
$session = Auth::decodeSession(
|
||||
$store->decode(
|
||||
$request->getCookie(
|
||||
Auth::$cookieName, // Get sessions
|
||||
$request->getCookie(Auth::$cookieName . '_legacy', '')
|
||||
$store->getKey(), // Get sessions
|
||||
$request->getCookie($store->getKey() . '_legacy', '')
|
||||
)
|
||||
);
|
||||
|
||||
// Get session from header for SSR clients
|
||||
if (empty($session['id']) && empty($session['secret'])) {
|
||||
if (empty($store->getProperty('id', '')) && empty($store->getProperty('secret', ''))) {
|
||||
$sessionHeader = $request->getHeader('x-appwrite-session', '');
|
||||
|
||||
if (!empty($sessionHeader)) {
|
||||
$session = Auth::decodeSession($sessionHeader);
|
||||
$store->decode($sessionHeader);
|
||||
}
|
||||
}
|
||||
|
||||
// Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies
|
||||
if ($response) {
|
||||
if ($response) { // if in http context - add debug header
|
||||
$response->addHeader('X-Debug-Fallback', 'false');
|
||||
}
|
||||
|
||||
if (empty($session['id']) && empty($session['secret'])) {
|
||||
if (empty($store->getProperty('id', '')) && empty($store->getProperty('secret', ''))) {
|
||||
if ($response) {
|
||||
$response->addHeader('X-Debug-Fallback', 'true');
|
||||
}
|
||||
$fallback = $request->getHeader('x-fallback-cookies', '');
|
||||
$fallback = \json_decode($fallback, true);
|
||||
$session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : ''));
|
||||
$store->decode(((isset($fallback[$store->getKey()])) ? $fallback[$store->getKey()] : ''));
|
||||
}
|
||||
|
||||
Auth::$unique = $session['id'] ?? '';
|
||||
Auth::$secret = $session['secret'] ?? '';
|
||||
|
||||
if (APP_MODE_ADMIN !== $mode) {
|
||||
if ($project->isEmpty()) {
|
||||
$user = new Document([]);
|
||||
} else {
|
||||
if ($project->getId() === 'console') {
|
||||
$user = $dbForPlatform->getDocument('users', Auth::$unique);
|
||||
$user = $dbForPlatform->getDocument('users', $store->getProperty('id', ''));
|
||||
} else {
|
||||
$user = $dbForProject->getDocument('users', Auth::$unique);
|
||||
$user = $dbForProject->getDocument('users', $store->getProperty('id', ''));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$user = $dbForPlatform->getDocument('users', Auth::$unique);
|
||||
$user = $dbForPlatform->getDocument('users', $store->getProperty('id', ''));
|
||||
}
|
||||
|
||||
if (
|
||||
$user->isEmpty() // Check a document has been found in the DB
|
||||
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)
|
||||
|| !Auth::sessionVerify($user->getAttribute('sessions', []), $store->getProperty('secret', ''), $proofForToken)
|
||||
) { // Validate user has valid login token
|
||||
$user = new Document([]);
|
||||
}
|
||||
@@ -276,7 +302,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
|
||||
$dbForPlatform->setMetadata('user', $user->getId());
|
||||
|
||||
return $user;
|
||||
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform']);
|
||||
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform', 'store', 'proofForToken']);
|
||||
|
||||
App::setResource('project', function ($dbForPlatform, $request, $console) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
@@ -294,13 +320,13 @@ App::setResource('project', function ($dbForPlatform, $request, $console) {
|
||||
return $project;
|
||||
}, ['dbForPlatform', 'request', 'console']);
|
||||
|
||||
App::setResource('session', function (Document $user) {
|
||||
App::setResource('session', function (Document $user, Store $store, Token $proofForToken) {
|
||||
if ($user->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
$sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret);
|
||||
$sessionId = Auth::sessionVerify($user->getAttribute('sessions'), $store->getProperty('secret', ''), $proofForToken);
|
||||
|
||||
if (!$sessionId) {
|
||||
return;
|
||||
@@ -313,7 +339,7 @@ App::setResource('session', function (Document $user) {
|
||||
}
|
||||
|
||||
return;
|
||||
}, ['user']);
|
||||
}, ['user', 'store', 'proofForToken']);
|
||||
|
||||
App::setResource('console', function () {
|
||||
return new Document(Config::getParam('console'));
|
||||
@@ -331,12 +357,8 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
|
||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
||||
}
|
||||
|
||||
$dbAdapter = $pools
|
||||
->get($dsn->getHost())
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database($dbAdapter, $cache);
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$database = new Database($adapter, $cache);
|
||||
|
||||
$database
|
||||
->setMetadata('host', \gethostname())
|
||||
@@ -349,25 +371,21 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getInternalId())
|
||||
->setTenant((int)$project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
return $database;
|
||||
}, ['pools', 'dbForPlatform', 'cache', 'project']);
|
||||
|
||||
App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
|
||||
$dbAdapter = $pools
|
||||
->get('console')
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database($dbAdapter, $cache);
|
||||
$adapter = new DatabasePool($pools->get('console'));
|
||||
$database = new Database($adapter, $cache);
|
||||
|
||||
$database
|
||||
->setNamespace('_console')
|
||||
@@ -380,7 +398,7 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
|
||||
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
|
||||
$databases = [];
|
||||
|
||||
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
@@ -406,13 +424,13 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getInternalId())
|
||||
->setTenant((int)$project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -422,12 +440,8 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
|
||||
return $database;
|
||||
}
|
||||
|
||||
$dbAdapter = $pools
|
||||
->get($dsn->getHost())
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database($dbAdapter, $cache);
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$database = new Database($adapter, $cache);
|
||||
$databases[$dsn->getHost()] = $database;
|
||||
$configure($database);
|
||||
|
||||
@@ -437,21 +451,15 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
|
||||
|
||||
App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
$database = null;
|
||||
return function (?Document $project = null) use ($pools, $cache, $database) {
|
||||
|
||||
return function (?Document $project = null) use ($pools, $cache, &$database) {
|
||||
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getInternalId());
|
||||
$database->setTenant((int)$project->getSequence());
|
||||
return $database;
|
||||
}
|
||||
|
||||
$dbAdapter = $pools
|
||||
->get('logs')
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database(
|
||||
$dbAdapter,
|
||||
$cache
|
||||
);
|
||||
$adapter = new DatabasePool($pools->get('logs'));
|
||||
$database = new Database($adapter, $cache);
|
||||
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
@@ -461,7 +469,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
|
||||
// set tenant
|
||||
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getInternalId());
|
||||
$database->setTenant((int)$project->getSequence());
|
||||
}
|
||||
|
||||
return $database;
|
||||
@@ -470,19 +478,18 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
|
||||
App::setResource('telemetry', fn () => new NoTelemetry());
|
||||
|
||||
App::setResource('cache', function (Group $pools) {
|
||||
App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
|
||||
$list = Config::getParam('pools-cache', []);
|
||||
$adapters = [];
|
||||
|
||||
foreach ($list as $value) {
|
||||
$adapters[] = $pools
|
||||
->get($value)
|
||||
->pop()
|
||||
->getResource();
|
||||
$adapters[] = new CachePool($pools->get($value));
|
||||
}
|
||||
|
||||
return new Cache(new Sharding($adapters));
|
||||
}, ['pools']);
|
||||
$cache = new Cache(new Sharding($adapters));
|
||||
$cache->setTelemetry($telemetry);
|
||||
return $cache;
|
||||
}, ['pools', 'telemetry']);
|
||||
|
||||
App::setResource('redis', function () {
|
||||
$host = System::getEnv('_APP_REDIS_HOST', 'localhost');
|
||||
@@ -506,24 +513,23 @@ App::setResource('timelimit', function (\Redis $redis) {
|
||||
}, ['redis']);
|
||||
|
||||
App::setResource('deviceForLocal', function (Telemetry $telemetry) {
|
||||
return new Local();
|
||||
return new Device\Telemetry($telemetry, new Local());
|
||||
}, ['telemetry']);
|
||||
|
||||
App::setResource('deviceForFiles', function ($project) {
|
||||
return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
App::setResource('deviceForSites', function ($project) {
|
||||
return getDevice(APP_STORAGE_SITES . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
App::setResource('deviceForImports', function ($project) {
|
||||
return getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
App::setResource('deviceForFunctions', function ($project) {
|
||||
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
App::setResource('deviceForBuilds', function ($project) {
|
||||
return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
App::setResource('deviceForFiles', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
App::setResource('deviceForSites', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_SITES . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
App::setResource('deviceForImports', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
App::setResource('deviceForFunctions', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
App::setResource('deviceForBuilds', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
function getDevice(string $root, string $connection = ''): Device
|
||||
{
|
||||
@@ -552,7 +558,8 @@ function getDevice(string $root, string $connection = ''): Device
|
||||
switch ($device) {
|
||||
case Storage::DEVICE_S3:
|
||||
if (!empty($url)) {
|
||||
return new S3($root, $accessKey, $accessSecret, $url, $region, $acl);
|
||||
$bucketRoot = (!empty($bucket) ? $bucket . '/' : '') . \ltrim($root, '/');
|
||||
return new S3($bucketRoot, $accessKey, $accessSecret, $url, $region, $acl);
|
||||
} else {
|
||||
return new AWS($root, $accessKey, $accessSecret, $bucket, $region, $acl);
|
||||
}
|
||||
@@ -584,7 +591,8 @@ function getDevice(string $root, string $connection = ''): Device
|
||||
$s3Acl = 'private';
|
||||
$s3EndpointUrl = System::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
|
||||
if (!empty($s3EndpointUrl)) {
|
||||
return new S3($root, $s3AccessKey, $s3SecretKey, $s3EndpointUrl, $s3Region, $s3Acl);
|
||||
$bucketRoot = (!empty($s3Bucket) ? $s3Bucket . '/' : '') . \ltrim($root, '/');
|
||||
return new S3($bucketRoot, $s3AccessKey, $s3SecretKey, $s3EndpointUrl, $s3Region, $s3Acl);
|
||||
} else {
|
||||
return new AWS($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
|
||||
}
|
||||
@@ -854,7 +862,7 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
|
||||
|
||||
$team = Authorization::skip(function () use ($dbForPlatform, $teamInternalId) {
|
||||
return $dbForPlatform->findOne('teams', [
|
||||
Query::equal('$internalId', [$teamInternalId]),
|
||||
Query::equal('$sequence', [$teamInternalId]),
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -895,7 +903,38 @@ App::setResource('apiKey', function (Request $request, Document $project): ?Key
|
||||
return Key::decode($project, $key);
|
||||
}, ['request', 'project']);
|
||||
|
||||
App::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
|
||||
|
||||
App::setResource('store', function (): Store {
|
||||
return new Store();
|
||||
});
|
||||
|
||||
App::setResource('proofForPassword', function (): Password {
|
||||
$hash = new Argon2();
|
||||
$hash
|
||||
->setMemoryCost(2048)
|
||||
->setTimeCost(4)
|
||||
->setThreads(3);
|
||||
|
||||
$password = new Password();
|
||||
$password
|
||||
->setHash($hash);
|
||||
|
||||
return $password;
|
||||
});
|
||||
|
||||
App::setResource('proofForToken', function (): Token {
|
||||
$token = new Token();
|
||||
$token->setHash(new Sha());
|
||||
return $token;
|
||||
});
|
||||
|
||||
App::setResource('proofForCode', function (): Code {
|
||||
$code = new Code();
|
||||
$code->setHash(new Sha());
|
||||
return $code;
|
||||
});
|
||||
|
||||
App::setResource('executor', fn () => new Executor());
|
||||
|
||||
App::setResource('resourceToken', function ($project, $dbForProject, $request) {
|
||||
$tokenJWT = $request->getParam('token');
|
||||
@@ -933,10 +972,10 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
|
||||
|
||||
return match ($token->getAttribute('resourceType')) {
|
||||
TOKENS_RESOURCE_TYPE_FILES => (function () use ($token, $dbForProject) {
|
||||
$internalIds = explode(':', $token->getAttribute('resourceInternalId'));
|
||||
$sequences = explode(':', $token->getAttribute('resourceInternalId'));
|
||||
$ids = explode(':', $token->getAttribute('resourceId'));
|
||||
|
||||
if (count($internalIds) !== 2 || count($ids) !== 2) {
|
||||
if (count($sequences) !== 2 || count($ids) !== 2) {
|
||||
return new Document([]);
|
||||
}
|
||||
|
||||
@@ -949,8 +988,8 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
|
||||
return new Document([
|
||||
'bucketId' => $ids[0],
|
||||
'fileId' => $ids[1],
|
||||
'bucketInternalId' => $internalIds[0],
|
||||
'fileInternalId' => $internalIds[1],
|
||||
'bucketInternalId' => $sequences[0],
|
||||
'fileInternalId' => $sequences[1],
|
||||
]);
|
||||
})(),
|
||||
|
||||
|
||||
+84
-52
@@ -5,6 +5,7 @@ use Appwrite\Extend\Exception;
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\PubSub\Adapter\Pool as PubSubPool;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Swoole\Http\Request as SwooleRequest;
|
||||
@@ -15,10 +16,15 @@ use Swoole\Timer;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\App;
|
||||
use Utopia\Auth\Hashes\Sha;
|
||||
use Utopia\Auth\Proofs\Token;
|
||||
use Utopia\Auth\Store;
|
||||
use Utopia\Cache\Adapter\Pool as CachePool;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\Pool as DatabasePool;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
@@ -28,13 +34,15 @@ use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter\None as NoTelemetry;
|
||||
use Utopia\WebSocket\Adapter;
|
||||
use Utopia\WebSocket\Server;
|
||||
|
||||
/**
|
||||
* @var \Utopia\Registry\Registry $register
|
||||
* @var Registry $register
|
||||
*/
|
||||
require_once __DIR__ . '/init.php';
|
||||
|
||||
@@ -46,17 +54,17 @@ if (!function_exists('getConsoleDB')) {
|
||||
{
|
||||
global $register;
|
||||
|
||||
/** @var \Utopia\Pools\Group $pools */
|
||||
static $database = null;
|
||||
|
||||
if ($database !== null) {
|
||||
return $database;
|
||||
}
|
||||
|
||||
/** @var Group $pools */
|
||||
$pools = $register->get('pools');
|
||||
|
||||
$dbAdapter = $pools
|
||||
->get('console')
|
||||
->pop()
|
||||
->getResource()
|
||||
;
|
||||
|
||||
$database = new Database($dbAdapter, getCache());
|
||||
|
||||
$adapter = new DatabasePool($pools->get('console'));
|
||||
$database = new Database($adapter, getCache());
|
||||
$database
|
||||
->setNamespace('_console')
|
||||
->setMetadata('host', \gethostname())
|
||||
@@ -72,7 +80,13 @@ if (!function_exists('getProjectDB')) {
|
||||
{
|
||||
global $register;
|
||||
|
||||
/** @var \Utopia\Pools\Group $pools */
|
||||
static $databases = [];
|
||||
|
||||
if (isset($databases[$project->getSequence()])) {
|
||||
return $databases[$project->getSequence()];
|
||||
}
|
||||
|
||||
/** @var Group $pools */
|
||||
$pools = $register->get('pools');
|
||||
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
@@ -86,11 +100,7 @@ if (!function_exists('getProjectDB')) {
|
||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
||||
}
|
||||
|
||||
$adapter = $pools
|
||||
->get($dsn->getHost())
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$database = new Database($adapter, getCache());
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
@@ -98,20 +108,20 @@ if (!function_exists('getProjectDB')) {
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getInternalId())
|
||||
->setTenant((int)$project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
$database
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', $project->getId());
|
||||
|
||||
return $database;
|
||||
return $databases[$project->getSequence()] = $database;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,20 +131,22 @@ if (!function_exists('getCache')) {
|
||||
{
|
||||
global $register;
|
||||
|
||||
$pools = $register->get('pools'); /** @var \Utopia\Pools\Group $pools */
|
||||
static $cache = null;
|
||||
|
||||
if ($cache !== null) {
|
||||
return $cache;
|
||||
}
|
||||
|
||||
$pools = $register->get('pools'); /** @var Group $pools */
|
||||
|
||||
$list = Config::getParam('pools-cache', []);
|
||||
$adapters = [];
|
||||
|
||||
foreach ($list as $value) {
|
||||
$adapters[] = $pools
|
||||
->get($value)
|
||||
->pop()
|
||||
->getResource()
|
||||
;
|
||||
$adapters[] = new CachePool($pools->get($value));
|
||||
}
|
||||
|
||||
return new Cache(new Sharding($adapters));
|
||||
return $cache = new Cache(new Sharding($adapters));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +154,12 @@ if (!function_exists('getCache')) {
|
||||
if (!function_exists('getRedis')) {
|
||||
function getRedis(): \Redis
|
||||
{
|
||||
static $redis = null;
|
||||
|
||||
if ($redis !== null) {
|
||||
return $redis;
|
||||
}
|
||||
|
||||
$host = System::getEnv('_APP_REDIS_HOST', 'localhost');
|
||||
$port = System::getEnv('_APP_REDIS_PORT', 6379);
|
||||
$pass = System::getEnv('_APP_REDIS_PASS', '');
|
||||
@@ -160,21 +178,39 @@ if (!function_exists('getRedis')) {
|
||||
if (!function_exists('getTimelimit')) {
|
||||
function getTimelimit(): TimeLimitRedis
|
||||
{
|
||||
return new TimeLimitRedis("", 0, 1, getRedis());
|
||||
static $timelimit = null;
|
||||
|
||||
if ($timelimit !== null) {
|
||||
return $timelimit;
|
||||
}
|
||||
|
||||
return $timelimit = new TimeLimitRedis("", 0, 1, getRedis());
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('getRealtime')) {
|
||||
function getRealtime(): Realtime
|
||||
{
|
||||
return new Realtime();
|
||||
static $realtime = null;
|
||||
|
||||
if ($realtime !== null) {
|
||||
return $realtime;
|
||||
}
|
||||
|
||||
return $realtime = new Realtime();
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('getTelemetry')) {
|
||||
function getTelemetry(int $workerId): Utopia\Telemetry\Adapter
|
||||
{
|
||||
return new NoTelemetry();
|
||||
static $telemetry = null;
|
||||
|
||||
if ($telemetry !== null) {
|
||||
return $telemetry;
|
||||
}
|
||||
|
||||
return $telemetry = new NoTelemetry();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +309,6 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
||||
sleep(DATABASE_RECONNECT_SLEEP);
|
||||
}
|
||||
} while (true);
|
||||
$register->get('pools')->reclaim();
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -299,9 +334,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
||||
|
||||
Authorization::skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
|
||||
} catch (Throwable $th) {
|
||||
call_user_func($logError, $th, "updateWorkerDocument");
|
||||
} finally {
|
||||
$register->get('pools')->reclaim();
|
||||
$logError($th, "updateWorkerDocument");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -370,8 +403,6 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||
'data' => $event['data']
|
||||
]));
|
||||
}
|
||||
|
||||
$register->get('pools')->reclaim();
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -407,8 +438,8 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||
}
|
||||
$start = time();
|
||||
|
||||
/** @var \Appwrite\PubSub\Adapter $pubsub */
|
||||
$pubsub = $register->get('pools')->get('pubsub')->pop()->getResource();
|
||||
$pubsub = new PubSubPool($register->get('pools')->get('pubsub'));
|
||||
|
||||
if ($pubsub->ping(true)) {
|
||||
$attempts = 0;
|
||||
Console::success('Pub/sub connection established (worker: ' . $workerId . ')');
|
||||
@@ -436,8 +467,6 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||
|
||||
$realtime->unsubscribe($connection);
|
||||
$realtime->subscribe($projectId, $connection, $roles, $channels);
|
||||
|
||||
$register->get('pools')->reclaim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,14 +492,12 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||
}
|
||||
});
|
||||
} catch (Throwable $th) {
|
||||
call_user_func($logError, $th, "pubSubConnection");
|
||||
$logError($th, "pubSubConnection");
|
||||
|
||||
Console::error('Pub/sub error: ' . $th->getMessage());
|
||||
$attempts++;
|
||||
sleep(DATABASE_RECONNECT_SLEEP);
|
||||
continue;
|
||||
} finally {
|
||||
$register->get('pools')->reclaim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,7 +599,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
$stats->incr($project->getId(), 'connections');
|
||||
$stats->incr($project->getId(), 'connectionsTotal');
|
||||
} catch (Throwable $th) {
|
||||
call_user_func($logError, $th, "initServer");
|
||||
$logError($th, "initServer");
|
||||
|
||||
// Handle SQL error code is 'HY000'
|
||||
$code = $th->getCode();
|
||||
@@ -596,8 +623,6 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
Console::error('[Error] Code: ' . $response['data']['code']);
|
||||
Console::error('[Error] Message: ' . $response['data']['message']);
|
||||
}
|
||||
} finally {
|
||||
$register->get('pools')->reclaim();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -649,15 +674,24 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
||||
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Payload is not valid.');
|
||||
}
|
||||
|
||||
$session = Auth::decodeSession($message['data']['session']);
|
||||
Auth::$unique = $session['id'] ?? '';
|
||||
Auth::$secret = $session['secret'] ?? '';
|
||||
$store = new Store();
|
||||
|
||||
$user = $database->getDocument('users', Auth::$unique);
|
||||
$store->decode($message['data']['session']);
|
||||
|
||||
$user = $database->getDocument('users', $store->getProperty('id', ''));
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* Moving forward, we should try to use our dependency injection container
|
||||
* to inject the proof for token.
|
||||
* This way we will have one source of truth for the proof for token.
|
||||
*/
|
||||
$proofForToken = new Token();
|
||||
$proofForToken->setHash(new Sha());
|
||||
|
||||
if (
|
||||
empty($user->getId()) // Check a document has been found in the DB
|
||||
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret) // Validate user has valid login token
|
||||
|| !Auth::sessionVerify($user->getAttribute('sessions', []), $store->getProperty('secret', ''), $proofForToken) // Validate user has valid login token
|
||||
) {
|
||||
// cookie not valid
|
||||
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.');
|
||||
@@ -696,8 +730,6 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
||||
if ($th->getCode() === 1008) {
|
||||
$server->close($connection, $th->getCode());
|
||||
}
|
||||
} finally {
|
||||
$register->get('pools')->reclaim();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -151,37 +151,6 @@
|
||||
<div class="center"><a href="/"><button>Go to homepage</button></a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="brand">
|
||||
<p>Powered by</p>
|
||||
<svg class="logo-dark" width="110" height="20" viewBox="0 0 110 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M31.8649 16.2461C33.6492 16.2461 34.5511 15.3184 34.9433 14.6867H35.1197C35.1981 15.3578 35.6687 15.9895 36.5903 15.9895H38.3353V14.0156H37.8843C37.5706 14.0156 37.4138 13.838 37.4138 13.5617V5.64661H35.1001V6.90986H34.9236C34.4727 6.27823 33.5315 5.39001 31.8061 5.39001C29.0611 5.39001 27.022 7.67965 27.022 10.818C27.022 13.9564 29.1003 16.2461 31.8649 16.2461ZM32.2767 13.9959C30.6493 13.9959 29.3748 12.7919 29.3748 10.8378C29.3748 8.92316 30.6101 7.62044 32.2571 7.62044C33.8256 7.62044 35.1393 8.78499 35.1393 10.8378C35.1393 12.5945 34.0217 13.9959 32.2767 13.9959Z" fill="#EDEDF0" />
|
||||
<path d="M39.7013 20H42.0149V14.6867H42.1914C42.6227 15.3184 43.5443 16.2461 45.3677 16.2461C48.1127 16.2461 50.1127 13.9169 50.1127 10.818C50.1127 7.69939 47.9755 5.39001 45.2109 5.39001C43.4462 5.39001 42.5835 6.35719 42.1718 6.89012H41.9953V5.64661H39.7013V20ZM44.8776 14.0551C43.2894 14.0551 41.9757 12.8708 41.9757 10.818C41.9757 9.06133 43.0933 7.58096 44.8383 7.58096C46.4657 7.58096 47.7402 8.86395 47.7402 10.818C47.7402 12.7326 46.5049 14.0551 44.8776 14.0551Z" fill="#EDEDF0" />
|
||||
<path d="M51.3065 20H53.6202V14.6867H53.7966C54.228 15.3184 55.1495 16.2461 56.973 16.2461C59.718 16.2461 61.5273 13.9169 61.5273 10.818C61.5273 7.69939 59.5807 5.39001 56.8161 5.39001C55.0515 5.39001 54.1888 6.35719 53.777 6.89012H53.6005V5.64661H51.3065V20ZM56.4828 14.0551C54.8946 14.0551 53.5809 12.8708 53.5809 10.818C53.5809 9.06133 54.6985 7.58096 56.4436 7.58096C58.071 7.58096 59.3454 8.86395 59.3454 10.818C59.3454 12.7326 58.1102 14.0551 56.4828 14.0551Z" fill="#EDEDF0" />
|
||||
<path d="M64.5857 16.2296H67.8601L69.7227 8.11721H69.8404L71.7031 16.2296H74.9579L77.5642 5.88678H75.2323L73.3697 14.0189H73.1932L71.3305 5.88678H68.2522L66.3699 14.0189H66.1935L64.3504 5.88678H61.8799L64.5857 16.2296Z" fill="#EDEDF0" />
|
||||
<path d="M78.7363 16.2296H81.0499V11.1174C81.0499 9.16334 81.9519 7.9593 83.6381 7.9593H84.6576V5.63019H83.893C82.5793 5.63019 81.5793 6.53815 81.1872 7.40663H81.0303V5.88678H78.7363V16.2296Z" fill="#EDEDF0" />
|
||||
<path d="M96.1391 16.2296H97.943V14.1571H96.1587C95.4529 14.1571 95.1588 13.8413 95.1588 13.111V7.93956H98.0606V5.88678H95.1588V2.98526H92.9628V5.88678H91.0413V7.93956H92.8255V13.1307C92.8255 15.3217 94.1392 16.2296 96.1391 16.2296Z" fill="#EDEDF0" />
|
||||
<path d="M104.15 16.2461C106.287 16.2461 108.17 15.1802 108.836 13.0287L106.719 12.5155C106.346 13.6603 105.268 14.2525 104.13 14.2525C102.444 14.2525 101.327 13.1472 101.307 11.4102H109.091V10.7588C109.091 7.67965 107.189 5.39001 104.052 5.39001C101.287 5.39001 98.915 7.58096 98.915 10.8378C98.915 13.9959 101.013 16.2461 104.15 16.2461ZM101.327 9.71269C101.464 8.46918 102.581 7.42305 104.052 7.42305C105.464 7.42305 106.621 8.31128 106.738 9.71269H101.327Z" fill="#EDEDF0" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M90.0125 16.2296H87.6989V7.93956H85.895V5.88678H90.0125V16.2296Z" fill="#EDEDF0" />
|
||||
<path d="M88.6834 4.45145C89.5265 4.45145 90.154 3.81983 90.154 2.99082C90.154 2.18155 89.5265 1.54993 88.6834 1.54993C87.8403 1.54993 87.2129 2.18155 87.2129 2.99082C87.2129 3.81983 87.8403 4.45145 88.6834 4.45145Z" fill="#EDEDF0" />
|
||||
<path d="M20.2007 13.6935V18.258H8.88588C5.5894 18.258 2.71111 16.4222 1.17116 13.6935C0.947288 13.2968 0.751353 12.8806 0.586995 12.4486C0.26435 11.6021 0.0615332 10.6938 0 9.74603V8.51195C0.0133592 8.30074 0.03441 8.09119 0.0619381 7.88413C0.118209 7.45921 0.203222 7.04343 0.314953 6.63926C1.37195 2.80758 4.8089 0 8.88588 0C12.9629 0 16.3994 2.80758 17.4564 6.63926H12.6184C11.8241 5.39025 10.4493 4.5645 8.88588 4.5645C7.32245 4.5645 5.94767 5.39025 5.15341 6.63926C4.91132 7.01895 4.72349 7.43764 4.60042 7.88413C4.49112 8.27999 4.43282 8.69744 4.43282 9.12899C4.43282 10.4373 4.96962 11.6166 5.83027 12.4486C6.62778 13.2209 7.70299 13.6935 8.88588 13.6935H20.2007Z" fill="#FD366E" />
|
||||
<path d="M20.2006 7.88412V12.4486H11.9414C12.8021 11.6166 13.3389 10.4373 13.3389 9.12899C13.3389 8.69744 13.2806 8.27999 13.1713 7.88412H20.2006Z" fill="#FD366E" />
|
||||
</svg>
|
||||
<svg class="logo-light" width="110" height="20" viewBox="0 0 110 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M31.8648 16.2461C33.649 16.2461 34.5509 15.3184 34.9431 14.6867H35.1195C35.198 15.3578 35.6685 15.9895 36.5901 15.9895H38.3351V14.0156H37.8841C37.5704 14.0156 37.4136 13.838 37.4136 13.5617V5.64661H35.0999V6.90986H34.9235C34.4725 6.27823 33.5314 5.39001 31.8059 5.39001C29.0609 5.39001 27.0218 7.67965 27.0218 10.818C27.0218 13.9564 29.1001 16.2461 31.8648 16.2461ZM32.2765 13.9959C30.6491 13.9959 29.3746 12.7919 29.3746 10.8378C29.3746 8.92316 30.6099 7.62044 32.2569 7.62044C33.8255 7.62044 35.1391 8.78499 35.1391 10.8378C35.1391 12.5945 34.0215 13.9959 32.2765 13.9959Z" fill="#2D2D31" />
|
||||
<path d="M39.7011 20H42.0147V14.6867H42.1912C42.6226 15.3184 43.5441 16.2461 45.3676 16.2461C48.1126 16.2461 50.1125 13.9169 50.1125 10.818C50.1125 7.69939 47.9753 5.39001 45.2107 5.39001C43.4461 5.39001 42.5833 6.35719 42.1716 6.89012H41.9951V5.64661H39.7011V20ZM44.8774 14.0551C43.2892 14.0551 41.9755 12.8708 41.9755 10.818C41.9755 9.06133 43.0931 7.58096 44.8382 7.58096C46.4656 7.58096 47.74 8.86395 47.74 10.818C47.74 12.7326 46.5048 14.0551 44.8774 14.0551Z" fill="#2D2D31" />
|
||||
<path d="M51.3063 20H53.62V14.6867H53.7964C54.2278 15.3184 55.1493 16.2461 56.9728 16.2461C59.7178 16.2461 61.5271 13.9169 61.5271 10.818C61.5271 7.69939 59.5805 5.39001 56.8159 5.39001C55.0513 5.39001 54.1886 6.35719 53.7768 6.89012H53.6004V5.64661H51.3063V20ZM56.4826 14.0551C54.8944 14.0551 53.5808 12.8708 53.5808 10.818C53.5808 9.06133 54.6984 7.58096 56.4434 7.58096C58.0708 7.58096 59.3453 8.86395 59.3453 10.818C59.3453 12.7326 58.11 14.0551 56.4826 14.0551Z" fill="#2D2D31" />
|
||||
<path d="M64.5855 16.2296H67.8599L69.7226 8.11721H69.8402L71.7029 16.2296H74.9577L77.564 5.88678H75.2322L73.3695 14.0189H73.193L71.3303 5.88678H68.252L66.3697 14.0189H66.1933L64.3502 5.88678H61.8797L64.5855 16.2296Z" fill="#2D2D31" />
|
||||
<path d="M78.7361 16.2296H81.0498V11.1174C81.0498 9.16334 81.9517 7.9593 83.6379 7.9593H84.6575V5.63019H83.8928C82.5791 5.63019 81.5791 6.53815 81.187 7.40663H81.0301V5.88678H78.7361V16.2296Z" fill="#2D2D31" />
|
||||
<path d="M96.1389 16.2296H97.9428V14.1571H96.1585C95.4527 14.1571 95.1586 13.8413 95.1586 13.111V7.93956H98.0604V5.88678H95.1586V2.98526H92.9626V5.88678H91.0411V7.93956H92.8253V13.1307C92.8253 15.3217 94.139 16.2296 96.1389 16.2296Z" fill="#2D2D31" />
|
||||
<path d="M104.15 16.2461C106.287 16.2461 108.169 15.1802 108.836 13.0287L106.718 12.5155C106.346 13.6603 105.268 14.2525 104.13 14.2525C102.444 14.2525 101.326 13.1472 101.307 11.4102H109.091V10.7588C109.091 7.67965 107.189 5.39001 104.052 5.39001C101.287 5.39001 98.9148 7.58096 98.9148 10.8378C98.9148 13.9959 101.013 16.2461 104.15 16.2461ZM101.326 9.71269C101.464 8.46918 102.581 7.42305 104.052 7.42305C105.464 7.42305 106.62 8.31128 106.738 9.71269H101.326Z" fill="#2D2D31" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M90.0123 16.2296H87.6987V7.93956H85.8948V5.88678H90.0123V16.2296Z" fill="#2D2D31" />
|
||||
<path d="M88.6835 4.45145C89.5266 4.45145 90.154 3.81983 90.154 2.99082C90.154 2.18155 89.5266 1.54993 88.6835 1.54993C87.8404 1.54993 87.213 2.18155 87.213 2.99082C87.213 3.81983 87.8404 4.45145 88.6835 4.45145Z" fill="#2D2D31" />
|
||||
<path d="M20.2007 13.6935V18.258H8.88588C5.5894 18.258 2.71111 16.4222 1.17116 13.6935C0.947288 13.2968 0.751353 12.8806 0.586995 12.4486C0.26435 11.6021 0.0615332 10.6938 0 9.74603V8.51195C0.0133592 8.30074 0.03441 8.09119 0.0619381 7.88413C0.118209 7.45921 0.203222 7.04343 0.314953 6.63926C1.37195 2.80758 4.8089 0 8.88588 0C12.9629 0 16.3994 2.80758 17.4564 6.63926H12.6184C11.8241 5.39025 10.4493 4.5645 8.88588 4.5645C7.32245 4.5645 5.94767 5.39025 5.15341 6.63926C4.91132 7.01895 4.72349 7.43764 4.60042 7.88413C4.49112 8.27999 4.43282 8.69744 4.43282 9.12899C4.43282 10.4373 4.96962 11.6166 5.83027 12.4486C6.62778 13.2209 7.70299 13.6935 8.88588 13.6935H20.2007Z" fill="#FD366E" />
|
||||
<path d="M20.2007 7.88412V12.4486H11.9415C12.8022 11.6166 13.339 10.4373 13.339 9.12899C13.339 8.69744 13.2807 8.27999 13.1714 7.88412H20.2007Z" fill="#FD366E" />
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -479,67 +479,39 @@ switch ($type) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div x-show="page === 'trace'" class="error-trace">
|
||||
<button class="back-button" x-on:click="page = 'error'">
|
||||
Back
|
||||
</button>
|
||||
<div class="trace-grid-header">Error trace</div>
|
||||
<?php foreach ($trace as $index => $traceItem): ?>
|
||||
<div class="trace-grid">
|
||||
<?php if (isset($traceItem['file'])): ?>
|
||||
<div class="trace-label">file</div>
|
||||
<div class="trace-value"><?php echo $this->print($traceItem['file']); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($development) : ?>
|
||||
<div x-show="page === 'trace'" class="error-trace">
|
||||
<button class="back-button" x-on:click="page = 'error'">
|
||||
Back
|
||||
</button>
|
||||
<div class="trace-grid-header">Error trace</div>
|
||||
<?php foreach ($trace as $index => $traceItem): ?>
|
||||
<div class="trace-grid">
|
||||
<?php if (isset($traceItem['file'])): ?>
|
||||
<div class="trace-label">file</div>
|
||||
<div class="trace-value"><?php echo $this->print($traceItem['file']); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($traceItem['line'])): ?>
|
||||
<div class="trace-label">line</div>
|
||||
<div class="trace-value"><?php echo $this->print($traceItem['line']); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($traceItem['line'])): ?>
|
||||
<div class="trace-label">line</div>
|
||||
<div class="trace-value"><?php echo $this->print($traceItem['line']); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($traceItem['function'])): ?>
|
||||
<div class="trace-label">function</div>
|
||||
<div class="trace-value"><?php echo $this->print($traceItem['function']); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($traceItem['function'])): ?>
|
||||
<div class="trace-label">function</div>
|
||||
<div class="trace-value"><?php echo $this->print($traceItem['function']); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($traceItem['args'])): ?>
|
||||
<div class="trace-label">args</div>
|
||||
<div class="trace-args"><pre><?php echo $this->print(\var_export($traceItem['args'], true)); ?></pre></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php if (isset($traceItem['args'])): ?>
|
||||
<div class="trace-label">args</div>
|
||||
<div class="trace-args"><pre><?php echo $this->print(\var_export($traceItem['args'], true)); ?></pre></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div x-show="page === 'error'" class="brand">
|
||||
<p>Powered by</p>
|
||||
<svg class="logo-dark" width="110" height="20" viewBox="0 0 110 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M31.8649 16.2461C33.6492 16.2461 34.5511 15.3184 34.9433 14.6867H35.1197C35.1981 15.3578 35.6687 15.9895 36.5903 15.9895H38.3353V14.0156H37.8843C37.5706 14.0156 37.4138 13.838 37.4138 13.5617V5.64661H35.1001V6.90986H34.9236C34.4727 6.27823 33.5315 5.39001 31.8061 5.39001C29.0611 5.39001 27.022 7.67965 27.022 10.818C27.022 13.9564 29.1003 16.2461 31.8649 16.2461ZM32.2767 13.9959C30.6493 13.9959 29.3748 12.7919 29.3748 10.8378C29.3748 8.92316 30.6101 7.62044 32.2571 7.62044C33.8256 7.62044 35.1393 8.86395 35.1393 10.8378C35.1393 12.5945 34.0217 13.9959 32.2767 13.9959Z" fill="#EDEDF0" />
|
||||
<path d="M39.7013 20H42.0149V14.6867H42.1914C42.6227 15.3184 43.5443 16.2461 45.3677 16.2461C48.1127 16.2461 50.1127 13.9169 50.1127 10.818C50.1127 7.69939 47.9755 5.39001 45.2109 5.39001C43.4462 5.39001 42.5835 6.35719 42.1718 6.89012H41.9953V5.63019H39.7013V20ZM44.8776 14.0551C43.2894 14.0551 41.9757 12.8708 41.9757 10.818C41.9757 9.06133 43.0933 7.58096 44.8383 7.58096C46.4657 7.58096 47.7402 8.86395 47.7402 10.818C47.7402 12.7326 46.5049 14.0551 44.8776 14.0551Z" fill="#EDEDF0" />
|
||||
<path d="M51.3065 20H53.6202V14.6867H53.7966C54.228 15.3184 55.1495 16.2461 56.973 16.2461C59.718 16.2461 61.5273 13.9169 61.5273 10.818C61.5273 7.69939 59.5807 5.39001 56.8161 5.39001C55.0515 5.39001 54.1888 6.35719 53.777 6.89012H53.6005V5.64661H51.3065V20ZM56.4828 14.0551C54.8946 14.0551 53.5809 12.8708 53.5809 10.818C53.5809 9.06133 54.6985 7.58096 56.4436 7.58096C58.071 7.58096 59.3454 8.86395 59.3454 10.818C59.3454 12.7326 58.1102 14.0551 56.4828 14.0551Z" fill="#EDEDF0" />
|
||||
<path d="M64.5857 16.2296H67.8601L69.7227 8.11721H69.8404L71.7031 16.2296H74.9579L77.5642 5.88678H75.2323L73.3697 14.0189H73.1932L71.3305 5.88678H68.2522L66.3699 14.0189H66.1935L64.3504 5.88678H61.8799L64.5857 16.2296Z" fill="#EDEDF0" />
|
||||
<path d="M78.7363 16.2296H81.0499V11.1174C81.0499 9.16334 81.9519 7.9593 83.6381 7.9593H84.6576V5.63019H83.893C82.5793 5.63019 81.5793 6.53815 81.1872 7.40663H81.0303V5.88678H78.7363V16.2296Z" fill="#EDEDF0" />
|
||||
<path d="M96.1391 16.2296H97.943V14.1571H96.1587C95.4529 14.1571 95.1588 13.8413 95.1588 13.111V7.93956H98.0606V5.88678H95.1588V2.98526H92.9628V5.88678H91.0413V7.93956H92.8255V13.1307C92.8255 15.3217 94.1392 16.2296 96.1391 16.2296Z" fill="#EDEDF0" />
|
||||
<path d="M104.15 16.2461C106.287 16.2461 108.17 15.1802 108.836 13.0287L106.719 12.5155C106.346 13.6603 105.268 14.2525 104.13 14.2525C102.444 14.2525 101.327 13.1472 101.307 11.4102H109.091V10.7588C109.091 7.67965 107.189 5.39001 104.052 5.39001C101.287 5.39001 98.915 7.58096 98.915 10.8378C98.915 13.9959 101.013 16.2461 104.15 16.2461ZM101.327 9.71269C101.464 8.46918 102.581 7.42305 104.052 7.42305C105.464 7.42305 106.621 8.31128 106.738 9.71269H101.327Z" fill="#EDEDF0" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M90.0125 16.2296H87.6989V7.93956H85.895V5.88678H90.0125V16.2296Z" fill="#EDEDF0" />
|
||||
<path d="M88.6834 4.45145C89.5265 4.45145 90.154 3.81983 90.154 2.99082C90.154 2.18155 89.5265 1.54993 88.6834 1.54993C87.8403 1.54993 87.2129 2.18155 87.2129 2.99082C87.2129 3.81983 87.8403 4.45145 88.6834 4.45145Z" fill="#EDEDF0" />
|
||||
<path d="M20.2007 13.6935V18.258H8.88588C5.5894 18.258 2.71111 16.4222 1.17116 13.6935C0.947288 13.2968 0.751353 12.8806 0.586995 12.4486C0.26435 11.6021 0.0615332 10.6938 0 9.74603V8.51195C0.0133592 8.30074 0.03441 8.09119 0.0619381 7.88413C0.118209 7.45921 0.203222 7.04343 0.314953 6.63926C1.37195 2.80758 4.8089 0 8.88588 0C12.9629 0 16.3994 2.80758 17.4564 6.63926H12.6184C11.8241 5.39025 10.4493 4.5645 8.88588 4.5645C7.32245 4.5645 5.94767 5.39025 5.15341 6.63926C4.91132 7.01895 4.72349 7.43764 4.60042 7.88413C4.49112 8.27999 4.43282 8.69744 4.43282 9.12899C4.43282 10.4373 4.96962 11.6166 5.83027 12.4486C6.62778 13.2209 7.70299 13.6935 8.88588 13.6935H20.2007Z" fill="#FD366E" />
|
||||
<path d="M20.2006 7.88412V12.4486H11.9414C12.8021 11.6166 13.3389 10.4373 13.3389 9.12899C13.3389 8.69744 13.2806 8.27999 13.1713 7.88412H20.2006Z" fill="#FD366E" />
|
||||
</svg>
|
||||
<svg class="logo-light" width="110" height="20" viewBox="0 0 110 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M31.8648 16.2461C33.649 16.2461 34.5509 15.3184 34.9431 14.6867H35.1195C35.198 15.3578 35.6685 15.9895 36.5901 15.9895H38.3351V14.0156H37.8841C37.5704 14.0156 37.4136 13.838 37.4136 13.5617V5.64661H35.0999V6.90986H34.9235C34.4725 6.27823 33.5314 5.39001 31.8059 5.39001C29.0609 5.39001 27.0218 7.67965 27.0218 10.818C27.0218 13.9564 29.1001 16.2461 31.8648 16.2461ZM32.2765 13.9959C30.6491 13.9959 29.3746 12.7919 29.3746 10.8378C29.3746 8.92316 30.6099 7.62044 32.2569 7.62044C33.8255 7.62044 35.1391 8.78499 35.1391 10.8378C35.1391 12.5945 34.0215 13.9959 32.2765 13.9959Z" fill="#2D2D31" />
|
||||
<path d="M39.7011 20H42.0147V14.6867H42.1912C42.6226 15.3184 43.5441 16.2461 45.3676 16.2461C48.1126 16.2461 50.1125 13.9169 50.1125 10.818C50.1125 7.69939 47.9753 5.39001 45.2107 5.39001C43.4461 5.39001 42.5833 6.35719 42.1716 6.89012H41.9951V5.64661H39.7011V20ZM44.8774 14.0551C43.2892 14.0551 41.9755 12.8708 41.9755 10.818C41.9755 9.06133 43.0931 7.58096 44.8382 7.58096C46.4656 7.58096 47.74 8.86395 47.74 10.818C47.74 12.7326 46.5048 14.0551 44.8774 14.0551Z" fill="#2D2D31" />
|
||||
<path d="M51.3063 20H53.62V14.6867H53.7964C54.2278 15.3184 55.1493 16.2461 56.9728 16.2461C59.7178 16.2461 61.5271 13.9169 61.5271 10.818C61.5271 7.69939 59.5805 5.39001 56.8159 5.39001C55.0513 5.39001 54.1886 6.35719 53.7768 6.89012H53.6004V5.64661H51.3063V20ZM56.4826 14.0551C54.8944 14.0551 53.5808 12.8708 53.5808 10.818C53.5808 9.06133 54.6984 7.58096 56.4434 7.58096C58.0708 7.58096 59.3453 8.86395 59.3453 10.818C59.3453 12.7326 58.11 14.0551 56.4826 14.0551Z" fill="#2D2D31" />
|
||||
<path d="M64.5855 16.2296H67.8599L69.7226 8.11721H69.8402L71.7029 16.2296H74.9577L77.564 5.88678H75.2322L73.3695 14.0189H73.193L71.3303 5.88678H68.252L66.3697 14.0189H66.1933L64.3502 5.88678H61.8797L64.5855 16.2296Z" fill="#2D2D31" />
|
||||
<path d="M78.7361 16.2296H81.0498V11.1174C81.0498 9.16334 81.9517 7.9593 83.6379 7.9593H84.6575V5.63019H83.8928C82.5791 5.63019 81.5791 6.53815 81.187 7.40663H81.0301V5.88678H78.7361V16.2296Z" fill="#2D2D31" />
|
||||
<path d="M96.1389 16.2296H97.9428V14.1571H96.1585C95.4527 14.1571 95.1586 13.8413 95.1586 13.111V7.93956H98.0604V5.88678H95.1586V2.98526H92.9626V5.88678H91.0411V7.93956H92.8253V13.1307C92.8253 15.3217 94.139 16.2296 96.1389 16.2296Z" fill="#2D2D31" />
|
||||
<path d="M104.15 16.2461C106.287 16.2461 108.169 15.1802 108.836 13.0287L106.718 12.5155C106.346 13.6603 105.268 14.2525 104.13 14.2525C102.444 14.2525 101.326 13.1472 101.307 11.4102H109.091V10.7588C109.091 7.67965 107.189 5.39001 104.052 5.39001C101.287 5.39001 98.9148 7.58096 98.9148 10.8378C98.9148 13.9959 101.013 16.2461 104.15 16.2461ZM101.326 9.71269C101.464 8.46918 102.581 7.42305 104.052 7.42305C105.464 7.42305 106.62 8.31128 106.738 9.71269H101.326Z" fill="#2D2D31" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M90.0123 16.2296H87.6987V7.93956H85.8948V5.88678H90.0123V16.2296Z" fill="#2D2D31" />
|
||||
<path d="M88.6835 4.45145C89.5266 4.45145 90.154 3.81983 90.154 2.99082C90.154 2.18155 89.5266 1.54993 88.6835 1.54993C87.8404 1.54993 87.213 2.18155 87.213 2.99082C87.213 3.81983 87.8404 4.45145 88.6835 4.45145Z" fill="#2D2D31" />
|
||||
<path d="M20.2007 13.6935V18.258H8.88588C5.5894 18.258 2.71111 16.4222 1.17116 13.6935C0.947288 13.2968 0.751353 12.8806 0.586995 12.4486C0.26435 11.6021 0.0615332 10.6938 0 9.74603V8.51195C0.0133592 8.30074 0.03441 8.09119 0.0619381 7.88413C0.118209 7.45921 0.203222 7.04343 0.314953 6.63926C1.37195 2.80758 4.8089 0 8.88588 0C12.9629 0 16.3994 2.80758 17.4564 6.63926H12.6184C11.8241 5.39025 10.4493 4.5645 8.88588 4.5645C7.32245 4.5645 5.94767 5.39025 5.15341 6.63926C4.91132 7.01895 4.72349 7.43764 4.60042 7.88413C4.49112 8.27999 4.43282 8.69744 4.43282 9.12899C4.43282 10.4373 4.96962 11.6166 5.83027 12.4486C6.62778 13.2209 7.70299 13.6935 8.88588 13.6935H20.2007Z" fill="#FD366E" />
|
||||
<path d="M20.2007 7.88412V12.4486H11.9415C12.8022 11.6166 13.339 10.4373 13.339 9.12899C13.339 8.69744 13.2807 8.27999 13.1714 7.88412H20.2007Z" fill="#FD366E" />
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -177,7 +177,7 @@ $image = $this->getParam('image', '');
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: <?php echo $organization; ?>/console:6.0.1
|
||||
image: <?php echo $organization; ?>/console:6.0.13
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -841,7 +841,7 @@ $image = $this->getParam('image', '');
|
||||
- _APP_DB_PASS
|
||||
|
||||
appwrite-assistant:
|
||||
image: appwrite/assistant:0.4.0
|
||||
image: appwrite/assistant:0.8.3
|
||||
container_name: appwrite-assistant
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
@@ -864,7 +864,7 @@ $image = $this->getParam('image', '');
|
||||
<<: *x-logging
|
||||
restart: unless-stopped
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/executor:0.7.14
|
||||
image: openruntimes/executor:0.7.20
|
||||
networks:
|
||||
- appwrite
|
||||
- runtimes
|
||||
|
||||
+58
-81
@@ -18,12 +18,16 @@ use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
use Executor\Executor;
|
||||
use Swoole\Process;
|
||||
use Swoole\Runtime;
|
||||
use Swoole\Timer;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\Cache\Adapter\Pool as CachePool;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\Pool as DatabasePool;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
@@ -33,29 +37,28 @@ use Utopia\Logger\Log;
|
||||
use Utopia\Logger\Logger;
|
||||
use Utopia\Platform\Service;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Queue\Broker\Pool as BrokerPool;
|
||||
use Utopia\Queue\Message;
|
||||
use Utopia\Queue\Publisher;
|
||||
use Utopia\Queue\Server;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\Storage\Device\Telemetry as TelemetryDevice;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter as Telemetry;
|
||||
use Utopia\Telemetry\Adapter\None as NoTelemetry;
|
||||
|
||||
Authorization::disable();
|
||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||
Runtime::enableCoroutine();
|
||||
|
||||
Server::setResource('register', fn () => $register);
|
||||
|
||||
Server::setResource('dbForPlatform', function (Cache $cache, Registry $register) {
|
||||
$pools = $register->get('pools');
|
||||
$database = $pools
|
||||
->get('console')
|
||||
->pop()
|
||||
->getResource();
|
||||
$adapter = new DatabasePool($pools->get('console'));
|
||||
$dbForPlatform = new Database($adapter, $cache);
|
||||
$dbForPlatform->setNamespace('_console');
|
||||
|
||||
$adapter = new Database($database, $cache);
|
||||
$adapter->setNamespace('_console');
|
||||
|
||||
return $adapter;
|
||||
return $dbForPlatform;
|
||||
}, ['cache', 'register']);
|
||||
|
||||
Server::setResource('project', function (Message $message, Database $dbForPlatform) {
|
||||
@@ -83,32 +86,21 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
|
||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
||||
}
|
||||
|
||||
$adapter = $pools
|
||||
->get($dsn->getHost())
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$database = new Database($adapter, $cache);
|
||||
|
||||
try {
|
||||
$dsn = new DSN($project->getAttribute('database'));
|
||||
} catch (\InvalidArgumentException) {
|
||||
// TODO: Temporary until all projects are using shared tables
|
||||
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
|
||||
}
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getInternalId())
|
||||
->setTenant((int)$project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
|
||||
@@ -139,24 +131,20 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getInternalId())
|
||||
->setTenant((int)$project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
return $database;
|
||||
}
|
||||
|
||||
$dbAdapter = $pools
|
||||
->get($dsn->getHost())
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database($dbAdapter, $cache);
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$database = new Database($adapter, $cache);
|
||||
|
||||
$databases[$dsn->getHost()] = $database;
|
||||
|
||||
@@ -165,13 +153,13 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
|
||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
->setTenant($project->getInternalId())
|
||||
->setTenant((int)$project->getSequence())
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$database
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getInternalId());
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
|
||||
@@ -184,19 +172,12 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
$database = null;
|
||||
return function (?Document $project = null) use ($pools, $cache, $database) {
|
||||
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getInternalId());
|
||||
$database->setTenant((int)$project->getSequence());
|
||||
return $database;
|
||||
}
|
||||
|
||||
$dbAdapter = $pools
|
||||
->get('logs')
|
||||
->pop()
|
||||
->getResource();
|
||||
|
||||
$database = new Database(
|
||||
$dbAdapter,
|
||||
$cache
|
||||
);
|
||||
$adapter = new DatabasePool($pools->get('logs'));
|
||||
$database = new Database($adapter, $cache);
|
||||
|
||||
$database
|
||||
->setSharedTables(true)
|
||||
@@ -206,7 +187,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
|
||||
// set tenant
|
||||
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$database->setTenant($project->getInternalId());
|
||||
$database->setTenant((int)$project->getSequence());
|
||||
}
|
||||
|
||||
return $database;
|
||||
@@ -234,11 +215,7 @@ Server::setResource('cache', function (Registry $register) {
|
||||
$adapters = [];
|
||||
|
||||
foreach ($list as $value) {
|
||||
$adapters[] = $pools
|
||||
->get($value)
|
||||
->pop()
|
||||
->getResource()
|
||||
;
|
||||
$adapters[] = new CachePool($pools->get($value));
|
||||
}
|
||||
|
||||
return new Cache(new Sharding($adapters));
|
||||
@@ -267,12 +244,13 @@ Server::setResource('timelimit', function (\Redis $redis) {
|
||||
|
||||
Server::setResource('log', fn () => new Log());
|
||||
|
||||
|
||||
Server::setResource('publisher', function (Group $pools) {
|
||||
return $pools->get('publisher')->pop()->getResource();
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
|
||||
Server::setResource('consumer', function (Group $pools) {
|
||||
return $pools->get('consumer')->pop()->getResource();
|
||||
return new BrokerPool(consumer: $pools->get('consumer'));
|
||||
}, ['pools']);
|
||||
|
||||
Server::setResource('queueForStatsUsage', function (Publisher $publisher) {
|
||||
@@ -337,29 +315,29 @@ Server::setResource('pools', function (Registry $register) {
|
||||
|
||||
Server::setResource('telemetry', fn () => new NoTelemetry());
|
||||
|
||||
Server::setResource('deviceForSites', function (Document $project) {
|
||||
return getDevice(APP_STORAGE_SITES . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
Server::setResource('deviceForSites', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_SITES . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
Server::setResource('deviceForImports', function (Document $project) {
|
||||
return getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
Server::setResource('deviceForImports', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
Server::setResource('deviceForFunctions', function (Document $project) {
|
||||
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
Server::setResource('deviceForFunctions', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
Server::setResource('deviceForFiles', function (Document $project) {
|
||||
return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
Server::setResource('deviceForFiles', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
Server::setResource('deviceForBuilds', function (Document $project) {
|
||||
return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
Server::setResource('deviceForBuilds', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
Server::setResource('deviceForCache', function (Document $project) {
|
||||
return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
Server::setResource('deviceForCache', function (Document $project, Telemetry $telemetry) {
|
||||
return new TelemetryDevice($telemetry, getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
Server::setResource(
|
||||
'isResourceBlocked',
|
||||
@@ -424,7 +402,7 @@ Server::setResource('logError', function (Registry $register, Document $project)
|
||||
};
|
||||
}, ['register', 'project']);
|
||||
|
||||
Server::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
|
||||
Server::setResource('executor', fn () => new Executor());
|
||||
|
||||
$pools = $register->get('pools');
|
||||
$platform = new Appwrite();
|
||||
@@ -463,13 +441,6 @@ try {
|
||||
|
||||
$worker = $platform->getWorker();
|
||||
|
||||
$worker
|
||||
->shutdown()
|
||||
->inject('pools')
|
||||
->action(function (Group $pools) {
|
||||
$pools->reclaim();
|
||||
});
|
||||
|
||||
$worker
|
||||
->error()
|
||||
->inject('error')
|
||||
@@ -477,8 +448,7 @@ $worker
|
||||
->inject('log')
|
||||
->inject('pools')
|
||||
->inject('project')
|
||||
->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($queueName) {
|
||||
$pools->reclaim();
|
||||
->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($worker, $queueName) {
|
||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
if ($logger) {
|
||||
@@ -514,8 +484,15 @@ $worker
|
||||
});
|
||||
|
||||
$worker->workerStart()
|
||||
->action(function () use ($workerName) {
|
||||
Console::info("Worker $workerName started");
|
||||
->action(function () use ($worker, $workerName) {
|
||||
Console::info("Worker $workerName started");
|
||||
|
||||
Process::signal(SIGTERM, function () use ($worker, $workerName) {
|
||||
Console::info("Stopping worker $workerName.");
|
||||
|
||||
$worker->stop();
|
||||
Timer::clearAll();
|
||||
});
|
||||
});
|
||||
|
||||
$worker->start();
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php doctor $@
|
||||
exec php /usr/src/code/app/cli.php doctor $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php install $@
|
||||
exec php /usr/src/code/app/cli.php install $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php maintenance $@
|
||||
exec php /usr/src/code/app/cli.php maintenance $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php migrate $@
|
||||
exec php /usr/src/code/app/cli.php migrate $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php queue-count --type=failed $@
|
||||
exec php /usr/src/code/app/cli.php queue-count --type=failed $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php queue-count --type=processing $@
|
||||
exec php /usr/src/code/app/cli.php queue-count --type=processing $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php queue-count --type=success $@
|
||||
exec php /usr/src/code/app/cli.php queue-count --type=success $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php queue-retry $@
|
||||
exec php /usr/src/code/app/cli.php queue-retry $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/realtime.php $@
|
||||
exec php /usr/src/code/app/realtime.php $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php schedule-executions $@
|
||||
exec php /usr/src/code/app/cli.php schedule-executions $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php schedule-functions $@
|
||||
exec php /usr/src/code/app/cli.php schedule-functions $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php schedule-messages $@
|
||||
exec php /usr/src/code/app/cli.php schedule-messages $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php screenshot $@
|
||||
exec php /usr/src/code/app/cli.php screenshot $@
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php sdks $@
|
||||
exec php /usr/src/code/app/cli.php sdks $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php specs $@
|
||||
exec php /usr/src/code/app/cli.php specs $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php ssl $@
|
||||
exec php /usr/src/code/app/cli.php ssl $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php stats-resources $@
|
||||
exec php /usr/src/code/app/cli.php stats-resources $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
/usr/src/code/vendor/bin/phpunit --configuration /usr/src/code/phpunit.xml $@
|
||||
exec /usr/src/code/vendor/bin/phpunit --configuration /usr/src/code/phpunit.xml $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php upgrade $@
|
||||
exec php /usr/src/code/app/cli.php upgrade $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/cli.php vars $@
|
||||
exec php /usr/src/code/app/cli.php vars $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php audits $@
|
||||
exec php /usr/src/code/app/worker.php audits $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php builds $@
|
||||
exec php /usr/src/code/app/worker.php builds $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php certificates $@
|
||||
exec php /usr/src/code/app/worker.php certificates $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php databases $@
|
||||
exec php /usr/src/code/app/worker.php databases $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php deletes $@
|
||||
exec php /usr/src/code/app/worker.php deletes $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php functions $@
|
||||
exec php /usr/src/code/app/worker.php functions $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php mails $@
|
||||
exec php /usr/src/code/app/worker.php mails $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php messaging $@
|
||||
exec php /usr/src/code/app/worker.php messaging $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php migrations $@
|
||||
exec php /usr/src/code/app/worker.php migrations $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php stats-resources $@
|
||||
exec php /usr/src/code/app/worker.php stats-resources $@
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php stats-usage $@
|
||||
exec php /usr/src/code/app/worker.php stats-usage $@
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php webhooks $@
|
||||
exec php /usr/src/code/app/worker.php webhooks $@
|
||||
|
||||
+11
-10
@@ -46,28 +46,29 @@
|
||||
"ext-sockets": "*",
|
||||
"appwrite/php-runtimes": "0.19.*",
|
||||
"appwrite/php-clamav": "2.0.*",
|
||||
"utopia-php/auth": "0.4.0",
|
||||
"utopia-php/abuse": "0.52.*",
|
||||
"utopia-php/analytics": "0.10.*",
|
||||
"utopia-php/audit": "0.55.*",
|
||||
"utopia-php/cache": "0.13.*",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.71.*",
|
||||
"utopia-php/detector": "0.1.*",
|
||||
"utopia-php/database": "0.69.*",
|
||||
"utopia-php/domains": "0.5.*",
|
||||
"utopia-php/domains": "0.8.*",
|
||||
"utopia-php/dsn": "0.2.1",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
"utopia-php/fetch": "0.4.*",
|
||||
"utopia-php/image": "0.8.*",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/logger": "0.6.*",
|
||||
"utopia-php/messaging": "0.17.*",
|
||||
"utopia-php/migration": "0.9.*",
|
||||
"utopia-php/messaging": "0.18.*",
|
||||
"utopia-php/migration": "0.10.*",
|
||||
"utopia-php/orchestration": "0.9.*",
|
||||
"utopia-php/platform": "0.7.*",
|
||||
"utopia-php/pools": "0.8.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
"utopia-php/queue": "0.10.*",
|
||||
"utopia-php/queue": "0.11.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/storage": "0.18.*",
|
||||
"utopia-php/swoole": "0.8.*",
|
||||
@@ -76,17 +77,17 @@
|
||||
"utopia-php/vcs": "0.10.*",
|
||||
"utopia-php/websocket": "0.3.*",
|
||||
"matomo/device-detector": "6.1.*",
|
||||
"dragonmantank/cron-expression": "3.3.2",
|
||||
"phpmailer/phpmailer": "6.9.1",
|
||||
"chillerlan/php-qrcode": "4.3.4",
|
||||
"adhocore/jwt": "1.1.2",
|
||||
"dragonmantank/cron-expression": "3.3.*",
|
||||
"phpmailer/phpmailer": "6.9.*",
|
||||
"chillerlan/php-qrcode": "4.3.*",
|
||||
"adhocore/jwt": "1.1.*",
|
||||
"spomky-labs/otphp": "^10.0",
|
||||
"webonyx/graphql-php": "14.11.*",
|
||||
"league/csv": "9.14.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-fileinfo": "*",
|
||||
"appwrite/sdk-generator": "0.40.*",
|
||||
"appwrite/sdk-generator": "0.41.*",
|
||||
"phpunit/phpunit": "9.*",
|
||||
"swoole/ide-helper": "5.1.2",
|
||||
"phpstan/phpstan": "1.8.*",
|
||||
|
||||
Generated
+280
-233
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -213,7 +213,7 @@ services:
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:6.0.1
|
||||
image: appwrite/console:6.0.41
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -934,7 +934,7 @@ services:
|
||||
|
||||
appwrite-assistant:
|
||||
container_name: appwrite-assistant
|
||||
image: appwrite/assistant:0.7.0
|
||||
image: appwrite/assistant:0.8.3
|
||||
networks:
|
||||
- appwrite
|
||||
environment:
|
||||
@@ -951,7 +951,7 @@ services:
|
||||
hostname: exc1
|
||||
<<: *x-logging
|
||||
stop_signal: SIGINT
|
||||
image: openruntimes/executor:0.7.14
|
||||
image: openruntimes/executor:0.7.20
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user