mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
970
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
281c52d58c | ||
|
|
098f891f7c | ||
|
|
0b0a1c20e9 | ||
|
|
d27dba05d2 | ||
|
|
1a2a725cb0 | ||
|
|
02de47f1dc | ||
|
|
241a0c88e1 | ||
|
|
27fae92b89 | ||
|
|
ba3f907028 | ||
|
|
c96232f764 | ||
|
|
20f2cbffda | ||
|
|
727bac5f3d | ||
|
|
bc34f95646 | ||
|
|
fe763093df | ||
|
|
5e10670d22 | ||
|
|
455554a6a2 | ||
|
|
2f6a82f4d5 | ||
|
|
149dfb9651 | ||
|
|
534096cac6 | ||
|
|
f59377e1bb | ||
|
|
1177178885 | ||
|
|
5fe5c82801 | ||
|
|
3ff15accfd | ||
|
|
5d25265e5c | ||
|
|
61ac661851 | ||
|
|
5fe6359fe6 | ||
|
|
38a2ec0a2f | ||
|
|
70c2079f75 | ||
|
|
8de9b7ffb2 | ||
|
|
d28831632e | ||
|
|
6e40070b98 | ||
|
|
738e256692 | ||
|
|
4f2142a470 | ||
|
|
11e4e9432e | ||
|
|
b1829bacfe | ||
|
|
de30b49515 | ||
|
|
15992da369 | ||
|
|
ad3adbda21 | ||
|
|
651cec65da | ||
|
|
fdbc01aa3d | ||
|
|
e08133976b | ||
|
|
52fdeccd11 | ||
|
|
f62979cfa8 | ||
|
|
55236524e3 | ||
|
|
4e9385f70b | ||
|
|
33fbab7e3d | ||
|
|
c0b7c47615 | ||
|
|
09152a08e1 | ||
|
|
af3388a51f | ||
|
|
16b2449787 | ||
|
|
d8cc770ba5 | ||
|
|
3e914a4f1b | ||
|
|
406630cb61 | ||
|
|
98c634c3c9 | ||
|
|
4208c52c7f | ||
|
|
9055645d06 | ||
|
|
a4239fc5dc | ||
|
|
9731953c09 | ||
|
|
f8fdecaa2d | ||
|
|
41d7114e97 | ||
|
|
4853e0803c | ||
|
|
40642b2aad | ||
|
|
cd806e80d7 | ||
|
|
0f313afcb5 | ||
|
|
a970512e3c | ||
|
|
7c229ad878 | ||
|
|
1276e74e79 | ||
|
|
800dd8bdac | ||
|
|
0099eef8c8 | ||
|
|
d04e43c615 | ||
|
|
3203ea5d43 | ||
|
|
b0e95171f8 | ||
|
|
28fe6f34de | ||
|
|
de7c8b8c27 | ||
|
|
a725b1c952 | ||
|
|
932ed94b8c | ||
|
|
3fea5ec914 | ||
|
|
2c8e1e1731 | ||
|
|
6f39c9d147 | ||
|
|
ac760d9ddc | ||
|
|
077698a0ee | ||
|
|
1a32ce60bd | ||
|
|
eb52ccfeae | ||
|
|
9c50d60350 | ||
|
|
fea58c2bc9 | ||
|
|
e5a1af3183 | ||
|
|
9b9ed93db2 | ||
|
|
6684364b98 | ||
|
|
540102ff97 | ||
|
|
c3f0bec1ff | ||
|
|
eaa105477a | ||
|
|
1e19e7d0b4 | ||
|
|
be7ea1f798 | ||
|
|
639ce4d026 | ||
|
|
a35c955e54 | ||
|
|
b44682b6ff | ||
|
|
fdc98389b1 | ||
|
|
05821c2993 | ||
|
|
5f19398a9b | ||
|
|
41ecccda1e | ||
|
|
572c49cc90 | ||
|
|
f7d7b8d3dd | ||
|
|
f5d243e229 | ||
|
|
51f44b02b9 | ||
|
|
8c1b9044b3 | ||
|
|
da05398389 | ||
|
|
c5086d8fba | ||
|
|
6f34592efe | ||
|
|
f841979de8 | ||
|
|
8d93a8aa09 | ||
|
|
af09f17e02 | ||
|
|
abc2795daa | ||
|
|
6264e616d3 | ||
|
|
56a8e38890 | ||
|
|
f905c10acc | ||
|
|
a6d23298ac | ||
|
|
af51e31197 | ||
|
|
cc4c147b15 | ||
|
|
66a40eb175 | ||
|
|
9b6408e53a | ||
|
|
4b1dba692c | ||
|
|
4da44cf13c | ||
|
|
24229c63ea | ||
|
|
21d9f182d6 | ||
|
|
2f091a1fab | ||
|
|
808f338c7c | ||
|
|
1f08344c8d | ||
|
|
384e606503 | ||
|
|
cdd61feec8 | ||
|
|
e9edf013b0 | ||
|
|
f52f864085 | ||
|
|
df5fb87925 | ||
|
|
bae0676a62 | ||
|
|
da4a1b4806 | ||
|
|
6794ef77fd | ||
|
|
dcf11df193 | ||
|
|
b922858428 | ||
|
|
8c7ccfcc12 | ||
|
|
606fea5871 | ||
|
|
d137b41c0b | ||
|
|
977a920959 | ||
|
|
bf9deb09f5 | ||
|
|
c759f9ec11 | ||
|
|
ea42b823d8 | ||
|
|
d312fe22ff | ||
|
|
6e59859f5c | ||
|
|
7b88792b6a | ||
|
|
27ac5cbb2a | ||
|
|
f2270b9ef8 | ||
|
|
58c536e232 | ||
|
|
6400b0dc48 | ||
|
|
44abf276d7 | ||
|
|
ed0929416b | ||
|
|
2e2af32be3 | ||
|
|
ea38a7f869 | ||
|
|
43fb92ca83 | ||
|
|
38f5d88b03 | ||
|
|
f7296d80b2 | ||
|
|
453bb8ab70 | ||
|
|
a98520302d | ||
|
|
6af3912eff | ||
|
|
f82726c2b4 | ||
|
|
0ab1ebd8c4 | ||
|
|
706e5f2453 | ||
|
|
2238f6d822 | ||
|
|
5fc2c1ceb8 | ||
|
|
e643e7a406 | ||
|
|
02b1c82226 | ||
|
|
4d92af5ba6 | ||
|
|
acec1ab699 | ||
|
|
1d82c67d1f | ||
|
|
eabf3993a0 | ||
|
|
47c5c55267 | ||
|
|
43ae7c4b14 | ||
|
|
38aae9e2c1 | ||
|
|
e74502a259 | ||
|
|
101283f345 | ||
|
|
8d9abd2f31 | ||
|
|
4d2a08e7fd | ||
|
|
a9e953d840 | ||
|
|
6caa347b26 | ||
|
|
5578e199e4 | ||
|
|
8623767926 | ||
|
|
d39fce55c1 | ||
|
|
70f31bb48c | ||
|
|
b65c375655 | ||
|
|
270250a563 | ||
|
|
9601860247 | ||
|
|
09ca143231 | ||
|
|
2afbfea0d0 | ||
|
|
928f535800 | ||
|
|
391e045775 | ||
|
|
8b23d4bab1 | ||
|
|
187ba0a575 | ||
|
|
68c85a52fb | ||
|
|
8cb4d2894b | ||
|
|
75efa28125 | ||
|
|
7952eafdf0 | ||
|
|
de9b0e6eee | ||
|
|
aeffeb8cc8 | ||
|
|
a11cbc3799 | ||
|
|
b0eb6434b8 | ||
|
|
fe28af292b | ||
|
|
6b4ecbcd23 | ||
|
|
dab6701f79 | ||
|
|
26edc75455 | ||
|
|
51763a6e84 | ||
|
|
60c336bfa1 | ||
|
|
f58c8c5370 | ||
|
|
4228bc9b63 | ||
|
|
ed8c39f649 | ||
|
|
c0bd4401c5 | ||
|
|
cd647e0c18 | ||
|
|
8906d94e8f | ||
|
|
e1e195cec6 | ||
|
|
1be83d15dd | ||
|
|
c9a7902e52 | ||
|
|
73a10a1dca | ||
|
|
7bd3c334ed | ||
|
|
68e0083566 | ||
|
|
f4e8d72593 | ||
|
|
d9d15b0fbe | ||
|
|
59c4536208 | ||
|
|
cc20484131 | ||
|
|
ec65071ae3 | ||
|
|
fe26dcb50a | ||
|
|
25a3266301 | ||
|
|
dd3f92856a | ||
|
|
bef10d7c84 | ||
|
|
e6e28f32ff | ||
|
|
382ffceed9 | ||
|
|
fb6ec704a1 | ||
|
|
0f8e1db044 | ||
|
|
cd72d70432 | ||
|
|
61cf767232 | ||
|
|
70364d7a07 | ||
|
|
772ca7d36c | ||
|
|
189f2e05e0 | ||
|
|
100870557b | ||
|
|
03cae4c5ee | ||
|
|
b2f7ed48dd | ||
|
|
28addd6702 | ||
|
|
4b8c68e5ed | ||
|
|
ecd77cb2e3 | ||
|
|
8d3070416a | ||
|
|
6e422ebe3c | ||
|
|
3c602e84e6 | ||
|
|
1cacb87c65 | ||
|
|
8e4bd363d2 | ||
|
|
b7c2220648 | ||
|
|
85de3d27d8 | ||
|
|
c7ab1dda9f | ||
|
|
c1f62ce0bd | ||
|
|
74d946e42a | ||
|
|
523a8b6ff6 | ||
|
|
d10757e69f | ||
|
|
06c87a28c9 | ||
|
|
b9e8c1d8bc | ||
|
|
53660f3100 | ||
|
|
bea723ff2e | ||
|
|
ba58a372d1 | ||
|
|
59e43b1035 | ||
|
|
304f67c88f | ||
|
|
8e2571539c | ||
|
|
3e0323dae8 | ||
|
|
d0181787b9 | ||
|
|
f08ea92df4 | ||
|
|
49ea2f6fd9 | ||
|
|
b972e7a1d4 | ||
|
|
c3120f4aaa | ||
|
|
969c58fda9 | ||
|
|
49814c2a7e | ||
|
|
f9141ca564 | ||
|
|
4edec1494a | ||
|
|
0b60898ad5 | ||
|
|
82ce35029a | ||
|
|
93f86e57ea | ||
|
|
a46c5d1029 | ||
|
|
88b229b13e | ||
|
|
3ed87a8ab9 | ||
|
|
6e86527eb6 | ||
|
|
5d9bdd4cc7 | ||
|
|
6852ee1f0f | ||
|
|
81c3d96629 | ||
|
|
1acbff316e | ||
|
|
f02707627f | ||
|
|
0df1396d52 | ||
|
|
c4509f5ce7 | ||
|
|
3f0857b36d | ||
|
|
e3f64c3234 | ||
|
|
2c36fab1c6 | ||
|
|
f8d92c04ad | ||
|
|
950cc420a2 | ||
|
|
cbb943cc88 | ||
|
|
862664dd62 | ||
|
|
498f9e40c8 | ||
|
|
dc40fd7ddc | ||
|
|
a6281992b1 | ||
|
|
3be50d2ab5 | ||
|
|
2a2bf7f154 | ||
|
|
9a8ba13dbf | ||
|
|
ecdea8b487 | ||
|
|
5f6e65c1aa | ||
|
|
932f34df92 | ||
|
|
fd5a900758 | ||
|
|
fb7223bd1b | ||
|
|
b1685a5ca1 | ||
|
|
c3656fd4e7 | ||
|
|
b3c9e6181f | ||
|
|
04733e25c8 | ||
|
|
67084374fb | ||
|
|
b431eb4a51 | ||
|
|
4d9521edc6 | ||
|
|
1dd0b419c5 | ||
|
|
636b0775fd | ||
|
|
c4a506445d | ||
|
|
8d046f021c | ||
|
|
2d0885ed70 | ||
|
|
ffe153630d | ||
|
|
6cd9fc72d9 | ||
|
|
312d1029c0 | ||
|
|
fb802c7fe4 | ||
|
|
942775c352 | ||
|
|
f5852a52e2 | ||
|
|
ff89da513f | ||
|
|
31cf1266e1 | ||
|
|
49ab765ee8 | ||
|
|
2495101dfd | ||
|
|
07cdd66296 | ||
|
|
e1b4917acf | ||
|
|
796a1b5bd2 | ||
|
|
cfab30b43f | ||
|
|
881525f349 | ||
|
|
8086d6eef0 | ||
|
|
badbcaf1a8 | ||
|
|
e6d9c5c5fd | ||
|
|
86c26f2dc7 | ||
|
|
2c453d5fd9 | ||
|
|
3bd8405deb | ||
|
|
a20ff31ba3 | ||
|
|
fed98ffdfe | ||
|
|
31a5a175b3 | ||
|
|
0ae70f8321 | ||
|
|
954ed45a36 | ||
|
|
4396c5f0fc | ||
|
|
57bc76edc7 | ||
|
|
fcd31f4cff | ||
|
|
2d7b01aa35 | ||
|
|
2243f0c72e | ||
|
|
c3c568653f | ||
|
|
403083b0ce | ||
|
|
e5ef825ac4 | ||
|
|
abd953974d | ||
|
|
7297188c1b | ||
|
|
2a672f5a63 | ||
|
|
a160b0f987 | ||
|
|
01e2eb5025 | ||
|
|
baffa615e6 | ||
|
|
bd87672607 | ||
|
|
31c326fe87 | ||
|
|
0dc319d244 | ||
|
|
2e784c0e8b | ||
|
|
1660c0b00c | ||
|
|
96327a9ebc | ||
|
|
608bda0b48 | ||
|
|
91d4b1b99f | ||
|
|
32a5ff4c8c | ||
|
|
31b1663f6b | ||
|
|
c152d8e001 | ||
|
|
8fd6208502 | ||
|
|
938505da51 | ||
|
|
30462b1951 | ||
|
|
b46de29735 | ||
|
|
d92413fd6b | ||
|
|
ae0cdaff63 | ||
|
|
4698323f38 | ||
|
|
612b064d2a | ||
|
|
2a7038bb8e | ||
|
|
2c434924de | ||
|
|
d9b7a018ea | ||
|
|
abcaf6cd73 | ||
|
|
81ee68e219 | ||
|
|
e14ae7ea4a | ||
|
|
ec21effd2c | ||
|
|
7399362c20 | ||
|
|
d5ad714f5a | ||
|
|
1389bab3d1 | ||
|
|
100189c0fb | ||
|
|
d36a09eec6 | ||
|
|
1a5e9748f3 | ||
|
|
22939831f2 | ||
|
|
2069c9fdad | ||
|
|
a4c18c4b77 | ||
|
|
cba700402b | ||
|
|
a9152c0c20 | ||
|
|
906f774a90 | ||
|
|
ec2f3b69cc | ||
|
|
5b843015b0 | ||
|
|
3a2ff9790c | ||
|
|
08bc6379bd | ||
|
|
60399aaa0a | ||
|
|
10c6315589 | ||
|
|
ad7552e676 | ||
|
|
536b6162ce | ||
|
|
93839ec36d | ||
|
|
17dc8e3f8a | ||
|
|
f4d01f4c37 | ||
|
|
6608023b9f | ||
|
|
cfb1f4fcdb | ||
|
|
05067302d2 | ||
|
|
7c3d2c8435 | ||
|
|
aa0111550b | ||
|
|
04c3182f7c | ||
|
|
5aca1fcfa9 | ||
|
|
254c12b084 | ||
|
|
6615c1a382 | ||
|
|
7ca4009457 | ||
|
|
1a54bd58a6 | ||
|
|
c443a2f999 | ||
|
|
ee9efe530d | ||
|
|
985476ac50 | ||
|
|
7e6799d6dd | ||
|
|
2e12ae4390 | ||
|
|
c79e8e5b95 | ||
|
|
9f537a5bee | ||
|
|
a60d19fb42 | ||
|
|
ef7cf41a61 | ||
|
|
8b1e97bae4 | ||
|
|
7b8f25696c | ||
|
|
91ad4097ff | ||
|
|
d9d38197dd | ||
|
|
a8b2ebb356 | ||
|
|
c53262a44c | ||
|
|
8339435fb5 | ||
|
|
424a357503 | ||
|
|
35cd2b5295 | ||
|
|
9a7512d1fd | ||
|
|
989fa2b8a0 | ||
|
|
a461d5bf72 | ||
|
|
cb2d0dc592 | ||
|
|
a9d1f6668f | ||
|
|
9d020fdef5 | ||
|
|
f25143b240 | ||
|
|
d89cdbcf5a | ||
|
|
8447732614 | ||
|
|
9f446da0a5 | ||
|
|
62effa7bc4 | ||
|
|
dedcdc2e0e | ||
|
|
854256e8a6 | ||
|
|
d72f69f792 | ||
|
|
7fdde1a518 | ||
|
|
6195674611 | ||
|
|
4804bdf110 | ||
|
|
cb356bc410 | ||
|
|
74d9a3c181 | ||
|
|
6bcdc8c769 | ||
|
|
c9c9db5640 | ||
|
|
a20140eb36 | ||
|
|
c0cb4b26a3 | ||
|
|
b842e03f38 | ||
|
|
16aca35bd4 | ||
|
|
2795505e82 | ||
|
|
5799ca1041 | ||
|
|
e11e7e6780 | ||
|
|
e93010c5fe | ||
|
|
d677b8ed88 | ||
|
|
0648de2046 | ||
|
|
3399308ee3 | ||
|
|
42f72b4477 | ||
|
|
25b6d59e30 | ||
|
|
5479e6f8f1 | ||
|
|
586e676299 | ||
|
|
26821c8372 | ||
|
|
56d78b1712 | ||
|
|
e4c4ffdb3c | ||
|
|
6e146889bf | ||
|
|
ab1683070b | ||
|
|
9f3d15bd1c | ||
|
|
a7199ff980 | ||
|
|
37dfe0d719 | ||
|
|
86856bf87d | ||
|
|
7c2ab4fcf6 | ||
|
|
29ea03b7bb | ||
|
|
434fcdc508 | ||
|
|
ed7c2fa451 | ||
|
|
d661195a5f | ||
|
|
50e7986f84 | ||
|
|
c7301af18a | ||
|
|
c0c0c921dc | ||
|
|
7c795da72f | ||
|
|
13750f7dbd | ||
|
|
69bdf50c85 | ||
|
|
ea02639767 | ||
|
|
bd19da2e7a | ||
|
|
b0b57935da | ||
|
|
7c3f8504bb | ||
|
|
6c0ce5cc41 | ||
|
|
403e5773a2 | ||
|
|
9d82892219 | ||
|
|
5eb7c36f50 | ||
|
|
68e8b7ca2b | ||
|
|
9c54cce5bd | ||
|
|
438d5185a9 | ||
|
|
cc0a97b10e | ||
|
|
d3cabcd5ac | ||
|
|
84133ef631 | ||
|
|
2be59a94da | ||
|
|
fca2e95fdd | ||
|
|
5683c310db | ||
|
|
795928a037 | ||
|
|
5f7ceecb30 | ||
|
|
bbfd22ce2e | ||
|
|
bc866dc717 | ||
|
|
94f9d0140b | ||
|
|
ab790f6c02 | ||
|
|
ff618a0565 | ||
|
|
b3db296f13 | ||
|
|
e019e426f1 | ||
|
|
adacec4ad1 | ||
|
|
a60d916c51 | ||
|
|
d522b1a04c | ||
|
|
4b740dce86 | ||
|
|
6a05bd4e0d | ||
|
|
0570567b2e | ||
|
|
3a29bcb41b | ||
|
|
1b3fe3046d | ||
|
|
7c91157dc8 | ||
|
|
584e4c0d75 | ||
|
|
f0477f8940 | ||
|
|
3bc81c82ec | ||
|
|
2f43a3566d | ||
|
|
0f0ea5e96c | ||
|
|
99377e26ee | ||
|
|
d809ab1ff3 | ||
|
|
a5c4fe0a2d | ||
|
|
12eb3b4eea | ||
|
|
adb451234b | ||
|
|
52a73856bc | ||
|
|
921b9154eb | ||
|
|
5ed1ca617f | ||
|
|
12f274223f | ||
|
|
368b61493f | ||
|
|
458df23797 | ||
|
|
369caae87d | ||
|
|
f48791fba6 | ||
|
|
e2b9c64f91 | ||
|
|
69e9a1bd8c | ||
|
|
1f606a591d | ||
|
|
e972ccedd6 | ||
|
|
e4369e5e85 | ||
|
|
45b2d58aa5 | ||
|
|
be1c12147f | ||
|
|
4157e89ab7 | ||
|
|
c151d2aaca | ||
|
|
baa40e4007 | ||
|
|
197e32f128 | ||
|
|
39e5c5e734 | ||
|
|
a8a8df602f | ||
|
|
d641177618 | ||
|
|
463bc4aac3 | ||
|
|
e597ee4493 | ||
|
|
00478e8533 | ||
|
|
9aad6721cd | ||
|
|
c560524a42 | ||
|
|
ec5fd1d798 | ||
|
|
f723948907 | ||
|
|
0ccaf9adff | ||
|
|
d490879615 | ||
|
|
709f616f7f | ||
|
|
a8a5b4d00c | ||
|
|
a27ba015e9 | ||
|
|
2106022507 | ||
|
|
e992ed11c1 | ||
|
|
50400c4b43 | ||
|
|
3cfb013f1e | ||
|
|
be27912a6e | ||
|
|
b73b450a2e | ||
|
|
4dc7206e04 | ||
|
|
386c6076b2 | ||
|
|
5be62c22e0 | ||
|
|
bd20ea0300 | ||
|
|
40f8a4cb02 | ||
|
|
b18d3bc678 | ||
|
|
0cc5088e8e | ||
|
|
2c4f5d3daa | ||
|
|
1675683e76 | ||
|
|
78dcfd2770 | ||
|
|
87de071f53 | ||
|
|
39585d0438 | ||
|
|
270dfd685d | ||
|
|
de29879793 | ||
|
|
a70e323a15 | ||
|
|
5dadd8caea | ||
|
|
ff1bfb61f1 | ||
|
|
a6f459ecca | ||
|
|
54c8a890d3 | ||
|
|
74b06cdd96 | ||
|
|
5113c9e50a | ||
|
|
0052487d49 | ||
|
|
9841f6ae87 | ||
|
|
9b58ae7ff5 | ||
|
|
2647379557 | ||
|
|
8fb7a2db32 | ||
|
|
59c41478ca | ||
|
|
37087c37ca | ||
|
|
066008293d | ||
|
|
b9acae5a25 | ||
|
|
d0452e5d3b | ||
|
|
f94f6642ea | ||
|
|
01dc8fa396 | ||
|
|
430c883a88 | ||
|
|
dbec8c01a9 | ||
|
|
0c37530974 | ||
|
|
5fa4a1232f | ||
|
|
645fbd4f1c | ||
|
|
b2f920d983 | ||
|
|
3c152b8349 | ||
|
|
bad427071e | ||
|
|
419d18bb1c | ||
|
|
6e1675c574 | ||
|
|
561af1d386 | ||
|
|
286f7e6b86 | ||
|
|
fe18afcfca | ||
|
|
348199712a | ||
|
|
1ae7d730ba | ||
|
|
39c70b3539 | ||
|
|
e0101def27 | ||
|
|
469732f482 | ||
|
|
4c22088146 | ||
|
|
9d04c61cbe | ||
|
|
48bf8001ae | ||
|
|
1691910023 | ||
|
|
d6474a85e2 | ||
|
|
8d7712a669 | ||
|
|
396602d887 | ||
|
|
bc85d04bbc | ||
|
|
ec44b1f272 | ||
|
|
efd8dc16b3 | ||
|
|
246c334127 | ||
|
|
11867bef50 | ||
|
|
8b0465f129 | ||
|
|
d54d0ea7b3 | ||
|
|
99f736aa52 | ||
|
|
48fd3b371e | ||
|
|
acea8812da | ||
|
|
13597d7c54 | ||
|
|
8f2cca5b0f | ||
|
|
a1c70a6956 | ||
|
|
87de19b610 | ||
|
|
f4d2583865 | ||
|
|
18f17ff9fb | ||
|
|
0d6095ba18 | ||
|
|
6a782ce373 | ||
|
|
33a8da45bd | ||
|
|
a7d09118e4 | ||
|
|
0e0009dd27 | ||
|
|
3159e5de0c | ||
|
|
e4bdbdd9ba | ||
|
|
624c91816b | ||
|
|
64a957859e | ||
|
|
fa053e493a | ||
|
|
1361dbdec5 | ||
|
|
8ae462c317 | ||
|
|
2997a11274 | ||
|
|
67a72edebf | ||
|
|
b4c77fbd5f | ||
|
|
e154407fc6 | ||
|
|
06b69360f4 | ||
|
|
aa2a45f142 | ||
|
|
54d1ef257d | ||
|
|
39e4a04625 | ||
|
|
9a89cc1332 | ||
|
|
f4637e1b7d | ||
|
|
8f390a70c7 | ||
|
|
f7e69624c6 | ||
|
|
e9c2ebbcf5 | ||
|
|
879ddb3074 | ||
|
|
c9e6bfbdb3 | ||
|
|
8e0bd8d18d | ||
|
|
d0be46be74 | ||
|
|
dd6cde6ae5 | ||
|
|
714c2f30d7 | ||
|
|
8c1f6aabb7 | ||
|
|
f707be6ed3 | ||
|
|
316b739a27 | ||
|
|
b04a85491b | ||
|
|
635dbdb5cb | ||
|
|
d06e41f81e | ||
|
|
bcb4ffa404 | ||
|
|
c0d4b58eea | ||
|
|
e2ab6a05ab | ||
|
|
6bbb522a87 | ||
|
|
7f2d3beb31 | ||
|
|
3d4917727b | ||
|
|
41ead533b8 | ||
|
|
c47f65d27c | ||
|
|
116ba6bc7b | ||
|
|
adc94cf1b4 | ||
|
|
257733aec0 | ||
|
|
fbbf846eb9 | ||
|
|
fbf985ea90 | ||
|
|
f061f724e0 | ||
|
|
125585831b | ||
|
|
3c418c1607 | ||
|
|
8f8c008e8b | ||
|
|
1a6f23c3a1 | ||
|
|
84ba7febe7 | ||
|
|
7b79642f4b | ||
|
|
949347764a | ||
|
|
32c4debffc | ||
|
|
2850879428 | ||
|
|
1c0c8f0eb6 | ||
|
|
ba510a5641 | ||
|
|
cb93b9e1e7 | ||
|
|
61d030b2c6 | ||
|
|
1d49d8284c | ||
|
|
65474df642 | ||
|
|
ac27c77426 | ||
|
|
00e3fe12f4 | ||
|
|
5c895579ed | ||
|
|
104a9b6074 | ||
|
|
5e3e0b6683 | ||
|
|
9468b08eb2 | ||
|
|
d2bf15b20b | ||
|
|
b1e5a5b987 | ||
|
|
9ca420a0e6 | ||
|
|
6f8c38dfa0 | ||
|
|
ddb86127b7 | ||
|
|
ea0a5a186c | ||
|
|
928590b175 | ||
|
|
7656ba11ff | ||
|
|
7ae7ccbe49 | ||
|
|
6d376df00a | ||
|
|
2b08db9a38 | ||
|
|
9d652d9e79 | ||
|
|
e1fffa775e | ||
|
|
2e60698ec1 | ||
|
|
90521c6bd7 | ||
|
|
558f7f06b4 | ||
|
|
19194dabc0 | ||
|
|
e1230f7465 | ||
|
|
def25a8c9b | ||
|
|
675227d25a | ||
|
|
4e5ad33d73 | ||
|
|
0e03842173 | ||
|
|
aa8af5a213 | ||
|
|
f1a3cd13ff | ||
|
|
00989fcdde | ||
|
|
c677374a11 | ||
|
|
3f0a8fbe21 | ||
|
|
7d5ae2f1c3 | ||
|
|
0f8ad539ad | ||
|
|
9839a4ef36 | ||
|
|
e91c762953 | ||
|
|
54260b5a9a | ||
|
|
67b95c4b39 | ||
|
|
90c4bad826 | ||
|
|
791f0dda79 | ||
|
|
3f6368f046 | ||
|
|
d802639b0e | ||
|
|
3d333633e4 | ||
|
|
79c043ec1f | ||
|
|
755e86020f | ||
|
|
96e69b55e1 | ||
|
|
c210b2b874 | ||
|
|
c0ef832784 | ||
|
|
bb7234fa98 | ||
|
|
dd6bd745df | ||
|
|
a8f3b63ded | ||
|
|
aa42608528 | ||
|
|
900f64bf8d | ||
|
|
dad8f597ca | ||
|
|
90f18c57ac | ||
|
|
6e7dd26f75 | ||
|
|
9151abc951 | ||
|
|
714f6b62ac | ||
|
|
316693798e | ||
|
|
c7f92fbb02 | ||
|
|
e715f9e90c | ||
|
|
172d25d67c | ||
|
|
f6ea6d2fc5 | ||
|
|
e626c2ad45 | ||
|
|
92c93357cf | ||
|
|
3b19ef6b1e | ||
|
|
c1a3b29a97 | ||
|
|
6faad29907 | ||
|
|
aa2385ffb9 | ||
|
|
8b99517dd0 | ||
|
|
33037a3ae5 | ||
|
|
b0852aeca3 | ||
|
|
09da4378c6 | ||
|
|
07738bdcf9 | ||
|
|
c6ca830801 | ||
|
|
44234da995 | ||
|
|
22dfcb78c4 | ||
|
|
60da24e521 | ||
|
|
f703a0db18 | ||
|
|
2a8ab8d610 | ||
|
|
a85b43654c | ||
|
|
ad8693c018 | ||
|
|
a0280e3c43 | ||
|
|
76216c3bf6 | ||
|
|
c8c4576bbc | ||
|
|
dac8766eb4 | ||
|
|
f6de256220 | ||
|
|
2f9b44930f | ||
|
|
627df0d11f | ||
|
|
fcd323baa4 | ||
|
|
0009b73b06 | ||
|
|
b5efbdbb0f | ||
|
|
a6e9d58f12 | ||
|
|
b496bcb6df | ||
|
|
030e26b223 | ||
|
|
5bf20289a5 | ||
|
|
9e69e41e02 | ||
|
|
773fa6ffd6 | ||
|
|
21bff77c50 | ||
|
|
472f167b4a | ||
|
|
f86bf6fdfd | ||
|
|
f3315c69a0 | ||
|
|
76701b0cca | ||
|
|
2cc73151d1 | ||
|
|
39e237dfb4 | ||
|
|
c624d94c45 | ||
|
|
b22188251d | ||
|
|
68d017f452 | ||
|
|
2ed854893d | ||
|
|
eb083a7e10 | ||
|
|
2076a429c6 | ||
|
|
59766b6ce6 | ||
|
|
36bff12939 | ||
|
|
8a601ee058 | ||
|
|
95861d068d | ||
|
|
313adc7023 | ||
|
|
bb40455cb7 | ||
|
|
86053e9028 | ||
|
|
b4c986839e | ||
|
|
4d81a901cb | ||
|
|
c11888c906 | ||
|
|
e6fb296666 | ||
|
|
d425c9f071 | ||
|
|
e219ee2d84 | ||
|
|
e66d0216c6 | ||
|
|
e3e20d2923 | ||
|
|
e0c503e249 | ||
|
|
c3a8db0ea4 | ||
|
|
228cabab3a | ||
|
|
2b3e7f67a4 | ||
|
|
c2d5e8ec11 | ||
|
|
028c5366c2 | ||
|
|
cca0c3ae45 | ||
|
|
0b85a64dab | ||
|
|
4eafce998d | ||
|
|
4963d98331 | ||
|
|
1633acbdc3 | ||
|
|
9e07579a52 | ||
|
|
16a2d61874 | ||
|
|
75e8635563 | ||
|
|
83e94629dc | ||
|
|
b00b609621 | ||
|
|
cdbd72ec3d | ||
|
|
a6104ad8cd | ||
|
|
6e3160c502 | ||
|
|
104ec3188f | ||
|
|
4e37699e25 | ||
|
|
118252d3af | ||
|
|
509f06c2f0 | ||
|
|
12458af84a | ||
|
|
66bfc5748b | ||
|
|
cf32ba0351 | ||
|
|
b47a4703c4 | ||
|
|
4ceff430f9 | ||
|
|
5603516d88 | ||
|
|
013d95ebf3 | ||
|
|
eca8d116c2 | ||
|
|
8210808db0 | ||
|
|
4a003f2826 | ||
|
|
54f39494c0 | ||
|
|
ae4edf119f | ||
|
|
7be7b3389c | ||
|
|
54555cb3cd | ||
|
|
a56293e018 | ||
|
|
f8b2878943 | ||
|
|
a0c58317bd | ||
|
|
e687c64ca0 | ||
|
|
3ff5472543 | ||
|
|
d979c0c914 | ||
|
|
a41b767400 | ||
|
|
ba0a495d3c | ||
|
|
6435fa84af | ||
|
|
8743330ad8 | ||
|
|
a37aa5551a | ||
|
|
b2521cf8a3 | ||
|
|
c853b6593a | ||
|
|
d980711c30 | ||
|
|
7ab55e0694 | ||
|
|
db244937ff | ||
|
|
bcb593e6df | ||
|
|
9bfab69062 | ||
|
|
5451a9cd23 | ||
|
|
5ea83b8827 | ||
|
|
6fe7d92fea | ||
|
|
28b02a4ba8 | ||
|
|
f4d456fa4c | ||
|
|
687252b15b | ||
|
|
f21b1c87a6 | ||
|
|
ccbde6c470 | ||
|
|
f189c41421 | ||
|
|
5fcb990b1c | ||
|
|
30bd25d134 | ||
|
|
36d2b84301 | ||
|
|
a80b921927 | ||
|
|
1373d62c52 | ||
|
|
d0481482ae | ||
|
|
78b9be2e78 | ||
|
|
a3b7cf84d5 | ||
|
|
add6a60af4 | ||
|
|
4543c182b5 | ||
|
|
051d6e580c | ||
|
|
36e4c8c399 | ||
|
|
7921bd62d9 | ||
|
|
74ecc20751 | ||
|
|
d477de256a | ||
|
|
cffbc77d05 | ||
|
|
16fe23244b | ||
|
|
7d7b2c7544 | ||
|
|
d2c66894fd | ||
|
|
1cd3e69161 | ||
|
|
5850caff35 | ||
|
|
0cce55565a | ||
|
|
6b429c9da9 | ||
|
|
8f6151a575 | ||
|
|
3f19fac0b4 | ||
|
|
08e5ed6a8a | ||
|
|
e4d554689f | ||
|
|
19912c87c9 | ||
|
|
0de1e64958 | ||
|
|
aa0a795ec6 | ||
|
|
d1b062a418 | ||
|
|
812d8a34cf | ||
|
|
ec02d881c9 | ||
|
|
b706571a21 | ||
|
|
134f90a464 | ||
|
|
eb553fa96c | ||
|
|
fd8b7eb6fc | ||
|
|
501411f5b5 | ||
|
|
bee844fcdb | ||
|
|
d18a67e6d8 | ||
|
|
25d44b5b4c | ||
|
|
ee7de0cb41 | ||
|
|
28642b9f04 | ||
|
|
c18308c024 | ||
|
|
1eafbf2ee2 | ||
|
|
b98bcdd4a1 | ||
|
|
4c8cd5b6c6 | ||
|
|
5b37a91b46 | ||
|
|
52647cef23 | ||
|
|
11ec31aefb | ||
|
|
73ca1efbeb | ||
|
|
340aa48d93 | ||
|
|
8ff72b341e | ||
|
|
a23402075e | ||
|
|
6b86ed0117 | ||
|
|
e2a45cec8e | ||
|
|
ec2eb2bfa3 | ||
|
|
10adfc5124 | ||
|
|
5f7aae0e60 | ||
|
|
4952608b8d | ||
|
|
42ebefef92 | ||
|
|
03d33601b0 |
@@ -15,6 +15,7 @@ _APP_SYSTEM_TEAM_EMAIL=team@appwrite.io
|
||||
_APP_EMAIL_SECURITY=security@appwrite.io
|
||||
_APP_EMAIL_CERTIFICATES=certificates@appwrite.io
|
||||
_APP_SYSTEM_RESPONSE_FORMAT=
|
||||
_APP_CUSTOM_DOMAIN_DENY_LIST=
|
||||
_APP_OPTIONS_ABUSE=disabled
|
||||
_APP_OPTIONS_ROUTER_PROTECTION=disabled
|
||||
_APP_OPTIONS_FORCE_HTTPS=disabled
|
||||
@@ -81,11 +82,12 @@ _APP_EXECUTOR_SECRET=your-secret-key
|
||||
_APP_EXECUTOR_HOST=http://proxy/v1
|
||||
_APP_FUNCTIONS_RUNTIMES=php-8.0,node-18.0,python-3.9,ruby-3.1
|
||||
_APP_MAINTENANCE_INTERVAL=86400
|
||||
_APP_MAINTENANCE_DELAY=
|
||||
_APP_MAINTENANCE_START_TIME=12:00
|
||||
_APP_MAINTENANCE_RETENTION_CACHE=2592000
|
||||
_APP_MAINTENANCE_RETENTION_EXECUTION=1209600
|
||||
_APP_MAINTENANCE_RETENTION_ABUSE=86400
|
||||
_APP_MAINTENANCE_RETENTION_AUDIT=1209600
|
||||
_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE=15778800
|
||||
_APP_USAGE_AGGREGATION_INTERVAL=30
|
||||
_APP_STATS_RESOURCES_INTERVAL=3600
|
||||
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
|
||||
|
||||
+1
-8
@@ -7,6 +7,7 @@ tasks:
|
||||
docker pull composer
|
||||
command: |
|
||||
docker run --rm --interactive --tty \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
--volume $PWD:/app \
|
||||
composer install \
|
||||
--ignore-platform-reqs \
|
||||
@@ -23,11 +24,3 @@ vscode:
|
||||
extensions:
|
||||
- ms-azuretools.vscode-docker
|
||||
- zobo.php-intellisense
|
||||
|
||||
github:
|
||||
# https://www.gitpod.io/docs/prebuilds#github-specific-configuration
|
||||
prebuilds:
|
||||
# enable for pull requests coming from forks (defaults to false)
|
||||
pullRequestsFromForks: true
|
||||
# add a check to pull requests (defaults to true)
|
||||
addCheck: false
|
||||
|
||||
+264
@@ -1,3 +1,267 @@
|
||||
# Version 1.6.2
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Notable changes
|
||||
|
||||
* Delete git folder to reduce build size in [9076](https://github.com/appwrite/appwrite/pull/9076)
|
||||
* Upgrade assistant in [9100](https://github.com/appwrite/appwrite/pull/9100)
|
||||
* Use redis adapter for abuse in [9121](https://github.com/appwrite/appwrite/pull/9121)
|
||||
* Set base specification CPUs to 0.5 again in [9146](https://github.com/appwrite/appwrite/pull/9146)
|
||||
* Add new push message parameters in [9060](https://github.com/appwrite/appwrite/pull/9060)
|
||||
* Update audits to include user type in [9211](https://github.com/appwrite/appwrite/pull/9211)
|
||||
* Enable HEIC in [9251](https://github.com/appwrite/appwrite/pull/9251)
|
||||
* Added teamName to membership redirect url in [9269](https://github.com/appwrite/appwrite/pull/9269)
|
||||
* Add support endpoint url for S3 in [9303](https://github.com/appwrite/appwrite/pull/9303)
|
||||
* Added RuPay Credit Card Icon in Avatars Service in [5046](https://github.com/appwrite/appwrite/pull/5046)
|
||||
* Add figma oauth provider in [9623](https://github.com/appwrite/appwrite/pull/9623)
|
||||
* Update console to version 5.2.58 in [9637](https://github.com/appwrite/appwrite/pull/9637)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Remove failed attribute in [9032](https://github.com/appwrite/appwrite/pull/9032)
|
||||
* Fix delete notFound attribute in [9038](https://github.com/appwrite/appwrite/pull/9038)
|
||||
* 🇮🇸 Added missing Icelandic translations for email strings. in [4848](https://github.com/appwrite/appwrite/pull/4848)
|
||||
* fix doc comment for filter method in [5769](https://github.com/appwrite/appwrite/pull/5769)
|
||||
* Delete attribute No throwing Exception on not found in [9157](https://github.com/appwrite/appwrite/pull/9157)
|
||||
* Fix VCS identity collision in [9138](https://github.com/appwrite/appwrite/pull/9138)
|
||||
* Fix disabling of email-otp when user wants to in [9200](https://github.com/appwrite/appwrite/pull/9200)
|
||||
* Ensure user can delete session in [9209](https://github.com/appwrite/appwrite/pull/9209)
|
||||
* Fix resend invitation in [9218](https://github.com/appwrite/appwrite/pull/9218)
|
||||
* Fix phone number parsing exception handling in [9246](https://github.com/appwrite/appwrite/pull/9246)
|
||||
* Fix amazon oauth in [9253](https://github.com/appwrite/appwrite/pull/9253)
|
||||
* Fix slack oauth scopes, and updated to v2 in [9228](https://github.com/appwrite/appwrite/pull/9228)
|
||||
* Fix forwarded user agent in [9271](https://github.com/appwrite/appwrite/pull/9271)
|
||||
* Fix WEBP File Preview Rendering Issue in [9321](https://github.com/appwrite/appwrite/pull/9321)
|
||||
* Fix build memory specifications in [9360](https://github.com/appwrite/appwrite/pull/9360)
|
||||
* Fix Self Hosting functions by adding missed config in [9373](https://github.com/appwrite/appwrite/pull/9373)
|
||||
* Fix resend team invite if already accepted in [9348](https://github.com/appwrite/appwrite/pull/9348)
|
||||
* Fix null errors on team invite in [9391](https://github.com/appwrite/appwrite/pull/9391)
|
||||
* Fix email (smtp) to multiple recipients in [9243](https://github.com/appwrite/appwrite/pull/9243)
|
||||
* Fix stats timing by using receivedAt date when available in [9428](https://github.com/appwrite/appwrite/pull/9428)
|
||||
* Make min/max params optional for attribute update in [9387](https://github.com/appwrite/appwrite/pull/9387)
|
||||
* Fix blocking of phone sessions when disabled on console in [9447](https://github.com/appwrite/appwrite/pull/9447)
|
||||
* Fix logging config in [9467](https://github.com/appwrite/appwrite/pull/9467)
|
||||
* Update audit timestamp origin in [9481](https://github.com/appwrite/appwrite/pull/9481)
|
||||
* Fix certificates in deletes worker in [9466](https://github.com/appwrite/appwrite/pull/9466)
|
||||
* Fix console audits delete in [9547](https://github.com/appwrite/appwrite/pull/9547)
|
||||
* Fix migrations in [9633](https://github.com/appwrite/appwrite/pull/9633)
|
||||
* Ensure all 4xx errors in OAuth redirect lead to the failure URL in [9679](https://github.com/appwrite/appwrite/pull/9679)
|
||||
* Treat 0 as unlimited for CPUs and memory in [9638](https://github.com/appwrite/appwrite/pull/9638)
|
||||
* Add contextual dispatch logic to fix high CPU usage in [9687](https://github.com/appwrite/appwrite/pull/9687)
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
* Merge 1.6.x into feat-custom-cf-hostnames in [8904](https://github.com/appwrite/appwrite/pull/8904)
|
||||
* Improve compression param checks in [8922](https://github.com/appwrite/appwrite/pull/8922)
|
||||
* upgrade utopia storage in [8930](https://github.com/appwrite/appwrite/pull/8930)
|
||||
* Feat migration in [8797](https://github.com/appwrite/appwrite/pull/8797)
|
||||
* feat fix web routes in [8962](https://github.com/appwrite/appwrite/pull/8962)
|
||||
* Fix no pool access in [9027](https://github.com/appwrite/appwrite/pull/9027)
|
||||
* feat: use environment variable to check rules format in [9039](https://github.com/appwrite/appwrite/pull/9039)
|
||||
* Update storage.php in [9037](https://github.com/appwrite/appwrite/pull/9037)
|
||||
* Upgrade db 0.53.200 in [9050](https://github.com/appwrite/appwrite/pull/9050)
|
||||
* Chore: upgrade utopia storage in [9066](https://github.com/appwrite/appwrite/pull/9066)
|
||||
* Update usage-dump payload in [9085](https://github.com/appwrite/appwrite/pull/9085)
|
||||
* GitHub Workflows security hardening in [3728](https://github.com/appwrite/appwrite/pull/3728)
|
||||
* Update add-oauth2-provider.md in [4313](https://github.com/appwrite/appwrite/pull/4313)
|
||||
* update readme-cn some doc in [5278](https://github.com/appwrite/appwrite/pull/5278)
|
||||
* Add accessibility features in [7042](https://github.com/appwrite/appwrite/pull/7042)
|
||||
* Add Appwrite Cloud to read me. in [5445](https://github.com/appwrite/appwrite/pull/5445)
|
||||
* Migration throw error in [9092](https://github.com/appwrite/appwrite/pull/9092)
|
||||
* Fix usage payload bug in [9097](https://github.com/appwrite/appwrite/pull/9097)
|
||||
* chore: replace occurrences of dbForConsole to dbForPlatform in [9096](https://github.com/appwrite/appwrite/pull/9096)
|
||||
* fix(realtime): decrement connectionCounter only if connection is known in [9055](https://github.com/appwrite/appwrite/pull/9055)
|
||||
* payload bug fix in [9098](https://github.com/appwrite/appwrite/pull/9098)
|
||||
* Fix usage payload bug in [9099](https://github.com/appwrite/appwrite/pull/9099)
|
||||
* Usage payload debug in [9101](https://github.com/appwrite/appwrite/pull/9101)
|
||||
* Usage payload debug in [9103](https://github.com/appwrite/appwrite/pull/9103)
|
||||
* Usage payload debug in [9104](https://github.com/appwrite/appwrite/pull/9104)
|
||||
* Feat: createFunction abuse labels in [9102](https://github.com/appwrite/appwrite/pull/9102)
|
||||
* Docs-create-document in [9105](https://github.com/appwrite/appwrite/pull/9105)
|
||||
* Docs: Create document and unknown attribute error messages. in [5427](https://github.com/appwrite/appwrite/pull/5427)
|
||||
* Fix: update project accessed at from router and schedulers in [9109](https://github.com/appwrite/appwrite/pull/9109)
|
||||
* chore: initial commit in [9111](https://github.com/appwrite/appwrite/pull/9111)
|
||||
* chore: optimise webhooks payload in [9115](https://github.com/appwrite/appwrite/pull/9115)
|
||||
* Revert "chore: initial commit" in [9117](https://github.com/appwrite/appwrite/pull/9117)
|
||||
* chore: fix attribute name in [9118](https://github.com/appwrite/appwrite/pull/9118)
|
||||
* Migrate to redis abuse in [9124](https://github.com/appwrite/appwrite/pull/9124)
|
||||
* Added webhooks usage stats in [9125](https://github.com/appwrite/appwrite/pull/9125)
|
||||
* chore remove abuse cleanup in [9137](https://github.com/appwrite/appwrite/pull/9137)
|
||||
* fix: remove abuse delete trigger in [9139](https://github.com/appwrite/appwrite/pull/9139)
|
||||
* Remove firebase OAuth API endpoints in [9144](https://github.com/appwrite/appwrite/pull/9144)
|
||||
* chore: release client sdks in [9112](https://github.com/appwrite/appwrite/pull/9112)
|
||||
* Update general.php in [9155](https://github.com/appwrite/appwrite/pull/9155)
|
||||
* feat(swoole): allow configuration override of available cpus in [9177](https://github.com/appwrite/appwrite/pull/9177)
|
||||
* Usage databases api read writes addition in [9142](https://github.com/appwrite/appwrite/pull/9142)
|
||||
* Fix dead connections in [9190](https://github.com/appwrite/appwrite/pull/9190)
|
||||
* Add hostname to audits in [9165](https://github.com/appwrite/appwrite/pull/9165)
|
||||
* chore: shifted authphone usage tracking to api calls in [9191](https://github.com/appwrite/appwrite/pull/9191)
|
||||
* Revert "Fix dead connections" in [9201](https://github.com/appwrite/appwrite/pull/9201)
|
||||
* Add assertEventually to messaging provider logs test in [9192](https://github.com/appwrite/appwrite/pull/9192)
|
||||
* feat project sms usage in [9198](https://github.com/appwrite/appwrite/pull/9198)
|
||||
* chore: add audit labels to project resources in [9056](https://github.com/appwrite/appwrite/pull/9056)
|
||||
* fix sms usage in [9207](https://github.com/appwrite/appwrite/pull/9207)
|
||||
* Update database in [9202](https://github.com/appwrite/appwrite/pull/9202)
|
||||
* Fix dead connections in [9213](https://github.com/appwrite/appwrite/pull/9213)
|
||||
* Revert "Fix dead connections" in [9214](https://github.com/appwrite/appwrite/pull/9214)
|
||||
* Add logs db init for consistency in [9163](https://github.com/appwrite/appwrite/pull/9163)
|
||||
* Split the collection definitions in [9153](https://github.com/appwrite/appwrite/pull/9153)
|
||||
* Log path with populated parameters in [9220](https://github.com/appwrite/appwrite/pull/9220)
|
||||
* Add missing scope on function template in [9208](https://github.com/appwrite/appwrite/pull/9208)
|
||||
* Add relatedCollection default in [9225](https://github.com/appwrite/appwrite/pull/9225)
|
||||
* fix: function usage in [9235](https://github.com/appwrite/appwrite/pull/9235)
|
||||
* feat: optimise events payloads in [9232](https://github.com/appwrite/appwrite/pull/9232)
|
||||
* Optimise webhook events in [9168](https://github.com/appwrite/appwrite/pull/9168)
|
||||
* fix: maintenance job missing type in [9238](https://github.com/appwrite/appwrite/pull/9238)
|
||||
* Update Fetch to 0.3.0 in [9245](https://github.com/appwrite/appwrite/pull/9245)
|
||||
* Fix maintenance job in [9247](https://github.com/appwrite/appwrite/pull/9247)
|
||||
* chore: add missing case for executions in [9248](https://github.com/appwrite/appwrite/pull/9248)
|
||||
* Add index dependency exception in [9226](https://github.com/appwrite/appwrite/pull/9226)
|
||||
* chore: fix benchmarking test when made from fork in [9233](https://github.com/appwrite/appwrite/pull/9233)
|
||||
* Update SDK Generator versions in [9188](https://github.com/appwrite/appwrite/pull/9188)
|
||||
* chore: skipped job instead of throwing error in [9250](https://github.com/appwrite/appwrite/pull/9250)
|
||||
* Implement new SDK Class on 1.6.x in [9237](https://github.com/appwrite/appwrite/pull/9237)
|
||||
* Delete collection before Appwrite's attributes in [9256](https://github.com/appwrite/appwrite/pull/9256)
|
||||
* Feat batch usage dump in [9255](https://github.com/appwrite/appwrite/pull/9255)
|
||||
* Fix cloud tests in [9261](https://github.com/appwrite/appwrite/pull/9261)
|
||||
* Usage: Databases reads writes in [9260](https://github.com/appwrite/appwrite/pull/9260)
|
||||
* Update: Latest sdk specs in [9274](https://github.com/appwrite/appwrite/pull/9274)
|
||||
* Revert "Feat batch usage dump" in [9276](https://github.com/appwrite/appwrite/pull/9276)
|
||||
* feat: add fast2SMS adapter in [9263](https://github.com/appwrite/appwrite/pull/9263)
|
||||
* Update Sdk Generator dependency in [9280](https://github.com/appwrite/appwrite/pull/9280)
|
||||
* Transformed at addition in [9281](https://github.com/appwrite/appwrite/pull/9281)
|
||||
* Docs: clarify update endpoints only work on draft messages in [9236](https://github.com/appwrite/appwrite/pull/9236)
|
||||
* Update sdk generator dependency in [9282](https://github.com/appwrite/appwrite/pull/9282)
|
||||
* Revert "Transformed at addition" in [9284](https://github.com/appwrite/appwrite/pull/9284)
|
||||
* replaced init for cloud link in [9285](https://github.com/appwrite/appwrite/pull/9285)
|
||||
* Add transformed at in [9289](https://github.com/appwrite/appwrite/pull/9289)
|
||||
* Make migrations use Dynamic keys for destination in [9291](https://github.com/appwrite/appwrite/pull/9291)
|
||||
* Make sessions limit tests assert eventually in [9298](https://github.com/appwrite/appwrite/pull/9298)
|
||||
* Chore update database in [9306](https://github.com/appwrite/appwrite/pull/9306)
|
||||
* feat: add AMQP queues in [9287](https://github.com/appwrite/appwrite/pull/9287)
|
||||
* fix(test): use assertEventually instead of while(true) in [9308](https://github.com/appwrite/appwrite/pull/9308)
|
||||
* fix(certificate worker): events are published without queue name in [9309](https://github.com/appwrite/appwrite/pull/9309)
|
||||
* chore: update utopia-php/queue to 0.8.1 in [9311](https://github.com/appwrite/appwrite/pull/9311)
|
||||
* chore: update utopia-php/queue to 0.8.2 in [9312](https://github.com/appwrite/appwrite/pull/9312)
|
||||
* fix(schedule-tasks): revert back to direct pool usage in [9313](https://github.com/appwrite/appwrite/pull/9313)
|
||||
* feat: custom app schemes in [9262](https://github.com/appwrite/appwrite/pull/9262)
|
||||
* Revert "feat: custom app schemes" in [9319](https://github.com/appwrite/appwrite/pull/9319)
|
||||
* Restore "feat: custom app schemes"" in [9320](https://github.com/appwrite/appwrite/pull/9320)
|
||||
* Revert "Restore "feat: custom app schemes""" in [9323](https://github.com/appwrite/appwrite/pull/9323)
|
||||
* chore: update dependencies in [9330](https://github.com/appwrite/appwrite/pull/9330)
|
||||
* Feat: logs DB in [9272](https://github.com/appwrite/appwrite/pull/9272)
|
||||
* Catch invalid index in [9329](https://github.com/appwrite/appwrite/pull/9329)
|
||||
* Fix: missing call for image transformations counting in [9342](https://github.com/appwrite/appwrite/pull/9342)
|
||||
* Fix drop abuse on shared table project delete in [9346](https://github.com/appwrite/appwrite/pull/9346)
|
||||
* Only run all table mode tests on db update in [9338](https://github.com/appwrite/appwrite/pull/9338)
|
||||
* Fix: missing periodic metric in [9350](https://github.com/appwrite/appwrite/pull/9350)
|
||||
* feat(builds): check if function is blocked before building in [9332](https://github.com/appwrite/appwrite/pull/9332)
|
||||
* feat: batch create audit logs in [9347](https://github.com/appwrite/appwrite/pull/9347)
|
||||
* Chore: Update migrations in [9355](https://github.com/appwrite/appwrite/pull/9355)
|
||||
* Fix: metric time was not being written to DB in [9354](https://github.com/appwrite/appwrite/pull/9354)
|
||||
* Fix patch index validation in [9356](https://github.com/appwrite/appwrite/pull/9356)
|
||||
* Fix image trnasformation metrics in [9370](https://github.com/appwrite/appwrite/pull/9370)
|
||||
* Use batch delete in worker in [9375](https://github.com/appwrite/appwrite/pull/9375)
|
||||
* Fix Model Platform is missing response key: store in [9361](https://github.com/appwrite/appwrite/pull/9361)
|
||||
* Feat key segmented usage in [9336](https://github.com/appwrite/appwrite/pull/9336)
|
||||
* Feat messaging metrics in [9353](https://github.com/appwrite/appwrite/pull/9353)
|
||||
* Fix removed audits for shared v2 in [9388](https://github.com/appwrite/appwrite/pull/9388)
|
||||
* chore: bump utopia-php/image to 0.8.0 in [9390](https://github.com/appwrite/appwrite/pull/9390)
|
||||
* Fix outdated CLI commands in documentation in [9122](https://github.com/appwrite/appwrite/pull/9122)
|
||||
* disable logs display in [9398](https://github.com/appwrite/appwrite/pull/9398)
|
||||
* Log batches per project in [9403](https://github.com/appwrite/appwrite/pull/9403)
|
||||
* Batch per project in [9410](https://github.com/appwrite/appwrite/pull/9410)
|
||||
* Fix: stats resources only queue projects accessed in last 3 hours in [9411](https://github.com/appwrite/appwrite/pull/9411)
|
||||
* Track options requests in [9397](https://github.com/appwrite/appwrite/pull/9397)
|
||||
* chore: bump docker-base in [9406](https://github.com/appwrite/appwrite/pull/9406)
|
||||
* refactor: migrate Realtime::send calls to queueForRealtime in [9325](https://github.com/appwrite/appwrite/pull/9325)
|
||||
* Revert "Fix: stats resources only queue projects accessed in last 3 hours" in [9424](https://github.com/appwrite/appwrite/pull/9424)
|
||||
* Remove usage and usage dump in favor of stats-usage and stats-usage-dump in [9339](https://github.com/appwrite/appwrite/pull/9339)
|
||||
* Fix: disable dual writing in [9429](https://github.com/appwrite/appwrite/pull/9429)
|
||||
* Disable transformedAt update for console users in [9425](https://github.com/appwrite/appwrite/pull/9425)
|
||||
* chore: add image transformation stats to usage endpoint in [9393](https://github.com/appwrite/appwrite/pull/9393)
|
||||
* chore: added timeout to deployment builds in tests in [9426](https://github.com/appwrite/appwrite/pull/9426)
|
||||
* fix: model for image transformations in usage project in [9442](https://github.com/appwrite/appwrite/pull/9442)
|
||||
* Feat: calculate database storage in stats-resources in [9443](https://github.com/appwrite/appwrite/pull/9443)
|
||||
* Activities batch writes in [9438](https://github.com/appwrite/appwrite/pull/9438)
|
||||
* chore: bump cache 0.12.x in [9412](https://github.com/appwrite/appwrite/pull/9412)
|
||||
* chore: queue console project for maintenance delete in [9479](https://github.com/appwrite/appwrite/pull/9479)
|
||||
* chore: added logsdb for deletes worker in [9462](https://github.com/appwrite/appwrite/pull/9462)
|
||||
* Feat: calculate and log time taken for each project in [9491](https://github.com/appwrite/appwrite/pull/9491)
|
||||
* chore: update initializing dbForLogs in [9494](https://github.com/appwrite/appwrite/pull/9494)
|
||||
* Feat bulk audit delete in [9487](https://github.com/appwrite/appwrite/pull/9487)
|
||||
* Prepare 1.6.2 release in [9499](https://github.com/appwrite/appwrite/pull/9499)
|
||||
* Regenerate specs in [9497](https://github.com/appwrite/appwrite/pull/9497)
|
||||
* Regenerate examples in [9498](https://github.com/appwrite/appwrite/pull/9498)
|
||||
* chore: bump sdk in [9414](https://github.com/appwrite/appwrite/pull/9414)
|
||||
* update queue to 0.9.* in [9505](https://github.com/appwrite/appwrite/pull/9505)
|
||||
* Feat improve delete queries in [9507](https://github.com/appwrite/appwrite/pull/9507)
|
||||
* Feat: Add rule attributes in [9508](https://github.com/appwrite/appwrite/pull/9508)
|
||||
* Sync main into 1.6.x in [9496](https://github.com/appwrite/appwrite/pull/9496)
|
||||
* Bump console to version 5.2.53 in [9495](https://github.com/appwrite/appwrite/pull/9495)
|
||||
* Prepare 1.6.1 release in [9294](https://github.com/appwrite/appwrite/pull/9294)
|
||||
* Improve delete ordering in [9512](https://github.com/appwrite/appwrite/pull/9512)
|
||||
* Cleanups in [9511](https://github.com/appwrite/appwrite/pull/9511)
|
||||
* Feat dynamic regions in [9408](https://github.com/appwrite/appwrite/pull/9408)
|
||||
* Feat env vars to system lib in [9515](https://github.com/appwrite/appwrite/pull/9515)
|
||||
* Feat: domains count in [9514](https://github.com/appwrite/appwrite/pull/9514)
|
||||
* Migration read from db in [9529](https://github.com/appwrite/appwrite/pull/9529)
|
||||
* feat: add pool telemetry in [9530](https://github.com/appwrite/appwrite/pull/9530)
|
||||
* Disable PDO persistence since we manage our own pool in [9526](https://github.com/appwrite/appwrite/pull/9526)
|
||||
* chore: set min operations to 1 for reads and writes in [9536](https://github.com/appwrite/appwrite/pull/9536)
|
||||
* Remove default region in [9430](https://github.com/appwrite/appwrite/pull/9430)
|
||||
* Use cursor pagination with bigger limit for maintenance project loop in [9546](https://github.com/appwrite/appwrite/pull/9546)
|
||||
* chore: stop tests on failure in [9525](https://github.com/appwrite/appwrite/pull/9525)
|
||||
* chore: only update total count for privileged users in [9554](https://github.com/appwrite/appwrite/pull/9554)
|
||||
* refactor: initialization of audit retention in [9563](https://github.com/appwrite/appwrite/pull/9563)
|
||||
* Delete worker queries fixes in [9523](https://github.com/appwrite/appwrite/pull/9523)
|
||||
* Bump database 0.62.x in [9568](https://github.com/appwrite/appwrite/pull/9568)
|
||||
* Fix: schedules region filtering in [9577](https://github.com/appwrite/appwrite/pull/9577)
|
||||
* Deletes worker fix selects for pagination in [9578](https://github.com/appwrite/appwrite/pull/9578)
|
||||
* Add $permissions for delete documents selects in [9579](https://github.com/appwrite/appwrite/pull/9579)
|
||||
* chore(audits): return queue pre-fetch results in [9533](https://github.com/appwrite/appwrite/pull/9533)
|
||||
* Revert "chore(audits): return queue pre-fetch results" in [9586](https://github.com/appwrite/appwrite/pull/9586)
|
||||
* Feat multi tenant insert in [9573](https://github.com/appwrite/appwrite/pull/9573)
|
||||
* Add order by for cursor in [9588](https://github.com/appwrite/appwrite/pull/9588)
|
||||
* Feat update fetch in [9592](https://github.com/appwrite/appwrite/pull/9592)
|
||||
* Fix tenant casting in [9598](https://github.com/appwrite/appwrite/pull/9598)
|
||||
* Feat update ws in [9602](https://github.com/appwrite/appwrite/pull/9602)
|
||||
* Update database in [9603](https://github.com/appwrite/appwrite/pull/9603)
|
||||
* Fix: image transformation cache in [9608](https://github.com/appwrite/appwrite/pull/9608)
|
||||
* Remove audit payload in [9610](https://github.com/appwrite/appwrite/pull/9610)
|
||||
* Sample rate from DSN in [9559](https://github.com/appwrite/appwrite/pull/9559)
|
||||
* Restrict role change for sole org owner in [9615](https://github.com/appwrite/appwrite/pull/9615)
|
||||
* chore: update php image to 0.8.1 in [9616](https://github.com/appwrite/appwrite/pull/9616)
|
||||
* feat: refactor executor setup in [9420](https://github.com/appwrite/appwrite/pull/9420)
|
||||
* chore: update gitpod.yml config in [9561](https://github.com/appwrite/appwrite/pull/9561)
|
||||
* chore: update dependencies in [9625](https://github.com/appwrite/appwrite/pull/9625)
|
||||
* Update migrations lib in [9628](https://github.com/appwrite/appwrite/pull/9628)
|
||||
* feat: cache telemetry in [9624](https://github.com/appwrite/appwrite/pull/9624)
|
||||
* Bump console to version 5.2.56 in [9631](https://github.com/appwrite/appwrite/pull/9631)
|
||||
* Multi region support in [8667](https://github.com/appwrite/appwrite/pull/8667)
|
||||
* Revert "Multi region support" in [9632](https://github.com/appwrite/appwrite/pull/9632)
|
||||
* Revert "Revert "Multi region support"" in [9636](https://github.com/appwrite/appwrite/pull/9636)
|
||||
* Fix tasks in [9644](https://github.com/appwrite/appwrite/pull/9644)
|
||||
* chore: updated the migration version to 8.6 in [9646](https://github.com/appwrite/appwrite/pull/9646)
|
||||
* Fix: merge the working of StatsUsage and StatsUsageDump in [9585](https://github.com/appwrite/appwrite/pull/9585)
|
||||
* Update database in [9643](https://github.com/appwrite/appwrite/pull/9643)
|
||||
* chore: fix error logging for CLI tasks in [9651](https://github.com/appwrite/appwrite/pull/9651)
|
||||
* fix: usage test assertion in [9653](https://github.com/appwrite/appwrite/pull/9653)
|
||||
* Fix keys in [9656](https://github.com/appwrite/appwrite/pull/9656)
|
||||
* Feat: multi tenant dual writing in [9583](https://github.com/appwrite/appwrite/pull/9583)
|
||||
* Fix/throwing 400 for null order attributes in [9657](https://github.com/appwrite/appwrite/pull/9657)
|
||||
* feat: sdk group attribute in [9596](https://github.com/appwrite/appwrite/pull/9596)
|
||||
* Add configurable function and build size in [9648](https://github.com/appwrite/appwrite/pull/9648)
|
||||
* feat: update API endpoint in the code examples in [8933](https://github.com/appwrite/appwrite/pull/8933)
|
||||
* chore: abstract token secret hiding to response model in [9574](https://github.com/appwrite/appwrite/pull/9574)
|
||||
* chore: update sdks in [9655](https://github.com/appwrite/appwrite/pull/9655)
|
||||
* feat: allow non-critical events to ignore exceptions when enqueuing the event in [9680](https://github.com/appwrite/appwrite/pull/9680)
|
||||
* Revert "Add configurable function and build size" in [9681](https://github.com/appwrite/appwrite/pull/9681)
|
||||
* core: introduce endpoint.docs in specs in [9685](https://github.com/appwrite/appwrite/pull/9685)
|
||||
* fix: remove content-type header from get request specs in [9666](https://github.com/appwrite/appwrite/pull/9666)
|
||||
* chore: update flutter sdk in [9691](https://github.com/appwrite/appwrite/pull/9691)
|
||||
|
||||
# Version 1.6.1
|
||||
|
||||
## What's Changed
|
||||
|
||||
@@ -86,7 +86,6 @@ RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/worker-migrations && \
|
||||
chmod +x /usr/local/bin/worker-webhooks && \
|
||||
chmod +x /usr/local/bin/worker-stats-usage && \
|
||||
chmod +x /usr/local/bin/worker-stats-usage-dump && \
|
||||
chmod +x /usr/local/bin/stats-resources && \
|
||||
chmod +x /usr/local/bin/worker-stats-resources
|
||||
|
||||
|
||||
+54
-36
@@ -9,11 +9,16 @@ use Appwrite\Event\StatsResources;
|
||||
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;
|
||||
@@ -21,9 +26,13 @@ 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;
|
||||
use Utopia\Telemetry\Adapter\None as NoTelemetry;
|
||||
|
||||
use function Swoole\Coroutine\run;
|
||||
|
||||
// Overwriting runtimes to be architecture agnostic for CLI
|
||||
Config::setParam('runtimes', (new Runtimes('v4'))->getAll(supported: false));
|
||||
@@ -40,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));
|
||||
@@ -63,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')
|
||||
@@ -86,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);
|
||||
@@ -136,12 +137,8 @@ CLI::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;
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
@@ -167,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());
|
||||
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)
|
||||
@@ -198,14 +189,14 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
};
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
CLI::setResource('queueForStatsUsage', function (Connection $publisher) {
|
||||
CLI::setResource('queueForStatsUsage', function (Publisher $publisher) {
|
||||
return new StatsUsage($publisher);
|
||||
}, ['publisher']);
|
||||
CLI::setResource('queueForStatsResources', function (Publisher $publisher) {
|
||||
return new StatsResources($publisher);
|
||||
}, ['publisher']);
|
||||
CLI::setResource('publisher', function (Group $pools) {
|
||||
return $pools->get('publisher')->pop()->getResource();
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
CLI::setResource('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
@@ -218,6 +209,13 @@ CLI::setResource('queueForCertificates', function (Publisher $publisher) {
|
||||
}, ['publisher']);
|
||||
CLI::setResource('logError', function (Registry $register) {
|
||||
return function (Throwable $error, string $namespace, string $action) use ($register) {
|
||||
Console::error('[Error] Timestamp: ' . date('c', time()));
|
||||
Console::error('[Error] Type: ' . get_class($error));
|
||||
Console::error('[Error] Message: ' . $error->getMessage());
|
||||
Console::error('[Error] File: ' . $error->getFile());
|
||||
Console::error('[Error] Line: ' . $error->getLine());
|
||||
Console::error('[Error] Trace: ' . $error->getTraceAsString());
|
||||
|
||||
$logger = $register->get('logger');
|
||||
|
||||
if ($logger) {
|
||||
@@ -236,6 +234,7 @@ CLI::setResource('logError', function (Registry $register) {
|
||||
$log->addExtra('file', $error->getFile());
|
||||
$log->addExtra('line', $error->getLine());
|
||||
$log->addExtra('trace', $error->getTraceAsString());
|
||||
$log->addExtra('detailedTrace', $error->getTrace());
|
||||
|
||||
$log->setAction($action);
|
||||
|
||||
@@ -249,22 +248,41 @@ CLI::setResource('logError', function (Registry $register) {
|
||||
Console::error('Error pushing log: ' . $th->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Console::warning("Failed: {$error->getMessage()}");
|
||||
Console::warning($error->getTraceAsString());
|
||||
};
|
||||
}, ['register']);
|
||||
|
||||
$platform = new Appwrite();
|
||||
$platform->init(Service::TYPE_TASK);
|
||||
CLI::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
|
||||
|
||||
CLI::setResource('telemetry', fn () => new NoTelemetry());
|
||||
|
||||
$platform = new Appwrite();
|
||||
$args = $platform->getEnv('argv');
|
||||
|
||||
if (!isset($args[0])) {
|
||||
Console::error('Missing task name');
|
||||
Console::exit(1);
|
||||
}
|
||||
|
||||
\array_shift($args);
|
||||
$taskName = $args[0];
|
||||
$platform->init(Service::TYPE_TASK);
|
||||
$cli = $platform->getCli();
|
||||
|
||||
$cli
|
||||
->error()
|
||||
->inject('error')
|
||||
->action(function (Throwable $error) {
|
||||
Console::error($error->getMessage());
|
||||
->inject('logError')
|
||||
->action(function (Throwable $error, callable $logError) use ($taskName) {
|
||||
call_user_func_array($logError, [
|
||||
$error,
|
||||
'Task',
|
||||
$taskName,
|
||||
]);
|
||||
|
||||
Timer::clearAll();
|
||||
});
|
||||
|
||||
$cli->run();
|
||||
$cli->shutdown()->action(fn () => Timer::clearAll());
|
||||
|
||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||
run($cli->run(...));
|
||||
|
||||
@@ -1038,7 +1038,7 @@ return [
|
||||
'$id' => ID::custom('providerUid'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'size' => 2048, // Decrease to 128 as in index length?
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
@@ -1107,14 +1107,14 @@ return [
|
||||
'$id' => ID::custom('_key_userInternalId_provider_providerUid'),
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'attributes' => ['userInternalId', 'provider', 'providerUid'],
|
||||
'lengths' => [11, 128, 128],
|
||||
'lengths' => [11, 128, 128], // providerUid is length 2000!
|
||||
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_provider_providerUid'),
|
||||
'type' => Database::INDEX_UNIQUE,
|
||||
'attributes' => ['provider', 'providerUid'],
|
||||
'lengths' => [128, 128],
|
||||
'lengths' => [128, 128], // providerUid is length 2000!
|
||||
'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
@@ -2352,6 +2352,20 @@ return [
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_expired'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['expired'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_session_internal_id'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['sessionInternalId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -356,7 +356,21 @@ return [
|
||||
'attributes' => ['pingedAt'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
]
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_database'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['database'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_region_accessed_at'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['region', 'accessedAt'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -480,6 +494,20 @@ return [
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_project_id_region'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectId', 'region'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_region_rt_active'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['region', 'resourceType', 'active'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1147,6 +1175,13 @@ return [
|
||||
'lengths' => [16],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_piid_riid_rt'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'resourceInternalId', 'resourceType'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1442,7 +1477,14 @@ return [
|
||||
'attributes' => ['resourceType'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
]
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_piid_riid_rt'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'resourceInternalId', 'resourceType'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1590,6 +1632,13 @@ return [
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_piid_prid_rt'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['projectInternalId', 'providerRepositoryId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -1182,6 +1182,13 @@ return [
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_resource_internal_id'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['resourceInternalId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1330,7 +1337,14 @@ return [
|
||||
'attributes' => ['deploymentId'],
|
||||
'lengths' => [Database::LENGTH_KEY],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
]
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_deployment_internal_id'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['deploymentInternalId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1602,6 +1616,13 @@ return [
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_function_internal_id'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['functionInternalId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1720,6 +1741,13 @@ return [
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_resource_internal_id_resource_type'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['resourceInternalId', 'resourceType'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ return [
|
||||
],
|
||||
Exception::GENERAL_ROUTE_NOT_FOUND => [
|
||||
'name' => Exception::GENERAL_ROUTE_NOT_FOUND,
|
||||
'description' => 'The requested route was not found. Please refer to the API docs and try again.',
|
||||
'description' => 'Route not found. Please ensure the endpoint is configured correctly and that the API route is valid for this SDK version. Refer to the API docs for more details.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::GENERAL_CURSOR_NOT_FOUND => [
|
||||
@@ -593,6 +593,11 @@ return [
|
||||
'description' => 'Database timed out. Try adjusting your queries or adding an index.',
|
||||
'code' => 408
|
||||
],
|
||||
Exception::DATABASE_QUERY_ORDER_NULL => [
|
||||
'name' => Exception::DATABASE_QUERY_ORDER_NULL,
|
||||
'description' => 'The order attribute had a null value. Cursor pagination requires all documents order attribute values are non-null.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Collections */
|
||||
Exception::COLLECTION_NOT_FOUND => [
|
||||
|
||||
@@ -142,6 +142,16 @@ return [
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'figma' => [
|
||||
'name' => 'Figma',
|
||||
'developers' => 'https://www.figma.com/developers/api#oauth2',
|
||||
'icon' => 'icon-figma',
|
||||
'enabled' => true,
|
||||
'sandbox' => false,
|
||||
'form' => false,
|
||||
'beta' => false,
|
||||
'mock' => false,
|
||||
],
|
||||
'github' => [
|
||||
'name' => 'GitHub',
|
||||
'developers' => 'https://developer.github.com/',
|
||||
|
||||
+16
-16
@@ -11,7 +11,7 @@ return [
|
||||
[
|
||||
'key' => 'web',
|
||||
'name' => 'Web',
|
||||
'version' => '17.0.1',
|
||||
'version' => '17.0.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-web',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -59,7 +59,7 @@ return [
|
||||
[
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
'version' => '15.0.0',
|
||||
'version' => '15.0.2',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-flutter',
|
||||
'package' => 'https://pub.dev/packages/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -77,7 +77,7 @@ return [
|
||||
[
|
||||
'key' => 'apple',
|
||||
'name' => 'Apple',
|
||||
'version' => '9.0.0',
|
||||
'version' => '9.0.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-apple',
|
||||
'enabled' => true,
|
||||
@@ -112,7 +112,7 @@ return [
|
||||
[
|
||||
'key' => 'android',
|
||||
'name' => 'Android',
|
||||
'version' => '6.1.0',
|
||||
'version' => '7.0.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-android',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
|
||||
'enabled' => true,
|
||||
@@ -134,7 +134,7 @@ return [
|
||||
[
|
||||
'key' => 'react-native',
|
||||
'name' => 'React Native',
|
||||
'version' => '0.7.1',
|
||||
'version' => '0.7.4',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-react-native',
|
||||
'package' => 'https://npmjs.com/package/react-native-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -217,7 +217,7 @@ return [
|
||||
[
|
||||
'key' => 'cli',
|
||||
'name' => 'Command Line',
|
||||
'version' => '6.2.2',
|
||||
'version' => '6.2.3',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
@@ -245,7 +245,7 @@ return [
|
||||
[
|
||||
'key' => 'nodejs',
|
||||
'name' => 'Node.js',
|
||||
'version' => '15.0.1',
|
||||
'version' => '16.1.0-rc.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-node',
|
||||
'package' => 'https://www.npmjs.com/package/node-appwrite',
|
||||
'enabled' => true,
|
||||
@@ -263,7 +263,7 @@ return [
|
||||
[
|
||||
'key' => 'deno',
|
||||
'name' => 'Deno',
|
||||
'version' => '12.2.0',
|
||||
'version' => '14.1.0-rc.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-deno',
|
||||
'package' => 'https://deno.land/x/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -281,7 +281,7 @@ return [
|
||||
[
|
||||
'key' => 'php',
|
||||
'name' => 'PHP',
|
||||
'version' => '12.2.0',
|
||||
'version' => '14.1.0-rc.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-php',
|
||||
'package' => 'https://packagist.org/packages/appwrite/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -299,7 +299,7 @@ return [
|
||||
[
|
||||
'key' => 'python',
|
||||
'name' => 'Python',
|
||||
'version' => '9.0.2',
|
||||
'version' => '10.1.0-rc.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-python',
|
||||
'package' => 'https://pypi.org/project/appwrite/',
|
||||
'enabled' => true,
|
||||
@@ -317,7 +317,7 @@ return [
|
||||
[
|
||||
'key' => 'ruby',
|
||||
'name' => 'Ruby',
|
||||
'version' => '12.2.0',
|
||||
'version' => '15.1.0-rc.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-ruby',
|
||||
'package' => 'https://rubygems.org/gems/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -335,7 +335,7 @@ return [
|
||||
[
|
||||
'key' => 'go',
|
||||
'name' => 'Go',
|
||||
'version' => '0.3.0',
|
||||
'version' => '0.6.0-rc.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-go',
|
||||
'enabled' => true,
|
||||
@@ -353,7 +353,7 @@ return [
|
||||
[
|
||||
'key' => 'dotnet',
|
||||
'name' => '.NET',
|
||||
'version' => '0.11.0',
|
||||
'version' => '0.13.0-rc.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
|
||||
'package' => 'https://www.nuget.org/packages/Appwrite',
|
||||
'enabled' => true,
|
||||
@@ -371,7 +371,7 @@ return [
|
||||
[
|
||||
'key' => 'dart',
|
||||
'name' => 'Dart',
|
||||
'version' => '14.0.0',
|
||||
'version' => '15.1.0-rc.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
||||
'package' => 'https://pub.dev/packages/dart_appwrite',
|
||||
'enabled' => true,
|
||||
@@ -389,7 +389,7 @@ return [
|
||||
[
|
||||
'key' => 'kotlin',
|
||||
'name' => 'Kotlin',
|
||||
'version' => '6.2.0',
|
||||
'version' => '8.1.0-rc.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
|
||||
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
|
||||
'enabled' => true,
|
||||
@@ -411,7 +411,7 @@ return [
|
||||
[
|
||||
'key' => 'swift',
|
||||
'name' => 'Swift',
|
||||
'version' => '8.0.0',
|
||||
'version' => '9.1.0-rc.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'package' => 'https://github.com/appwrite/sdk-for-swift',
|
||||
'enabled' => true,
|
||||
|
||||
@@ -65,9 +65,6 @@ return [
|
||||
'tests' => false,
|
||||
'optional' => true,
|
||||
'icon' => '/images/services/databases.png',
|
||||
'globalAttributes' => [
|
||||
'databaseId'
|
||||
]
|
||||
],
|
||||
'locale' => [
|
||||
'key' => 'locale',
|
||||
|
||||
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
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
@@ -4,7 +4,6 @@ return [
|
||||
// Accepted inputs files
|
||||
"jpg" => "image/jpeg",
|
||||
"jpeg" => "image/jpeg",
|
||||
"gif" => "image/gif",
|
||||
"png" => "image/png",
|
||||
"heic" => "image/heic",
|
||||
"webp" => "image/webp",
|
||||
|
||||
@@ -4,7 +4,6 @@ return [
|
||||
// Accepted outputs files
|
||||
"jpg" => "image/jpeg",
|
||||
"jpeg" => "image/jpeg",
|
||||
"gif" => "image/gif",
|
||||
"png" => "image/png",
|
||||
"webp" => "image/webp",
|
||||
"heic" => "image/heic",
|
||||
|
||||
@@ -79,6 +79,15 @@ return [
|
||||
'question' => 'Enter your Appwrite hostname',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_CUSTOM_DOMAIN_DENY_LIST',
|
||||
'description' => 'List of reserved or prohibited domains when configuring custom domains.',
|
||||
'introduction' => '',
|
||||
'default' => 'example.com,test.com,app.example.com',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_DOMAIN_FUNCTIONS',
|
||||
'description' => 'A domain to use for function preview URLs. Setting to empty turns off function preview URLs.',
|
||||
@@ -791,7 +800,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_FUNCTIONS_CPUS',
|
||||
'description' => 'The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it\'s empty, CPU limit will be disabled.',
|
||||
'description' => 'The maximum number of CPU core a single cloud function is allowed to use. Please note that setting a value higher than available cores will result in a function error, which might result in an error. The default value is empty. When it\'s empty or 0, CPU limit will be disabled.',
|
||||
'introduction' => '0.7.0',
|
||||
'default' => '0',
|
||||
'required' => false,
|
||||
@@ -800,7 +809,7 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_FUNCTIONS_MEMORY',
|
||||
'description' => 'The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty, memory limit will be disabled.',
|
||||
'description' => 'The maximum amount of memory a single cloud function is allowed to use in megabytes. The default value is empty. When it\'s empty or 0, memory limit will be disabled.',
|
||||
'introduction' => '0.7.0',
|
||||
'default' => '0',
|
||||
'required' => false,
|
||||
@@ -1021,13 +1030,22 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_DELAY',
|
||||
'description' => 'Delay value containing the number of seconds that the Appwrite maintenance process should wait before executing system cleanups and optimizations. The default value is 0 seconds.',
|
||||
'description' => 'Deprecated with 1.6.2 use _APP_MAINTENANCE_START_TIME instead to run the maintenance at a specific time per day.',
|
||||
'introduction' => '1.5.0',
|
||||
'default' => '0',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_START_TIME',
|
||||
'description' => 'The time of day (in 24-hour format) when the maintenance process should start. The default value is 00:00.',
|
||||
'introduction' => '1.6.2',
|
||||
'default' => '00:00',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_RETENTION_CACHE',
|
||||
'description' => 'The maximum duration (in seconds) upto which to retain cached files. The default value is 2592000 seconds (30 days).',
|
||||
@@ -1048,13 +1066,22 @@ return [
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT',
|
||||
'description' => 'IThe maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days).',
|
||||
'description' => 'The maximum duration (in seconds) upto which to retain audit logs. The default value is 1209600 seconds (14 days).',
|
||||
'introduction' => '0.7.0',
|
||||
'default' => '1209600',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE',
|
||||
'description' => 'The maximum duration (in seconds) upto which to retain console audit logs. The default value is 15778800 seconds (6 months).',
|
||||
'introduction' => '1.6.2',
|
||||
'default' => '15778800',
|
||||
'required' => false,
|
||||
'question' => '',
|
||||
'filter' => ''
|
||||
],
|
||||
[
|
||||
'name' => '_APP_MAINTENANCE_RETENTION_ABUSE',
|
||||
'description' => 'The maximum duration (in seconds) upto which to retain abuse logs. The default value is 86400 seconds (1 day).',
|
||||
|
||||
+90
-109
@@ -43,6 +43,7 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -155,9 +156,6 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
|
||||
|
||||
|
||||
$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails) {
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
/** @var Utopia\Database\Document $user */
|
||||
$userFromRequest = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
|
||||
@@ -273,7 +271,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
||||
->setAttribute('current', true)
|
||||
->setAttribute('countryName', $countryName)
|
||||
->setAttribute('expire', $expire)
|
||||
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $sessionSecret) : '')
|
||||
->setAttribute('secret', Auth::encodeSession($user->getId(), $sessionSecret))
|
||||
;
|
||||
|
||||
$response->dynamic($session, Response::MODEL_SESSION);
|
||||
@@ -289,6 +287,7 @@ App::post('/v1/account')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'create',
|
||||
description: '/docs/references/account/create.md',
|
||||
auth: [],
|
||||
@@ -432,6 +431,7 @@ App::get('/v1/account')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'get',
|
||||
description: '/docs/references/account/get.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -461,6 +461,7 @@ App::delete('/v1/account')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'delete',
|
||||
description: '/docs/references/account/delete.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -513,6 +514,7 @@ App::get('/v1/account/sessions')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'listSessions',
|
||||
description: '/docs/references/account/list-sessions.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -530,9 +532,6 @@ App::get('/v1/account/sessions')
|
||||
->inject('project')
|
||||
->action(function (Response $response, Document $user, Locale $locale, Document $project) {
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
$current = Auth::sessionVerify($sessions, Auth::$secret);
|
||||
@@ -542,7 +541,7 @@ App::get('/v1/account/sessions')
|
||||
|
||||
$session->setAttribute('countryName', $countryName);
|
||||
$session->setAttribute('current', ($current == $session->getId()) ? true : false);
|
||||
$session->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $session->getAttribute('secret', '') : '');
|
||||
$session->setAttribute('secret', $session->getAttribute('secret', ''));
|
||||
|
||||
$sessions[$key] = $session;
|
||||
}
|
||||
@@ -562,6 +561,7 @@ App::delete('/v1/account/sessions')
|
||||
->label('audits.resource', 'user/{user.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'deleteSessions',
|
||||
description: '/docs/references/account/delete-sessions.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -631,6 +631,7 @@ App::get('/v1/account/sessions/:sessionId')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'getSession',
|
||||
description: '/docs/references/account/get-session.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -649,10 +650,6 @@ App::get('/v1/account/sessions/:sessionId')
|
||||
->inject('project')
|
||||
->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Document $project) {
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
$sessionId = ($sessionId === 'current')
|
||||
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
|
||||
@@ -665,7 +662,7 @@ App::get('/v1/account/sessions/:sessionId')
|
||||
$session
|
||||
->setAttribute('current', ($session->getAttribute('secret') == Auth::hash(Auth::$secret)))
|
||||
->setAttribute('countryName', $countryName)
|
||||
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $session->getAttribute('secret', '') : '')
|
||||
->setAttribute('secret', $session->getAttribute('secret', ''))
|
||||
;
|
||||
|
||||
return $response->dynamic($session, Response::MODEL_SESSION);
|
||||
@@ -684,6 +681,7 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||
->label('audits.resource', 'user/{user.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'deleteSession',
|
||||
description: '/docs/references/account/delete-session.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -721,9 +719,7 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||
continue;
|
||||
}
|
||||
|
||||
$dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $session) {
|
||||
return $dbForProject->deleteDocument('sessions', $session->getId());
|
||||
});
|
||||
$dbForProject->deleteDocument('sessions', $session->getId());
|
||||
|
||||
unset($sessions[$key]);
|
||||
|
||||
@@ -772,6 +768,7 @@ App::patch('/v1/account/sessions/:sessionId')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'updateSession',
|
||||
description: '/docs/references/account/update-session.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -856,6 +853,7 @@ App::post('/v1/account/sessions/email')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'createEmailPasswordSession',
|
||||
description: '/docs/references/account/create-session-email-password.md',
|
||||
auth: [],
|
||||
@@ -897,10 +895,6 @@ App::post('/v1/account/sessions/email')
|
||||
throw new Exception(Exception::USER_BLOCKED); // User is in status blocked
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$user->setAttributes($profile->getArrayCopy());
|
||||
|
||||
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]);
|
||||
@@ -966,7 +960,7 @@ App::post('/v1/account/sessions/email')
|
||||
$session
|
||||
->setAttribute('current', true)
|
||||
->setAttribute('countryName', $countryName)
|
||||
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '')
|
||||
->setAttribute('secret', Auth::encodeSession($user->getId(), $secret))
|
||||
;
|
||||
|
||||
$queueForEvents
|
||||
@@ -996,6 +990,7 @@ App::post('/v1/account/sessions/anonymous')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'createAnonymousSession',
|
||||
description: '/docs/references/account/create-session-anonymous.md',
|
||||
auth: [],
|
||||
@@ -1019,9 +1014,6 @@ App::post('/v1/account/sessions/anonymous')
|
||||
->inject('queueForEvents')
|
||||
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents) {
|
||||
$protocol = $request->getProtocol();
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
if ('console' === $project->getId()) {
|
||||
throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED, 'Failed to create anonymous user');
|
||||
@@ -1123,7 +1115,7 @@ App::post('/v1/account/sessions/anonymous')
|
||||
$session
|
||||
->setAttribute('current', true)
|
||||
->setAttribute('countryName', $countryName)
|
||||
->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '')
|
||||
->setAttribute('secret', Auth::encodeSession($user->getId(), $secret))
|
||||
;
|
||||
|
||||
$response->dynamic($session, Response::MODEL_SESSION);
|
||||
@@ -1139,6 +1131,7 @@ App::post('/v1/account/sessions/token')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'createSession',
|
||||
description: '/docs/references/account/create-session.md',
|
||||
auth: [],
|
||||
@@ -1172,6 +1165,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
||||
->label('scope', 'sessions.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'createOAuth2Session',
|
||||
description: '/docs/references/account/create-session-oauth2.md',
|
||||
type: MethodType::WEBAUTH,
|
||||
@@ -1243,7 +1237,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
||||
});
|
||||
|
||||
App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
->desc('OAuth2 callback')
|
||||
->desc('Get OAuth2 callback')
|
||||
->groups(['account'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('scope', 'public')
|
||||
@@ -1273,7 +1267,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
});
|
||||
|
||||
App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
->desc('OAuth2 callback')
|
||||
->desc('Create OAuth2 callback')
|
||||
->groups(['account'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('scope', 'public')
|
||||
@@ -1304,7 +1298,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
});
|
||||
|
||||
App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->desc('OAuth2 redirect')
|
||||
->desc('Get OAuth2 redirect')
|
||||
->groups(['api', 'account', 'session'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
@@ -1449,7 +1443,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
Query::notEqual('userInternalId', $user->getInternalId()),
|
||||
]);
|
||||
if (!$identityWithMatchingEmail->isEmpty()) {
|
||||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
||||
$failureRedirect(Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$userWithMatchingEmail = $dbForProject->find('users', [
|
||||
@@ -1457,7 +1451,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
Query::notEqual('$id', $userId),
|
||||
]);
|
||||
if (!empty($userWithMatchingEmail)) {
|
||||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
||||
$failureRedirect(Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$sessionUpgrade = true;
|
||||
@@ -1486,7 +1480,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
|
||||
if ($user === false || $user->isEmpty()) { // No user logged in or with OAuth2 provider ID, create new one or connect with account with same email
|
||||
if (empty($email)) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
|
||||
$failureRedirect(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1529,7 +1523,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
Query::equal('providerEmail', [$email]),
|
||||
]);
|
||||
if (!$identityWithMatchingEmail->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
|
||||
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -1601,7 +1595,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
Query::notEqual('userInternalId', $user->getInternalId()),
|
||||
]);
|
||||
if (!empty($identitiesWithMatchingEmail)) {
|
||||
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
|
||||
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
|
||||
}
|
||||
|
||||
$dbForProject->createDocument('identities', new Document([
|
||||
@@ -1769,6 +1763,7 @@ App::get('/v1/account/tokens/oauth2/:provider')
|
||||
->label('scope', 'sessions.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'tokens',
|
||||
name: 'createOAuth2Token',
|
||||
description: '/docs/references/account/create-token-oauth2.md',
|
||||
auth: [],
|
||||
@@ -1849,6 +1844,7 @@ App::post('/v1/account/tokens/magic-url')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'tokens',
|
||||
name: 'createMagicURLToken',
|
||||
description: '/docs/references/account/create-token-magic-url.md',
|
||||
auth: [],
|
||||
@@ -1884,9 +1880,6 @@ App::post('/v1/account/tokens/magic-url')
|
||||
$phrase = Phrase::generate();
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$result = $dbForProject->findOne('users', [Query::equal('email', [$email])]);
|
||||
if (!$result->isEmpty()) {
|
||||
@@ -2006,6 +1999,7 @@ App::post('/v1/account/tokens/magic-url')
|
||||
|
||||
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
|
||||
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
|
||||
|
||||
$replyTo = "";
|
||||
|
||||
if ($smtpEnabled) {
|
||||
@@ -2068,17 +2062,11 @@ App::post('/v1/account/tokens/magic-url')
|
||||
->setRecipient($email)
|
||||
->trigger();
|
||||
|
||||
// Set to unhashed secret for events and server responses
|
||||
$token->setAttribute('secret', $tokenSecret);
|
||||
|
||||
$queueForEvents
|
||||
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
|
||||
|
||||
// Hide secret for clients
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$token->setAttribute('secret', '');
|
||||
}
|
||||
|
||||
if (!empty($phrase)) {
|
||||
$token->setAttribute('phrase', $phrase);
|
||||
}
|
||||
@@ -2098,6 +2086,7 @@ App::post('/v1/account/tokens/email')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'tokens',
|
||||
name: 'createEmailToken',
|
||||
description: '/docs/references/account/create-token-email.md',
|
||||
auth: [],
|
||||
@@ -2110,7 +2099,7 @@ App::post('/v1/account/tokens/email')
|
||||
contentType: ContentType::JSON,
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},email:{param-email}')
|
||||
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
|
||||
@@ -2131,10 +2120,6 @@ App::post('/v1/account/tokens/email')
|
||||
$phrase = Phrase::generate();
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$result = $dbForProject->findOne('users', [Query::equal('email', [$email])]);
|
||||
if (!$result->isEmpty()) {
|
||||
$user->setAttributes($result->getArrayCopy());
|
||||
@@ -2303,17 +2288,11 @@ App::post('/v1/account/tokens/email')
|
||||
->setRecipient($email)
|
||||
->trigger();
|
||||
|
||||
// Set to unhashed secret for events and server responses
|
||||
$token->setAttribute('secret', $tokenSecret);
|
||||
|
||||
$queueForEvents
|
||||
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
|
||||
|
||||
// Hide secret for clients
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$token->setAttribute('secret', '');
|
||||
}
|
||||
|
||||
if (!empty($phrase)) {
|
||||
$token->setAttribute('phrase', $phrase);
|
||||
}
|
||||
@@ -2333,6 +2312,7 @@ App::put('/v1/account/sessions/magic-url')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'updateMagicURLSession',
|
||||
description: '/docs/references/account/create-session.md',
|
||||
auth: [],
|
||||
@@ -2370,6 +2350,7 @@ App::put('/v1/account/sessions/phone')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'sessions',
|
||||
name: 'updatePhoneSession',
|
||||
description: '/docs/references/account/create-session.md',
|
||||
auth: [],
|
||||
@@ -2408,6 +2389,7 @@ App::post('/v1/account/tokens/phone')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'tokens',
|
||||
name: 'createPhoneToken',
|
||||
description: '/docs/references/account/create-token-phone.md',
|
||||
auth: [],
|
||||
@@ -2439,10 +2421,6 @@ App::post('/v1/account/tokens/phone')
|
||||
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$result = $dbForProject->findOne('users', [Query::equal('phone', [$phone])]);
|
||||
if (!$result->isEmpty()) {
|
||||
$user->setAttributes($result->getArrayCopy());
|
||||
@@ -2594,14 +2572,13 @@ App::post('/v1/account/tokens/phone')
|
||||
}
|
||||
}
|
||||
|
||||
// Set to unhashed secret for events and server responses
|
||||
$token->setAttribute('secret', $secret);
|
||||
|
||||
$queueForEvents
|
||||
->setPayload($response->output($token, Response::MODEL_TOKEN), sensitive: ['secret']);
|
||||
|
||||
// Hide secret for clients
|
||||
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? Auth::encodeSession($user->getId(), $secret) : '');
|
||||
// Encode secret for clients
|
||||
$token->setAttribute('secret', Auth::encodeSession($user->getId(), $secret));
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -2616,6 +2593,7 @@ App::post('/v1/account/jwts')
|
||||
->label('auth.type', 'jwt')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'tokens',
|
||||
name: 'createJWT',
|
||||
description: '/docs/references/account/create-jwt.md',
|
||||
auth: [],
|
||||
@@ -2664,6 +2642,7 @@ App::get('/v1/account/prefs')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'getPrefs',
|
||||
description: '/docs/references/account/get-prefs.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -2690,6 +2669,7 @@ App::get('/v1/account/logs')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'logs',
|
||||
name: 'listLogs',
|
||||
description: '/docs/references/account/list-logs.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -2766,6 +2746,7 @@ App::patch('/v1/account/name')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'updateName',
|
||||
description: '/docs/references/account/update-name.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -2787,7 +2768,7 @@ App::patch('/v1/account/name')
|
||||
|
||||
$user->setAttribute('name', $name);
|
||||
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
@@ -2804,6 +2785,7 @@ App::patch('/v1/account/password')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'updatePassword',
|
||||
description: '/docs/references/account/update-password.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -2861,7 +2843,7 @@ App::patch('/v1/account/password')
|
||||
->setAttribute('hash', Auth::DEFAULT_ALGO)
|
||||
->setAttribute('hashOptions', Auth::DEFAULT_ALGO_OPTIONS);
|
||||
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
@@ -2877,6 +2859,7 @@ App::patch('/v1/account/email')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'updateEmail',
|
||||
description: '/docs/references/account/update-email.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -2945,7 +2928,7 @@ App::patch('/v1/account/email')
|
||||
}
|
||||
|
||||
try {
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
/**
|
||||
* @var Document $oldTarget
|
||||
*/
|
||||
@@ -2973,6 +2956,7 @@ App::patch('/v1/account/phone')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'updatePhone',
|
||||
description: '/docs/references/account/update-phone.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3030,7 +3014,7 @@ App::patch('/v1/account/phone')
|
||||
}
|
||||
|
||||
try {
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
/**
|
||||
* @var Document $oldTarget
|
||||
*/
|
||||
@@ -3058,6 +3042,7 @@ App::patch('/v1/account/prefs')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'updatePrefs',
|
||||
description: '/docs/references/account/update-prefs.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3079,7 +3064,7 @@ App::patch('/v1/account/prefs')
|
||||
|
||||
$user->setAttribute('prefs', $prefs);
|
||||
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
@@ -3095,6 +3080,7 @@ App::patch('/v1/account/status')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'account',
|
||||
name: 'updateStatus',
|
||||
description: '/docs/references/account/update-status.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3116,7 +3102,7 @@ App::patch('/v1/account/status')
|
||||
|
||||
$user->setAttribute('status', false);
|
||||
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId())
|
||||
@@ -3145,6 +3131,7 @@ App::post('/v1/account/recovery')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'recovery',
|
||||
name: 'createRecovery',
|
||||
description: '/docs/references/account/create-recovery.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3174,11 +3161,6 @@ App::post('/v1/account/recovery')
|
||||
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
|
||||
}
|
||||
$url = htmlentities($url);
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$email = \strtolower($email);
|
||||
|
||||
$profile = $dbForProject->findOne('users', [
|
||||
@@ -3302,19 +3284,13 @@ App::post('/v1/account/recovery')
|
||||
->setSubject($subject)
|
||||
->trigger();
|
||||
|
||||
// Set to unhashed secret for events and server responses
|
||||
$recovery->setAttribute('secret', $secret);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $profile->getId())
|
||||
->setParam('tokenId', $recovery->getId())
|
||||
->setUser($profile)
|
||||
->setPayload($response->output($recovery, Response::MODEL_TOKEN), sensitive: ['secret']);
|
||||
|
||||
// Hide secret for clients
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$recovery->setAttribute('secret', '');
|
||||
}
|
||||
->setPayload(Response::showSensitive(fn () => $response->output($recovery, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -3322,7 +3298,7 @@ App::post('/v1/account/recovery')
|
||||
});
|
||||
|
||||
App::put('/v1/account/recovery')
|
||||
->desc('Create password recovery (confirmation)')
|
||||
->desc('Update password recovery (confirmation)')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'sessions.write')
|
||||
->label('event', 'users.[userId].recovery.[tokenId].update')
|
||||
@@ -3331,6 +3307,7 @@ App::put('/v1/account/recovery')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'recovery',
|
||||
name: 'updateRecovery',
|
||||
description: '/docs/references/account/update-recovery.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3407,7 +3384,7 @@ App::put('/v1/account/recovery')
|
||||
$queueForEvents
|
||||
->setParam('userId', $profile->getId())
|
||||
->setParam('tokenId', $recoveryDocument->getId())
|
||||
;
|
||||
->setPayload(Response::showSensitive(fn () => $response->output($recoveryDocument, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
|
||||
$response->dynamic($recoveryDocument, Response::MODEL_TOKEN);
|
||||
});
|
||||
@@ -3421,6 +3398,7 @@ App::post('/v1/account/verification')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'verification',
|
||||
name: 'createVerification',
|
||||
description: '/docs/references/account/create-email-verification.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3454,9 +3432,6 @@ App::post('/v1/account/verification')
|
||||
throw new Exception(Exception::USER_EMAIL_ALREADY_VERIFIED);
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
$verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION);
|
||||
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
|
||||
|
||||
@@ -3565,18 +3540,12 @@ App::post('/v1/account/verification')
|
||||
->setName($user->getAttribute('name') ?? '')
|
||||
->trigger();
|
||||
|
||||
// Set to unhashed secret for events and server responses
|
||||
$verification->setAttribute('secret', $verificationSecret);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('tokenId', $verification->getId())
|
||||
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);
|
||||
|
||||
// Hide secret for clients
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$verification->setAttribute('secret', '');
|
||||
}
|
||||
->setPayload(Response::showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -3584,7 +3553,7 @@ App::post('/v1/account/verification')
|
||||
});
|
||||
|
||||
App::put('/v1/account/verification')
|
||||
->desc('Create email verification (confirmation)')
|
||||
->desc('Update email verification (confirmation)')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'public')
|
||||
->label('event', 'users.[userId].verification.[tokenId].update')
|
||||
@@ -3592,6 +3561,7 @@ App::put('/v1/account/verification')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'verification',
|
||||
name: 'updateVerification',
|
||||
description: '/docs/references/account/update-email-verification.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3643,7 +3613,8 @@ App::put('/v1/account/verification')
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $userId)
|
||||
->setParam('tokenId', $verification->getId());
|
||||
->setParam('tokenId', $verification->getId())
|
||||
->setPayload(Response::showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
|
||||
$response->dynamic($verification, Response::MODEL_TOKEN);
|
||||
});
|
||||
@@ -3658,6 +3629,7 @@ App::post('/v1/account/verification/phone')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'verification',
|
||||
name: 'createPhoneVerification',
|
||||
description: '/docs/references/account/create-phone-verification.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3696,10 +3668,6 @@ App::post('/v1/account/verification/phone')
|
||||
throw new Exception(Exception::USER_PHONE_ALREADY_VERIFIED);
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
$secret = null;
|
||||
$sendSMS = true;
|
||||
$mockNumbers = $project->getAttribute('auths', [])['mockNumbers'] ?? [];
|
||||
@@ -3788,7 +3756,6 @@ App::post('/v1/account/verification/phone')
|
||||
}
|
||||
}
|
||||
|
||||
// Set to unhashed secret for events and server responses
|
||||
$verification->setAttribute('secret', $secret);
|
||||
|
||||
$queueForEvents
|
||||
@@ -3796,11 +3763,6 @@ App::post('/v1/account/verification/phone')
|
||||
->setParam('tokenId', $verification->getId())
|
||||
->setPayload($response->output($verification, Response::MODEL_TOKEN), sensitive: ['secret']);
|
||||
|
||||
// Hide secret for clients
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$verification->setAttribute('secret', '');
|
||||
}
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($verification, Response::MODEL_TOKEN);
|
||||
@@ -3815,6 +3777,7 @@ App::put('/v1/account/verification/phone')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'verification',
|
||||
name: 'updatePhoneVerification',
|
||||
description: '/docs/references/account/update-phone-verification.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3880,6 +3843,7 @@ App::patch('/v1/account/mfa')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'updateMFA',
|
||||
description: '/docs/references/account/update-mfa.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3902,7 +3866,7 @@ App::patch('/v1/account/mfa')
|
||||
|
||||
$user->setAttribute('mfa', $mfa);
|
||||
|
||||
$user = $dbForProject->withRequestTimestamp($requestTimestamp, fn () => $dbForProject->updateDocument('users', $user->getId(), $user));
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
if ($mfa) {
|
||||
$factors = $session->getAttribute('factors', []);
|
||||
@@ -3933,6 +3897,7 @@ App::get('/v1/account/mfa/factors')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'listMfaFactors',
|
||||
description: '/docs/references/account/list-mfa-factors.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -3973,6 +3938,7 @@ App::post('/v1/account/mfa/authenticators/:type')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'createMfaAuthenticator',
|
||||
description: '/docs/references/account/create-mfa-authenticator.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4040,7 +4006,7 @@ App::post('/v1/account/mfa/authenticators/:type')
|
||||
});
|
||||
|
||||
App::put('/v1/account/mfa/authenticators/:type')
|
||||
->desc('Verify authenticator')
|
||||
->desc('Update authenticator (confirmation)')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.mfa')
|
||||
->label('scope', 'account')
|
||||
@@ -4049,6 +4015,7 @@ App::put('/v1/account/mfa/authenticators/:type')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'updateMfaAuthenticator',
|
||||
description: '/docs/references/account/update-mfa-authenticator.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4118,6 +4085,7 @@ App::post('/v1/account/mfa/recovery-codes')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'createMfaRecoveryCodes',
|
||||
description: '/docs/references/account/create-mfa-recovery-codes.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4155,7 +4123,7 @@ App::post('/v1/account/mfa/recovery-codes')
|
||||
});
|
||||
|
||||
App::patch('/v1/account/mfa/recovery-codes')
|
||||
->desc('Regenerate MFA recovery codes')
|
||||
->desc('Update MFA recovery codes (regenerate)')
|
||||
->groups(['api', 'account', 'mfaProtected'])
|
||||
->label('event', 'users.[userId].update.mfa')
|
||||
->label('scope', 'account')
|
||||
@@ -4164,6 +4132,7 @@ App::patch('/v1/account/mfa/recovery-codes')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'updateMfaRecoveryCodes',
|
||||
description: '/docs/references/account/update-mfa-recovery-codes.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4200,11 +4169,12 @@ App::patch('/v1/account/mfa/recovery-codes')
|
||||
});
|
||||
|
||||
App::get('/v1/account/mfa/recovery-codes')
|
||||
->desc('Get MFA recovery codes')
|
||||
->desc('List MFA recovery codes')
|
||||
->groups(['api', 'account', 'mfaProtected'])
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'getMfaRecoveryCodes',
|
||||
description: '/docs/references/account/get-mfa-recovery-codes.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4243,6 +4213,7 @@ App::delete('/v1/account/mfa/authenticators/:type')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'deleteMfaAuthenticator',
|
||||
description: '/docs/references/account/delete-mfa-authenticator.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4288,6 +4259,7 @@ App::post('/v1/account/mfa/challenge')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'createMfaChallenge',
|
||||
description: '/docs/references/account/create-mfa-challenge.md',
|
||||
auth: [],
|
||||
@@ -4498,7 +4470,7 @@ App::post('/v1/account/mfa/challenge')
|
||||
});
|
||||
|
||||
App::put('/v1/account/mfa/challenge')
|
||||
->desc('Create MFA challenge (confirmation)')
|
||||
->desc('Update MFA challenge (confirmation)')
|
||||
->groups(['api', 'account', 'mfa'])
|
||||
->label('scope', 'account')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
@@ -4507,6 +4479,7 @@ App::put('/v1/account/mfa/challenge')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'mfa',
|
||||
name: 'updateMfaChallenge',
|
||||
description: '/docs/references/account/update-mfa-challenge.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4600,6 +4573,7 @@ App::post('/v1/account/targets/push')
|
||||
->label('event', 'users.[userId].targets.[targetId].create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'pushTargets',
|
||||
name: 'createPushTarget',
|
||||
description: '/docs/references/account/create-push-target.md',
|
||||
auth: [AuthType::SESSION],
|
||||
@@ -4680,6 +4654,7 @@ App::put('/v1/account/targets/:targetId/push')
|
||||
->label('event', 'users.[userId].targets.[targetId].update')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'pushTargets',
|
||||
name: 'updatePushTarget',
|
||||
description: '/docs/references/account/update-push-target.md',
|
||||
auth: [AuthType::SESSION],
|
||||
@@ -4744,6 +4719,7 @@ App::delete('/v1/account/targets/:targetId/push')
|
||||
->label('event', 'users.[userId].targets.[targetId].delete')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'pushTargets',
|
||||
name: 'deletePushTarget',
|
||||
description: '/docs/references/account/delete-push-target.md',
|
||||
auth: [AuthType::SESSION],
|
||||
@@ -4794,6 +4770,7 @@ App::get('/v1/account/identities')
|
||||
->label('scope', 'account')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'identities',
|
||||
name: 'listIdentities',
|
||||
description: '/docs/references/account/list-identities.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -4845,8 +4822,11 @@ App::get('/v1/account/identities')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
$results = $dbForProject->find('identities', $queries);
|
||||
try {
|
||||
$results = $dbForProject->find('identities', $queries);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
$total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
@@ -4865,6 +4845,7 @@ App::delete('/v1/account/identities/:identityId')
|
||||
->label('audits.userId', '{user.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'account',
|
||||
group: 'identities',
|
||||
name: 'deleteIdentity',
|
||||
description: '/docs/references/account/delete-identity.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
|
||||
@@ -171,6 +171,7 @@ App::get('/v1/avatars/credit-cards/:code')
|
||||
->label('cache.resource', 'avatar/credit-card')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getCreditCard',
|
||||
description: '/docs/references/avatars/get-credit-card.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -186,7 +187,7 @@ App::get('/v1/avatars/credit-cards/:code')
|
||||
->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-credit-cards'))), 'Credit Card Code. Possible values: ' . \implode(', ', \array_keys(Config::getParam('avatar-credit-cards'))) . '.')
|
||||
->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
|
||||
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
|
||||
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
|
||||
->param('quality', -1, new Range(-1, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true)
|
||||
->inject('response')
|
||||
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response));
|
||||
|
||||
@@ -198,6 +199,7 @@ App::get('/v1/avatars/browsers/:code')
|
||||
->label('cache.resource', 'avatar/browser')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getBrowser',
|
||||
description: '/docs/references/avatars/get-browser.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -213,7 +215,7 @@ App::get('/v1/avatars/browsers/:code')
|
||||
->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-browsers'))), 'Browser Code.')
|
||||
->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
|
||||
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
|
||||
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
|
||||
->param('quality', -1, new Range(-1, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true)
|
||||
->inject('response')
|
||||
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response));
|
||||
|
||||
@@ -225,6 +227,7 @@ App::get('/v1/avatars/flags/:code')
|
||||
->label('cache.resource', 'avatar/flag')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getFlag',
|
||||
description: '/docs/references/avatars/get-flag.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -240,7 +243,7 @@ App::get('/v1/avatars/flags/:code')
|
||||
->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-flags'))), 'Country Code. ISO Alpha-2 country code format.')
|
||||
->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
|
||||
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
|
||||
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
|
||||
->param('quality', -1, new Range(-1, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true)
|
||||
->inject('response')
|
||||
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response));
|
||||
|
||||
@@ -252,6 +255,7 @@ App::get('/v1/avatars/image')
|
||||
->label('cache.resource', 'avatar/image')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getImage',
|
||||
description: '/docs/references/avatars/get-image.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -322,6 +326,7 @@ App::get('/v1/avatars/favicon')
|
||||
->label('cache.resource', 'avatar/favicon')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getFavicon',
|
||||
description: '/docs/references/avatars/get-favicon.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -472,6 +477,7 @@ App::get('/v1/avatars/qr')
|
||||
->label('scope', 'avatars.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getQR',
|
||||
description: '/docs/references/avatars/get-qr.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -520,6 +526,7 @@ App::get('/v1/avatars/initials')
|
||||
->label('cache.resource', 'avatar/initials')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'avatars',
|
||||
group: null,
|
||||
name: 'getInitials',
|
||||
description: '/docs/references/avatars/get-initials.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
|
||||
@@ -27,6 +27,7 @@ App::get('/v1/console/variables')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'console',
|
||||
group: 'console',
|
||||
name: 'variables',
|
||||
description: '/docs/references/console/variables.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -67,11 +68,12 @@ App::get('/v1/console/variables')
|
||||
});
|
||||
|
||||
App::post('/v1/console/assistant')
|
||||
->desc('Ask query')
|
||||
->desc('Create assistant query')
|
||||
->groups(['api', 'assistant'])
|
||||
->label('scope', 'assistant.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'assistant',
|
||||
group: 'console',
|
||||
name: 'chat',
|
||||
description: '/docs/references/assistant/chat.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
+1103
-393
File diff suppressed because it is too large
Load Diff
@@ -37,6 +37,7 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -150,6 +151,7 @@ App::post('/v1/functions')
|
||||
->label('audits.resource', 'function/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'functions',
|
||||
name: 'create',
|
||||
description: '/docs/references/functions/create-function.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -184,8 +186,8 @@ App::post('/v1/functions')
|
||||
->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
|
||||
$plan,
|
||||
Config::getParam('runtime-specifications', []),
|
||||
App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
|
||||
App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
|
||||
System::getEnv('_APP_FUNCTIONS_CPUS', 0),
|
||||
System::getEnv('_APP_FUNCTIONS_MEMORY', 0)
|
||||
), 'Runtime specification for the function and builds.', true, ['plan'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
@@ -431,6 +433,7 @@ App::get('/v1/functions')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'functions',
|
||||
name: 'list',
|
||||
description: '/docs/references/functions/list-functions.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -483,10 +486,15 @@ App::get('/v1/functions')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
try {
|
||||
$functions = $dbForProject->find('functions', $queries);
|
||||
$total = $dbForProject->count('functions', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'functions' => $dbForProject->find('functions', $queries),
|
||||
'total' => $dbForProject->count('functions', $filterQueries, APP_LIMIT_COUNT),
|
||||
'functions' => $functions,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_FUNCTION_LIST);
|
||||
});
|
||||
|
||||
@@ -497,6 +505,7 @@ App::get('/v1/functions/runtimes')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'runtimes',
|
||||
name: 'listRuntimes',
|
||||
description: '/docs/references/functions/list-runtimes.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -536,6 +545,7 @@ App::get('/v1/functions/specifications')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'runtimes',
|
||||
name: 'listSpecifications',
|
||||
description: '/docs/references/functions/list-specifications.md',
|
||||
auth: [AuthType::KEY, AuthType::ADMIN],
|
||||
@@ -559,8 +569,12 @@ App::get('/v1/functions/specifications')
|
||||
$spec['enabled'] = in_array($spec['slug'], $plan['runtimeSpecifications']);
|
||||
}
|
||||
|
||||
$maxCpus = System::getEnv('_APP_FUNCTIONS_CPUS', 0);
|
||||
$maxMemory = System::getEnv('_APP_FUNCTIONS_MEMORY', 0);
|
||||
|
||||
// Only add specs that are within the limits set by environment variables
|
||||
if ($spec['cpus'] <= System::getEnv('_APP_FUNCTIONS_CPUS', 1) && $spec['memory'] <= System::getEnv('_APP_FUNCTIONS_MEMORY', 512)) {
|
||||
// Treat 0 as no limit
|
||||
if ((empty($maxCpus) || $spec['cpus'] <= $maxCpus) && (empty($maxMemory) || $spec['memory'] <= $maxMemory)) {
|
||||
$runtimeSpecs[] = $spec;
|
||||
}
|
||||
}
|
||||
@@ -578,6 +592,7 @@ App::get('/v1/functions/:functionId')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'functions',
|
||||
name: 'get',
|
||||
description: '/docs/references/functions/get-function.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -608,6 +623,7 @@ App::get('/v1/functions/:functionId/usage')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: null,
|
||||
name: 'getFunctionUsage',
|
||||
description: '/docs/references/functions/get-function-usage.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -719,6 +735,7 @@ App::get('/v1/functions/usage')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: null,
|
||||
name: 'getUsage',
|
||||
description: '/docs/references/functions/get-functions-usage.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -828,6 +845,7 @@ App::put('/v1/functions/:functionId')
|
||||
->label('audits.resource', 'function/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'functions',
|
||||
name: 'update',
|
||||
description: '/docs/references/functions/update-function.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -858,8 +876,8 @@ App::put('/v1/functions/:functionId')
|
||||
->param('specification', APP_FUNCTION_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
|
||||
$plan,
|
||||
Config::getParam('runtime-specifications', []),
|
||||
App::getEnv('_APP_FUNCTIONS_CPUS', APP_FUNCTION_CPUS_DEFAULT),
|
||||
App::getEnv('_APP_FUNCTIONS_MEMORY', APP_FUNCTION_MEMORY_DEFAULT)
|
||||
System::getEnv('_APP_FUNCTIONS_CPUS', 0),
|
||||
System::getEnv('_APP_FUNCTIONS_MEMORY', 0)
|
||||
), 'Runtime specification for the function and builds.', true, ['plan'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
@@ -869,7 +887,8 @@ App::put('/v1/functions/:functionId')
|
||||
->inject('queueForBuilds')
|
||||
->inject('dbForPlatform')
|
||||
->inject('gitHub')
|
||||
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, ?string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForPlatform, GitHub $github) use ($redeployVcs) {
|
||||
->inject('executor')
|
||||
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, ?string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForPlatform, GitHub $github, Executor $executor) use ($redeployVcs) {
|
||||
// TODO: If only branch changes, re-deploy
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
@@ -972,7 +991,6 @@ App::put('/v1/functions/:functionId')
|
||||
|
||||
// Enforce Cold Start if spec limits change.
|
||||
if ($function->getAttribute('specification') !== $specification && !empty($function->getAttribute('deployment'))) {
|
||||
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
|
||||
try {
|
||||
$executor->deleteRuntime($project->getId(), $function->getAttribute('deployment'));
|
||||
} catch (\Throwable $th) {
|
||||
@@ -1028,11 +1046,12 @@ App::put('/v1/functions/:functionId')
|
||||
|
||||
App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Download deployment')
|
||||
->desc('Get deployment download')
|
||||
->label('scope', 'functions.read')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'deployments',
|
||||
name: 'getDeploymentDownload',
|
||||
description: '/docs/references/functions/get-deployment-download.md',
|
||||
auth: [AuthType::KEY, AuthType::JWT],
|
||||
@@ -1129,6 +1148,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'deployments',
|
||||
name: 'updateDeployment',
|
||||
description: '/docs/references/functions/update-function-deployment.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1197,6 +1217,7 @@ App::delete('/v1/functions/:functionId')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'functions',
|
||||
name: 'delete',
|
||||
description: '/docs/references/functions/delete-function.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1252,6 +1273,7 @@ App::post('/v1/functions/:functionId/deployments')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'deployments',
|
||||
name: 'createDeployment',
|
||||
description: '/docs/references/functions/create-deployment.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1263,7 +1285,7 @@ App::post('/v1/functions/:functionId/deployments')
|
||||
],
|
||||
type: MethodType::UPLOAD,
|
||||
packaging: true,
|
||||
requestType: 'multipart/form-data',
|
||||
requestType: ContentType::MULTIPART,
|
||||
))
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('entrypoint', null, new Text(1028), 'Entrypoint File.', true)
|
||||
@@ -1474,6 +1496,7 @@ App::get('/v1/functions/:functionId/deployments')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'deployments',
|
||||
name: 'listDeployments',
|
||||
description: '/docs/references/functions/list-deployments.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1537,9 +1560,12 @@ App::get('/v1/functions/:functionId/deployments')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
$results = $dbForProject->find('deployments', $queries);
|
||||
$total = $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT);
|
||||
try {
|
||||
$results = $dbForProject->find('deployments', $queries);
|
||||
$total = $dbForProject->count('deployments', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
foreach ($results as $result) {
|
||||
$build = $dbForProject->getDocument('builds', $result->getAttribute('buildId', ''));
|
||||
@@ -1563,6 +1589,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'deployments',
|
||||
name: 'getDeployment',
|
||||
description: '/docs/references/functions/get-deployment.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1615,6 +1642,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'deployments',
|
||||
name: 'deleteDeployment',
|
||||
description: '/docs/references/functions/delete-deployment.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1680,7 +1708,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
|
||||
App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||
->alias('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
||||
->groups(['api', 'functions'])
|
||||
->desc('Rebuild deployment')
|
||||
->desc('Create deployment build')
|
||||
->label('scope', 'functions.write')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
|
||||
@@ -1688,6 +1716,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'deployments',
|
||||
name: 'createBuild',
|
||||
description: '/docs/references/functions/create-build.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1763,6 +1792,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'deployments',
|
||||
name: 'updateDeploymentBuild',
|
||||
description: '/docs/references/functions/update-deployment-build.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1779,7 +1809,8 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents) {
|
||||
->inject('executor')
|
||||
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Executor $executor) {
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
@@ -1834,7 +1865,6 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
|
||||
$dbForProject->purgeCachedDocument('deployments', $deployment->getId());
|
||||
|
||||
try {
|
||||
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
|
||||
$executor->deleteRuntime($project->getId(), $deploymentId . "-build");
|
||||
} catch (\Throwable $th) {
|
||||
// Don't throw if the deployment doesn't exist
|
||||
@@ -1858,6 +1888,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||
->label('event', 'functions.[functionId].executions.[executionId].create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'executions',
|
||||
name: 'createExecution',
|
||||
description: '/docs/references/functions/create-execution.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1868,7 +1899,6 @@ App::post('/v1/functions/:functionId/executions')
|
||||
)
|
||||
],
|
||||
contentType: ContentType::MULTIPART,
|
||||
requestType: 'application/json',
|
||||
))
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('body', '', new Text(10485760, 0), 'HTTP body of execution. Default value is empty string.', true)
|
||||
@@ -1886,8 +1916,9 @@ App::post('/v1/functions/:functionId/executions')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('executor')
|
||||
->inject('geodb')
|
||||
->action(function (string $functionId, string $body, mixed $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForPlatform, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb) {
|
||||
->action(function (string $functionId, string $body, mixed $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForPlatform, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb) {
|
||||
$async = \strval($async) === 'true' || \strval($async) === '1';
|
||||
|
||||
if (!$async && !is_null($scheduledAt)) {
|
||||
@@ -2160,7 +2191,6 @@ App::post('/v1/functions/:functionId/executions')
|
||||
]);
|
||||
|
||||
/** Execute function */
|
||||
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
|
||||
try {
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$command = $runtime['startCommand'];
|
||||
@@ -2226,15 +2256,6 @@ App::post('/v1/functions/:functionId/executions')
|
||||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$execution->setAttribute('logs', '');
|
||||
$execution->setAttribute('errors', '');
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
foreach (($executionResponse['headers'] ?? []) as $key => $value) {
|
||||
$headers[] = ['name' => $key, 'value' => $value];
|
||||
@@ -2266,6 +2287,7 @@ App::get('/v1/functions/:functionId/executions')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'executions',
|
||||
name: 'listExecutions',
|
||||
description: '/docs/references/functions/list-executions.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -2331,19 +2353,11 @@ App::get('/v1/functions/:functionId/executions')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
$results = $dbForProject->find('executions', $queries);
|
||||
$total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT);
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$results = array_map(function ($execution) {
|
||||
$execution->setAttribute('logs', '');
|
||||
$execution->setAttribute('errors', '');
|
||||
return $execution;
|
||||
}, $results);
|
||||
try {
|
||||
$results = $dbForProject->find('executions', $queries);
|
||||
$total = $dbForProject->count('executions', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
@@ -2359,6 +2373,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'executions',
|
||||
name: 'getExecution',
|
||||
description: '/docs/references/functions/get-execution.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -2394,14 +2409,6 @@ App::get('/v1/functions/:functionId/executions/:executionId')
|
||||
throw new Exception(Exception::EXECUTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
if (!$isPrivilegedUser && !$isAppUser) {
|
||||
$execution->setAttribute('logs', '');
|
||||
$execution->setAttribute('errors', '');
|
||||
}
|
||||
|
||||
$response->dynamic($execution, Response::MODEL_EXECUTION);
|
||||
});
|
||||
|
||||
@@ -2415,6 +2422,7 @@ App::delete('/v1/functions/:functionId/executions/:executionId')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'executions',
|
||||
name: 'deleteExecution',
|
||||
description: '/docs/references/functions/delete-execution.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2492,6 +2500,7 @@ App::post('/v1/functions/:functionId/variables')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'variables',
|
||||
name: 'createVariable',
|
||||
description: '/docs/references/functions/create-variable.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2562,6 +2571,7 @@ App::get('/v1/functions/:functionId/variables')
|
||||
'sdk',
|
||||
new Method(
|
||||
namespace: 'functions',
|
||||
group: 'variables',
|
||||
name: 'listVariables',
|
||||
description: '/docs/references/functions/list-variables.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2598,6 +2608,7 @@ App::get('/v1/functions/:functionId/variables/:variableId')
|
||||
'sdk',
|
||||
new Method(
|
||||
namespace: 'functions',
|
||||
group: 'variables',
|
||||
name: 'getVariable',
|
||||
description: '/docs/references/functions/get-variable.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2646,6 +2657,7 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'variables',
|
||||
name: 'updateVariable',
|
||||
description: '/docs/references/functions/update-variable.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2713,6 +2725,7 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'variables',
|
||||
name: 'deleteVariable',
|
||||
description: '/docs/references/functions/delete-variable.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2767,6 +2780,7 @@ App::get('/v1/functions/templates')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'templates',
|
||||
name: 'listTemplates',
|
||||
description: '/docs/references/functions/list-templates.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2810,6 +2824,7 @@ App::get('/v1/functions/templates/:templateId')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'templates',
|
||||
name: 'getTemplate',
|
||||
description: '/docs/references/functions/get-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -44,6 +44,7 @@ App::get('/v1/graphql')
|
||||
->label('scope', 'graphql')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'graphql',
|
||||
group: 'graphql',
|
||||
name: 'get',
|
||||
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
|
||||
hide: true,
|
||||
@@ -90,6 +91,7 @@ App::post('/v1/graphql/mutation')
|
||||
->label('scope', 'graphql')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'graphql',
|
||||
group: 'graphql',
|
||||
name: 'mutation',
|
||||
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
|
||||
description: '/docs/references/graphql/post.md',
|
||||
@@ -140,6 +142,7 @@ App::post('/v1/graphql')
|
||||
->label('scope', 'graphql')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'graphql',
|
||||
group: 'graphql',
|
||||
name: 'query',
|
||||
auth: [AuthType::KEY, AuthType::SESSION, AuthType::JWT],
|
||||
description: '/docs/references/graphql/post.md',
|
||||
|
||||
+87
-108
@@ -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;
|
||||
@@ -32,9 +35,10 @@ App::get('/v1/health')
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'health',
|
||||
group: 'health',
|
||||
name: 'get',
|
||||
auth: [AuthType::KEY],
|
||||
description: '/docs/references/health/get.md',
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
@@ -69,10 +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,
|
||||
@@ -84,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'),
|
||||
@@ -95,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);
|
||||
|
||||
@@ -106,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([
|
||||
@@ -129,10 +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,
|
||||
@@ -144,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),
|
||||
@@ -193,10 +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,
|
||||
@@ -208,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),
|
||||
@@ -257,10 +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,
|
||||
@@ -320,10 +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,
|
||||
@@ -345,17 +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,
|
||||
@@ -377,17 +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,
|
||||
@@ -433,17 +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,
|
||||
@@ -465,17 +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,
|
||||
@@ -497,17 +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,
|
||||
@@ -530,17 +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,
|
||||
@@ -562,17 +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,
|
||||
@@ -594,17 +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,
|
||||
@@ -626,17 +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,
|
||||
@@ -658,17 +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,
|
||||
@@ -690,17 +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,
|
||||
@@ -729,10 +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,
|
||||
@@ -756,47 +767,16 @@ App::get('/v1/health/queue/stats-usage')
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
});
|
||||
|
||||
App::get('/v1/health/queue/stats-usage-dump')
|
||||
->desc('Get usage dump queue')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'health.read')
|
||||
->label('sdk', new Method(
|
||||
auth: [AuthType::KEY],
|
||||
namespace: 'health',
|
||||
name: 'getQueueStatsUsageDump',
|
||||
description: '/docs/references/health/get-queue-stats-usage-dump.md',
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_HEALTH_QUEUE,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
|
||||
->inject('publisher')
|
||||
->inject('response')
|
||||
->action(function (int|string $threshold, Publisher $publisher, Response $response) {
|
||||
$threshold = \intval($threshold);
|
||||
|
||||
$size = $publisher->getQueueSize(new Queue(Event::STATS_USAGE_DUMP_QUEUE_NAME));
|
||||
|
||||
if ($size >= $threshold) {
|
||||
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE);
|
||||
});
|
||||
|
||||
App::get('/v1/health/storage/local')
|
||||
->desc('Get local storage')
|
||||
->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,
|
||||
@@ -842,10 +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,
|
||||
@@ -889,10 +870,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,
|
||||
@@ -934,10 +916,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,
|
||||
@@ -954,7 +937,6 @@ App::get('/v1/health/queue/failed/:name')
|
||||
Event::FUNCTIONS_QUEUE_NAME,
|
||||
Event::STATS_RESOURCES_QUEUE_NAME,
|
||||
Event::STATS_USAGE_QUEUE_NAME,
|
||||
Event::STATS_USAGE_DUMP_QUEUE_NAME,
|
||||
Event::WEBHOOK_QUEUE_NAME,
|
||||
Event::CERTIFICATES_QUEUE_NAME,
|
||||
Event::BUILDS_QUEUE_NAME,
|
||||
@@ -980,9 +962,6 @@ App::get('/v1/health/stats') // Currently only used internally
|
||||
->desc('Get system stats')
|
||||
->groups(['api', 'health'])
|
||||
->label('scope', 'root')
|
||||
// ->label('sdk.auth', [APP_AUTH_TYPE_KEY])
|
||||
// ->label('sdk.namespace', 'health')
|
||||
// ->label('sdk.method', 'getStats')
|
||||
->label('docs', false)
|
||||
->inject('response')
|
||||
->inject('register')
|
||||
|
||||
@@ -17,6 +17,7 @@ App::get('/v1/locale')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'get',
|
||||
description: '/docs/references/locale/get-locale.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -80,6 +81,7 @@ App::get('/v1/locale/codes')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listCodes',
|
||||
description: '/docs/references/locale/list-locale-codes.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -105,6 +107,7 @@ App::get('/v1/locale/countries')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listCountries',
|
||||
description: '/docs/references/locale/list-countries.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -141,6 +144,7 @@ App::get('/v1/locale/countries/eu')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listCountriesEU',
|
||||
description: '/docs/references/locale/list-countries-eu.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -179,6 +183,7 @@ App::get('/v1/locale/countries/phones')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listCountriesPhones',
|
||||
description: '/docs/references/locale/list-countries-phones.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -216,6 +221,7 @@ App::get('/v1/locale/continents')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listContinents',
|
||||
description: '/docs/references/locale/list-continents.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -251,6 +257,7 @@ App::get('/v1/locale/currencies')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listCurrencies',
|
||||
description: '/docs/references/locale/list-currencies.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -277,6 +284,7 @@ App::get('/v1/locale/languages')
|
||||
->label('scope', 'locale.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'locale',
|
||||
group: null,
|
||||
name: 'listLanguages',
|
||||
description: '/docs/references/locale/list-languages.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
|
||||
@@ -30,6 +30,7 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
@@ -63,6 +64,7 @@ App::post('/v1/messaging/providers/mailgun')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createMailgunProvider',
|
||||
description: '/docs/references/messaging/create-mailgun-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -156,6 +158,7 @@ App::post('/v1/messaging/providers/sendgrid')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createSendgridProvider',
|
||||
description: '/docs/references/messaging/create-sendgrid-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -237,6 +240,7 @@ App::post('/v1/messaging/providers/smtp')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createSmtpProvider',
|
||||
description: '/docs/references/messaging/create-smtp-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -331,6 +335,7 @@ App::post('/v1/messaging/providers/msg91')
|
||||
->label('event', 'providers.[providerId].create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createMsg91Provider',
|
||||
description: '/docs/references/messaging/create-msg91-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -413,6 +418,7 @@ App::post('/v1/messaging/providers/telesign')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createTelesignProvider',
|
||||
description: '/docs/references/messaging/create-telesign-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -496,6 +502,7 @@ App::post('/v1/messaging/providers/textmagic')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createTextmagicProvider',
|
||||
description: '/docs/references/messaging/create-textmagic-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -579,6 +586,7 @@ App::post('/v1/messaging/providers/twilio')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createTwilioProvider',
|
||||
description: '/docs/references/messaging/create-twilio-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -662,6 +670,7 @@ App::post('/v1/messaging/providers/vonage')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createVonageProvider',
|
||||
description: '/docs/references/messaging/create-vonage-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -745,6 +754,7 @@ App::post('/v1/messaging/providers/fcm')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createFcmProvider',
|
||||
description: '/docs/references/messaging/create-fcm-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -814,6 +824,7 @@ App::post('/v1/messaging/providers/apns')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'createApnsProvider',
|
||||
description: '/docs/references/messaging/create-apns-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -903,6 +914,7 @@ App::get('/v1/messaging/providers')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'listProviders',
|
||||
description: '/docs/references/messaging/list-providers.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -951,10 +963,15 @@ App::get('/v1/messaging/providers')
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
try {
|
||||
$providers = $dbForProject->find('providers', $queries);
|
||||
$total = $dbForProject->count('providers', $queries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'providers' => $dbForProject->find('providers', $queries),
|
||||
'total' => $dbForProject->count('providers', $queries, APP_LIMIT_COUNT),
|
||||
'providers' => $providers,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_PROVIDER_LIST);
|
||||
});
|
||||
|
||||
@@ -965,6 +982,7 @@ App::get('/v1/messaging/providers/:providerId/logs')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'listProviderLogs',
|
||||
description: '/docs/references/messaging/list-provider-logs.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1061,6 +1079,7 @@ App::get('/v1/messaging/providers/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'getProvider',
|
||||
description: '/docs/references/messaging/get-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1094,6 +1113,7 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateMailgunProvider',
|
||||
description: '/docs/references/messaging/update-mailgun-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1206,6 +1226,7 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateSendgridProvider',
|
||||
description: '/docs/references/messaging/update-sendgrid-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1303,6 +1324,7 @@ App::patch('/v1/messaging/providers/smtp/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateSmtpProvider',
|
||||
description: '/docs/references/messaging/update-smtp-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1431,6 +1453,7 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateMsg91Provider',
|
||||
description: '/docs/references/messaging/update-msg91-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1517,6 +1540,7 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateTelesignProvider',
|
||||
description: '/docs/references/messaging/update-telesign-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1605,6 +1629,7 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateTextmagicProvider',
|
||||
description: '/docs/references/messaging/update-textmagic-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1693,6 +1718,7 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateTwilioProvider',
|
||||
description: '/docs/references/messaging/update-twilio-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1781,6 +1807,7 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateVonageProvider',
|
||||
description: '/docs/references/messaging/update-vonage-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1869,6 +1896,7 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateFcmProvider',
|
||||
description: '/docs/references/messaging/update-fcm-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -1944,6 +1972,7 @@ App::patch('/v1/messaging/providers/apns/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'updateApnsProvider',
|
||||
description: '/docs/references/messaging/update-apns-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2045,6 +2074,7 @@ App::delete('/v1/messaging/providers/:providerId')
|
||||
->label('resourceType', RESOURCE_TYPE_PROVIDERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'providers',
|
||||
name: 'deleteProvider',
|
||||
description: '/docs/references/messaging/delete-provider.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2087,6 +2117,7 @@ App::post('/v1/messaging/topics')
|
||||
->label('resourceType', RESOURCE_TYPE_TOPICS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'topics',
|
||||
name: 'createTopic',
|
||||
description: '/docs/references/messaging/create-topic.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2133,6 +2164,7 @@ App::get('/v1/messaging/topics')
|
||||
->label('resourceType', RESOURCE_TYPE_TOPICS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'topics',
|
||||
name: 'listTopics',
|
||||
description: '/docs/references/messaging/list-topics.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2181,10 +2213,15 @@ App::get('/v1/messaging/topics')
|
||||
|
||||
$cursor->setValue($cursorDocument[0]);
|
||||
}
|
||||
|
||||
try {
|
||||
$topics = $dbForProject->find('topics', $queries);
|
||||
$total = $dbForProject->count('topics', $queries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'topics' => $dbForProject->find('topics', $queries),
|
||||
'total' => $dbForProject->count('topics', $queries, APP_LIMIT_COUNT),
|
||||
'topics' => $topics,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_TOPIC_LIST);
|
||||
});
|
||||
|
||||
@@ -2195,6 +2232,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
|
||||
->label('resourceType', RESOURCE_TYPE_TOPICS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'topics',
|
||||
name: 'listTopicLogs',
|
||||
description: '/docs/references/messaging/list-topic-logs.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2292,6 +2330,7 @@ App::get('/v1/messaging/topics/:topicId')
|
||||
->label('resourceType', RESOURCE_TYPE_TOPICS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'topics',
|
||||
name: 'getTopic',
|
||||
description: '/docs/references/messaging/get-topic.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2326,6 +2365,7 @@ App::patch('/v1/messaging/topics/:topicId')
|
||||
->label('resourceType', RESOURCE_TYPE_TOPICS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'topics',
|
||||
name: 'updateTopic',
|
||||
description: '/docs/references/messaging/update-topic.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2376,6 +2416,7 @@ App::delete('/v1/messaging/topics/:topicId')
|
||||
->label('resourceType', RESOURCE_TYPE_TOPICS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'topics',
|
||||
name: 'deleteTopic',
|
||||
description: '/docs/references/messaging/delete-topic.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2423,6 +2464,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'subscribers',
|
||||
name: 'createSubscriber',
|
||||
description: '/docs/references/messaging/create-subscriber.md',
|
||||
auth: [AuthType::JWT, AuthType::SESSION, AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2522,6 +2564,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'subscribers',
|
||||
name: 'listSubscribers',
|
||||
description: '/docs/references/messaging/list-subscribers.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2579,8 +2622,11 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
$subscribers = $dbForProject->find('subscribers', $queries);
|
||||
try {
|
||||
$subscribers = $dbForProject->find('subscribers', $queries);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) {
|
||||
return function () use ($subscriber, $dbForProject) {
|
||||
@@ -2607,6 +2653,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'subscribers',
|
||||
name: 'listSubscriberLogs',
|
||||
description: '/docs/references/messaging/list-subscriber-logs.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2704,6 +2751,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'subscribers',
|
||||
name: 'getSubscriber',
|
||||
description: '/docs/references/messaging/get-subscriber.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2752,6 +2800,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->label('resourceType', RESOURCE_TYPE_SUBSCRIBERS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'subscribers',
|
||||
name: 'deleteSubscriber',
|
||||
description: '/docs/references/messaging/delete-subscriber.md',
|
||||
auth: [AuthType::JWT, AuthType::SESSION, AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2818,6 +2867,7 @@ App::post('/v1/messaging/messages/email')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'createEmail',
|
||||
description: '/docs/references/messaging/create-email.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -2976,6 +3026,7 @@ App::post('/v1/messaging/messages/sms')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'createSms',
|
||||
description: '/docs/references/messaging/create-sms.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3098,6 +3149,7 @@ App::post('/v1/messaging/messages/push')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'createPush',
|
||||
description: '/docs/references/messaging/create-push.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3312,6 +3364,7 @@ App::get('/v1/messaging/messages')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'listMessages',
|
||||
description: '/docs/references/messaging/list-messages.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3360,10 +3413,15 @@ App::get('/v1/messaging/messages')
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
try {
|
||||
$messages = $dbForProject->find('messages', $queries);
|
||||
$total = $dbForProject->count('messages', $queries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'messages' => $dbForProject->find('messages', $queries),
|
||||
'total' => $dbForProject->count('messages', $queries, APP_LIMIT_COUNT),
|
||||
'messages' => $messages,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_MESSAGE_LIST);
|
||||
});
|
||||
|
||||
@@ -3374,6 +3432,7 @@ App::get('/v1/messaging/messages/:messageId/logs')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'logs',
|
||||
name: 'listMessageLogs',
|
||||
description: '/docs/references/messaging/list-message-logs.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3471,6 +3530,7 @@ App::get('/v1/messaging/messages/:messageId/targets')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'listTargets',
|
||||
description: '/docs/references/messaging/list-message-targets.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3533,10 +3593,15 @@ App::get('/v1/messaging/messages/:messageId/targets')
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
try {
|
||||
$targets = $dbForProject->find('targets', $queries);
|
||||
$total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'targets' => $dbForProject->find('targets', $queries),
|
||||
'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT),
|
||||
'targets' => $targets,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_TARGET_LIST);
|
||||
});
|
||||
|
||||
@@ -3547,6 +3612,7 @@ App::get('/v1/messaging/messages/:messageId')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'getMessage',
|
||||
description: '/docs/references/messaging/get-message.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3580,6 +3646,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'updateEmail',
|
||||
description: '/docs/references/messaging/update-email.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3786,6 +3853,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'updateSms',
|
||||
description: '/docs/references/messaging/update-sms.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -3947,6 +4015,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'updatePush',
|
||||
description: '/docs/references/messaging/update-push.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
@@ -4206,6 +4275,7 @@ App::delete('/v1/messaging/messages/:messageId')
|
||||
->label('resourceType', RESOURCE_TYPE_MESSAGES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'messaging',
|
||||
group: 'messages',
|
||||
name: 'delete',
|
||||
description: '/docs/references/messaging/delete-message.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
|
||||
@@ -12,6 +12,7 @@ use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Query;
|
||||
@@ -31,12 +32,13 @@ include_once __DIR__ . '/../shared/api.php';
|
||||
|
||||
App::post('/v1/migrations/appwrite')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Migrate Appwrite data')
|
||||
->desc('Create Appwrite migration')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'createAppwriteMigration',
|
||||
description: '/docs/references/migrations/migration-appwrite.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -92,12 +94,13 @@ App::post('/v1/migrations/appwrite')
|
||||
|
||||
App::post('/v1/migrations/firebase')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Migrate Firebase data')
|
||||
->desc('Create Firebase migration')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'createFirebaseMigration',
|
||||
description: '/docs/references/migrations/migration-firebase.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -158,12 +161,13 @@ App::post('/v1/migrations/firebase')
|
||||
|
||||
App::post('/v1/migrations/supabase')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Migrate Supabase data')
|
||||
->desc('Create Supabase migration')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'createSupabaseMigration',
|
||||
description: '/docs/references/migrations/migration-supabase.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -224,12 +228,13 @@ App::post('/v1/migrations/supabase')
|
||||
|
||||
App::post('/v1/migrations/nhost')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Migrate NHost data')
|
||||
->desc('Create NHost migration')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].create')
|
||||
->label('audits.event', 'migration.create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'createNHostMigration',
|
||||
description: '/docs/references/migrations/migration-nhost.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -296,6 +301,7 @@ App::get('/v1/migrations')
|
||||
->label('scope', 'migrations.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'list',
|
||||
description: '/docs/references/migrations/list-migrations.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -347,10 +353,15 @@ App::get('/v1/migrations')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
try {
|
||||
$migrations = $dbForProject->find('migrations', $queries);
|
||||
$total = $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'migrations' => $dbForProject->find('migrations', $queries),
|
||||
'total' => $dbForProject->count('migrations', $filterQueries, APP_LIMIT_COUNT),
|
||||
'migrations' => $migrations,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_MIGRATION_LIST);
|
||||
});
|
||||
|
||||
@@ -360,6 +371,7 @@ App::get('/v1/migrations/:migrationId')
|
||||
->label('scope', 'migrations.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'get',
|
||||
description: '/docs/references/migrations/get-migration.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -385,10 +397,11 @@ App::get('/v1/migrations/:migrationId')
|
||||
|
||||
App::get('/v1/migrations/appwrite/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Generate a report on Appwrite data')
|
||||
->desc('Get Appwrite migration report')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'getAppwriteReport',
|
||||
description: '/docs/references/migrations/migration-appwrite-report.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -408,6 +421,7 @@ App::get('/v1/migrations/appwrite/report')
|
||||
->inject('project')
|
||||
->inject('user')
|
||||
->action(function (array $resources, string $endpoint, string $projectID, string $key, Response $response) {
|
||||
|
||||
$appwrite = new Appwrite($projectID, $endpoint, $key);
|
||||
|
||||
try {
|
||||
@@ -432,10 +446,11 @@ App::get('/v1/migrations/appwrite/report')
|
||||
|
||||
App::get('/v1/migrations/firebase/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Generate a report on Firebase data')
|
||||
->desc('Get Firebase migration report')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'getFirebaseReport',
|
||||
description: '/docs/references/migrations/migration-firebase-report.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -484,10 +499,11 @@ App::get('/v1/migrations/firebase/report')
|
||||
|
||||
App::get('/v1/migrations/supabase/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Generate a report on Supabase Data')
|
||||
->desc('Get Supabase migration report')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'getSupabaseReport',
|
||||
description: '/docs/references/migrations/migration-supabase-report.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -532,10 +548,11 @@ App::get('/v1/migrations/supabase/report')
|
||||
|
||||
App::get('/v1/migrations/nhost/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Generate a report on NHost Data')
|
||||
->desc('Get NHost migration report')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'getNHostReport',
|
||||
description: '/docs/references/migrations/migration-nhost-report.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -580,13 +597,14 @@ App::get('/v1/migrations/nhost/report')
|
||||
|
||||
App::patch('/v1/migrations/:migrationId')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Retry migration')
|
||||
->desc('Update retry migration')
|
||||
->label('scope', 'migrations.write')
|
||||
->label('event', 'migrations.[migrationId].retry')
|
||||
->label('audits.event', 'migration.retry')
|
||||
->label('audits.resource', 'migrations/{request.migrationId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'retry',
|
||||
description: '/docs/references/migrations/retry-migration.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -637,6 +655,7 @@ App::delete('/v1/migrations/:migrationId')
|
||||
->label('audits.resource', 'migrations/{request.migrationId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'migrations',
|
||||
group: null,
|
||||
name: 'delete',
|
||||
description: '/docs/references/migrations/delete-migration.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -26,6 +26,7 @@ App::get('/v1/project/usage')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'getUsage',
|
||||
description: '/docs/references/project/get-usage.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -387,6 +388,7 @@ App::post('/v1/project/variables')
|
||||
->label('audits.event', 'variable.create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'createVariable',
|
||||
description: '/docs/references/project/create-variable.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -446,6 +448,7 @@ App::get('/v1/project/variables')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'listVariables',
|
||||
description: '/docs/references/project/list-variables.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -476,6 +479,7 @@ App::get('/v1/project/variables/:variableId')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'getVariable',
|
||||
description: '/docs/references/project/get-variable.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -505,6 +509,7 @@ App::put('/v1/project/variables/:variableId')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'updateVariable',
|
||||
description: '/docs/references/project/update-variable.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -556,6 +561,7 @@ App::delete('/v1/project/variables/:variableId')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'project',
|
||||
group: null,
|
||||
name: 'deleteVariable',
|
||||
description: '/docs/references/project/delete-variable.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -24,10 +24,12 @@ 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;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -68,6 +70,7 @@ App::post('/v1/projects')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'create',
|
||||
description: '/docs/references/projects/create.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -138,6 +141,14 @@ App::post('/v1/projects')
|
||||
|
||||
$databases = Config::getParam('pools-database', []);
|
||||
|
||||
if ($region !== 'default') {
|
||||
$databaseKeys = System::getEnv('_APP_DATABASE_KEYS', '');
|
||||
$keys = explode(',', $databaseKeys);
|
||||
$databases = array_filter($keys, function ($value) use ($region) {
|
||||
return str_contains($value, $region);
|
||||
});
|
||||
}
|
||||
|
||||
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
|
||||
$index = \array_search($databaseOverride, $databases);
|
||||
if ($index !== false) {
|
||||
@@ -205,17 +216,17 @@ App::post('/v1/projects')
|
||||
$dsn = new DSN('mysql://' . $dsn);
|
||||
}
|
||||
|
||||
$adapter = $pools->get($dsn->getHost())->pop()->getResource();
|
||||
$dbForProject = new Database($adapter, $cache);
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
|
||||
|
||||
$projectTables = !\in_array($dsn->getHost(), $sharedTables);
|
||||
$sharedTablesV1 = \in_array($dsn->getHost(), $sharedTablesV1);
|
||||
$sharedTablesV2 = !$projectTables && !$sharedTablesV1;
|
||||
$sharedTables = $sharedTablesV1 || $sharedTablesV2;
|
||||
|
||||
if (!$sharedTablesV2) {
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$dbForProject = new Database($adapter, $cache);
|
||||
|
||||
if ($sharedTables) {
|
||||
$dbForProject
|
||||
->setSharedTables(true)
|
||||
@@ -297,6 +308,7 @@ App::get('/v1/projects')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'list',
|
||||
description: '/docs/references/projects/list.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -349,10 +361,15 @@ App::get('/v1/projects')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
try {
|
||||
$projects = $dbForPlatform->find('projects', $queries);
|
||||
$total = $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'projects' => $dbForPlatform->find('projects', $queries),
|
||||
'total' => $dbForPlatform->count('projects', $filterQueries, APP_LIMIT_COUNT),
|
||||
'projects' => $projects,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_PROJECT_LIST);
|
||||
});
|
||||
|
||||
@@ -362,6 +379,7 @@ App::get('/v1/projects/:projectId')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'get',
|
||||
description: '/docs/references/projects/get.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -394,6 +412,7 @@ App::patch('/v1/projects/:projectId')
|
||||
->label('audits.resource', 'project/{request.projectId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'update',
|
||||
description: '/docs/references/projects/update.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -447,6 +466,7 @@ App::patch('/v1/projects/:projectId/team')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateTeam',
|
||||
description: '/docs/references/projects/update-team.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -521,6 +541,7 @@ App::patch('/v1/projects/:projectId/service')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateServiceStatus',
|
||||
description: '/docs/references/projects/update-service-status.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -558,6 +579,7 @@ App::patch('/v1/projects/:projectId/service/all')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateServiceStatusAll',
|
||||
description: '/docs/references/projects/update-service-status-all.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -598,6 +620,7 @@ App::patch('/v1/projects/:projectId/api')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateApiStatus',
|
||||
description: '/docs/references/projects/update-api-status.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -635,6 +658,7 @@ App::patch('/v1/projects/:projectId/api/all')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateApiStatusAll',
|
||||
description: '/docs/references/projects/update-api-status-all.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -675,6 +699,7 @@ App::patch('/v1/projects/:projectId/oauth2')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateOAuth2',
|
||||
description: '/docs/references/projects/update-oauth2.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -725,6 +750,7 @@ App::patch('/v1/projects/:projectId/auth/session-alerts')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateSessionAlerts',
|
||||
description: '/docs/references/projects/update-session-alerts.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -762,6 +788,7 @@ App::patch('/v1/projects/:projectId/auth/memberships-privacy')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateMembershipsPrivacy',
|
||||
description: '/docs/references/projects/update-memberships-privacy.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -803,6 +830,7 @@ App::patch('/v1/projects/:projectId/auth/limit')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateAuthLimit',
|
||||
description: '/docs/references/projects/update-auth-limit.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -840,6 +868,7 @@ App::patch('/v1/projects/:projectId/auth/duration')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateAuthDuration',
|
||||
description: '/docs/references/projects/update-auth-duration.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -877,6 +906,7 @@ App::patch('/v1/projects/:projectId/auth/:method')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateAuthStatus',
|
||||
description: '/docs/references/projects/update-auth-status.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -917,6 +947,7 @@ App::patch('/v1/projects/:projectId/auth/password-history')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateAuthPasswordHistory',
|
||||
description: '/docs/references/projects/update-auth-password-history.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -954,6 +985,7 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateAuthPasswordDictionary',
|
||||
description: '/docs/references/projects/update-auth-password-dictionary.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -986,11 +1018,12 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/personal-data')
|
||||
->desc('Enable or disable checking user passwords for similarity with their personal data.')
|
||||
->desc('Update personal data check')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updatePersonalDataCheck',
|
||||
description: '/docs/references/projects/update-personal-data-check.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1028,6 +1061,7 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateAuthSessionsLimit',
|
||||
description: '/docs/references/projects/update-auth-sessions-limit.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1065,6 +1099,7 @@ App::patch('/v1/projects/:projectId/auth/mock-numbers')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'updateMockNumbers',
|
||||
description: '/docs/references/projects/update-mock-numbers.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1112,6 +1147,7 @@ App::delete('/v1/projects/:projectId')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'delete',
|
||||
description: '/docs/references/projects/delete.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1155,6 +1191,7 @@ App::post('/v1/projects/:projectId/webhooks')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'webhooks',
|
||||
name: 'createWebhook',
|
||||
description: '/docs/references/projects/create-webhook.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1219,6 +1256,7 @@ App::get('/v1/projects/:projectId/webhooks')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'webhooks',
|
||||
name: 'listWebhooks',
|
||||
description: '/docs/references/projects/list-webhooks.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1257,6 +1295,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
->label('scope', 'projects.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'webhooks',
|
||||
name: 'getWebhook',
|
||||
description: '/docs/references/projects/get-webhook.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1297,6 +1336,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'webhooks',
|
||||
name: 'updateWebhook',
|
||||
description: '/docs/references/projects/update-webhook.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1362,6 +1402,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'webhooks',
|
||||
name: 'updateWebhookSignature',
|
||||
description: '/docs/references/projects/update-webhook-signature.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1407,6 +1448,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'webhooks',
|
||||
name: 'deleteWebhook',
|
||||
description: '/docs/references/projects/delete-webhook.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1454,6 +1496,7 @@ App::post('/v1/projects/:projectId/keys')
|
||||
->label('scope', 'keys.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'keys',
|
||||
name: 'createKey',
|
||||
description: '/docs/references/projects/create-key.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1510,6 +1553,7 @@ App::get('/v1/projects/:projectId/keys')
|
||||
->label('scope', 'keys.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'keys',
|
||||
name: 'listKeys',
|
||||
description: '/docs/references/projects/list-keys.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1548,6 +1592,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
|
||||
->label('scope', 'keys.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'keys',
|
||||
name: 'getKey',
|
||||
description: '/docs/references/projects/get-key.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1588,6 +1633,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||
->label('scope', 'keys.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'keys',
|
||||
name: 'updateKey',
|
||||
description: '/docs/references/projects/update-key.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1640,6 +1686,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
||||
->label('scope', 'keys.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'keys',
|
||||
name: 'deleteKey',
|
||||
description: '/docs/references/projects/delete-key.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1687,6 +1734,7 @@ App::post('/v1/projects/:projectId/jwts')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'auth',
|
||||
name: 'createJWT',
|
||||
description: '/docs/references/projects/create-jwt.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1730,6 +1778,7 @@ App::post('/v1/projects/:projectId/platforms')
|
||||
->label('scope', 'platforms.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'platforms',
|
||||
name: 'createPlatform',
|
||||
description: '/docs/references/projects/create-platform.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1786,6 +1835,7 @@ App::get('/v1/projects/:projectId/platforms')
|
||||
->label('scope', 'platforms.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'platforms',
|
||||
name: 'listPlatforms',
|
||||
description: '/docs/references/projects/list-platforms.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1824,6 +1874,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
|
||||
->label('scope', 'platforms.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'platforms',
|
||||
name: 'getPlatform',
|
||||
description: '/docs/references/projects/get-platform.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1864,6 +1915,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
|
||||
->label('scope', 'platforms.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'platforms',
|
||||
name: 'updatePlatform',
|
||||
description: '/docs/references/projects/update-platform.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1919,6 +1971,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||
->label('scope', 'platforms.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'platforms',
|
||||
name: 'deletePlatform',
|
||||
description: '/docs/references/projects/delete-platform.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1966,6 +2019,7 @@ App::patch('/v1/projects/:projectId/smtp')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'updateSmtp',
|
||||
description: '/docs/references/projects/update-smtp.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2062,6 +2116,7 @@ App::post('/v1/projects/:projectId/smtp/tests')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'createSmtpTest',
|
||||
description: '/docs/references/projects/create-smtp-test.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2128,6 +2183,7 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'getSmsTemplate',
|
||||
description: '/docs/references/projects/get-sms-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2175,6 +2231,7 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'getEmailTemplate',
|
||||
description: '/docs/references/projects/get-email-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2233,6 +2290,7 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'updateSmsTemplate',
|
||||
description: '/docs/references/projects/update-sms-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2279,6 +2337,7 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'updateEmailTemplate',
|
||||
description: '/docs/references/projects/update-email-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2335,6 +2394,7 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'deleteSmsTemplate',
|
||||
description: '/docs/references/projects/delete-sms-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -2380,11 +2440,12 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
});
|
||||
|
||||
App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
->desc('Reset custom email template')
|
||||
->desc('Delete custom email template')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'templates',
|
||||
name: 'deleteEmailTemplate',
|
||||
description: '/docs/references/projects/delete-email-template.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -35,6 +35,7 @@ App::post('/v1/proxy/rules')
|
||||
->label('audits.resource', 'rule/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
name: 'createRule',
|
||||
description: '/docs/references/proxy/create-rule.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -55,14 +56,25 @@ App::post('/v1/proxy/rules')
|
||||
->inject('dbForPlatform')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $domain, string $resourceType, string $resourceId, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject) {
|
||||
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
if ($domain === $mainDomain) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.');
|
||||
}
|
||||
|
||||
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
|
||||
if ($functionsDomain != '' && str_ends_with($domain, $functionsDomain)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or it\'s subdomain to specific resource. Please use different domain.');
|
||||
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS');
|
||||
$denyListDomains = System::getEnv('_APP_CUSTOM_DOMAIN_DENY_LIST');
|
||||
|
||||
if (!empty($denyListDomains)) {
|
||||
$functionsDomain .= ',' . $denyListDomains;
|
||||
}
|
||||
|
||||
$deniedDomains = array_map('trim', explode(',', $functionsDomain));
|
||||
|
||||
foreach ($deniedDomains as $deniedDomain) {
|
||||
if (str_ends_with($domain, $deniedDomain)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or its subdomain to a specific resource. Please use a different domain.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($domain === 'localhost' || $domain === APP_HOSTNAME_INTERNAL) {
|
||||
@@ -173,6 +185,7 @@ App::get('/v1/proxy/rules')
|
||||
->label('scope', 'rules.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
name: 'listRules',
|
||||
description: '/docs/references/proxy/list-rules.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -247,6 +260,7 @@ App::get('/v1/proxy/rules/:ruleId')
|
||||
->label('scope', 'rules.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
name: 'getRule',
|
||||
description: '/docs/references/proxy/get-rule.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -284,6 +298,7 @@ App::delete('/v1/proxy/rules/:ruleId')
|
||||
->label('audits.resource', 'rule/{request.ruleId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
name: 'deleteRule',
|
||||
description: '/docs/references/proxy/delete-rule.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -328,6 +343,7 @@ App::patch('/v1/proxy/rules/:ruleId/verification')
|
||||
->label('audits.resource', 'rule/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'proxy',
|
||||
group: null,
|
||||
name: 'updateRuleVerification',
|
||||
description: '/docs/references/proxy/update-rule-verification.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -24,6 +24,7 @@ use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Exception\NotFound as NotFoundException;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -62,6 +63,7 @@ App::post('/v1/storage/buckets')
|
||||
->label('audits.resource', 'bucket/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'buckets',
|
||||
name: 'createBucket',
|
||||
description: '/docs/references/storage/create-bucket.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -164,6 +166,7 @@ App::get('/v1/storage/buckets')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'buckets',
|
||||
name: 'listBuckets',
|
||||
description: '/docs/references/storage/list-buckets.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -216,10 +219,15 @@ App::get('/v1/storage/buckets')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
try {
|
||||
$buckets = $dbForProject->find('buckets', $queries);
|
||||
$total = $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'buckets' => $dbForProject->find('buckets', $queries),
|
||||
'total' => $dbForProject->count('buckets', $filterQueries, APP_LIMIT_COUNT),
|
||||
'buckets' => $buckets,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_BUCKET_LIST);
|
||||
});
|
||||
|
||||
@@ -230,6 +238,7 @@ App::get('/v1/storage/buckets/:bucketId')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'buckets',
|
||||
name: 'getBucket',
|
||||
description: '/docs/references/storage/get-bucket.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -264,6 +273,7 @@ App::put('/v1/storage/buckets/:bucketId')
|
||||
->label('audits.resource', 'bucket/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'buckets',
|
||||
name: 'updateBucket',
|
||||
description: '/docs/references/storage/update-bucket.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -334,6 +344,7 @@ App::delete('/v1/storage/buckets/:bucketId')
|
||||
->label('audits.resource', 'bucket/{request.bucketId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'buckets',
|
||||
name: 'deleteBucket',
|
||||
description: '/docs/references/storage/delete-bucket.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -387,17 +398,18 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'createFile',
|
||||
description: '/docs/references/storage/create-file.md',
|
||||
type: MethodType::UPLOAD,
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
requestType: 'multipart/form-data',
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_CREATED,
|
||||
model: Response::MODEL_FILE,
|
||||
)
|
||||
]
|
||||
],
|
||||
type: MethodType::UPLOAD,
|
||||
requestType: ContentType::MULTIPART
|
||||
))
|
||||
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new CustomId(), 'File ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
@@ -756,6 +768,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'listFiles',
|
||||
description: '/docs/references/storage/list-files.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -837,6 +850,8 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
||||
}
|
||||
} catch (NotFoundException) {
|
||||
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
@@ -853,6 +868,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'getFile',
|
||||
description: '/docs/references/storage/get-file.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -909,6 +925,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
->label('cache.resource', 'file/{request.fileId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'getFilePreview',
|
||||
description: '/docs/references/storage/get-file-preview.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -926,7 +943,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
||||
->param('width', 0, new Range(0, 4000), 'Resize preview image width, Pass an integer between 0 to 4000.', true)
|
||||
->param('height', 0, new Range(0, 4000), 'Resize preview image height, Pass an integer between 0 to 4000.', true)
|
||||
->param('gravity', Image::GRAVITY_CENTER, new WhiteList(Image::getGravityTypes()), 'Image crop gravity. Can be one of ' . implode(",", Image::getGravityTypes()), true)
|
||||
->param('quality', 100, new Range(0, 100), 'Preview image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
|
||||
->param('quality', -1, new Range(-1, 100), 'Preview image quality. Pass an integer between 0 to 100. Defaults to keep existing image quality.', true)
|
||||
->param('borderWidth', 0, new Range(0, 100), 'Preview image border in pixels. Pass an integer between 0 to 100. Defaults to 0.', true)
|
||||
->param('borderColor', '', new HexColor(), 'Preview image border color. Use a valid HEX color, no # is needed for prefix.', true)
|
||||
->param('borderRadius', 0, new Range(0, 4000), 'Preview image border radius in pixels. Pass an integer between 0 to 4000.', true)
|
||||
@@ -979,7 +996,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 {
|
||||
@@ -1091,6 +1108,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'getFileDownload',
|
||||
description: '/docs/references/storage/get-file-download.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1144,13 +1162,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');
|
||||
@@ -1159,7 +1170,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));
|
||||
}
|
||||
|
||||
@@ -1174,6 +1185,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);
|
||||
@@ -1207,12 +1225,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) {
|
||||
@@ -1239,6 +1260,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'getFileView',
|
||||
description: '/docs/references/storage/get-file-view.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1299,15 +1321,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');
|
||||
@@ -1316,8 +1329,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) {
|
||||
@@ -1331,6 +1344,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);
|
||||
@@ -1364,6 +1386,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;
|
||||
@@ -1396,9 +1419,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
|
||||
->groups(['api', 'storage'])
|
||||
->label('scope', 'public')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', '*/*')
|
||||
->label('sdk.methodType', 'location')
|
||||
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new UID(), 'File ID.')
|
||||
->param('jwt', '', new Text(2048, 0), 'JSON Web Token to validate', true)
|
||||
@@ -1454,14 +1474,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');
|
||||
@@ -1470,8 +1482,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) {
|
||||
@@ -1485,6 +1497,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);
|
||||
@@ -1518,6 +1538,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;
|
||||
@@ -1559,6 +1580,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'updateFile',
|
||||
description: '/docs/references/storage/update-file.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1673,6 +1695,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: 'files',
|
||||
name: 'deleteFile',
|
||||
description: '/docs/references/storage/delete-file.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1772,6 +1795,7 @@ App::get('/v1/storage/usage')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: null,
|
||||
name: 'getUsage',
|
||||
description: '/docs/references/storage/get-usage.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1858,6 +1882,7 @@ App::get('/v1/storage/:bucketId/usage')
|
||||
->label('resourceType', RESOURCE_TYPE_BUCKETS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'storage',
|
||||
group: null,
|
||||
name: 'getBucketUsage',
|
||||
description: '/docs/references/storage/get-bucket-usage.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -33,6 +33,7 @@ use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Authorization as AuthorizationException;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -62,6 +63,7 @@ App::post('/v1/teams')
|
||||
->label('audits.resource', 'team/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'create',
|
||||
description: '/docs/references/teams/create-team.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -152,6 +154,7 @@ App::get('/v1/teams')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'list',
|
||||
description: '/docs/references/teams/list-teams.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -204,9 +207,12 @@ App::get('/v1/teams')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
$results = $dbForProject->find('teams', $queries);
|
||||
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
|
||||
try {
|
||||
$results = $dbForProject->find('teams', $queries);
|
||||
$total = $dbForProject->count('teams', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'teams' => $results,
|
||||
@@ -220,6 +226,7 @@ App::get('/v1/teams/:teamId')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'get',
|
||||
description: '/docs/references/teams/get-team.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -250,6 +257,7 @@ App::get('/v1/teams/:teamId/prefs')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'getPrefs',
|
||||
description: '/docs/references/teams/get-team-prefs.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -285,6 +293,7 @@ App::put('/v1/teams/:teamId')
|
||||
->label('audits.resource', 'team/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'updateName',
|
||||
description: '/docs/references/teams/update-team-name.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -313,9 +322,7 @@ App::put('/v1/teams/:teamId')
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('search', implode(' ', [$teamId, $name]));
|
||||
|
||||
$team = $dbForProject->withRequestTimestamp($requestTimestamp, function () use ($dbForProject, $team) {
|
||||
return $dbForProject->updateDocument('teams', $team->getId(), $team);
|
||||
});
|
||||
$team = $dbForProject->updateDocument('teams', $team->getId(), $team);
|
||||
|
||||
$queueForEvents->setParam('teamId', $team->getId());
|
||||
|
||||
@@ -332,6 +339,7 @@ App::put('/v1/teams/:teamId/prefs')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'updatePrefs',
|
||||
description: '/docs/references/teams/update-team-prefs.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -371,6 +379,7 @@ App::delete('/v1/teams/:teamId')
|
||||
->label('audits.resource', 'team/{request.teamId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'teams',
|
||||
name: 'delete',
|
||||
description: '/docs/references/teams/delete-team.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -429,6 +438,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
->label('audits.userId', '{request.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'memberships',
|
||||
name: 'createMembership',
|
||||
description: '/docs/references/teams/create-team-membership.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -615,7 +625,10 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
$membership = ($isPrivilegedUser || $isAppUser) ?
|
||||
Authorization::skip(fn () => $dbForProject->createDocument('memberships', $membership)) :
|
||||
$dbForProject->createDocument('memberships', $membership);
|
||||
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
|
||||
|
||||
if ($isPrivilegedUser || $isAppUser) {
|
||||
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
|
||||
}
|
||||
|
||||
} elseif ($membership->getAttribute('confirm') === false) {
|
||||
$membership->setAttribute('secret', Auth::hash($secret));
|
||||
@@ -793,6 +806,7 @@ App::get('/v1/teams/:teamId/memberships')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'memberships',
|
||||
name: 'listMemberships',
|
||||
description: '/docs/references/teams/list-team-members.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -856,17 +870,20 @@ App::get('/v1/teams/:teamId/memberships')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
try {
|
||||
$memberships = $dbForProject->find(
|
||||
collection: 'memberships',
|
||||
queries: $queries,
|
||||
);
|
||||
$total = $dbForProject->count(
|
||||
collection: 'memberships',
|
||||
queries: $filterQueries,
|
||||
max: APP_LIMIT_COUNT
|
||||
);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$memberships = $dbForProject->find(
|
||||
collection: 'memberships',
|
||||
queries: $queries,
|
||||
);
|
||||
|
||||
$total = $dbForProject->count(
|
||||
collection: 'memberships',
|
||||
queries: $filterQueries,
|
||||
max: APP_LIMIT_COUNT
|
||||
);
|
||||
|
||||
$memberships = array_filter($memberships, fn (Document $membership) => !empty($membership->getAttribute('userId')));
|
||||
|
||||
@@ -931,6 +948,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'memberships',
|
||||
name: 'getMembership',
|
||||
description: '/docs/references/teams/get-team-member.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1017,6 +1035,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->label('audits.resource', 'team/{request.teamId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'memberships',
|
||||
name: 'updateMembership',
|
||||
description: '/docs/references/teams/update-team-membership.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1031,7 +1050,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->param('membershipId', '', new UID(), 'Membership ID.')
|
||||
->param('roles', [], function (Document $project) {
|
||||
if ($project->getId() === 'console') {
|
||||
;
|
||||
$roles = array_keys(Config::getParam('roles', []));
|
||||
array_filter($roles, function ($role) {
|
||||
return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]);
|
||||
@@ -1043,9 +1061,10 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('project')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) {
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
if ($team->isEmpty()) {
|
||||
@@ -1066,6 +1085,24 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
|
||||
|
||||
if ($project->getId() === 'console') {
|
||||
// Quick check: fetch up to 2 owners to determine if only one exists
|
||||
$ownersCount = $dbForProject->count(
|
||||
collection: 'memberships',
|
||||
queries: [
|
||||
Query::contains('roles', ['owner']),
|
||||
Query::equal('teamInternalId', [$team->getInternalId()])
|
||||
],
|
||||
max: 2
|
||||
);
|
||||
|
||||
// Prevent role change if there's only one owner left,
|
||||
// the requester is that owner, and the new `$roles` no longer include 'owner'!
|
||||
if ($ownersCount === 1 && $isOwner && !\in_array('owner', $roles)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'There must be at least one owner in the organization.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to modify roles');
|
||||
}
|
||||
@@ -1105,6 +1142,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
->label('audits.userId', '{request.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'memberships',
|
||||
name: 'updateMembershipStatus',
|
||||
description: '/docs/references/teams/update-team-membership-status.md',
|
||||
auth: [AuthType::SESSION, AuthType::JWT],
|
||||
@@ -1262,6 +1300,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->label('audits.resource', 'team/{request.teamId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'memberships',
|
||||
name: 'deleteMembership',
|
||||
description: '/docs/references/teams/delete-team-membership.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -1332,6 +1371,7 @@ App::get('/v1/teams/:teamId/logs')
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'teams',
|
||||
group: 'logs',
|
||||
name: 'listLogs',
|
||||
description: '/docs/references/teams/get-team-logs.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -9,6 +9,8 @@ use Appwrite\Auth\Validator\PasswordDictionary;
|
||||
use Appwrite\Auth\Validator\PasswordHistory;
|
||||
use Appwrite\Auth\Validator\PersonalData;
|
||||
use Appwrite\Auth\Validator\Phone;
|
||||
use Appwrite\Deletes\Identities as DeleteIdentities;
|
||||
use Appwrite\Deletes\Targets as DeleteTargets;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
@@ -33,6 +35,7 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -191,6 +194,7 @@ App::post('/v1/users')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'create',
|
||||
description: '/docs/references/users/create-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -225,6 +229,7 @@ App::post('/v1/users/bcrypt')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createBcryptUser',
|
||||
description: '/docs/references/users/create-bcrypt-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -259,6 +264,7 @@ App::post('/v1/users/md5')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createMD5User',
|
||||
description: '/docs/references/users/create-md5-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -293,6 +299,7 @@ App::post('/v1/users/argon2')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createArgon2User',
|
||||
description: '/docs/references/users/create-argon2-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -327,6 +334,7 @@ App::post('/v1/users/sha')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createSHAUser',
|
||||
description: '/docs/references/users/create-sha-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -368,6 +376,7 @@ App::post('/v1/users/phpass')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createPHPassUser',
|
||||
description: '/docs/references/users/create-phpass-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -402,6 +411,7 @@ App::post('/v1/users/scrypt')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createScryptUser',
|
||||
description: '/docs/references/users/create-scrypt-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -449,6 +459,7 @@ App::post('/v1/users/scrypt-modified')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'createScryptModifiedUser',
|
||||
description: '/docs/references/users/create-scrypt-modified-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -487,6 +498,7 @@ App::post('/v1/users/:userId/targets')
|
||||
->label('scope', 'targets.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'targets',
|
||||
name: 'createTarget',
|
||||
description: '/docs/references/users/create-target.md',
|
||||
auth: [AuthType::KEY, AuthType::ADMIN],
|
||||
@@ -578,6 +590,7 @@ App::get('/v1/users')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'list',
|
||||
description: '/docs/references/users/list-users.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -630,10 +643,15 @@ App::get('/v1/users')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
try {
|
||||
$users = $dbForProject->find('users', $queries);
|
||||
$total = $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'users' => $dbForProject->find('users', $queries),
|
||||
'total' => $dbForProject->count('users', $filterQueries, APP_LIMIT_COUNT),
|
||||
'users' => $users,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_USER_LIST);
|
||||
});
|
||||
|
||||
@@ -643,6 +661,7 @@ App::get('/v1/users/:userId')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'get',
|
||||
description: '/docs/references/users/get-user.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -673,6 +692,7 @@ App::get('/v1/users/:userId/prefs')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'getPrefs',
|
||||
description: '/docs/references/users/get-user-prefs.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -705,6 +725,7 @@ App::get('/v1/users/:userId/targets/:targetId')
|
||||
->label('scope', 'targets.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'targets',
|
||||
name: 'getTarget',
|
||||
description: '/docs/references/users/get-user-target.md',
|
||||
auth: [AuthType::KEY, AuthType::ADMIN],
|
||||
@@ -742,6 +763,7 @@ App::get('/v1/users/:userId/sessions')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'sessions',
|
||||
name: 'listSessions',
|
||||
description: '/docs/references/users/list-user-sessions.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -788,6 +810,7 @@ App::get('/v1/users/:userId/memberships')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'memberships',
|
||||
name: 'listMemberships',
|
||||
description: '/docs/references/users/list-user-memberships.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -832,6 +855,7 @@ App::get('/v1/users/:userId/logs')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'logs',
|
||||
name: 'listLogs',
|
||||
description: '/docs/references/users/list-user-logs.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -928,6 +952,7 @@ App::get('/v1/users/:userId/targets')
|
||||
->label('scope', 'targets.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'targets',
|
||||
name: 'listTargets',
|
||||
description: '/docs/references/users/list-user-targets.md',
|
||||
auth: [AuthType::KEY, AuthType::ADMIN],
|
||||
@@ -980,10 +1005,15 @@ App::get('/v1/users/:userId/targets')
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
try {
|
||||
$targets = $dbForProject->find('targets', $queries);
|
||||
$total = $dbForProject->count('targets', $queries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'targets' => $dbForProject->find('targets', $queries),
|
||||
'total' => $dbForProject->count('targets', $queries, APP_LIMIT_COUNT),
|
||||
'targets' => $targets,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_TARGET_LIST);
|
||||
});
|
||||
|
||||
@@ -993,6 +1023,7 @@ App::get('/v1/users/identities')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'identities',
|
||||
name: 'listIdentities',
|
||||
description: '/docs/references/users/list-identities.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1045,10 +1076,15 @@ App::get('/v1/users/identities')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
try {
|
||||
$identities = $dbForProject->find('identities', $queries);
|
||||
$total = $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'identities' => $dbForProject->find('identities', $queries),
|
||||
'total' => $dbForProject->count('identities', $filterQueries, APP_LIMIT_COUNT),
|
||||
'identities' => $identities,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_IDENTITY_LIST);
|
||||
});
|
||||
|
||||
@@ -1062,6 +1098,7 @@ App::patch('/v1/users/:userId/status')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updateStatus',
|
||||
description: '/docs/references/users/update-user-status.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1102,6 +1139,7 @@ App::put('/v1/users/:userId/labels')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updateLabels',
|
||||
description: '/docs/references/users/update-user-labels.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1144,6 +1182,7 @@ App::patch('/v1/users/:userId/verification/phone')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updatePhoneVerification',
|
||||
description: '/docs/references/users/update-user-phone-verification.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1185,6 +1224,7 @@ App::patch('/v1/users/:userId/name')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updateName',
|
||||
description: '/docs/references/users/update-user-name.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1227,6 +1267,7 @@ App::patch('/v1/users/:userId/password')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updatePassword',
|
||||
description: '/docs/references/users/update-user-password.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1309,6 +1350,7 @@ App::patch('/v1/users/:userId/email')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updateEmail',
|
||||
description: '/docs/references/users/update-user-email.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1408,6 +1450,7 @@ App::patch('/v1/users/:userId/phone')
|
||||
->label('audits.resource', 'user/{response.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updatePhone',
|
||||
description: '/docs/references/users/update-user-phone.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1497,6 +1540,7 @@ App::patch('/v1/users/:userId/verification')
|
||||
->label('audits.userId', '{request.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updateEmailVerification',
|
||||
description: '/docs/references/users/update-user-email-verification.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1534,6 +1578,7 @@ App::patch('/v1/users/:userId/prefs')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updatePrefs',
|
||||
description: '/docs/references/users/update-user-prefs.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1574,6 +1619,7 @@ App::patch('/v1/users/:userId/targets/:targetId')
|
||||
->label('scope', 'targets.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'targets',
|
||||
name: 'updateTarget',
|
||||
description: '/docs/references/users/update-target.md',
|
||||
auth: [AuthType::KEY, AuthType::ADMIN],
|
||||
@@ -1678,6 +1724,7 @@ App::patch('/v1/users/:userId/mfa')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'updateMfa',
|
||||
description: '/docs/references/users/update-user-mfa.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1717,6 +1764,7 @@ App::get('/v1/users/:userId/mfa/factors')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'mfa',
|
||||
name: 'listMfaFactors',
|
||||
description: '/docs/references/users/list-mfa-factors.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1755,6 +1803,7 @@ App::get('/v1/users/:userId/mfa/recovery-codes')
|
||||
->label('usage.metric', 'users.{scope}.requests.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'mfa',
|
||||
name: 'getMfaRecoveryCodes',
|
||||
description: '/docs/references/users/get-mfa-recovery-codes.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1799,6 +1848,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'mfa',
|
||||
name: 'createMfaRecoveryCodes',
|
||||
description: '/docs/references/users/create-mfa-recovery-codes.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1840,7 +1890,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
|
||||
});
|
||||
|
||||
App::put('/v1/users/:userId/mfa/recovery-codes')
|
||||
->desc('Regenerate MFA recovery codes')
|
||||
->desc('Update MFA recovery codes (regenerate)')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.mfa.recovery-codes')
|
||||
->label('scope', 'users.write')
|
||||
@@ -1850,6 +1900,7 @@ App::put('/v1/users/:userId/mfa/recovery-codes')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'mfa',
|
||||
name: 'updateMfaRecoveryCodes',
|
||||
description: '/docs/references/users/update-mfa-recovery-codes.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1900,6 +1951,7 @@ App::delete('/v1/users/:userId/mfa/authenticators/:type')
|
||||
->label('usage.metric', 'users.{scope}.requests.update')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'mfa',
|
||||
name: 'deleteMfaAuthenticator',
|
||||
description: '/docs/references/users/delete-mfa-authenticator.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -1947,6 +1999,7 @@ App::post('/v1/users/:userId/sessions')
|
||||
->label('usage.metric', 'sessions.{scope}.requests.create')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'sessions',
|
||||
name: 'createSession',
|
||||
description: '/docs/references/users/create-session.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2031,6 +2084,7 @@ App::post('/v1/users/:userId/tokens')
|
||||
->label('audits.resource', 'user/{request.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'sessions',
|
||||
name: 'createToken',
|
||||
description: '/docs/references/users/create-token.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2093,6 +2147,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
||||
->label('audits.resource', 'user/{request.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'sessions',
|
||||
name: 'deleteSession',
|
||||
description: '/docs/references/users/delete-user-session.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2143,6 +2198,7 @@ App::delete('/v1/users/:userId/sessions')
|
||||
->label('audits.resource', 'user/{user.$id}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'sessions',
|
||||
name: 'deleteSessions',
|
||||
description: '/docs/references/users/delete-user-sessions.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2192,6 +2248,7 @@ App::delete('/v1/users/:userId')
|
||||
->label('audits.resource', 'user/{request.userId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'users',
|
||||
name: 'delete',
|
||||
description: '/docs/references/users/delete.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2220,6 +2277,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()]));
|
||||
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
@@ -2241,6 +2300,7 @@ App::delete('/v1/users/:userId/targets/:targetId')
|
||||
->label('scope', 'targets.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'targets',
|
||||
name: 'deleteTarget',
|
||||
description: '/docs/references/users/delete-target.md',
|
||||
auth: [AuthType::KEY, AuthType::ADMIN],
|
||||
@@ -2298,6 +2358,7 @@ App::delete('/v1/users/identities/:identityId')
|
||||
->label('audits.resource', 'identity/{request.$identityId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'identities',
|
||||
name: 'deleteIdentity',
|
||||
description: '/docs/references/users/delete-identity.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2337,6 +2398,7 @@ App::post('/v1/users/:userId/jwts')
|
||||
->label('scope', 'users.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: 'sessions',
|
||||
name: 'createJWT',
|
||||
description: '/docs/references/users/create-user-jwt.md',
|
||||
auth: [AuthType::KEY],
|
||||
@@ -2392,6 +2454,7 @@ App::get('/v1/users/usage')
|
||||
->label('scope', 'users.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'users',
|
||||
group: null,
|
||||
name: 'getUsage',
|
||||
description: '/docs/references/users/get-usage.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
@@ -19,6 +19,7 @@ use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
@@ -269,12 +270,13 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
};
|
||||
|
||||
App::get('/v1/vcs/github/authorize')
|
||||
->desc('Install GitHub app')
|
||||
->desc('Create GitHub app installation')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.read')
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'installations',
|
||||
name: 'createGitHubInstallation',
|
||||
description: '/docs/references/vcs/create-github-installation.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -318,7 +320,7 @@ App::get('/v1/vcs/github/authorize')
|
||||
});
|
||||
|
||||
App::get('/v1/vcs/github/callback')
|
||||
->desc('Capture installation and authorization from GitHub app')
|
||||
->desc('Get installation and authorization from GitHub app')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'public')
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
@@ -456,6 +458,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'getRepositoryContents',
|
||||
description: '/docs/references/vcs/get-repository-contents.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -517,11 +520,12 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
});
|
||||
|
||||
App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection')
|
||||
->desc('Detect runtime settings from source code')
|
||||
->desc('Create runtime settings detection')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'createRepositoryDetection',
|
||||
description: '/docs/references/vcs/create-repository-detection.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -599,6 +603,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'listRepositories',
|
||||
description: '/docs/references/vcs/list-repositories.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -699,6 +704,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
->label('scope', 'vcs.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'createRepository',
|
||||
description: '/docs/references/vcs/create-repository.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -811,6 +817,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'getRepository',
|
||||
description: '/docs/references/vcs/get-repository.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -865,6 +872,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'listRepositoryBranches',
|
||||
description: '/docs/references/vcs/list-repository-branches.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1058,6 +1066,7 @@ App::get('/v1/vcs/installations')
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'installations',
|
||||
name: 'listInstallations',
|
||||
description: '/docs/references/vcs/list-installations.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1113,9 +1122,12 @@ App::get('/v1/vcs/installations')
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
$results = $dbForPlatform->find('installations', $queries);
|
||||
$total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT);
|
||||
try {
|
||||
$results = $dbForPlatform->find('installations', $queries);
|
||||
$total = $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT);
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'installations' => $results,
|
||||
@@ -1129,6 +1141,7 @@ App::get('/v1/vcs/installations/:installationId')
|
||||
->label('scope', 'vcs.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'installations',
|
||||
name: 'getInstallation',
|
||||
description: '/docs/references/vcs/get-installation.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1163,6 +1176,7 @@ App::delete('/v1/vcs/installations/:installationId')
|
||||
->label('scope', 'vcs.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'installations',
|
||||
name: 'deleteInstallation',
|
||||
description: '/docs/references/vcs/delete-installation.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
@@ -1198,11 +1212,12 @@ App::delete('/v1/vcs/installations/:installationId')
|
||||
});
|
||||
|
||||
App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositoryId')
|
||||
->desc('Authorize external deployment')
|
||||
->desc('Update external deployment (authorize)')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'updateExternalDeployments',
|
||||
description: '/docs/references/vcs/update-external-deployments.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
|
||||
+29
-41
@@ -50,7 +50,7 @@ Config::setParam('domainVerification', false);
|
||||
Config::setParam('cookieDomain', 'localhost');
|
||||
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
|
||||
|
||||
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname)
|
||||
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname)
|
||||
{
|
||||
$utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml');
|
||||
|
||||
@@ -72,11 +72,19 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
}
|
||||
|
||||
if ($route->isEmpty()) {
|
||||
if ($host === System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) {
|
||||
|
||||
$appDomainFunctionsFallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
|
||||
$appDomainFunctions = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
|
||||
|
||||
if (!empty($appDomainFunctionsFallback) && \str_ends_with($host, $appDomainFunctionsFallback)) {
|
||||
$appDomainFunctions = $appDomainFunctionsFallback;
|
||||
}
|
||||
|
||||
if ($host === $appDomainFunctions) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.');
|
||||
}
|
||||
|
||||
if (\str_ends_with($host, System::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) {
|
||||
if (\str_ends_with($host, $appDomainFunctions)) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.');
|
||||
}
|
||||
|
||||
@@ -125,6 +133,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
if (empty($method)) {
|
||||
$utopia->getRoute()?->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
group: 'executions',
|
||||
name: 'createExecution',
|
||||
description: '/docs/references/functions/create-execution.md',
|
||||
auth: [AuthType::SESSION, AuthType::KEY, AuthType::JWT],
|
||||
@@ -135,7 +144,6 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
)
|
||||
],
|
||||
contentType: ContentType::MULTIPART,
|
||||
requestType: 'application/json',
|
||||
));
|
||||
} else {
|
||||
/** @var Method $method */
|
||||
@@ -339,7 +347,6 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
]);
|
||||
|
||||
/** Execute function */
|
||||
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
|
||||
try {
|
||||
$version = $function->getAttribute('version', 'v2');
|
||||
$command = $runtime['startCommand'];
|
||||
@@ -503,9 +510,10 @@ App::init()
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForCertificates')
|
||||
->inject('queueForFunctions')
|
||||
->inject('executor')
|
||||
->inject('isResourceBlocked')
|
||||
->inject('previewHostname')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked, string $previewHostname) {
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, Executor $executor, callable $isResourceBlocked, string $previewHostname) {
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
@@ -513,7 +521,7 @@ App::init()
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
// Only run Router when external domain
|
||||
if ($host !== $mainDomain || !empty($previewHostname)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -742,11 +750,12 @@ App::options()
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('executor')
|
||||
->inject('geodb')
|
||||
->inject('isResourceBlocked')
|
||||
->inject('previewHostname')
|
||||
->inject('project')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project) {
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, Document $project) {
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
@@ -754,7 +763,7 @@ App::options()
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
// Only run Router when external domain
|
||||
if ($host !== $mainDomain || !empty($previewHostname)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -826,35 +835,11 @@ App::error()
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'Utopia\Database\Exception\Conflict':
|
||||
$error = new AppwriteException(AppwriteException::DOCUMENT_UPDATE_CONFLICT, previous: $error);
|
||||
break;
|
||||
case 'Utopia\Database\Exception\Timeout':
|
||||
$error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error);
|
||||
break;
|
||||
case 'Utopia\Database\Exception\Query':
|
||||
$error = new AppwriteException(AppwriteException::GENERAL_QUERY_INVALID, $error->getMessage(), previous: $error);
|
||||
break;
|
||||
case 'Utopia\Database\Exception\Structure':
|
||||
$error = new AppwriteException(AppwriteException::DOCUMENT_INVALID_STRUCTURE, $error->getMessage(), previous: $error);
|
||||
break;
|
||||
case 'Utopia\Database\Exception\Duplicate':
|
||||
$error = new AppwriteException(AppwriteException::DOCUMENT_ALREADY_EXISTS);
|
||||
break;
|
||||
case 'Utopia\Database\Exception\Restricted':
|
||||
$error = new AppwriteException(AppwriteException::DOCUMENT_DELETE_RESTRICTED);
|
||||
break;
|
||||
case 'Utopia\Database\Exception\Authorization':
|
||||
$error = new AppwriteException(AppwriteException::USER_UNAUTHORIZED);
|
||||
break;
|
||||
case 'Utopia\Database\Exception\Relationship':
|
||||
$error = new AppwriteException(AppwriteException::RELATIONSHIP_VALUE_INVALID, $error->getMessage(), previous: $error);
|
||||
break;
|
||||
case 'Utopia\Database\Exception\NotFound':
|
||||
$error = new AppwriteException(AppwriteException::COLLECTION_NOT_FOUND, $error->getMessage(), previous: $error);
|
||||
break;
|
||||
case 'Utopia\Database\Exception\Dependency':
|
||||
$error = new AppwriteException(AppwriteException::INDEX_DEPENDENCY, null, previous: $error);
|
||||
case 'Utopia\Database\Exception\Timeout':
|
||||
$error = new AppwriteException(AppwriteException::DATABASE_TIMEOUT, previous: $error);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -871,17 +856,18 @@ App::error()
|
||||
if (!empty($providerConfig) && $error->getCode() >= 400 && $error->getCode() < 500) {
|
||||
// Register error logger
|
||||
try {
|
||||
$loggingProvider = new DSN($providerConfig ?? '');
|
||||
$loggingProvider = new DSN($providerConfig);
|
||||
$providerName = $loggingProvider->getScheme();
|
||||
|
||||
if (!empty($providerName) && $providerName === 'sentry') {
|
||||
$key = $loggingProvider->getPassword();
|
||||
$projectId = $loggingProvider->getUser() ?? '';
|
||||
$host = 'https://' . $loggingProvider->getHost();
|
||||
$sampleRate = $loggingProvider->getParam('sample', 0.01);
|
||||
|
||||
$adapter = new Sentry($projectId, $key, $host);
|
||||
$logger = new Logger($adapter);
|
||||
$logger->setSample(0.01);
|
||||
$logger->setSample($sampleRate);
|
||||
$publish = true;
|
||||
} else {
|
||||
throw new \Exception('Invalid experimental logging provider');
|
||||
@@ -1061,10 +1047,11 @@ App::get('/robots.txt')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('executor')
|
||||
->inject('geodb')
|
||||
->inject('isResourceBlocked')
|
||||
->inject('previewHostname')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
|
||||
$host = $request->getHostname() ?? '';
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
|
||||
@@ -1072,7 +1059,7 @@ App::get('/robots.txt')
|
||||
$template = new View(__DIR__ . '/../views/general/robots.phtml');
|
||||
$response->text($template->render(false));
|
||||
} else {
|
||||
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname);
|
||||
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1089,10 +1076,11 @@ App::get('/humans.txt')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('executor')
|
||||
->inject('geodb')
|
||||
->inject('isResourceBlocked')
|
||||
->inject('previewHostname')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname) {
|
||||
$host = $request->getHostname() ?? '';
|
||||
$mainDomain = System::getEnv('_APP_DOMAIN', '');
|
||||
|
||||
@@ -1100,7 +1088,7 @@ App::get('/humans.txt')
|
||||
$template = new View(__DIR__ . '/../views/general/humans.phtml');
|
||||
$response->text($template->render(false));
|
||||
} else {
|
||||
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname);
|
||||
router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $previewHostname);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -92,8 +92,20 @@ $eventDatabaseListener = function (Document $project, Document $document, Respon
|
||||
|
||||
$usageDatabaseListener = function (string $event, Document $document, StatsUsage $queueForStatsUsage) {
|
||||
$value = 1;
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
$value = -1;
|
||||
|
||||
switch ($event) {
|
||||
case Database::EVENT_DOCUMENT_DELETE:
|
||||
$value = -1;
|
||||
break;
|
||||
case Database::EVENT_DOCUMENTS_DELETE:
|
||||
$value = -1 * $document->getAttribute('modified', 0);
|
||||
break;
|
||||
case Database::EVENT_DOCUMENTS_CREATE:
|
||||
$value = $document->getAttribute('modified', 0);
|
||||
break;
|
||||
case Database::EVENT_DOCUMENTS_UPSERT:
|
||||
$value = $document->getAttribute('created', 0);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
@@ -238,34 +250,36 @@ App::init()
|
||||
subject: 'keys'
|
||||
);
|
||||
|
||||
if ($dbKey) {
|
||||
$accessedAt = $dbKey->getAttribute('accessedAt', '');
|
||||
if (!$dbKey) {
|
||||
throw new Exception(Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
|
||||
$dbKey->setAttribute('accessedAt', DateTime::now());
|
||||
$accessedAt = $dbKey->getAttribute('accessedAt', '');
|
||||
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
|
||||
$dbKey->setAttribute('accessedAt', DateTime::now());
|
||||
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
|
||||
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
|
||||
}
|
||||
|
||||
$sdkValidator = new WhiteList($servers, true);
|
||||
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
|
||||
|
||||
if ($sdkValidator->isValid($sdk)) {
|
||||
$sdks = $dbKey->getAttribute('sdks', []);
|
||||
|
||||
if (!in_array($sdk, $sdks)) {
|
||||
$sdks[] = $sdk;
|
||||
$dbKey->setAttribute('sdks', $sdks);
|
||||
|
||||
/** Update access time as well */
|
||||
$dbKey->setAttribute('accessedAt', Datetime::now());
|
||||
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
|
||||
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
|
||||
}
|
||||
|
||||
$sdkValidator = new WhiteList($servers, true);
|
||||
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
|
||||
|
||||
if ($sdkValidator->isValid($sdk)) {
|
||||
$sdks = $dbKey->getAttribute('sdks', []);
|
||||
|
||||
if (!in_array($sdk, $sdks)) {
|
||||
$sdks[] = $sdk;
|
||||
$dbKey->setAttribute('sdks', $sdks);
|
||||
|
||||
/** Update access time as well */
|
||||
$dbKey->setAttribute('accessedAt', Datetime::now());
|
||||
$dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey);
|
||||
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
|
||||
}
|
||||
}
|
||||
|
||||
$queueForAudits->setUser($user);
|
||||
}
|
||||
|
||||
$queueForAudits->setUser($user);
|
||||
}
|
||||
} // Admin User Authentication
|
||||
elseif (($project->getId() === 'console' && !$team->isEmpty() && !$user->isEmpty()) || ($project->getId() !== 'console' && !$user->isEmpty() && $mode === APP_MODE_ADMIN)) {
|
||||
@@ -326,6 +340,8 @@ App::init()
|
||||
*/
|
||||
$method = $route->getLabel('sdk', false);
|
||||
|
||||
// Take the first method if there's more than one,
|
||||
// namespace can not differ between methods on the same route
|
||||
if (\is_array($method)) {
|
||||
$method = $method[0];
|
||||
}
|
||||
@@ -390,7 +406,8 @@ App::init()
|
||||
->inject('timelimit')
|
||||
->inject('mode')
|
||||
->inject('apiKey')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, string $mode, ?Key $apiKey) use ($usageDatabaseListener, $eventDatabaseListener) {
|
||||
->inject('plan')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, string $mode, ?Key $apiKey, array $plan) use ($usageDatabaseListener, $eventDatabaseListener) {
|
||||
|
||||
$route = $utopia->getRoute();
|
||||
|
||||
@@ -508,6 +525,9 @@ App::init()
|
||||
$dbForProject
|
||||
->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
|
||||
->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
|
||||
->on(Database::EVENT_DOCUMENTS_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
|
||||
->on(Database::EVENT_DOCUMENTS_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
|
||||
->on(Database::EVENT_DOCUMENTS_UPSERT, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage))
|
||||
->on(Database::EVENT_DOCUMENT_CREATE, 'create-trigger-events', fn ($event, $document) => $eventDatabaseListener(
|
||||
$project,
|
||||
$document,
|
||||
@@ -520,7 +540,11 @@ App::init()
|
||||
|
||||
$useCache = $route->getLabel('cache', false);
|
||||
if ($useCache) {
|
||||
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
||||
$route = $utopia->match($request);
|
||||
$isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview';
|
||||
$isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
|
||||
$key = $request->cacheIdentifier();
|
||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||
$cache = new Cache(
|
||||
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
|
||||
@@ -529,10 +553,10 @@ App::init()
|
||||
$data = $cache->load($key, $timestamp);
|
||||
|
||||
if (!empty($data) && !$cacheLog->isEmpty()) {
|
||||
$parts = explode('/', $cacheLog->getAttribute('resourceType'));
|
||||
$parts = explode('/', $cacheLog->getAttribute('resourceType', ''));
|
||||
$type = $parts[0] ?? null;
|
||||
|
||||
if ($type === 'bucket') {
|
||||
if ($type === 'bucket' && (!$isImageTransformation || !$isDisabled)) {
|
||||
$bucketId = $parts[1] ?? null;
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
@@ -573,8 +597,10 @@ App::init()
|
||||
$response
|
||||
->addHeader('Cache-Control', sprintf('private, max-age=%d', $timestamp))
|
||||
->addHeader('X-Appwrite-Cache', 'hit')
|
||||
->setContentType($cacheLog->getAttribute('mimeType'))
|
||||
->send($data);
|
||||
->setContentType($cacheLog->getAttribute('mimeType'));
|
||||
if (!$isImageTransformation || !$isDisabled) {
|
||||
$response->send($data);
|
||||
}
|
||||
} else {
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
@@ -777,7 +803,7 @@ App::shutdown()
|
||||
$resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user);
|
||||
}
|
||||
|
||||
$key = md5($request->getURI() . '*' . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER);
|
||||
$key = $request->cacheIdentifier();
|
||||
$signature = md5($data['payload']);
|
||||
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
|
||||
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
|
||||
|
||||
+97
-96
@@ -14,9 +14,11 @@ use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
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;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
@@ -43,29 +45,6 @@ $http = new Server(
|
||||
$payloadSize = 12 * (1024 * 1024); // 12MB - adding slight buffer for headers and other data that might be sent with the payload - update later with valid testing
|
||||
$totalWorkers = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
|
||||
|
||||
$http
|
||||
->set([
|
||||
'worker_num' => $totalWorkers,
|
||||
'dispatch_func' => 'dispatch',
|
||||
'open_http2_protocol' => true,
|
||||
'http_compression' => false,
|
||||
'package_max_length' => $payloadSize,
|
||||
'buffer_output_size' => $payloadSize,
|
||||
'task_worker_num' => 1, // required for the task to fetch domains background
|
||||
]);
|
||||
|
||||
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
|
||||
Console::success('Worker ' . ++$workerId . ' started successfully');
|
||||
});
|
||||
|
||||
$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) {
|
||||
Console::success('Starting reload...');
|
||||
});
|
||||
|
||||
$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
|
||||
Console::success('Reload completed...');
|
||||
});
|
||||
|
||||
/**
|
||||
* Assigns HTTP requests to worker threads by analyzing its payload/content.
|
||||
*
|
||||
@@ -83,76 +62,105 @@ $http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
|
||||
*/
|
||||
function dispatch(Server $server, int $fd, int $type, $data = null): int
|
||||
{
|
||||
global $totalWorkers, $domains;
|
||||
$resolveWorkerId = function (Server $server, $data = null) {
|
||||
global $totalWorkers, $domains;
|
||||
|
||||
// If data is not set we can send request to any worker
|
||||
// first we try to pick idle worker, if not we randomly pick a worker
|
||||
if ($data === null) {
|
||||
// If data is not set we can send request to any worker
|
||||
// first we try to pick idle worker, if not we randomly pick a worker
|
||||
if ($data === null) {
|
||||
for ($i = 0; $i < $totalWorkers; $i++) {
|
||||
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
return rand(0, $totalWorkers - 1);
|
||||
}
|
||||
|
||||
$riskyWorkersPercent = intval(System::getEnv('_APP_RISKY_WORKERS_PERCENT', 80)) / 100; // Decimal form 0 to 1
|
||||
|
||||
// Each worker has numeric ID, starting from 0 and incrementing
|
||||
// From 0 to riskyWorkers, we consider safe workers
|
||||
// From riskyWorkers to totalWorkers, we consider risky workers
|
||||
$riskyWorkers = (int)floor($totalWorkers * $riskyWorkersPercent); // Absolute amount of risky workers
|
||||
|
||||
$domain = '';
|
||||
// max up to 3 as first line has request details and second line has host
|
||||
$lines = explode("\n", $data, 3);
|
||||
$request = $lines[0];
|
||||
if (count($lines) > 1) {
|
||||
$domain = trim(explode('Host: ', $lines[1])[1]);
|
||||
}
|
||||
|
||||
// Sync executions are considered risky
|
||||
$risky = false;
|
||||
if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) {
|
||||
$risky = true;
|
||||
} elseif (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
|
||||
$risky = true;
|
||||
} elseif ($domains->get(md5($domain), 'value') === 1) {
|
||||
// executions request coming from custom domain
|
||||
$risky = true;
|
||||
}
|
||||
|
||||
if ($risky) {
|
||||
// If risky request, only consider risky workers
|
||||
for ($j = $riskyWorkers; $j < $totalWorkers; $j++) {
|
||||
/** Reference https://openswoole.com/docs/modules/swoole-server-getWorkerStatus#description */
|
||||
if ($server->getWorkerStatus($j) === SWOOLE_WORKER_IDLE) {
|
||||
// If idle worker found, give to him
|
||||
return $j;
|
||||
}
|
||||
}
|
||||
|
||||
// If no idle workers, give to random risky worker
|
||||
$worker = rand($riskyWorkers, $totalWorkers - 1);
|
||||
Console::warning("swoole_dispatch: Risky branch: did not find a idle worker, picking random worker {$worker}");
|
||||
return $worker;
|
||||
}
|
||||
|
||||
// If safe request, give to any idle worker
|
||||
// Its fine to pick risky worker here, because it's idle. Idle is never actually risky
|
||||
for ($i = 0; $i < $totalWorkers; $i++) {
|
||||
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
return rand(0, $totalWorkers - 1);
|
||||
}
|
||||
|
||||
$riskyWorkersPercent = intval(System::getEnv('_APP_RISKY_WORKERS_PERCENT', 80)) / 100; // Decimal form 0 to 1
|
||||
|
||||
// Each worker has numeric ID, starting from 0 and incrementing
|
||||
// From 0 to riskyWorkers, we consider safe workers
|
||||
// From riskyWorkers to totalWorkers, we consider risky workers
|
||||
$riskyWorkers = (int) floor($totalWorkers * $riskyWorkersPercent); // Absolute amount of risky workers
|
||||
|
||||
$domain = '';
|
||||
// max up to 3 as first line has request details and second line has host
|
||||
$lines = explode("\n", $data, 3);
|
||||
$request = $lines[0];
|
||||
if (count($lines) > 1) {
|
||||
$domain = trim(explode('Host: ', $lines[1])[1]);
|
||||
}
|
||||
|
||||
// Sync executions are considered risky
|
||||
$risky = false;
|
||||
if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) {
|
||||
$risky = true;
|
||||
} elseif (str_ends_with($domain, System::getEnv('_APP_DOMAIN_FUNCTIONS'))) {
|
||||
$risky = true;
|
||||
} elseif ($domains->get(md5($domain), 'value') === 1) {
|
||||
// executions request coming from custom domain
|
||||
$risky = true;
|
||||
}
|
||||
|
||||
if ($risky) {
|
||||
// If risky request, only consider risky workers
|
||||
for ($j = $riskyWorkers; $j < $totalWorkers; $j++) {
|
||||
/** Reference https://openswoole.com/docs/modules/swoole-server-getWorkerStatus#description */
|
||||
if ($server->getWorkerStatus($j) === SWOOLE_WORKER_IDLE) {
|
||||
// If idle worker found, give to him
|
||||
return $j;
|
||||
}
|
||||
}
|
||||
|
||||
// If no idle workers, give to random risky worker
|
||||
$worker = rand($riskyWorkers, $totalWorkers - 1);
|
||||
Console::warning("swoole_dispatch: Risky branch: did not find a idle worker, picking random worker {$worker}");
|
||||
// If no idle worker found, give to random safe worker
|
||||
// We avoid risky workers here, as it could be in work - not idle. Thats exactly when they are risky.
|
||||
$worker = rand(0, $riskyWorkers - 1);
|
||||
Console::warning("swoole_dispatch: Non-risky branch: did not find a idle worker, picking random worker {$worker}");
|
||||
return $worker;
|
||||
}
|
||||
|
||||
// If safe request, give to any idle worker
|
||||
// Its fine to pick risky worker here, because it's idle. Idle is never actually risky
|
||||
for ($i = 0; $i < $totalWorkers; $i++) {
|
||||
if ($server->getWorkerStatus($i) === SWOOLE_WORKER_IDLE) {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
|
||||
// If no idle worker found, give to random safe worker
|
||||
// We avoid risky workers here, as it could be in work - not idle. Thats exactly when they are risky.
|
||||
$worker = rand(0, $riskyWorkers - 1);
|
||||
Console::warning("swoole_dispatch: Non-risky branch: did not find a idle worker, picking random worker {$worker}");
|
||||
return $worker;
|
||||
};
|
||||
$workerId = $resolveWorkerId($server, $data);
|
||||
$server->bind($fd, $workerId);
|
||||
return $workerId;
|
||||
}
|
||||
|
||||
|
||||
$http
|
||||
->set([
|
||||
Constant::OPTION_WORKER_NUM => $totalWorkers,
|
||||
Constant::OPTION_DISPATCH_FUNC => dispatch(...),
|
||||
Constant::OPTION_DISPATCH_MODE => SWOOLE_DISPATCH_UIDMOD,
|
||||
Constant::OPTION_HTTP_COMPRESSION => false,
|
||||
Constant::OPTION_PACKAGE_MAX_LENGTH => $payloadSize,
|
||||
Constant::OPTION_OUTPUT_BUFFER_SIZE => $payloadSize,
|
||||
Constant::OPTION_TASK_WORKER_NUM => 1, // required for the task to fetch domains background
|
||||
]);
|
||||
|
||||
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
|
||||
Console::success('Worker ' . ++$workerId . ' started successfully');
|
||||
});
|
||||
|
||||
$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) {
|
||||
Console::success('Starting reload...');
|
||||
});
|
||||
|
||||
$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
|
||||
Console::success('Reload completed...');
|
||||
});
|
||||
|
||||
include __DIR__ . '/controllers/general.php';
|
||||
|
||||
function createDatabase(App $app, string $resourceKey, string $dbName, array $collections, mixed $pools, callable $extraSetup = null): void
|
||||
@@ -161,7 +169,7 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co
|
||||
$sleep = 1;
|
||||
$attempts = 0;
|
||||
|
||||
do {
|
||||
while (true) {
|
||||
try {
|
||||
$attempts++;
|
||||
$resource = $app->getResource($resourceKey);
|
||||
@@ -170,13 +178,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...");
|
||||
|
||||
@@ -312,11 +319,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)
|
||||
@@ -326,7 +329,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');
|
||||
}
|
||||
|
||||
@@ -352,7 +355,6 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
|
||||
}
|
||||
}
|
||||
|
||||
$pools->reclaim();
|
||||
Console::success('[Setup] - Server database init completed...');
|
||||
});
|
||||
|
||||
@@ -467,6 +469,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);
|
||||
|
||||
@@ -484,13 +487,11 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
|
||||
];
|
||||
|
||||
$swooleResponse->end(\json_encode($output));
|
||||
} finally {
|
||||
$pools->reclaim();
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch domains every `DOMAIN_SYNC_TIMER` seconds and update in the memory
|
||||
$http->on('Task', function () use ($register, $domains) {
|
||||
$http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
|
||||
$lastSyncUpdate = null;
|
||||
$pools = $register->get('pools');
|
||||
App::setResource('pools', fn () => $pools);
|
||||
|
||||
@@ -26,6 +26,7 @@ const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000;
|
||||
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
|
||||
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
|
||||
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
|
||||
const APP_LIMIT_DATABASE_BATCH = 100; // Default maximum batch size for database operations
|
||||
const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
|
||||
@@ -77,12 +77,21 @@ Database::addFilter(
|
||||
]);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
use Utopia\Database\Adapter\MySQL;
|
||||
use Utopia\Database\Adapter\SQL;
|
||||
use Utopia\Database\PDO;
|
||||
use Utopia\Domains\Validator\PublicDomain;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Logger\Adapter\AppSignal;
|
||||
@@ -215,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(
|
||||
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
|
||||
));
|
||||
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) {
|
||||
|
||||
+44
-41
@@ -21,12 +21,15 @@ use Appwrite\Extend\Exception;
|
||||
use Appwrite\GraphQL\Schema;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Executor\Executor;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Adapter\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\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
@@ -36,8 +39,10 @@ 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;
|
||||
use Utopia\Storage\Device\Backblaze;
|
||||
use Utopia\Storage\Device\DOSpaces;
|
||||
use Utopia\Storage\Device\Linode;
|
||||
@@ -46,6 +51,8 @@ use Utopia\Storage\Device\S3;
|
||||
use Utopia\Storage\Device\Wasabi;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter as Telemetry;
|
||||
use Utopia\Telemetry\Adapter\None as NoTelemetry;
|
||||
use Utopia\Validator\Hostname;
|
||||
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
|
||||
|
||||
@@ -68,10 +75,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);
|
||||
@@ -325,12 +332,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())
|
||||
@@ -356,12 +359,8 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
|
||||
}, ['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')
|
||||
@@ -374,7 +373,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') {
|
||||
@@ -416,12 +415,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);
|
||||
|
||||
@@ -431,21 +426,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());
|
||||
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)
|
||||
@@ -462,20 +451,22 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
};
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
App::setResource('cache', function (Group $pools) {
|
||||
App::setResource('telemetry', fn () => new NoTelemetry());
|
||||
|
||||
App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
|
||||
$list = Config::getParam('pools-cache', []);
|
||||
$adapters = [];
|
||||
|
||||
foreach ($list as $value) {
|
||||
$adapters[] = $pools
|
||||
->get($value)
|
||||
->pop()
|
||||
->getResource()
|
||||
;
|
||||
$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');
|
||||
@@ -540,7 +531,12 @@ function getDevice(string $root, string $connection = ''): Device
|
||||
|
||||
switch ($device) {
|
||||
case Storage::DEVICE_S3:
|
||||
return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl, $url);
|
||||
if (!empty($url)) {
|
||||
return new S3($root, $accessKey, $accessSecret, $url, $region, $acl);
|
||||
} else {
|
||||
return new AWS($root, $accessKey, $accessSecret, $bucket, $region, $acl);
|
||||
}
|
||||
// no break
|
||||
case STORAGE::DEVICE_DO_SPACES:
|
||||
$device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl);
|
||||
$device->setHttpVersion(S3::HTTP_VERSION_1_1);
|
||||
@@ -567,7 +563,12 @@ function getDevice(string $root, string $connection = ''): Device
|
||||
$s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', '');
|
||||
$s3Acl = 'private';
|
||||
$s3EndpointUrl = System::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
|
||||
return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl, $s3EndpointUrl);
|
||||
if (!empty($s3EndpointUrl)) {
|
||||
return new S3($root, $s3AccessKey, $s3SecretKey, $s3EndpointUrl, $s3Region, $s3Acl);
|
||||
} else {
|
||||
return new AWS($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl);
|
||||
}
|
||||
// no break
|
||||
case Storage::DEVICE_DO_SPACES:
|
||||
$doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', '');
|
||||
$doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', '');
|
||||
@@ -822,3 +823,5 @@ 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')));
|
||||
|
||||
+65
-45
@@ -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,12 @@ use Swoole\Timer;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\App;
|
||||
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 +31,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 +51,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 +77,13 @@ if (!function_exists('getProjectDB')) {
|
||||
{
|
||||
global $register;
|
||||
|
||||
/** @var \Utopia\Pools\Group $pools */
|
||||
static $databases = [];
|
||||
|
||||
if (isset($databases[$project->getInternalId()])) {
|
||||
return $databases[$project->getInternalId()];
|
||||
}
|
||||
|
||||
/** @var Group $pools */
|
||||
$pools = $register->get('pools');
|
||||
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
@@ -86,11 +97,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', ''));
|
||||
@@ -111,7 +118,7 @@ if (!function_exists('getProjectDB')) {
|
||||
->setMetadata('host', \gethostname())
|
||||
->setMetadata('project', $project->getId());
|
||||
|
||||
return $database;
|
||||
return $databases[$project->getInternalId()] = $database;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,20 +128,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 +151,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 +175,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 +306,6 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
||||
sleep(DATABASE_RECONNECT_SLEEP);
|
||||
}
|
||||
} while (true);
|
||||
$register->get('pools')->reclaim();
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -299,9 +331,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 +400,6 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||
'data' => $event['data']
|
||||
]));
|
||||
}
|
||||
|
||||
$register->get('pools')->reclaim();
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -407,8 +435,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 +464,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 +489,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 +596,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 +620,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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -696,8 +718,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();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -144,10 +144,12 @@ $image = $this->getParam('image', '');
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_MAINTENANCE_INTERVAL
|
||||
- _APP_MAINTENANCE_DELAY
|
||||
- _APP_MAINTENANCE_START_TIME
|
||||
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
|
||||
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
- _APP_SMS_PROVIDER
|
||||
@@ -167,7 +169,7 @@ $image = $this->getParam('image', '');
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: <?php echo $organization; ?>/console:5.2.53
|
||||
image: <?php echo $organization; ?>/console:5.2.58
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -340,6 +342,7 @@ $image = $this->getParam('image', '');
|
||||
- _APP_EXECUTOR_HOST
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
|
||||
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
|
||||
- _APP_EMAIL_CERTIFICATES
|
||||
@@ -651,6 +654,7 @@ $image = $this->getParam('image', '');
|
||||
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
|
||||
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
|
||||
@@ -741,34 +745,6 @@ $image = $this->getParam('image', '');
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
|
||||
appwrite-worker-stats-usage-dump:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
entrypoint: worker-stats-usage-dump
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-stats-usage-dump
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_USAGE_STATS
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
|
||||
appwrite-task-scheduler-functions:
|
||||
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
|
||||
entrypoint: schedule-functions
|
||||
|
||||
+28
-64
@@ -15,17 +15,17 @@ use Appwrite\Event\Messaging;
|
||||
use Appwrite\Event\Migration;
|
||||
use Appwrite\Event\Realtime;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Event\StatsUsageDump;
|
||||
/** remove */
|
||||
/** /remove */
|
||||
use Appwrite\Event\Webhook;
|
||||
use Appwrite\Platform\Appwrite;
|
||||
use Executor\Executor;
|
||||
use Swoole\Runtime;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\Cache\Adapter\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;
|
||||
@@ -35,6 +35,7 @@ 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;
|
||||
@@ -42,21 +43,17 @@ use Utopia\Registry\Registry;
|
||||
use Utopia\System\System;
|
||||
|
||||
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) {
|
||||
@@ -84,20 +81,9 @@ 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)) {
|
||||
@@ -152,12 +138,8 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
|
||||
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;
|
||||
|
||||
@@ -189,15 +171,8 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
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)
|
||||
@@ -215,15 +190,18 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||
}, ['pools', 'cache']);
|
||||
|
||||
Server::setResource('abuseRetention', function () {
|
||||
return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400);
|
||||
return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400); // 1 day
|
||||
});
|
||||
|
||||
Server::setResource('auditRetention', function () {
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600));
|
||||
});
|
||||
Server::setResource('auditRetention', function (Document $project) {
|
||||
if ($project->getId() === 'console') {
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE', 15778800)); // 6 months
|
||||
}
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 1209600)); // 14 days
|
||||
}, ['project']);
|
||||
|
||||
Server::setResource('executionRetention', function () {
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600));
|
||||
return DateTime::addSeconds(new \DateTime(), -1 * System::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', 1209600)); // 14 days
|
||||
});
|
||||
|
||||
Server::setResource('cache', function (Registry $register) {
|
||||
@@ -232,11 +210,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));
|
||||
@@ -266,21 +240,17 @@ 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) {
|
||||
return new StatsUsage($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForStatsUsageDump', function (Publisher $publisher) {
|
||||
return new StatsUsageDump($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForDatabase', function (Publisher $publisher) {
|
||||
return new EventDatabase($publisher);
|
||||
}, ['publisher']);
|
||||
@@ -412,6 +382,8 @@ Server::setResource('logError', function (Registry $register, Document $project)
|
||||
};
|
||||
}, ['register', 'project']);
|
||||
|
||||
Server::setResource('executor', fn () => new Executor(fn (string $projectId, string $deploymentId) => System::getEnv('_APP_EXECUTOR_HOST')));
|
||||
|
||||
$pools = $register->get('pools');
|
||||
$platform = new Appwrite();
|
||||
$args = $platform->getEnv('argv');
|
||||
@@ -449,13 +421,6 @@ try {
|
||||
|
||||
$worker = $platform->getWorker();
|
||||
|
||||
$worker
|
||||
->shutdown()
|
||||
->inject('pools')
|
||||
->action(function (Group $pools) {
|
||||
$pools->reclaim();
|
||||
});
|
||||
|
||||
$worker
|
||||
->error()
|
||||
->inject('error')
|
||||
@@ -463,8 +428,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) {
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
php /usr/src/code/app/worker.php stats-usage-dump $@
|
||||
+11
-11
@@ -30,7 +30,7 @@
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0.0",
|
||||
"php": ">=8.3.0",
|
||||
"ext-curl": "*",
|
||||
"ext-imagick": "*",
|
||||
"ext-mbstring": "*",
|
||||
@@ -48,31 +48,31 @@
|
||||
"utopia-php/abuse": "0.52.*",
|
||||
"utopia-php/analytics": "0.10.*",
|
||||
"utopia-php/audit": "0.55.*",
|
||||
"utopia-php/cache": "0.12.*",
|
||||
"utopia-php/cache": "0.13.*",
|
||||
"utopia-php/cli": "0.15.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.61.*",
|
||||
"utopia-php/database": "0.69.*",
|
||||
"utopia-php/domains": "0.5.*",
|
||||
"utopia-php/dsn": "0.2.1",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
"utopia-php/fetch": "0.3.*",
|
||||
"utopia-php/fetch": "0.4.*",
|
||||
"utopia-php/image": "0.8.*",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/logger": "0.6.*",
|
||||
"utopia-php/messaging": "0.16.*",
|
||||
"utopia-php/migration": "0.8.*",
|
||||
"utopia-php/messaging": "0.17.*",
|
||||
"utopia-php/migration": "0.9.*",
|
||||
"utopia-php/orchestration": "0.9.*",
|
||||
"utopia-php/platform": "0.7.*",
|
||||
"utopia-php/pools": "0.7.*",
|
||||
"utopia-php/pools": "0.8.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
"utopia-php/queue": "0.9.*",
|
||||
"utopia-php/queue": "0.10.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/storage": "0.18.*",
|
||||
"utopia-php/swoole": "0.8.*",
|
||||
"utopia-php/system": "0.9.*",
|
||||
"utopia-php/telemetry": "0.1.*",
|
||||
"utopia-php/vcs": "0.9.*",
|
||||
"utopia-php/websocket": "0.1.*",
|
||||
"utopia-php/websocket": "0.3.*",
|
||||
"matomo/device-detector": "6.1.*",
|
||||
"dragonmantank/cron-expression": "3.3.2",
|
||||
"phpmailer/phpmailer": "6.9.1",
|
||||
@@ -99,8 +99,8 @@
|
||||
"php": "8.3"
|
||||
},
|
||||
"allow-plugins": {
|
||||
"php-http/discovery": false,
|
||||
"tbachert/spi": false
|
||||
"php-http/discovery": true,
|
||||
"tbachert/spi": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+226
-491
File diff suppressed because it is too large
Load Diff
+12
-35
@@ -171,6 +171,7 @@ services:
|
||||
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
|
||||
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
- _APP_SMS_PROVIDER
|
||||
@@ -197,11 +198,14 @@ services:
|
||||
- _APP_DATABASE_SHARED_TABLES_V1
|
||||
- _APP_DATABASE_SHARED_NAMESPACE
|
||||
- _APP_FUNCTIONS_CREATION_ABUSE_LIMIT
|
||||
- _APP_CUSTOM_DOMAIN_DENY_LIST
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:5.2.53
|
||||
image: appwrite/console:5.2.58
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
@@ -389,6 +393,8 @@ services:
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
- _APP_DATABASE_SHARED_TABLES_V1
|
||||
- _APP_EMAIL_CERTIFICATES
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
|
||||
|
||||
appwrite-worker-databases:
|
||||
entrypoint: worker-databases
|
||||
@@ -486,6 +492,8 @@ services:
|
||||
- _APP_STORAGE_WASABI_REGION
|
||||
- _APP_STORAGE_WASABI_BUCKET
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
appwrite-worker-certificates:
|
||||
entrypoint: worker-certificates
|
||||
@@ -721,9 +729,10 @@ services:
|
||||
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||
- _APP_MAINTENANCE_RETENTION_ABUSE
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT
|
||||
- _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE
|
||||
- _APP_MAINTENANCE_RETENTION_USAGE_HOURLY
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
- _APP_MAINTENANCE_DELAY
|
||||
- _APP_MAINTENANCE_START_TIME
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
appwrite-task-stats-resources:
|
||||
@@ -819,38 +828,6 @@ services:
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
appwrite-worker-stats-usage-dump:
|
||||
entrypoint: worker-stats-usage-dump
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-stats-usage-dump
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_USAGE_STATS
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
- _APP_STATS_USAGE_DUAL_WRITING_DBS
|
||||
|
||||
appwrite-task-scheduler-functions:
|
||||
entrypoint: schedule-functions
|
||||
<<: *x-logging
|
||||
@@ -1127,4 +1104,4 @@ volumes:
|
||||
appwrite-certificates:
|
||||
appwrite-functions:
|
||||
appwrite-builds:
|
||||
appwrite-config:
|
||||
appwrite-config:
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Account account = new Account(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Databases databases = new Databases(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Databases databases = new Databases(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Databases databases = new Databases(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Databases databases = new Databases(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Databases databases = new Databases(client);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Client client = new Client(getApplicationContext())
|
||||
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("5df5acd0d48c2"); // Your project ID
|
||||
|
||||
Functions functions = new Functions(client);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user